@cruxjs/app 0.1.9 → 0.2.0
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/README.md +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
</div>
|
|
9
9
|
|
|
10
10
|
<div align="center">
|
|
11
|
-
<img src="https://img.shields.io/badge/v-0.
|
|
11
|
+
<img src="https://img.shields.io/badge/v-0.2.0-black"/>
|
|
12
12
|
<a href="https://github.com/cruxjs-org"><img src="https://img.shields.io/badge/🔥-@cruxjs-black"/></a>
|
|
13
13
|
<br>
|
|
14
14
|
<img src="https://img.shields.io/badge/coverage-~%25-brightgreen" alt="Test Coverage" />
|
package/dist/index.cjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
'use strict';var base=require('@cruxjs/base'),logger=require('@minejs/logger'),server=require('@minejs/server'),db=require('@minejs/db'),k=require('sass'),v=require('path');function _interopNamespace(e){if(e&&e.__esModule)return e;var n=Object.create(null);if(e){Object.keys(e).forEach(function(k){if(k!=='default'){var d=Object.getOwnPropertyDescriptor(e,k);Object.defineProperty(n,k,d.get?d:{enumerable:true,get:function(){return e[k]}});}})}n.default=e;return Object.freeze(n)}var k__namespace=/*#__PURE__*/_interopNamespace(k);var v__namespace=/*#__PURE__*/_interopNamespace(v);async function j(s,r){if(!s.client)return null;r.info("Building client...");try{let e=await Bun.build({entrypoints:[s.client.entry],outdir:s.client.output,target:s.client.target||"browser",minify:s.client.minify??!s.debug,sourcemap:s.client.sourcemap??s.debug?"inline":"none",external:s.client.external||[]});if(!e.success)throw new Error("Build failed");let t=e.outputs.map(i=>i.path);return await H(t,s,r),r.debug(`Client built \u2192 ${t.join(", ")}`),{success:!0,outputs:t}}catch(e){throw r.error("Failed to build client",e.message),e}}async function H(s,r,e){if(!r.client?.entry)return;let t=s.find(i=>i.endsWith(".js"));if(t){e.info("Optimizing bundle (removing unused icons)...");try{let n=await Bun.file(t).text(),u=[],
|
|
1
|
+
'use strict';var base=require('@cruxjs/base'),logger=require('@minejs/logger'),server=require('@minejs/server'),db=require('@minejs/db'),k=require('sass'),v=require('path');function _interopNamespace(e){if(e&&e.__esModule)return e;var n=Object.create(null);if(e){Object.keys(e).forEach(function(k){if(k!=='default'){var d=Object.getOwnPropertyDescriptor(e,k);Object.defineProperty(n,k,d.get?d:{enumerable:true,get:function(){return e[k]}});}})}n.default=e;return Object.freeze(n)}var k__namespace=/*#__PURE__*/_interopNamespace(k);var v__namespace=/*#__PURE__*/_interopNamespace(v);async function j(s,r){if(!s.client)return null;r.info("Building client...");try{let e=await Bun.build({entrypoints:[s.client.entry],outdir:s.client.output,target:s.client.target||"browser",minify:s.client.minify??!s.debug,sourcemap:s.client.sourcemap??s.debug?"inline":"none",external:s.client.external||[]});if(!e.success)throw new Error("Build failed");let t=e.outputs.map(i=>i.path);return await H(t,s,r),r.debug(`Client built \u2192 ${t.join(", ")}`),{success:!0,outputs:t}}catch(e){throw r.error("Failed to build client",e.message),e}}async function H(s,r,e){if(!r.client?.entry)return;let t=s.find(i=>i.endsWith(".js"));if(t){e.info("Optimizing bundle (removing unused icons)...");try{let n=await Bun.file(t).text(),u=[],p=new Set,o=0;for(;o<n.length;){let f=n[o];if(f==='"'||f==="'"){let y=f;for(o++;o<n.length&&!(n[o]===y&&n[o-1]!=="\\");)o++;}else if(f==="{"){let y=n.substring(o+1,o+100);if(/^\s*(?:["'][\w-]+["']|\w+)\s*:\s*\{\s*(?:category|viewBox|svg)\b/.test(y)){let a=o,l=1,d=o+1,b=!1,C="";for(;d<n.length;){let g=n[d];if(b)g===C&&n[d-1]!=="\\"&&(b=!1);else if(g==='"'||g==="'")b=!0,C=g;else if(g==="{")l++;else if(g==="}"&&(l--,l===0))break;d++;}let E=d+1,$=n.substring(a,E),R=[],x=/(?:["']([\w-]+)["']|(\w+))\s*:\s*\{/g,A;for(;(A=x.exec($))!==null;){let g=A[1]||A[2];R.push(g),p.add(g);}u.push({start:a,end:E,keys:R}),o=E-1;}}o++;}if(e.debug(`Found ${u.length} icon maps with ${p.size} total icons.`),p.size===0)return;let w=v__namespace.dirname(r.client.entry),c=new Bun.Glob("**/*.{ts,tsx,js,jsx}"),m=new Set;m.add("angle-down");for await(let f of c.scan({cwd:w})){let y=v__namespace.join(w,f),a=await Bun.file(y).text();for(let l of p)if(!m.has(l)){let d=l.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");new RegExp(`\\b${d}\\b`).test(a)&&m.add(l);}}e.debug(`Used icons: ${m.size}`);let h="",S=0;for(let f of u){h+=n.substring(S,f.start);let y=n.substring(f.start,f.end),a;try{a=new Function("return "+y)();}catch{e.warn("Failed to parse map, keeping as is"),h+=y,S=f.end;continue}let l=0;for(let d of f.keys)m.has(d)||(delete a[d],l++);l>0?h+=JSON.stringify(a):h+=y,S=f.end;}h+=n.substring(S),await Bun.write(t,h),e.info("Bundle optimized");}catch(i){e.warn("Failed to optimize bundle",i.message);}}}async function I(s,r){if(!s.style)return r.info("No style config provided, skipping style build"),null;r.info(`Building styles from: ${s.style.entry}...`);try{let e=s.style.output,t=e.lastIndexOf("/"),i=e.substring(0,t),n=e.substring(t+1),{mkdir:u}=await import('fs/promises');await u(i,{recursive:!0});let p=k__namespace.compile(s.style.entry,{style:s.style.minify?"compressed":"expanded",sourceMap:!!s.style.sourcemap});return await Bun.write(e,p.css),r.info(`Compiled SCSS to CSS \u2192 ${n}`),r.debug(`Styles built \u2192 ${e}`),{success:!0,output:e}}catch(e){return r.error("Failed to build styles",e.message),{success:false,output:s.style?.output||"unknown"}}}async function z(s,r,e){let t=new Map;if(!s.database&&r.length===0)return e.info("No database config, skipping setup"),t;let i=s.database?Array.isArray(s.database)?s.database:[s.database]:[];e.info("Setting up databases...");for(let n of i){let u=n.name||"default",p=new db.DB(n.connection);if(n.schema)try{let o=await import(n.schema),w=o.default||o.schema;if(Array.isArray(w))for(let c of w)p.defineSchema(c);}catch(o){throw e.error(`Failed to load schema: ${n.schema}`,o.message),o}for(let o of r)p.defineSchema(o);t.set(u,p),e.debug(`Database '${u}' ready`);}if(t.size===0&&r.length>0){let n=new db.DB(":memory:");for(let u of r)n.defineSchema(u);t.set("default",n),e.debug("Database 'default' ready (in-memory for plugins)");}return t}async function P(s,r){if(!s.api)return [];r.info(`Loading routes from ${s.api.directory}...`);let e=[];try{let t=await T(s.api.directory);for(let i of t)try{let n=await import(i),u=n.default||n.routes;Array.isArray(u)&&e.push(...u);}catch(n){r.error(`Failed to load routes from ${i}`,n.message);}return r.debug(`Routes loaded \u2192 ${e.length} routes`),e}catch(t){return r.error("Failed to scan route directory",t.message),[]}}async function T(s){let r=[];try{let e=await Array.fromAsync(new Bun.Glob("**/*.{ts,js}").scan(s));for(let t of e)r.push(`${s}/${t}`);}catch{}return r}function Q(s,r){let e=r?.onConfig?r.onConfig(s):s,t=new logger.Logger(e.debug?"debug":"info",true,"CruxJS"),i=new base.PluginRegistry(t),n=new base.ResourceMerger(t),u=new Map,p=new Map,o=null,w=null,c={config:e,databases:u,plugins:[]},m={config:e,server:null,databases:u,plugins:[],middlewares:p,start:async()=>{},stop:async()=>{},restart:async()=>{},getContext:()=>c,getMiddleware:a=>p.get(a)};async function h(){if(!e.plugins||e.plugins.length===0){t.info("No plugins to register");return}t.debug("REGISTER");for(let l of e.plugins)await i.register(l,m);c.plugins=i.getAll(),i.collectMiddlewares().forEach((l,d)=>p.set(d,l));}async function S(){t.debug("AWAKE");try{w=await j(e,t),c.clientBuild=w;let a=await I(e,t);c.styleBuild=a;let l=i.collectSchemas(),d=await z(e,l,t);u.clear(),d.forEach((b,C)=>u.set(C,b)),await i.callHook("onAwake",c),await r?.onAwake?.(c);}catch(a){throw await r?.onError?.(c,"AWAKE",a),a}}async function f(){t.debug("START");try{let a=await P(e,t),l=i.collectRoutes(),b=[...e.routes||[],...a],C=n.mergeRoutes(b,l),E=e.static?Array.isArray(e.static)?e.static:[e.static]:[],$=i.collectStatic(),R=n.mergeStatic(E,$);t.info("Creating server...");let x;for(let A of c.plugins){let g=A;if(g.__spaErrorHandler){x=g.__spaErrorHandler,console.log(`[CruxJS] \u2713 Error handler collected from: ${A.name}`);break}}o=await server.server({port:e.server?.port||3e3,hostname:e.server?.host||"localhost",logging:e.server?.logging,routes:C,static:R.length>0?R:void 0,security:e.security,middlewares:e.middlewares,i18n:e.i18n?{defaultLanguage:e.i18n.defaultLanguage,supportedLanguages:e.i18n.supportedLanguages,basePath:e.i18n.basePath,fileExtension:e.i18n.fileExtension||"json"}:void 0,onError:x}),u.forEach((A,g)=>{o.db.set(g,A);}),c.server=o,m.server=o,t.debug(`Server created \u2192 ${e.server?.host||"localhost"}:${e.server?.port||3e3}`),await i.callHook("onStart",c),await r?.onStart?.(c);}catch(a){throw await r?.onError?.(c,"START",a),a}}async function y(){t.debug("READY");try{await o.start(),t.debug(`Server running on http://${e.server?.host||"localhost"}:${e.server?.port||3e3}`),await i.callHook("onReady",c),await r?.onReady?.(c);}catch(a){throw await r?.onError?.(c,"READY",a),a}}return {config:e,server:o,databases:u,plugins:c.plugins,middlewares:p,async start(){await h(),await S(),await f(),await y();},async stop(){t.info("Stopping server...");try{o&&await o.stop(),u.forEach((a,l)=>{a.close(),t.info(`Database '${l}' closed`);}),await i.callHook("onShutdown",c),await r?.onFinish?.(c),t.debug("Server stopped");}catch(a){throw t.error("Failed to stop server",a.message),a}},async restart(){await this.stop(),await this.start();},getContext(){return c},getMiddleware(a){return p.get(a)}}}
|
|
2
2
|
exports.createApp=Q;exports.optimizeBundle=H;Object.keys(base).forEach(function(k){if(k!=='default'&&!Object.prototype.hasOwnProperty.call(exports,k))Object.defineProperty(exports,k,{enumerable:true,get:function(){return base[k]}})});//# sourceMappingURL=index.cjs.map
|
|
3
3
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":["buildClient","config","logger","result","outputs","o","optimizeBundle","err","bundlePath","p","content","maps","allKeys","i","char","quote","chunk","start","braceCount","j","inStr","strChar","c","end","mapContent","keys","keyRegex","match","key","sourceDir","v","glob","usedIcons","file","filePath","fileContent","icon","escapedIcon","newContent","lastIndex","map","obj","removed","buildStyles","outputPath","lastSlashIndex","outputDir","outputFilename","mkdir","compileResult","k","setupDatabases","additionalSchemas","databases","dbConfigs","dbConfig","name","db","DB","schemaModule","schema","table","loadRoutes","routes","files","scanDirectory","module","exported","dir","entries","entry","createApp","userConfig","hooks","Logger","pluginRegistry","PluginRegistry","resourceMerger","ResourceMerger","middlewares","serverInstance","clientBuild","ctx","partialApp","phaseRegister","plugin","mw","phaseAwake","styleBuild","pluginSchemas","dbs","phaseStart","userApiRoutes","pluginRoutes","allUserRoutes","mergedRoutes","userStatic","pluginStatic","mergedStatic","errorHandler","pluginWithHandler","createServer","phaseReady"],"mappings":"skBA0CI,eAAeA,CAAAA,CAAYC,CAAAA,CAAmBC,EAAgB,CAC1D,GAAI,CAACD,CAAAA,CAAO,MAAA,CAAQ,OAAO,IAAA,CAE3BC,CAAAA,CAAO,KAAK,oBAAoB,CAAA,CAEhC,GAAI,CACA,IAAMC,EAAS,MAAM,GAAA,CAAI,MAAM,CAC3B,WAAA,CAAa,CAACF,CAAAA,CAAO,MAAA,CAAO,KAAK,CAAA,CACjC,MAAA,CAAQA,EAAO,MAAA,CAAO,MAAA,CACtB,OAAQA,CAAAA,CAAO,MAAA,CAAO,QAAU,SAAA,CAChC,MAAA,CAAQA,EAAO,MAAA,CAAO,MAAA,EAAU,CAACA,CAAAA,CAAO,KAAA,CACxC,UAAWA,CAAAA,CAAO,MAAA,CAAO,WAAaA,CAAAA,CAAO,KAAA,CAAQ,SAAW,MAAA,CAChE,QAAA,CAAUA,EAAO,MAAA,CAAO,QAAA,EAAY,EACxC,CAAC,EAED,GAAI,CAACE,EAAO,OAAA,CAAS,MAAM,IAAI,KAAA,CAAM,cAAc,CAAA,CAEnD,IAAMC,CAAAA,CAAUD,CAAAA,CAAO,QAAQ,GAAA,CAAIE,CAAAA,EAAKA,EAAE,IAAI,CAAA,CAE9C,aAAMC,CAAAA,CAAeF,CAAAA,CAASH,EAAQC,CAAM,CAAA,CAE5CA,EAAO,KAAA,CAAM,CAAA,oBAAA,EAAkBE,EAAQ,IAAA,CAAK,IAAI,CAAC,CAAA,CAAE,CAAA,CAE5C,CAAE,OAAA,CAAS,CAAA,CAAA,CAAM,QAAAA,CAAQ,CACpC,OAASG,CAAAA,CAAK,CACV,MAAAL,CAAAA,CAAO,KAAA,CAAM,yBAA2BK,CAAAA,CAAc,OAAO,EACvDA,CACV,CACJ,CAEA,eAAsBD,CAAAA,CAAeF,CAAAA,CAAmBH,CAAAA,CAAmBC,CAAAA,CAAgB,CACvF,GAAI,CAACD,CAAAA,CAAO,QAAQ,KAAA,CAAO,OAE3B,IAAMO,CAAAA,CAAaJ,CAAAA,CAAQ,KAAKK,CAAAA,EAAKA,CAAAA,CAAE,SAAS,KAAK,CAAC,EACtD,GAAKD,CAAAA,CAEL,CAAAN,CAAAA,CAAO,IAAA,CAAK,8CAA8C,CAAA,CAE1D,GAAI,CAEA,IAAMQ,CAAAA,CAAU,MADH,GAAA,CAAI,IAAA,CAAKF,CAAU,CAAA,CACL,IAAA,GAGrBG,CAAAA,CAAuD,GACvDC,CAAAA,CAAU,IAAI,IAEhBC,CAAAA,CAAI,CAAA,CACR,KAAOA,CAAAA,CAAIH,CAAAA,CAAQ,MAAA,EAAQ,CACvB,IAAMI,CAAAA,CAAOJ,EAAQG,CAAC,CAAA,CAEtB,GAAIC,CAAAA,GAAS,GAAA,EAAOA,IAAS,GAAA,CAAK,CAC9B,IAAMC,CAAAA,CAAQD,CAAAA,CAEd,IADAD,CAAAA,EAAAA,CACOA,CAAAA,CAAIH,EAAQ,MAAA,EACX,EAAAA,EAAQG,CAAC,CAAA,GAAME,GAASL,CAAAA,CAAQG,CAAAA,CAAE,CAAC,CAAA,GAAM,IAAA,CAAA,EAC7CA,IAER,CAAA,KAAA,GAAWC,CAAAA,GAAS,IAAK,CAIrB,IAAME,EAAQN,CAAAA,CAAQ,SAAA,CAAUG,EAAI,CAAA,CAAGA,CAAAA,CAAI,GAAG,CAAA,CAC9C,GAAI,mEAAmE,IAAA,CAAKG,CAAK,EAAG,CAEhF,IAAMC,EAAQJ,CAAAA,CACVK,CAAAA,CAAa,EACbC,CAAAA,CAAIN,CAAAA,CAAI,EACRO,CAAAA,CAAQ,CAAA,CAAA,CACRC,EAAU,EAAA,CAEd,KAAOF,EAAIT,CAAAA,CAAQ,MAAA,EAAQ,CACvB,IAAMY,CAAAA,CAAIZ,EAAQS,CAAC,CAAA,CACnB,GAAIC,CAAAA,CACIE,CAAAA,GAAMD,GAAWX,CAAAA,CAAQS,CAAAA,CAAE,CAAC,CAAA,GAAM,IAAA,GAAMC,EAAQ,CAAA,CAAA,CAAA,CAAA,KAAA,GAEhDE,CAAAA,GAAM,KAAOA,CAAAA,GAAM,GAAA,CACnBF,EAAQ,CAAA,CAAA,CACRC,CAAAA,CAAUC,UACHA,CAAAA,GAAM,GAAA,CACbJ,YACOI,CAAAA,GAAM,GAAA,GACbJ,CAAAA,EAAAA,CACIA,CAAAA,GAAe,CAAA,CAAA,CAAG,MAG9BC,IACJ,CAEA,IAAMI,EAAMJ,CAAAA,CAAI,CAAA,CACVK,EAAad,CAAAA,CAAQ,SAAA,CAAUO,EAAOM,CAAG,CAAA,CAGzCE,EAAiB,EAAC,CAClBC,EAAW,sCAAA,CACbC,CAAAA,CACJ,MAAQA,CAAAA,CAAQD,CAAAA,CAAS,KAAKF,CAAU,CAAA,IAAO,MAAM,CACjD,IAAMI,EAAMD,CAAAA,CAAM,CAAC,GAAKA,CAAAA,CAAM,CAAC,EAC/BF,CAAAA,CAAK,IAAA,CAAKG,CAAG,CAAA,CACbhB,CAAAA,CAAQ,IAAIgB,CAAG,EACnB,CAEAjB,CAAAA,CAAK,IAAA,CAAK,CAAC,KAAA,CAAAM,CAAAA,CAAO,GAAA,CAAAM,EAAK,IAAA,CAAAE,CAAI,CAAC,CAAA,CAG5BZ,CAAAA,CAAIU,EAAM,EACd,CACJ,CACAV,CAAAA,GACJ,CAIA,GAFAX,CAAAA,CAAO,KAAA,CAAM,SAASS,CAAAA,CAAK,MAAM,mBAAmBC,CAAAA,CAAQ,IAAI,eAAe,CAAA,CAE3EA,CAAAA,CAAQ,OAAS,CAAA,CAAG,OAGxB,IAAMiB,CAAAA,CAAiBC,YAAA,CAAA,OAAA,CAAQ7B,EAAO,MAAA,CAAO,KAAK,EAC5C8B,CAAAA,CAAO,IAAI,IAAI,IAAA,CAAK,sBAAsB,EAC1CC,CAAAA,CAAY,IAAI,IAEtB,UAAA,IAAiBC,CAAAA,IAAQF,CAAAA,CAAK,IAAA,CAAK,CAAE,GAAA,CAAKF,CAAU,CAAC,CAAA,CAAG,CACpD,IAAMK,CAAAA,CAAgBJ,kBAAKD,CAAAA,CAAWI,CAAI,EACpCE,CAAAA,CAAc,MAAM,IAAI,IAAA,CAAKD,CAAQ,EAAE,IAAA,EAAK,CAElD,QAAWE,CAAAA,IAAQxB,CAAAA,CACf,GAAI,CAACoB,CAAAA,CAAU,IAAII,CAAI,CAAA,CAAG,CAGtB,IAAMC,CAAAA,CAAcD,EAAK,OAAA,CAAQ,qBAAA,CAAuB,MAAM,CAAA,CAChD,IAAI,OAAO,CAAA,GAAA,EAAMC,CAAW,KAAK,CAAA,CAErC,IAAA,CAAKF,CAAW,CAAA,EACtBH,CAAAA,CAAU,IAAII,CAAI,EAE1B,CAER,CAEAlC,CAAAA,CAAO,MAAM,CAAA,YAAA,EAAe8B,CAAAA,CAAU,IAAI,CAAA,CAAE,CAAA,CAG5C,IAAIM,CAAAA,CAAa,EAAA,CACbC,EAAY,CAAA,CAEhB,IAAA,IAAWC,KAAO7B,CAAAA,CAAM,CAEpB2B,GAAc5B,CAAAA,CAAQ,SAAA,CAAU6B,EAAWC,CAAAA,CAAI,KAAK,EAGpD,IAAMhB,CAAAA,CAAad,EAAQ,SAAA,CAAU8B,CAAAA,CAAI,MAAOA,CAAAA,CAAI,GAAG,EAEnDC,CAAAA,CACJ,GAAI,CAECA,CAAAA,CADgB,IAAI,SAAS,SAAA,CAAYjB,CAAU,IAExD,CAAA,KAAY,CACRtB,CAAAA,CAAO,IAAA,CAAK,oCAAoC,EAChDoC,CAAAA,EAAcd,CAAAA,CACde,EAAYC,CAAAA,CAAI,GAAA,CAChB,QACJ,CAEA,IAAIE,EAAU,CAAA,CACd,IAAA,IAAWd,KAAOY,CAAAA,CAAI,IAAA,CACbR,EAAU,GAAA,CAAIJ,CAAG,IAClB,OAAOa,CAAAA,CAAIb,CAAG,CAAA,CACdc,CAAAA,EAAAA,CAAAA,CAIJA,EAAU,CAAA,CACVJ,CAAAA,EAAc,KAAK,SAAA,CAAUG,CAAG,EAEhCH,CAAAA,EAAcd,CAAAA,CAGlBe,EAAYC,CAAAA,CAAI,IACpB,CAEAF,CAAAA,EAAc5B,CAAAA,CAAQ,UAAU6B,CAAS,CAAA,CAEzC,MAAM,GAAA,CAAI,KAAA,CAAM/B,CAAAA,CAAY8B,CAAU,CAAA,CACrCpC,CAAAA,CAAO,KAAK,kBAAkB,EAEnC,OAASK,CAAAA,CAAK,CACTL,EAAO,IAAA,CAAK,2BAAA,CAA8BK,EAAc,OAAO,EACpE,EACJ,CAsBA,eAAeoC,EAAY1C,CAAAA,CAAmBC,CAAAA,CAAgB,CAC1D,GAAI,CAACD,EAAO,KAAA,CACR,OAAAC,EAAO,IAAA,CAAK,gDAAgD,EACrD,IAAA,CAGXA,CAAAA,CAAO,KAAK,CAAA,sBAAA,EAAyBD,CAAAA,CAAO,MAAM,KAAK,CAAA,GAAA,CAAK,EAE5D,GAAI,CAEA,IAAM2C,CAAAA,CAAa3C,CAAAA,CAAO,MAAM,MAAA,CAC1B4C,CAAAA,CAAiBD,CAAAA,CAAW,WAAA,CAAY,GAAG,CAAA,CAC3CE,EAAYF,CAAAA,CAAW,SAAA,CAAU,EAAGC,CAAc,CAAA,CAClDE,EAAiBH,CAAAA,CAAW,SAAA,CAAUC,EAAiB,CAAC,CAAA,CAGxD,CAAE,KAAA,CAAAG,CAAM,EAAI,MAAM,OAAO,aAAa,CAAA,CAC5C,MAAMA,EAAMF,CAAAA,CAAW,CAAE,UAAW,CAAA,CAAK,CAAC,EAG1C,IAAMG,CAAAA,CAAqBC,qBAAQjD,CAAAA,CAAO,KAAA,CAAM,MAAO,CACnD,KAAA,CAAOA,EAAO,KAAA,CAAM,MAAA,CAAS,aAAe,UAAA,CAC5C,SAAA,CAAW,EAAAA,CAAAA,CAAO,KAAA,CAAM,SAC5B,CAAC,CAAA,CAGD,OAAA,MAAM,IAAI,KAAA,CAAM2C,CAAAA,CAAYK,EAAc,GAAG,CAAA,CAE7C/C,EAAO,IAAA,CAAK,CAAA,4BAAA,EAA0B6C,CAAc,CAAA,CAAE,CAAA,CACtD7C,EAAO,KAAA,CAAM,CAAA,oBAAA,EAAkB0C,CAAU,CAAA,CAAE,CAAA,CAEpC,CAAE,OAAA,CAAS,CAAA,CAAA,CAAM,OAAQA,CAAW,CAC/C,OAASrC,CAAAA,CAAK,CACV,OAAAL,CAAAA,CAAO,KAAA,CAAM,yBAA2BK,CAAAA,CAAc,OAAO,EACtD,CAAE,OAAA,CAAS,MAAO,MAAA,CAAQN,CAAAA,CAAO,OAAO,MAAA,EAAU,SAAU,CACvE,CACJ,CAsBA,eAAekD,CAAAA,CACXlD,CAAAA,CACAmD,CAAAA,CACAlD,EACF,CACE,IAAMmD,EAAY,IAAI,GAAA,CAEtB,GAAI,CAACpD,CAAAA,CAAO,UAAYmD,CAAAA,CAAkB,MAAA,GAAW,EACjD,OAAAlD,CAAAA,CAAO,KAAK,oCAAoC,CAAA,CACzCmD,EAGX,IAAMC,CAAAA,CAAYrD,EAAO,QAAA,CACnB,KAAA,CAAM,QAAQA,CAAAA,CAAO,QAAQ,EACzBA,CAAAA,CAAO,QAAA,CACP,CAACA,CAAAA,CAAO,QAAQ,EACpB,EAAC,CAEPC,EAAO,IAAA,CAAK,yBAAyB,EAErC,IAAA,IAAWqD,CAAAA,IAAYD,EAAW,CAC9B,IAAME,CAAAA,CAAOD,CAAAA,CAAS,IAAA,EAAQ,SAAA,CACxBE,EAAK,IAAIC,KAAAA,CAAGH,EAAS,UAAU,CAAA,CAGrC,GAAIA,CAAAA,CAAS,MAAA,CACT,GAAI,CACA,IAAMI,EAAe,MAAM,OAAOJ,EAAS,MAAA,CAAA,CACrCK,CAAAA,CAASD,EAAa,OAAA,EAAWA,CAAAA,CAAa,OAEpD,GAAI,KAAA,CAAM,QAAQC,CAAM,CAAA,CACpB,QAAWC,CAAAA,IAASD,CAAAA,CAChBH,EAAG,YAAA,CAAaI,CAAK,EAGjC,CAAA,MAAStD,CAAAA,CAAK,CACV,MAAAL,CAAAA,CAAO,MAAM,CAAA,uBAAA,EAA0BqD,CAAAA,CAAS,MAAM,CAAA,CAAA,CAAKhD,CAAAA,CAAc,OAAO,CAAA,CAC1EA,CACV,CAIJ,QAAWqD,CAAAA,IAAUR,CAAAA,CACjBK,EAAG,YAAA,CAAaG,CAAM,EAG1BP,CAAAA,CAAU,GAAA,CAAIG,EAAMC,CAAE,CAAA,CACtBvD,EAAO,KAAA,CAAM,CAAA,UAAA,EAAasD,CAAI,CAAA,OAAA,CAAS,EAC3C,CAGA,GAAIH,CAAAA,CAAU,OAAS,CAAA,EAAKD,CAAAA,CAAkB,OAAS,CAAA,CAAG,CACtD,IAAMK,CAAAA,CAAK,IAAIC,MAAG,UAAU,CAAA,CAE5B,QAAWE,CAAAA,IAAUR,CAAAA,CACjBK,EAAG,YAAA,CAAaG,CAAM,EAG1BP,CAAAA,CAAU,GAAA,CAAI,UAAWI,CAAE,CAAA,CAC3BvD,EAAO,KAAA,CAAM,kDAAkD,EACnE,CAEA,OAAOmD,CACX,CA+DA,eAAeS,EAAW7D,CAAAA,CAAmBC,CAAAA,CAA4C,CACrF,GAAI,CAACD,EAAO,GAAA,CAAK,OAAO,EAAC,CAEzBC,CAAAA,CAAO,KAAK,CAAA,oBAAA,EAAuBD,CAAAA,CAAO,IAAI,SAAS,CAAA,GAAA,CAAK,EAE5D,IAAM8D,CAAAA,CAA4B,EAAC,CAEnC,GAAI,CACA,IAAMC,CAAAA,CAAQ,MAAMC,CAAAA,CAAchE,CAAAA,CAAO,IAAI,SAAS,CAAA,CAEtD,QAAWgC,CAAAA,IAAQ+B,CAAAA,CACf,GAAI,CACA,IAAME,CAAAA,CAAS,MAAM,OAAOjC,CAAAA,CAAAA,CACtBkC,EAAWD,CAAAA,CAAO,OAAA,EAAWA,EAAO,MAAA,CAEtC,KAAA,CAAM,QAAQC,CAAQ,CAAA,EACtBJ,EAAO,IAAA,CAAK,GAAGI,CAAQ,EAE/B,CAAA,MAAS5D,EAAK,CACVL,CAAAA,CAAO,MAAM,CAAA,2BAAA,EAA8B+B,CAAI,GAAK1B,CAAAA,CAAc,OAAO,EAC7E,CAGJ,OAAAL,EAAO,KAAA,CAAM,CAAA,qBAAA,EAAmB6D,EAAO,MAAM,CAAA,OAAA,CAAS,EAC/CA,CACX,CAAA,MAASxD,EAAK,CACV,OAAAL,EAAO,KAAA,CAAM,gCAAA,CAAmCK,EAAc,OAAO,CAAA,CAC9D,EACX,CACJ,CAeA,eAAe0D,CAAAA,CAAcG,CAAAA,CAAgC,CACzD,IAAMJ,CAAAA,CAAkB,EAAC,CAEzB,GAAI,CACA,IAAMK,CAAAA,CAAU,MAAM,KAAA,CAAM,SAAA,CACxB,IAAI,GAAA,CAAI,IAAA,CAAK,cAAc,CAAA,CAAE,IAAA,CAAKD,CAAG,CACzC,CAAA,CAEA,QAAWE,CAAAA,IAASD,CAAAA,CAChBL,EAAM,IAAA,CAAK,CAAA,EAAGI,CAAG,CAAA,CAAA,EAAIE,CAAK,EAAE,EAEpC,CAAA,KAAQ,CAER,CAEA,OAAON,CACX,CAkDO,SAASO,EACZC,CAAAA,CACAC,CAAAA,CACW,CAEX,IAAMxE,CAAAA,CAAoBwE,CAAAA,EAAO,SAC3BA,CAAAA,CAAM,QAAA,CAASD,CAAU,CAAA,CACzBA,CAAAA,CAEAtE,EAAS,IAAIwE,aAAAA,CAAOzE,EAAO,KAAA,CAAQ,OAAA,CAAU,OAAQ,IAAA,CAAM,QAAQ,EACnE0E,CAAAA,CAAiB,IAAIC,oBAAe1E,CAAM,CAAA,CAC1C2E,EAAiB,IAAIC,mBAAAA,CAAe5E,CAAM,CAAA,CAE1CmD,CAAAA,CAAY,IAAI,GAAA,CAChB0B,CAAAA,CAAc,IAAI,GAAA,CACpBC,CAAAA,CAAsB,KACtBC,CAAAA,CAAmB,IAAA,CAEjBC,EAAwB,CAC1B,MAAA,CAAAjF,EACA,SAAA,CAAAoD,CAAAA,CACA,QAAS,EACb,CAAA,CAGM8B,CAAAA,CAA0B,CAC5B,MAAA,CAAAlF,EACA,MAAA,CAAQ,IAAA,CACR,UAAAoD,CAAAA,CACA,OAAA,CAAS,EAAC,CACV,WAAA,CAAA0B,EACA,KAAA,CAAO,SAAY,CAAE,CAAA,CACrB,IAAA,CAAM,SAAY,CAAE,CAAA,CACpB,QAAS,SAAY,CAAE,EACvB,UAAA,CAAY,IAAMG,EAClB,aAAA,CAAgB1B,CAAAA,EAAiBuB,EAAY,GAAA,CAAIvB,CAAI,CACzD,CAAA,CAMA,eAAe4B,GAAgB,CAC3B,GAAI,CAACnF,CAAAA,CAAO,OAAA,EAAWA,EAAO,OAAA,CAAQ,MAAA,GAAW,EAAG,CAChDC,CAAAA,CAAO,IAAA,CAAK,wBAAwB,CAAA,CACpC,MACJ,CAEAA,CAAAA,CAAO,KAAA,CAAM,UAAU,CAAA,CAEvB,IAAA,IAAWmF,KAAUpF,CAAAA,CAAO,OAAA,CACxB,MAAM0E,CAAAA,CAAe,QAAA,CAASU,EAAQF,CAAU,CAAA,CAGpDD,EAAI,OAAA,CAAUP,CAAAA,CAAe,QAAO,CAGVA,CAAAA,CAAe,oBAAmB,CAC1C,OAAA,CAAQ,CAACW,CAAAA,CAAI9B,CAAAA,GAASuB,EAAY,GAAA,CAAIvB,CAAAA,CAAM8B,CAAE,CAAC,EACrE,CAMA,eAAeC,CAAAA,EAAa,CACxBrF,CAAAA,CAAO,KAAA,CAAM,OAAO,CAAA,CAEpB,GAAI,CAKA+E,CAAAA,CAAc,MAAMjF,CAAAA,CAAYC,CAAAA,CAAQC,CAAM,CAAA,CAC9CgF,EAAI,WAAA,CAAcD,CAAAA,CAGlB,IAAMO,CAAAA,CAAa,MAAM7C,EAAY1C,CAAAA,CAAQC,CAAM,EACnDgF,CAAAA,CAAI,UAAA,CAAaM,EAGjB,IAAMC,CAAAA,CAAgBd,EAAe,cAAA,EAAe,CAG9Ce,EAAM,MAAMvC,CAAAA,CAAelD,EAAQwF,CAAAA,CAAevF,CAAM,EAC9DmD,CAAAA,CAAU,KAAA,GACVqC,CAAAA,CAAI,OAAA,CAAQ,CAACjC,CAAAA,CAAID,CAAAA,GAASH,EAAU,GAAA,CAAIG,CAAAA,CAAMC,CAAE,CAAC,CAAA,CAGjD,MAAMkB,CAAAA,CAAe,QAAA,CAAS,UAAWO,CAAG,CAAA,CAG5C,MAAMT,CAAAA,EAAO,OAAA,GAAUS,CAAG,EAC9B,CAAA,MAAS3E,CAAAA,CAAK,CACV,MAAA,MAAMkE,CAAAA,EAAO,UAAUS,CAAAA,CAAK,OAAA,CAAS3E,CAAY,CAAA,CAC3CA,CACV,CACJ,CAMA,eAAeoF,GAAa,CACxBzF,CAAAA,CAAO,MAAM,OAAO,CAAA,CAEpB,GAAI,CAEA,IAAM0F,EAAgB,MAAM9B,CAAAA,CAAW7D,EAAQC,CAAM,CAAA,CAG/C2F,EAAelB,CAAAA,CAAe,aAAA,GAI9BmB,CAAAA,CAAgB,CAAC,GADJ7F,CAAAA,CAAO,MAAA,EAAU,EAAC,CACC,GAAG2F,CAAa,CAAA,CAChDG,CAAAA,CAAelB,EAAe,WAAA,CAAYiB,CAAAA,CAAeD,CAAY,CAAA,CAGrEG,CAAAA,CAAa/F,EAAO,MAAA,CACpB,KAAA,CAAM,QAAQA,CAAAA,CAAO,MAAM,EACvBA,CAAAA,CAAO,MAAA,CACP,CAACA,CAAAA,CAAO,MAAM,EAClB,EAAC,CACDgG,EAAetB,CAAAA,CAAe,aAAA,GAC9BuB,CAAAA,CAAerB,CAAAA,CAAe,YAAYmB,CAAAA,CAAYC,CAAY,EAExE/F,CAAAA,CAAO,IAAA,CAAK,oBAAoB,CAAA,CAGhC,IAAIiG,EACJ,IAAA,IAAWd,CAAAA,IAAUH,EAAI,OAAA,CAAS,CAC9B,IAAMkB,CAAAA,CAAoBf,CAAAA,CAC1B,GAAIe,CAAAA,CAAkB,iBAAA,CAAmB,CACrCD,CAAAA,CAAeC,CAAAA,CAAkB,iBAAA,CACjC,QAAQ,GAAA,CAAI,CAAA,8CAAA,EAA4Cf,EAAO,IAAI,CAAA,CAAE,EACrE,KACJ,CACJ,CAGAL,CAAAA,CAAiB,MAAMqB,cAAa,CAChC,IAAA,CAAMpG,EAAO,MAAA,EAAQ,IAAA,EAAQ,IAC7B,QAAA,CAAUA,CAAAA,CAAO,QAAQ,IAAA,EAAQ,WAAA,CACjC,QAASA,CAAAA,CAAO,MAAA,EAAQ,QACxB,MAAA,CAAQ8F,CAAAA,CACR,OAAQG,CAAAA,CAAa,MAAA,CAAS,EAAIA,CAAAA,CAAe,KAAA,CAAA,CACjD,SAAUjG,CAAAA,CAAO,QAAA,CACjB,YAAaA,CAAAA,CAAO,WAAA,CACpB,KAAMA,CAAAA,CAAO,IAAA,CACP,CACE,eAAA,CAAiBA,CAAAA,CAAO,IAAA,CAAK,gBAC7B,kBAAA,CAAoBA,CAAAA,CAAO,KAAK,kBAAA,CAChC,QAAA,CAAUA,EAAO,IAAA,CAAK,QAAA,CACtB,cAAeA,CAAAA,CAAO,IAAA,CAAK,eAAiB,MAChD,CAAA,CACE,OACN,OAAA,CAASkG,CACb,CAAC,CAAA,CAGD9C,CAAAA,CAAU,QAAQ,CAACI,CAAAA,CAAID,IAAS,CAC5BwB,CAAAA,CAAe,GAAG,GAAA,CAAIxB,CAAAA,CAAMC,CAAE,EAClC,CAAC,EAEDyB,CAAAA,CAAI,MAAA,CAASF,EACbG,CAAAA,CAAW,MAAA,CAASH,EAEpB9E,CAAAA,CAAO,KAAA,CACH,yBAAoBD,CAAAA,CAAO,MAAA,EAAQ,IAAA,EAAQ,WAAW,CAAA,CAAA,EAAIA,CAAAA,CAAO,QAAQ,IAAA,EAAQ,GAAI,EACzF,CAAA,CAGA,MAAM0E,EAAe,QAAA,CAAS,SAAA,CAAWO,CAAG,CAAA,CAG5C,MAAMT,GAAO,OAAA,GAAUS,CAAG,EAC9B,CAAA,MAAS3E,CAAAA,CAAK,CACV,MAAA,MAAMkE,CAAAA,EAAO,UAAUS,CAAAA,CAAK,OAAA,CAAS3E,CAAY,CAAA,CAC3CA,CACV,CACJ,CAMA,eAAe+F,GAAa,CACxBpG,CAAAA,CAAO,MAAM,OAAO,CAAA,CAEpB,GAAI,CACA,MAAM8E,EAAe,KAAA,EAAM,CAE3B9E,EAAO,KAAA,CACH,CAAA,yBAAA,EAA4BD,CAAAA,CAAO,MAAA,EAAQ,IAAA,EAAQ,WAAW,IAAIA,CAAAA,CAAO,MAAA,EAAQ,MAAQ,GAAI,CAAA,CACjG,EAGA,MAAM0E,CAAAA,CAAe,SAAS,SAAA,CAAWO,CAAG,EAG5C,MAAMT,CAAAA,EAAO,UAAUS,CAAG,EAC9B,OAAS3E,CAAAA,CAAK,CACV,YAAMkE,CAAAA,EAAO,OAAA,GAAUS,EAAK,OAAA,CAAS3E,CAAY,EAC3CA,CACV,CACJ,CAMA,OAAO,CACH,OAAAN,CAAAA,CACA,MAAA,CAAQ+E,EACR,SAAA,CAAA3B,CAAAA,CACA,QAAS6B,CAAAA,CAAI,OAAA,CACb,YAAAH,CAAAA,CAEA,MAAM,KAAA,EAAQ,CACV,MAAMK,CAAAA,GACN,MAAMG,CAAAA,GACN,MAAMI,CAAAA,GACN,MAAMW,CAAAA,GACV,CAAA,CAEA,MAAM,MAAO,CACTpG,CAAAA,CAAO,KAAK,oBAAoB,CAAA,CAEhC,GAAI,CACI8E,CAAAA,EACA,MAAMA,CAAAA,CAAe,IAAA,GAGzB3B,CAAAA,CAAU,OAAA,CAAQ,CAACI,CAAAA,CAAID,CAAAA,GAAS,CAC5BC,CAAAA,CAAG,KAAA,GACHvD,CAAAA,CAAO,IAAA,CAAK,aAAasD,CAAI,CAAA,QAAA,CAAU,EAC3C,CAAC,CAAA,CAGD,MAAMmB,CAAAA,CAAe,QAAA,CAAS,YAAA,CAAcO,CAAG,CAAA,CAE/C,MAAMT,GAAO,QAAA,GAAWS,CAAG,EAE3BhF,CAAAA,CAAO,KAAA,CAAM,gBAAgB,EACjC,CAAA,MAASK,EAAK,CACV,MAAAL,EAAO,KAAA,CAAM,uBAAA,CAA0BK,EAAc,OAAO,CAAA,CACtDA,CACV,CACJ,CAAA,CAEA,MAAM,OAAA,EAAU,CACZ,MAAM,IAAA,CAAK,IAAA,GACX,MAAM,IAAA,CAAK,QACf,CAAA,CAEA,YAAa,CACT,OAAO2E,CACX,CAAA,CAEA,aAAA,CAAc1B,EAAc,CACxB,OAAOuB,EAAY,GAAA,CAAIvB,CAAI,CAC/B,CACJ,CACJ","file":"index.cjs","sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\r\n// src/index.ts\r\n//\r\n// Made with ❤️ by Maysara.\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ PACK ════════════════════════════════════════╗\r\n\r\n import { AppConfig, LifecycleContext, LifecycleHooks, AppInstance, RouteDefinition, AppMiddleware, PluginRegistry, ResourceMerger } from '@cruxjs/base';\r\n import { Logger } from '@minejs/logger';\r\n import { server as createServer } from '@minejs/server';\r\n import { DB } from '@minejs/db';\r\n import type { TableSchema } from '@minejs/db';\r\n import * as sass from 'sass';\r\n import * as path from 'path';\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ CORE ════════════════════════════════════════╗\r\n\r\n /**\r\n * Builds the client bundle using Bun's bundler\r\n *\r\n * Browser.tsx is a template file that:\r\n * - Imports user's client config from ./config.ts\r\n * - Reads i18n config from HTML meta tag (injected by server)\r\n * - Bootstraps ClientManager automatically via signal\r\n *\r\n * @param {AppConfig} config - The application configuration containing client build settings\r\n * @param {Logger} logger - Logger instance for logging build progress and errors\r\n * @returns {Promise<{success: boolean, outputs: string[]} | null>} Build result with output paths, or null if no client config\r\n * @throws {Error} If the build process fails\r\n *\r\n * @example\r\n * const result = await buildClient(config, logger);\r\n * if (result?.success) {\r\n * console.log('Built to:', result.outputs);\r\n * }\r\n */\r\n async function buildClient(config: AppConfig, logger: Logger) {\r\n if (!config.client) return null;\r\n\r\n logger.info('Building client...');\r\n\r\n try {\r\n const result = await Bun.build({\r\n entrypoints: [config.client.entry],\r\n outdir: config.client.output,\r\n target: config.client.target || 'browser',\r\n minify: config.client.minify ?? !config.debug,\r\n sourcemap: config.client.sourcemap ?? config.debug ? 'inline' : 'none',\r\n external: config.client.external || []\r\n });\r\n\r\n if (!result.success) throw new Error('Build failed');\r\n\r\n const outputs = result.outputs.map(o => o.path);\r\n \r\n await optimizeBundle(outputs, config, logger);\r\n\r\n logger.debug(`Client built → ${outputs.join(', ')}`);\r\n\r\n return { success: true, outputs };\r\n } catch (err) {\r\n logger.error('Failed to build client', (err as Error).message);\r\n throw err;\r\n }\r\n }\r\n\r\n export async function optimizeBundle(outputs: string[], config: AppConfig, logger: Logger) {\r\n if (!config.client?.entry) return;\r\n \r\n const bundlePath = outputs.find(p => p.endsWith('.js'));\r\n if (!bundlePath) return;\r\n\r\n logger.info('Optimizing bundle (removing unused icons)...');\r\n \r\n try {\r\n const file = Bun.file(bundlePath);\r\n const content = await file.text();\r\n \r\n // Step 1: Find all maps and keys\r\n const maps: {start: number, end: number, keys: string[]}[] = [];\r\n const allKeys = new Set<string>();\r\n \r\n let i = 0;\r\n while (i < content.length) {\r\n const char = content[i];\r\n // Skip strings\r\n if (char === '\"' || char === \"'\") {\r\n const quote = char;\r\n i++;\r\n while (i < content.length) {\r\n if (content[i] === quote && content[i-1] !== '\\\\') break;\r\n i++;\r\n }\r\n } else if (char === '{') {\r\n // Check if this is an icon map start\r\n // Look ahead for key:{category: or key:{viewBox: or key:{svg:\r\n // We check content AFTER the opening brace\r\n const chunk = content.substring(i + 1, i + 100);\r\n if (/^\\s*(?:[\"'][\\w-]+[\"']|\\w+)\\s*:\\s*\\{\\s*(?:category|viewBox|svg)\\b/.test(chunk)) {\r\n // Found a map!\r\n const start = i;\r\n let braceCount = 1;\r\n let j = i + 1;\r\n let inStr = false;\r\n let strChar = '';\r\n \r\n while (j < content.length) {\r\n const c = content[j];\r\n if (inStr) {\r\n if (c === strChar && content[j-1] !== '\\\\') inStr = false;\r\n } else {\r\n if (c === '\"' || c === \"'\") {\r\n inStr = true;\r\n strChar = c;\r\n } else if (c === '{') {\r\n braceCount++;\r\n } else if (c === '}') {\r\n braceCount--;\r\n if (braceCount === 0) break;\r\n }\r\n }\r\n j++;\r\n }\r\n \r\n const end = j + 1;\r\n const mapContent = content.substring(start, end);\r\n \r\n // Extract keys\r\n const keys: string[] = [];\r\n const keyRegex = /(?:[\"']([\\w-]+)[\"']|(\\w+))\\s*:\\s*\\{/g;\r\n let match;\r\n while ((match = keyRegex.exec(mapContent)) !== null) {\r\n const key = match[1] || match[2];\r\n keys.push(key);\r\n allKeys.add(key);\r\n }\r\n \r\n maps.push({start, end, keys});\r\n \r\n // Advance i to end\r\n i = end - 1; \r\n }\r\n }\r\n i++;\r\n }\r\n \r\n logger.debug(`Found ${maps.length} icon maps with ${allKeys.size} total icons.`);\r\n \r\n if (allKeys.size === 0) return;\r\n\r\n // Step 2: Scan usage\r\n const sourceDir = path.dirname(config.client.entry);\r\n const glob = new Bun.Glob('**/*.{ts,tsx,js,jsx}');\r\n const usedIcons = new Set<string>();\r\n \r\n for await (const file of glob.scan({ cwd: sourceDir })) {\r\n const filePath = path.join(sourceDir, file);\r\n const fileContent = await Bun.file(filePath).text();\r\n \r\n for (const icon of allKeys) {\r\n if (!usedIcons.has(icon)) {\r\n // Use word boundary to avoid partial matches (e.g. \"x\" in \"box\")\r\n // Escape special regex characters in icon name\r\n const escapedIcon = icon.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\r\n const regex = new RegExp(`\\\\b${escapedIcon}\\\\b`);\r\n \r\n if (regex.test(fileContent)) {\r\n usedIcons.add(icon);\r\n }\r\n }\r\n }\r\n }\r\n \r\n logger.debug(`Used icons: ${usedIcons.size}`);\r\n \r\n // Step 3: Rewrite\r\n let newContent = '';\r\n let lastIndex = 0;\r\n \r\n for (const map of maps) {\r\n // Append content before map\r\n newContent += content.substring(lastIndex, map.start);\r\n \r\n // Optimize map\r\n const mapContent = content.substring(map.start, map.end);\r\n \r\n let obj;\r\n try {\r\n const parseFn = new Function('return ' + mapContent);\r\n obj = parseFn();\r\n } catch (e) {\r\n logger.warn('Failed to parse map, keeping as is');\r\n newContent += mapContent;\r\n lastIndex = map.end;\r\n continue;\r\n }\r\n \r\n let removed = 0;\r\n for (const key of map.keys) {\r\n if (!usedIcons.has(key)) {\r\n delete obj[key];\r\n removed++;\r\n }\r\n }\r\n \r\n if (removed > 0) {\r\n newContent += JSON.stringify(obj);\r\n } else {\r\n newContent += mapContent;\r\n }\r\n \r\n lastIndex = map.end;\r\n }\r\n \r\n newContent += content.substring(lastIndex);\r\n \r\n await Bun.write(bundlePath, newContent);\r\n logger.info('Bundle optimized');\r\n \r\n } catch (err) {\r\n logger.warn('Failed to optimize bundle', (err as Error).message);\r\n }\r\n }\r\n\r\n /**\r\n * Builds SCSS/CSS styles using a bundler\r\n *\r\n * Handles:\r\n * - Compiling SCSS to CSS\r\n * - Minifying CSS if configured\r\n * - Generating source maps if configured\r\n * - Outputting to specified directory\r\n *\r\n * @param {AppConfig} config - The application configuration containing style settings\r\n * @param {Logger} logger - Logger instance for logging build progress and errors\r\n * @returns {Promise<{success: boolean, output: string} | null>} Build result with output path, or null if no style config\r\n * @throws {Error} If the build process fails\r\n *\r\n * @example\r\n * const result = await buildStyles(config, logger);\r\n * if (result?.success) {\r\n * console.log('Styles built to:', result.output);\r\n * }\r\n */\r\n async function buildStyles(config: AppConfig, logger: Logger) {\r\n if (!config.style) {\r\n logger.info('No style config provided, skipping style build');\r\n return null;\r\n }\r\n\r\n logger.info(`Building styles from: ${config.style.entry}...`);\r\n\r\n try {\r\n // Parse output path to get directory and filename\r\n const outputPath = config.style.output;\r\n const lastSlashIndex = outputPath.lastIndexOf('/');\r\n const outputDir = outputPath.substring(0, lastSlashIndex);\r\n const outputFilename = outputPath.substring(lastSlashIndex + 1);\r\n\r\n // Ensure output directory exists\r\n const { mkdir } = await import('fs/promises');\r\n await mkdir(outputDir, { recursive: true });\r\n\r\n // Use sass to compile SCSS to CSS\r\n const compileResult = sass.compile(config.style.entry, {\r\n style: config.style.minify ? 'compressed' : 'expanded',\r\n sourceMap: config.style.sourcemap ? true : false\r\n });\r\n\r\n // Write CSS file directly without JS wrapper\r\n await Bun.write(outputPath, compileResult.css);\r\n\r\n logger.info(`Compiled SCSS to CSS → ${outputFilename}`);\r\n logger.debug(`Styles built → ${outputPath}`);\r\n\r\n return { success: true, output: outputPath };\r\n } catch (err) {\r\n logger.error('Failed to build styles', (err as Error).message);\r\n return { success: false, output: config.style?.output || 'unknown' };\r\n }\r\n }\r\n\r\n /**\r\n * Initializes and configures database connections\r\n *\r\n * Handles:\r\n * - Multiple database instances (primary, cache, etc.)\r\n * - User-defined schemas from config\r\n * - Plugin schemas from plugins\r\n * - In-memory databases for plugin-only scenarios\r\n *\r\n * @param {AppConfig} config - Application configuration with database settings\r\n * @param {TableSchema[]} additionalSchemas - Database schemas provided by plugins\r\n * @param {Logger} logger - Logger instance for logging setup progress\r\n * @returns {Promise<Map<string, DB>>} Map of database instances by name\r\n * @throws {Error} If schema loading or database initialization fails\r\n *\r\n * @example\r\n * const databases = await setupDatabases(config, pluginSchemas, logger);\r\n * const db = databases.get('primary');\r\n * const result = db.query('SELECT * FROM users');\r\n */\r\n async function setupDatabases(\r\n config: AppConfig,\r\n additionalSchemas: TableSchema[],\r\n logger: Logger\r\n ) {\r\n const databases = new Map<string, DB>();\r\n\r\n if (!config.database && additionalSchemas.length === 0) {\r\n logger.info('No database config, skipping setup');\r\n return databases;\r\n }\r\n\r\n const dbConfigs = config.database\r\n ? Array.isArray(config.database)\r\n ? config.database\r\n : [config.database]\r\n : [];\r\n\r\n logger.info('Setting up databases...');\r\n\r\n for (const dbConfig of dbConfigs) {\r\n const name = dbConfig.name || 'default';\r\n const db = new DB(dbConfig.connection);\r\n\r\n // Load user schema\r\n if (dbConfig.schema) {\r\n try {\r\n const schemaModule = await import(dbConfig.schema);\r\n const schema = schemaModule.default || schemaModule.schema;\r\n\r\n if (Array.isArray(schema)) {\r\n for (const table of schema) {\r\n db.defineSchema(table);\r\n }\r\n }\r\n } catch (err) {\r\n logger.error(`Failed to load schema: ${dbConfig.schema}`, (err as Error).message);\r\n throw err;\r\n }\r\n }\r\n\r\n // Load plugin schemas for this database\r\n for (const schema of additionalSchemas) {\r\n db.defineSchema(schema);\r\n }\r\n\r\n databases.set(name, db);\r\n logger.debug(`Database '${name}' ready`);\r\n }\r\n\r\n // If no user database but plugins need one, create default\r\n if (databases.size === 0 && additionalSchemas.length > 0) {\r\n const db = new DB(':memory:');\r\n\r\n for (const schema of additionalSchemas) {\r\n db.defineSchema(schema);\r\n }\r\n\r\n databases.set('default', db);\r\n logger.debug(`Database 'default' ready (in-memory for plugins)`);\r\n }\r\n\r\n return databases;\r\n }\r\n\r\n /**\r\n * Initializes internationalization (i18n) support\r\n *\r\n * Loads language files and configures the i18n system based on:\r\n * - Default language\r\n * - Supported languages\r\n * - Base path for translation files\r\n * - File extension (json, cjson, etc.)\r\n *\r\n * @param {AppConfig} config - Application configuration with i18n settings\r\n * @param {Logger} logger - Logger instance for logging setup progress\r\n * @returns {Promise<void>}\r\n * @throws {Error} If i18n setup or language file loading fails\r\n *\r\n * @example\r\n * await setupI18n({\r\n * i18n: {\r\n * defaultLanguage: 'en',\r\n * supportedLanguages: ['en', 'ar'],\r\n * basePath: './src/i18n'\r\n * }\r\n * }, logger);\r\n */\r\n // async function setupI18n(config: AppConfig, logger: Logger) {\r\n // if (!config.i18n) return;\r\n\r\n // logger.info('Setting up i18n...');\r\n\r\n // try {\r\n // await _setupI18n({\r\n // defaultLanguage: config.i18n.defaultLanguage,\r\n // supportedLanguages: config.i18n.supportedLanguages,\r\n // basePath: config.i18n.basePath,\r\n // fileExtension: config.i18n.fileExtension || 'json'\r\n // });\r\n\r\n // logger.debug(`i18n ready → ${config.i18n.supportedLanguages.join(', ')}`);\r\n // } catch (err) {\r\n // logger.error('Failed to setup i18n', (err as Error).message);\r\n // throw err;\r\n // }\r\n // }\r\n\r\n /**\r\n * Dynamically loads route definitions from API directory\r\n *\r\n * Scans the specified directory for route files and imports them.\r\n * Each route file should export either:\r\n * - `routes` property with RouteDefinition array\r\n * - Default export with RouteDefinition array\r\n *\r\n * @param {AppConfig} config - Application configuration with api.directory\r\n * @param {Logger} logger - Logger instance for logging scan progress\r\n * @returns {Promise<RouteDefinition[]>} Array of loaded route definitions\r\n *\r\n * @example\r\n * const routes = await loadRoutes({\r\n * api: { directory: './src/server/api' }\r\n * }, logger);\r\n * console.log(`Loaded ${routes.length} routes`);\r\n */\r\n async function loadRoutes(config: AppConfig, logger: Logger): Promise<RouteDefinition[]> {\r\n if (!config.api) return [];\r\n\r\n logger.info(`Loading routes from ${config.api.directory}...`);\r\n\r\n const routes: RouteDefinition[] = [];\r\n\r\n try {\r\n const files = await scanDirectory(config.api.directory);\r\n\r\n for (const file of files) {\r\n try {\r\n const module = await import(file);\r\n const exported = module.default || module.routes;\r\n\r\n if (Array.isArray(exported)) {\r\n routes.push(...exported);\r\n }\r\n } catch (err) {\r\n logger.error(`Failed to load routes from ${file}`, (err as Error).message);\r\n }\r\n }\r\n\r\n logger.debug(`Routes loaded → ${routes.length} routes`);\r\n return routes;\r\n } catch (err) {\r\n logger.error('Failed to scan route directory', (err as Error).message);\r\n return [];\r\n }\r\n }\r\n\r\n /**\r\n * Scans a directory for TypeScript/JavaScript files\r\n *\r\n * Uses Bun's Glob API to recursively find all .ts and .js files in a directory.\r\n * Gracefully handles non-existent directories (returns empty array).\r\n *\r\n * @param {string} dir - Directory path to scan\r\n * @returns {Promise<string[]>} Array of absolute file paths found\r\n *\r\n * @example\r\n * const files = await scanDirectory('./src/server/api');\r\n * // Returns: ['./src/server/api/users.ts', './src/server/api/posts.ts']\r\n */\r\n async function scanDirectory(dir: string): Promise<string[]> {\r\n const files: string[] = [];\r\n\r\n try {\r\n const entries = await Array.fromAsync(\r\n new Bun.Glob('**/*.{ts,js}').scan(dir)\r\n );\r\n\r\n for (const entry of entries) {\r\n files.push(`${dir}/${entry}`);\r\n }\r\n } catch {\r\n // Directory doesn't exist\r\n }\r\n\r\n return files;\r\n }\r\n\r\n /**\r\n * Creates a CruxJS application instance with full lifecycle management\r\n *\r\n * This is the main entry point for building a CruxJS application. It:\r\n * 1. Registers plugins (Phase 0)\r\n * 2. Builds client, initializes databases, setups i18n (Phase 1: AWAKE)\r\n * 3. Creates server, merges routes and middleware (Phase 2: START)\r\n * 4. Starts the server and enables request handling (Phase 3: READY)\r\n *\r\n * Phases execute sequentially when `app.start()` is called.\r\n *\r\n * @param {AppConfig} userConfig - Application configuration object\r\n * @param {LifecycleHooks} [hooks] - Optional lifecycle event handlers\r\n * @returns {AppInstance} Application instance with control methods\r\n *\r\n * @throws {Error} Will throw if any lifecycle phase fails (unless caught in onError hook)\r\n *\r\n * @example\r\n * // Basic usage\r\n * const app = createApp({\r\n * debug: true,\r\n * server: { port: 3000 },\r\n * api: { directory: './src/api' },\r\n * plugins: [spaPlugin]\r\n * });\r\n * await app.start();\r\n *\r\n * @example\r\n * // With lifecycle hooks\r\n * const app = createApp(config, {\r\n * onAwake: async (ctx) => {\r\n * console.log('⏰ App awoken, databases ready');\r\n * },\r\n * onReady: async (ctx) => {\r\n * console.log('✅ Server ready:', ctx.server.getURL());\r\n * },\r\n * onError: async (ctx, phase, error) => {\r\n * console.error(`Error in ${phase}:`, error.message);\r\n * }\r\n * });\r\n *\r\n * @example\r\n * // With cleanup\r\n * const app = createApp(config);\r\n * await app.start();\r\n * // ... server is running\r\n * await app.stop(); // Cleanup\r\n */\r\n export function createApp(\r\n userConfig: AppConfig,\r\n hooks?: LifecycleHooks\r\n ): AppInstance {\r\n // Apply config hook\r\n const config: AppConfig = hooks?.onConfig\r\n ? hooks.onConfig(userConfig) as AppConfig\r\n : userConfig;\r\n\r\n const logger = new Logger(config.debug ? 'debug' : 'info', true, 'CruxJS');\r\n const pluginRegistry = new PluginRegistry(logger);\r\n const resourceMerger = new ResourceMerger(logger);\r\n\r\n const databases = new Map<string, DB>();\r\n const middlewares = new Map<string, AppMiddleware>();\r\n let serverInstance: any = null;\r\n let clientBuild: any = null;\r\n\r\n const ctx: LifecycleContext = {\r\n config,\r\n databases,\r\n plugins: []\r\n };\r\n\r\n // Create partial app instance for plugin registration\r\n const partialApp: AppInstance = {\r\n config,\r\n server: null,\r\n databases,\r\n plugins: [],\r\n middlewares,\r\n start: async () => { },\r\n stop: async () => { },\r\n restart: async () => { },\r\n getContext: () => ctx,\r\n getMiddleware: (name: string) => middlewares.get(name)\r\n };\r\n\r\n // ─────────────────────────────────────────────────────────\r\n // Phase 0: Plugin Registration\r\n // ─────────────────────────────────────────────────────────\r\n\r\n async function phaseRegister() {\r\n if (!config.plugins || config.plugins.length === 0) {\r\n logger.info('No plugins to register');\r\n return;\r\n }\r\n\r\n logger.debug('REGISTER');\r\n\r\n for (const plugin of config.plugins) {\r\n await pluginRegistry.register(plugin, partialApp);\r\n }\r\n\r\n ctx.plugins = pluginRegistry.getAll();\r\n\r\n // Collect plugin middlewares\r\n const pluginMiddlewares = pluginRegistry.collectMiddlewares();\r\n pluginMiddlewares.forEach((mw, name) => middlewares.set(name, mw));\r\n }\r\n\r\n // ─────────────────────────────────────────────────────────\r\n // Phase 1: AWAKE\r\n // ─────────────────────────────────────────────────────────\r\n\r\n async function phaseAwake() {\r\n logger.debug('AWAKE');\r\n\r\n try {\r\n // // Setup i18n\r\n // await setupI18n(config, logger);\r\n\r\n // Build client\r\n clientBuild = await buildClient(config, logger);\r\n ctx.clientBuild = clientBuild;\r\n\r\n // Build styles if configured\r\n const styleBuild = await buildStyles(config, logger);\r\n ctx.styleBuild = styleBuild;\r\n\r\n // Collect plugin schemas\r\n const pluginSchemas = pluginRegistry.collectSchemas();\r\n\r\n // Setup databases\r\n const dbs = await setupDatabases(config, pluginSchemas, logger);\r\n databases.clear();\r\n dbs.forEach((db, name) => databases.set(name, db));\r\n\r\n // Call plugin hooks\r\n await pluginRegistry.callHook('onAwake', ctx);\r\n\r\n // Call user hook\r\n await hooks?.onAwake?.(ctx);\r\n } catch (err) {\r\n await hooks?.onError?.(ctx, 'AWAKE', err as Error);\r\n throw err;\r\n }\r\n }\r\n\r\n // ─────────────────────────────────────────────────────────\r\n // Phase 2: START\r\n // ─────────────────────────────────────────────────────────\r\n\r\n async function phaseStart() {\r\n logger.debug('START');\r\n\r\n try {\r\n // Load user routes\r\n const userApiRoutes = await loadRoutes(config, logger);\r\n\r\n // Collect plugin routes\r\n const pluginRoutes = pluginRegistry.collectRoutes();\r\n\r\n // Merge user routes + plugin routes\r\n const userRoutes = config.routes || [];\r\n const allUserRoutes = [...userRoutes, ...userApiRoutes];\r\n const mergedRoutes = resourceMerger.mergeRoutes(allUserRoutes, pluginRoutes);\r\n\r\n // Collect static configs\r\n const userStatic = config.static\r\n ? Array.isArray(config.static)\r\n ? config.static\r\n : [config.static]\r\n : [];\r\n const pluginStatic = pluginRegistry.collectStatic();\r\n const mergedStatic = resourceMerger.mergeStatic(userStatic, pluginStatic);\r\n\r\n logger.info('Creating server...');\r\n\r\n // Collect error handlers from plugins (especially SPA plugin)\r\n let errorHandler: ((statusCode: number, path: string) => Response) | undefined;\r\n for (const plugin of ctx.plugins) {\r\n const pluginWithHandler = plugin as any;\r\n if (pluginWithHandler.__spaErrorHandler) {\r\n errorHandler = pluginWithHandler.__spaErrorHandler;\r\n console.log(`[CruxJS] ✓ Error handler collected from: ${plugin.name}`);\r\n break; // Use first error handler found\r\n }\r\n }\r\n\r\n // Create server\r\n serverInstance = await createServer({\r\n port: config.server?.port || 3000,\r\n hostname: config.server?.host || 'localhost',\r\n logging: config.server?.logging,\r\n routes: mergedRoutes,\r\n static: mergedStatic.length > 0 ? mergedStatic : undefined,\r\n security: config.security,\r\n middlewares: config.middlewares,\r\n i18n: config.i18n\r\n ? {\r\n defaultLanguage: config.i18n.defaultLanguage,\r\n supportedLanguages: config.i18n.supportedLanguages,\r\n basePath: config.i18n.basePath,\r\n fileExtension: config.i18n.fileExtension || 'json'\r\n }\r\n : undefined,\r\n onError: errorHandler\r\n });\r\n\r\n // Inject databases\r\n databases.forEach((db, name) => {\r\n serverInstance.db.set(name, db);\r\n });\r\n\r\n ctx.server = serverInstance;\r\n partialApp.server = serverInstance;\r\n\r\n logger.debug(\r\n `Server created → ${config.server?.host || 'localhost'}:${config.server?.port || 3000}`\r\n );\r\n\r\n // Call plugin hooks\r\n await pluginRegistry.callHook('onStart', ctx);\r\n\r\n // Call user hook\r\n await hooks?.onStart?.(ctx);\r\n } catch (err) {\r\n await hooks?.onError?.(ctx, 'START', err as Error);\r\n throw err;\r\n }\r\n }\r\n\r\n // ─────────────────────────────────────────────────────────\r\n // Phase 3: READY\r\n // ─────────────────────────────────────────────────────────\r\n\r\n async function phaseReady() {\r\n logger.debug('READY');\r\n\r\n try {\r\n await serverInstance.start();\r\n\r\n logger.debug(\r\n `Server running on http://${config.server?.host || 'localhost'}:${config.server?.port || 3000}`\r\n );\r\n\r\n // Call plugin hooks\r\n await pluginRegistry.callHook('onReady', ctx);\r\n\r\n // Call user hook\r\n await hooks?.onReady?.(ctx);\r\n } catch (err) {\r\n await hooks?.onError?.(ctx, 'READY', err as Error);\r\n throw err;\r\n }\r\n }\r\n\r\n // ─────────────────────────────────────────────────────────\r\n // App Instance\r\n // ─────────────────────────────────────────────────────────\r\n\r\n return {\r\n config,\r\n server: serverInstance,\r\n databases,\r\n plugins: ctx.plugins,\r\n middlewares,\r\n\r\n async start() {\r\n await phaseRegister();\r\n await phaseAwake();\r\n await phaseStart();\r\n await phaseReady();\r\n },\r\n\r\n async stop() {\r\n logger.info('Stopping server...');\r\n\r\n try {\r\n if (serverInstance) {\r\n await serverInstance.stop();\r\n }\r\n\r\n databases.forEach((db, name) => {\r\n db.close();\r\n logger.info(`Database '${name}' closed`);\r\n });\r\n\r\n // Call plugin hooks\r\n await pluginRegistry.callHook('onShutdown', ctx);\r\n\r\n await hooks?.onFinish?.(ctx);\r\n\r\n logger.debug('Server stopped');\r\n } catch (err) {\r\n logger.error('Failed to stop server', (err as Error).message);\r\n throw err;\r\n }\r\n },\r\n\r\n async restart() {\r\n await this.stop();\r\n await this.start();\r\n },\r\n\r\n getContext() {\r\n return ctx;\r\n },\r\n\r\n getMiddleware(name: string) {\r\n return middlewares.get(name);\r\n }\r\n };\r\n }\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ ════ ════════════════════════════════════════╗\r\n\r\n export * from '@cruxjs/base';\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["buildClient","config","logger","result","outputs","o","optimizeBundle","err","bundlePath","p","content","maps","allKeys","i","char","quote","chunk","start","braceCount","j","inStr","strChar","c","end","mapContent","keys","keyRegex","match","key","sourceDir","v","glob","usedIcons","file","filePath","fileContent","icon","escapedIcon","newContent","lastIndex","map","obj","removed","buildStyles","outputPath","lastSlashIndex","outputDir","outputFilename","mkdir","compileResult","k","setupDatabases","additionalSchemas","databases","dbConfigs","dbConfig","name","db","DB","schemaModule","schema","table","loadRoutes","routes","files","scanDirectory","module","exported","dir","entries","entry","createApp","userConfig","hooks","Logger","pluginRegistry","PluginRegistry","resourceMerger","ResourceMerger","middlewares","serverInstance","clientBuild","ctx","partialApp","phaseRegister","plugin","mw","phaseAwake","styleBuild","pluginSchemas","dbs","phaseStart","userApiRoutes","pluginRoutes","allUserRoutes","mergedRoutes","userStatic","pluginStatic","mergedStatic","errorHandler","pluginWithHandler","createServer","phaseReady"],"mappings":"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","file":"index.cjs","sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\r\n// src/index.ts\r\n//\r\n// Made with ❤️ by Maysara.\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ PACK ════════════════════════════════════════╗\r\n\r\n import { AppConfig, LifecycleContext, LifecycleHooks, AppInstance, RouteDefinition, AppMiddleware, PluginRegistry, ResourceMerger } from '@cruxjs/base';\r\n import { Logger } from '@minejs/logger';\r\n import { server as createServer } from '@minejs/server';\r\n import { DB } from '@minejs/db';\r\n import type { TableSchema } from '@minejs/db';\r\n import * as sass from 'sass';\r\n import * as path from 'path';\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ CORE ════════════════════════════════════════╗\r\n\r\n /**\r\n * Builds the client bundle using Bun's bundler\r\n *\r\n * Browser.tsx is a template file that:\r\n * - Imports user's client config from ./config.ts\r\n * - Reads i18n config from HTML meta tag (injected by server)\r\n * - Bootstraps ClientManager automatically via signal\r\n *\r\n * @param {AppConfig} config - The application configuration containing client build settings\r\n * @param {Logger} logger - Logger instance for logging build progress and errors\r\n * @returns {Promise<{success: boolean, outputs: string[]} | null>} Build result with output paths, or null if no client config\r\n * @throws {Error} If the build process fails\r\n *\r\n * @example\r\n * const result = await buildClient(config, logger);\r\n * if (result?.success) {\r\n * console.log('Built to:', result.outputs);\r\n * }\r\n */\r\n async function buildClient(config: AppConfig, logger: Logger) {\r\n if (!config.client) return null;\r\n\r\n logger.info('Building client...');\r\n\r\n try {\r\n const result = await Bun.build({\r\n entrypoints: [config.client.entry],\r\n outdir: config.client.output,\r\n target: config.client.target || 'browser',\r\n minify: config.client.minify ?? !config.debug,\r\n sourcemap: config.client.sourcemap ?? config.debug ? 'inline' : 'none',\r\n external: config.client.external || []\r\n });\r\n\r\n if (!result.success) throw new Error('Build failed');\r\n\r\n const outputs = result.outputs.map(o => o.path);\r\n \r\n await optimizeBundle(outputs, config, logger);\r\n\r\n logger.debug(`Client built → ${outputs.join(', ')}`);\r\n\r\n return { success: true, outputs };\r\n } catch (err) {\r\n logger.error('Failed to build client', (err as Error).message);\r\n throw err;\r\n }\r\n }\r\n\r\n export async function optimizeBundle(outputs: string[], config: AppConfig, logger: Logger) {\r\n if (!config.client?.entry) return;\r\n \r\n const bundlePath = outputs.find(p => p.endsWith('.js'));\r\n if (!bundlePath) return;\r\n\r\n logger.info('Optimizing bundle (removing unused icons)...');\r\n \r\n try {\r\n const file = Bun.file(bundlePath);\r\n const content = await file.text();\r\n \r\n // Step 1: Find all maps and keys\r\n const maps: {start: number, end: number, keys: string[]}[] = [];\r\n const allKeys = new Set<string>();\r\n \r\n let i = 0;\r\n while (i < content.length) {\r\n const char = content[i];\r\n // Skip strings\r\n if (char === '\"' || char === \"'\") {\r\n const quote = char;\r\n i++;\r\n while (i < content.length) {\r\n if (content[i] === quote && content[i-1] !== '\\\\') break;\r\n i++;\r\n }\r\n } else if (char === '{') {\r\n // Check if this is an icon map start\r\n // Look ahead for key:{category: or key:{viewBox: or key:{svg:\r\n // We check content AFTER the opening brace\r\n const chunk = content.substring(i + 1, i + 100);\r\n if (/^\\s*(?:[\"'][\\w-]+[\"']|\\w+)\\s*:\\s*\\{\\s*(?:category|viewBox|svg)\\b/.test(chunk)) {\r\n // Found a map!\r\n const start = i;\r\n let braceCount = 1;\r\n let j = i + 1;\r\n let inStr = false;\r\n let strChar = '';\r\n \r\n while (j < content.length) {\r\n const c = content[j];\r\n if (inStr) {\r\n if (c === strChar && content[j-1] !== '\\\\') inStr = false;\r\n } else {\r\n if (c === '\"' || c === \"'\") {\r\n inStr = true;\r\n strChar = c;\r\n } else if (c === '{') {\r\n braceCount++;\r\n } else if (c === '}') {\r\n braceCount--;\r\n if (braceCount === 0) break;\r\n }\r\n }\r\n j++;\r\n }\r\n \r\n const end = j + 1;\r\n const mapContent = content.substring(start, end);\r\n \r\n // Extract keys\r\n const keys: string[] = [];\r\n const keyRegex = /(?:[\"']([\\w-]+)[\"']|(\\w+))\\s*:\\s*\\{/g;\r\n let match;\r\n while ((match = keyRegex.exec(mapContent)) !== null) {\r\n const key = match[1] || match[2];\r\n keys.push(key);\r\n allKeys.add(key);\r\n }\r\n \r\n maps.push({start, end, keys});\r\n \r\n // Advance i to end\r\n i = end - 1; \r\n }\r\n }\r\n i++;\r\n }\r\n \r\n logger.debug(`Found ${maps.length} icon maps with ${allKeys.size} total icons.`);\r\n \r\n if (allKeys.size === 0) return;\r\n\r\n // Step 2: Scan usage\r\n const sourceDir = path.dirname(config.client.entry);\r\n const glob = new Bun.Glob('**/*.{ts,tsx,js,jsx}');\r\n const usedIcons = new Set<string>();\r\n\r\n // Always include these icons\r\n usedIcons.add('angle-down');\r\n \r\n for await (const file of glob.scan({ cwd: sourceDir })) {\r\n const filePath = path.join(sourceDir, file);\r\n const fileContent = await Bun.file(filePath).text();\r\n \r\n for (const icon of allKeys) {\r\n if (!usedIcons.has(icon)) {\r\n // Use word boundary to avoid partial matches (e.g. \"x\" in \"box\")\r\n // Escape special regex characters in icon name\r\n const escapedIcon = icon.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\r\n const regex = new RegExp(`\\\\b${escapedIcon}\\\\b`);\r\n \r\n if (regex.test(fileContent)) {\r\n usedIcons.add(icon);\r\n }\r\n }\r\n }\r\n }\r\n \r\n logger.debug(`Used icons: ${usedIcons.size}`);\r\n \r\n // Step 3: Rewrite\r\n let newContent = '';\r\n let lastIndex = 0;\r\n \r\n for (const map of maps) {\r\n // Append content before map\r\n newContent += content.substring(lastIndex, map.start);\r\n \r\n // Optimize map\r\n const mapContent = content.substring(map.start, map.end);\r\n \r\n let obj;\r\n try {\r\n const parseFn = new Function('return ' + mapContent);\r\n obj = parseFn();\r\n } catch (e) {\r\n logger.warn('Failed to parse map, keeping as is');\r\n newContent += mapContent;\r\n lastIndex = map.end;\r\n continue;\r\n }\r\n \r\n let removed = 0;\r\n for (const key of map.keys) {\r\n if (!usedIcons.has(key)) {\r\n delete obj[key];\r\n removed++;\r\n }\r\n }\r\n \r\n if (removed > 0) {\r\n newContent += JSON.stringify(obj);\r\n } else {\r\n newContent += mapContent;\r\n }\r\n \r\n lastIndex = map.end;\r\n }\r\n \r\n newContent += content.substring(lastIndex);\r\n \r\n await Bun.write(bundlePath, newContent);\r\n logger.info('Bundle optimized');\r\n \r\n } catch (err) {\r\n logger.warn('Failed to optimize bundle', (err as Error).message);\r\n }\r\n }\r\n\r\n /**\r\n * Builds SCSS/CSS styles using a bundler\r\n *\r\n * Handles:\r\n * - Compiling SCSS to CSS\r\n * - Minifying CSS if configured\r\n * - Generating source maps if configured\r\n * - Outputting to specified directory\r\n *\r\n * @param {AppConfig} config - The application configuration containing style settings\r\n * @param {Logger} logger - Logger instance for logging build progress and errors\r\n * @returns {Promise<{success: boolean, output: string} | null>} Build result with output path, or null if no style config\r\n * @throws {Error} If the build process fails\r\n *\r\n * @example\r\n * const result = await buildStyles(config, logger);\r\n * if (result?.success) {\r\n * console.log('Styles built to:', result.output);\r\n * }\r\n */\r\n async function buildStyles(config: AppConfig, logger: Logger) {\r\n if (!config.style) {\r\n logger.info('No style config provided, skipping style build');\r\n return null;\r\n }\r\n\r\n logger.info(`Building styles from: ${config.style.entry}...`);\r\n\r\n try {\r\n // Parse output path to get directory and filename\r\n const outputPath = config.style.output;\r\n const lastSlashIndex = outputPath.lastIndexOf('/');\r\n const outputDir = outputPath.substring(0, lastSlashIndex);\r\n const outputFilename = outputPath.substring(lastSlashIndex + 1);\r\n\r\n // Ensure output directory exists\r\n const { mkdir } = await import('fs/promises');\r\n await mkdir(outputDir, { recursive: true });\r\n\r\n // Use sass to compile SCSS to CSS\r\n const compileResult = sass.compile(config.style.entry, {\r\n style: config.style.minify ? 'compressed' : 'expanded',\r\n sourceMap: config.style.sourcemap ? true : false\r\n });\r\n\r\n // Write CSS file directly without JS wrapper\r\n await Bun.write(outputPath, compileResult.css);\r\n\r\n logger.info(`Compiled SCSS to CSS → ${outputFilename}`);\r\n logger.debug(`Styles built → ${outputPath}`);\r\n\r\n return { success: true, output: outputPath };\r\n } catch (err) {\r\n logger.error('Failed to build styles', (err as Error).message);\r\n return { success: false, output: config.style?.output || 'unknown' };\r\n }\r\n }\r\n\r\n /**\r\n * Initializes and configures database connections\r\n *\r\n * Handles:\r\n * - Multiple database instances (primary, cache, etc.)\r\n * - User-defined schemas from config\r\n * - Plugin schemas from plugins\r\n * - In-memory databases for plugin-only scenarios\r\n *\r\n * @param {AppConfig} config - Application configuration with database settings\r\n * @param {TableSchema[]} additionalSchemas - Database schemas provided by plugins\r\n * @param {Logger} logger - Logger instance for logging setup progress\r\n * @returns {Promise<Map<string, DB>>} Map of database instances by name\r\n * @throws {Error} If schema loading or database initialization fails\r\n *\r\n * @example\r\n * const databases = await setupDatabases(config, pluginSchemas, logger);\r\n * const db = databases.get('primary');\r\n * const result = db.query('SELECT * FROM users');\r\n */\r\n async function setupDatabases(\r\n config: AppConfig,\r\n additionalSchemas: TableSchema[],\r\n logger: Logger\r\n ) {\r\n const databases = new Map<string, DB>();\r\n\r\n if (!config.database && additionalSchemas.length === 0) {\r\n logger.info('No database config, skipping setup');\r\n return databases;\r\n }\r\n\r\n const dbConfigs = config.database\r\n ? Array.isArray(config.database)\r\n ? config.database\r\n : [config.database]\r\n : [];\r\n\r\n logger.info('Setting up databases...');\r\n\r\n for (const dbConfig of dbConfigs) {\r\n const name = dbConfig.name || 'default';\r\n const db = new DB(dbConfig.connection);\r\n\r\n // Load user schema\r\n if (dbConfig.schema) {\r\n try {\r\n const schemaModule = await import(dbConfig.schema);\r\n const schema = schemaModule.default || schemaModule.schema;\r\n\r\n if (Array.isArray(schema)) {\r\n for (const table of schema) {\r\n db.defineSchema(table);\r\n }\r\n }\r\n } catch (err) {\r\n logger.error(`Failed to load schema: ${dbConfig.schema}`, (err as Error).message);\r\n throw err;\r\n }\r\n }\r\n\r\n // Load plugin schemas for this database\r\n for (const schema of additionalSchemas) {\r\n db.defineSchema(schema);\r\n }\r\n\r\n databases.set(name, db);\r\n logger.debug(`Database '${name}' ready`);\r\n }\r\n\r\n // If no user database but plugins need one, create default\r\n if (databases.size === 0 && additionalSchemas.length > 0) {\r\n const db = new DB(':memory:');\r\n\r\n for (const schema of additionalSchemas) {\r\n db.defineSchema(schema);\r\n }\r\n\r\n databases.set('default', db);\r\n logger.debug(`Database 'default' ready (in-memory for plugins)`);\r\n }\r\n\r\n return databases;\r\n }\r\n\r\n /**\r\n * Initializes internationalization (i18n) support\r\n *\r\n * Loads language files and configures the i18n system based on:\r\n * - Default language\r\n * - Supported languages\r\n * - Base path for translation files\r\n * - File extension (json, cjson, etc.)\r\n *\r\n * @param {AppConfig} config - Application configuration with i18n settings\r\n * @param {Logger} logger - Logger instance for logging setup progress\r\n * @returns {Promise<void>}\r\n * @throws {Error} If i18n setup or language file loading fails\r\n *\r\n * @example\r\n * await setupI18n({\r\n * i18n: {\r\n * defaultLanguage: 'en',\r\n * supportedLanguages: ['en', 'ar'],\r\n * basePath: './src/i18n'\r\n * }\r\n * }, logger);\r\n */\r\n // async function setupI18n(config: AppConfig, logger: Logger) {\r\n // if (!config.i18n) return;\r\n\r\n // logger.info('Setting up i18n...');\r\n\r\n // try {\r\n // await _setupI18n({\r\n // defaultLanguage: config.i18n.defaultLanguage,\r\n // supportedLanguages: config.i18n.supportedLanguages,\r\n // basePath: config.i18n.basePath,\r\n // fileExtension: config.i18n.fileExtension || 'json'\r\n // });\r\n\r\n // logger.debug(`i18n ready → ${config.i18n.supportedLanguages.join(', ')}`);\r\n // } catch (err) {\r\n // logger.error('Failed to setup i18n', (err as Error).message);\r\n // throw err;\r\n // }\r\n // }\r\n\r\n /**\r\n * Dynamically loads route definitions from API directory\r\n *\r\n * Scans the specified directory for route files and imports them.\r\n * Each route file should export either:\r\n * - `routes` property with RouteDefinition array\r\n * - Default export with RouteDefinition array\r\n *\r\n * @param {AppConfig} config - Application configuration with api.directory\r\n * @param {Logger} logger - Logger instance for logging scan progress\r\n * @returns {Promise<RouteDefinition[]>} Array of loaded route definitions\r\n *\r\n * @example\r\n * const routes = await loadRoutes({\r\n * api: { directory: './src/server/api' }\r\n * }, logger);\r\n * console.log(`Loaded ${routes.length} routes`);\r\n */\r\n async function loadRoutes(config: AppConfig, logger: Logger): Promise<RouteDefinition[]> {\r\n if (!config.api) return [];\r\n\r\n logger.info(`Loading routes from ${config.api.directory}...`);\r\n\r\n const routes: RouteDefinition[] = [];\r\n\r\n try {\r\n const files = await scanDirectory(config.api.directory);\r\n\r\n for (const file of files) {\r\n try {\r\n const module = await import(file);\r\n const exported = module.default || module.routes;\r\n\r\n if (Array.isArray(exported)) {\r\n routes.push(...exported);\r\n }\r\n } catch (err) {\r\n logger.error(`Failed to load routes from ${file}`, (err as Error).message);\r\n }\r\n }\r\n\r\n logger.debug(`Routes loaded → ${routes.length} routes`);\r\n return routes;\r\n } catch (err) {\r\n logger.error('Failed to scan route directory', (err as Error).message);\r\n return [];\r\n }\r\n }\r\n\r\n /**\r\n * Scans a directory for TypeScript/JavaScript files\r\n *\r\n * Uses Bun's Glob API to recursively find all .ts and .js files in a directory.\r\n * Gracefully handles non-existent directories (returns empty array).\r\n *\r\n * @param {string} dir - Directory path to scan\r\n * @returns {Promise<string[]>} Array of absolute file paths found\r\n *\r\n * @example\r\n * const files = await scanDirectory('./src/server/api');\r\n * // Returns: ['./src/server/api/users.ts', './src/server/api/posts.ts']\r\n */\r\n async function scanDirectory(dir: string): Promise<string[]> {\r\n const files: string[] = [];\r\n\r\n try {\r\n const entries = await Array.fromAsync(\r\n new Bun.Glob('**/*.{ts,js}').scan(dir)\r\n );\r\n\r\n for (const entry of entries) {\r\n files.push(`${dir}/${entry}`);\r\n }\r\n } catch {\r\n // Directory doesn't exist\r\n }\r\n\r\n return files;\r\n }\r\n\r\n /**\r\n * Creates a CruxJS application instance with full lifecycle management\r\n *\r\n * This is the main entry point for building a CruxJS application. It:\r\n * 1. Registers plugins (Phase 0)\r\n * 2. Builds client, initializes databases, setups i18n (Phase 1: AWAKE)\r\n * 3. Creates server, merges routes and middleware (Phase 2: START)\r\n * 4. Starts the server and enables request handling (Phase 3: READY)\r\n *\r\n * Phases execute sequentially when `app.start()` is called.\r\n *\r\n * @param {AppConfig} userConfig - Application configuration object\r\n * @param {LifecycleHooks} [hooks] - Optional lifecycle event handlers\r\n * @returns {AppInstance} Application instance with control methods\r\n *\r\n * @throws {Error} Will throw if any lifecycle phase fails (unless caught in onError hook)\r\n *\r\n * @example\r\n * // Basic usage\r\n * const app = createApp({\r\n * debug: true,\r\n * server: { port: 3000 },\r\n * api: { directory: './src/api' },\r\n * plugins: [spaPlugin]\r\n * });\r\n * await app.start();\r\n *\r\n * @example\r\n * // With lifecycle hooks\r\n * const app = createApp(config, {\r\n * onAwake: async (ctx) => {\r\n * console.log('⏰ App awoken, databases ready');\r\n * },\r\n * onReady: async (ctx) => {\r\n * console.log('✅ Server ready:', ctx.server.getURL());\r\n * },\r\n * onError: async (ctx, phase, error) => {\r\n * console.error(`Error in ${phase}:`, error.message);\r\n * }\r\n * });\r\n *\r\n * @example\r\n * // With cleanup\r\n * const app = createApp(config);\r\n * await app.start();\r\n * // ... server is running\r\n * await app.stop(); // Cleanup\r\n */\r\n export function createApp(\r\n userConfig: AppConfig,\r\n hooks?: LifecycleHooks\r\n ): AppInstance {\r\n // Apply config hook\r\n const config: AppConfig = hooks?.onConfig\r\n ? hooks.onConfig(userConfig) as AppConfig\r\n : userConfig;\r\n\r\n const logger = new Logger(config.debug ? 'debug' : 'info', true, 'CruxJS');\r\n const pluginRegistry = new PluginRegistry(logger);\r\n const resourceMerger = new ResourceMerger(logger);\r\n\r\n const databases = new Map<string, DB>();\r\n const middlewares = new Map<string, AppMiddleware>();\r\n let serverInstance: any = null;\r\n let clientBuild: any = null;\r\n\r\n const ctx: LifecycleContext = {\r\n config,\r\n databases,\r\n plugins: []\r\n };\r\n\r\n // Create partial app instance for plugin registration\r\n const partialApp: AppInstance = {\r\n config,\r\n server: null,\r\n databases,\r\n plugins: [],\r\n middlewares,\r\n start: async () => { },\r\n stop: async () => { },\r\n restart: async () => { },\r\n getContext: () => ctx,\r\n getMiddleware: (name: string) => middlewares.get(name)\r\n };\r\n\r\n // ─────────────────────────────────────────────────────────\r\n // Phase 0: Plugin Registration\r\n // ─────────────────────────────────────────────────────────\r\n\r\n async function phaseRegister() {\r\n if (!config.plugins || config.plugins.length === 0) {\r\n logger.info('No plugins to register');\r\n return;\r\n }\r\n\r\n logger.debug('REGISTER');\r\n\r\n for (const plugin of config.plugins) {\r\n await pluginRegistry.register(plugin, partialApp);\r\n }\r\n\r\n ctx.plugins = pluginRegistry.getAll();\r\n\r\n // Collect plugin middlewares\r\n const pluginMiddlewares = pluginRegistry.collectMiddlewares();\r\n pluginMiddlewares.forEach((mw, name) => middlewares.set(name, mw));\r\n }\r\n\r\n // ─────────────────────────────────────────────────────────\r\n // Phase 1: AWAKE\r\n // ─────────────────────────────────────────────────────────\r\n\r\n async function phaseAwake() {\r\n logger.debug('AWAKE');\r\n\r\n try {\r\n // // Setup i18n\r\n // await setupI18n(config, logger);\r\n\r\n // Build client\r\n clientBuild = await buildClient(config, logger);\r\n ctx.clientBuild = clientBuild;\r\n\r\n // Build styles if configured\r\n const styleBuild = await buildStyles(config, logger);\r\n ctx.styleBuild = styleBuild;\r\n\r\n // Collect plugin schemas\r\n const pluginSchemas = pluginRegistry.collectSchemas();\r\n\r\n // Setup databases\r\n const dbs = await setupDatabases(config, pluginSchemas, logger);\r\n databases.clear();\r\n dbs.forEach((db, name) => databases.set(name, db));\r\n\r\n // Call plugin hooks\r\n await pluginRegistry.callHook('onAwake', ctx);\r\n\r\n // Call user hook\r\n await hooks?.onAwake?.(ctx);\r\n } catch (err) {\r\n await hooks?.onError?.(ctx, 'AWAKE', err as Error);\r\n throw err;\r\n }\r\n }\r\n\r\n // ─────────────────────────────────────────────────────────\r\n // Phase 2: START\r\n // ─────────────────────────────────────────────────────────\r\n\r\n async function phaseStart() {\r\n logger.debug('START');\r\n\r\n try {\r\n // Load user routes\r\n const userApiRoutes = await loadRoutes(config, logger);\r\n\r\n // Collect plugin routes\r\n const pluginRoutes = pluginRegistry.collectRoutes();\r\n\r\n // Merge user routes + plugin routes\r\n const userRoutes = config.routes || [];\r\n const allUserRoutes = [...userRoutes, ...userApiRoutes];\r\n const mergedRoutes = resourceMerger.mergeRoutes(allUserRoutes, pluginRoutes);\r\n\r\n // Collect static configs\r\n const userStatic = config.static\r\n ? Array.isArray(config.static)\r\n ? config.static\r\n : [config.static]\r\n : [];\r\n const pluginStatic = pluginRegistry.collectStatic();\r\n const mergedStatic = resourceMerger.mergeStatic(userStatic, pluginStatic);\r\n\r\n logger.info('Creating server...');\r\n\r\n // Collect error handlers from plugins (especially SPA plugin)\r\n let errorHandler: ((statusCode: number, path: string) => Response) | undefined;\r\n for (const plugin of ctx.plugins) {\r\n const pluginWithHandler = plugin as any;\r\n if (pluginWithHandler.__spaErrorHandler) {\r\n errorHandler = pluginWithHandler.__spaErrorHandler;\r\n console.log(`[CruxJS] ✓ Error handler collected from: ${plugin.name}`);\r\n break; // Use first error handler found\r\n }\r\n }\r\n\r\n // Create server\r\n serverInstance = await createServer({\r\n port: config.server?.port || 3000,\r\n hostname: config.server?.host || 'localhost',\r\n logging: config.server?.logging,\r\n routes: mergedRoutes,\r\n static: mergedStatic.length > 0 ? mergedStatic : undefined,\r\n security: config.security,\r\n middlewares: config.middlewares,\r\n i18n: config.i18n\r\n ? {\r\n defaultLanguage: config.i18n.defaultLanguage,\r\n supportedLanguages: config.i18n.supportedLanguages,\r\n basePath: config.i18n.basePath,\r\n fileExtension: config.i18n.fileExtension || 'json'\r\n }\r\n : undefined,\r\n onError: errorHandler\r\n });\r\n\r\n // Inject databases\r\n databases.forEach((db, name) => {\r\n serverInstance.db.set(name, db);\r\n });\r\n\r\n ctx.server = serverInstance;\r\n partialApp.server = serverInstance;\r\n\r\n logger.debug(\r\n `Server created → ${config.server?.host || 'localhost'}:${config.server?.port || 3000}`\r\n );\r\n\r\n // Call plugin hooks\r\n await pluginRegistry.callHook('onStart', ctx);\r\n\r\n // Call user hook\r\n await hooks?.onStart?.(ctx);\r\n } catch (err) {\r\n await hooks?.onError?.(ctx, 'START', err as Error);\r\n throw err;\r\n }\r\n }\r\n\r\n // ─────────────────────────────────────────────────────────\r\n // Phase 3: READY\r\n // ─────────────────────────────────────────────────────────\r\n\r\n async function phaseReady() {\r\n logger.debug('READY');\r\n\r\n try {\r\n await serverInstance.start();\r\n\r\n logger.debug(\r\n `Server running on http://${config.server?.host || 'localhost'}:${config.server?.port || 3000}`\r\n );\r\n\r\n // Call plugin hooks\r\n await pluginRegistry.callHook('onReady', ctx);\r\n\r\n // Call user hook\r\n await hooks?.onReady?.(ctx);\r\n } catch (err) {\r\n await hooks?.onError?.(ctx, 'READY', err as Error);\r\n throw err;\r\n }\r\n }\r\n\r\n // ─────────────────────────────────────────────────────────\r\n // App Instance\r\n // ─────────────────────────────────────────────────────────\r\n\r\n return {\r\n config,\r\n server: serverInstance,\r\n databases,\r\n plugins: ctx.plugins,\r\n middlewares,\r\n\r\n async start() {\r\n await phaseRegister();\r\n await phaseAwake();\r\n await phaseStart();\r\n await phaseReady();\r\n },\r\n\r\n async stop() {\r\n logger.info('Stopping server...');\r\n\r\n try {\r\n if (serverInstance) {\r\n await serverInstance.stop();\r\n }\r\n\r\n databases.forEach((db, name) => {\r\n db.close();\r\n logger.info(`Database '${name}' closed`);\r\n });\r\n\r\n // Call plugin hooks\r\n await pluginRegistry.callHook('onShutdown', ctx);\r\n\r\n await hooks?.onFinish?.(ctx);\r\n\r\n logger.debug('Server stopped');\r\n } catch (err) {\r\n logger.error('Failed to stop server', (err as Error).message);\r\n throw err;\r\n }\r\n },\r\n\r\n async restart() {\r\n await this.stop();\r\n await this.start();\r\n },\r\n\r\n getContext() {\r\n return ctx;\r\n },\r\n\r\n getMiddleware(name: string) {\r\n return middlewares.get(name);\r\n }\r\n };\r\n }\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ ════ ════════════════════════════════════════╗\r\n\r\n export * from '@cruxjs/base';\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝"]}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {PluginRegistry,ResourceMerger}from'@cruxjs/base';export*from'@cruxjs/base';import {Logger}from'@minejs/logger';import {server}from'@minejs/server';import {DB}from'@minejs/db';import*as k from'sass';import*as v from'path';async function j(s,r){if(!s.client)return null;r.info("Building client...");try{let e=await Bun.build({entrypoints:[s.client.entry],outdir:s.client.output,target:s.client.target||"browser",minify:s.client.minify??!s.debug,sourcemap:s.client.sourcemap??s.debug?"inline":"none",external:s.client.external||[]});if(!e.success)throw new Error("Build failed");let t=e.outputs.map(i=>i.path);return await H(t,s,r),r.debug(`Client built \u2192 ${t.join(", ")}`),{success:!0,outputs:t}}catch(e){throw r.error("Failed to build client",e.message),e}}async function H(s,r,e){if(!r.client?.entry)return;let t=s.find(i=>i.endsWith(".js"));if(t){e.info("Optimizing bundle (removing unused icons)...");try{let n=await Bun.file(t).text(),u=[],
|
|
1
|
+
import {PluginRegistry,ResourceMerger}from'@cruxjs/base';export*from'@cruxjs/base';import {Logger}from'@minejs/logger';import {server}from'@minejs/server';import {DB}from'@minejs/db';import*as k from'sass';import*as v from'path';async function j(s,r){if(!s.client)return null;r.info("Building client...");try{let e=await Bun.build({entrypoints:[s.client.entry],outdir:s.client.output,target:s.client.target||"browser",minify:s.client.minify??!s.debug,sourcemap:s.client.sourcemap??s.debug?"inline":"none",external:s.client.external||[]});if(!e.success)throw new Error("Build failed");let t=e.outputs.map(i=>i.path);return await H(t,s,r),r.debug(`Client built \u2192 ${t.join(", ")}`),{success:!0,outputs:t}}catch(e){throw r.error("Failed to build client",e.message),e}}async function H(s,r,e){if(!r.client?.entry)return;let t=s.find(i=>i.endsWith(".js"));if(t){e.info("Optimizing bundle (removing unused icons)...");try{let n=await Bun.file(t).text(),u=[],p=new Set,o=0;for(;o<n.length;){let f=n[o];if(f==='"'||f==="'"){let y=f;for(o++;o<n.length&&!(n[o]===y&&n[o-1]!=="\\");)o++;}else if(f==="{"){let y=n.substring(o+1,o+100);if(/^\s*(?:["'][\w-]+["']|\w+)\s*:\s*\{\s*(?:category|viewBox|svg)\b/.test(y)){let a=o,l=1,d=o+1,b=!1,C="";for(;d<n.length;){let g=n[d];if(b)g===C&&n[d-1]!=="\\"&&(b=!1);else if(g==='"'||g==="'")b=!0,C=g;else if(g==="{")l++;else if(g==="}"&&(l--,l===0))break;d++;}let E=d+1,$=n.substring(a,E),R=[],x=/(?:["']([\w-]+)["']|(\w+))\s*:\s*\{/g,A;for(;(A=x.exec($))!==null;){let g=A[1]||A[2];R.push(g),p.add(g);}u.push({start:a,end:E,keys:R}),o=E-1;}}o++;}if(e.debug(`Found ${u.length} icon maps with ${p.size} total icons.`),p.size===0)return;let w=v.dirname(r.client.entry),c=new Bun.Glob("**/*.{ts,tsx,js,jsx}"),m=new Set;m.add("angle-down");for await(let f of c.scan({cwd:w})){let y=v.join(w,f),a=await Bun.file(y).text();for(let l of p)if(!m.has(l)){let d=l.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");new RegExp(`\\b${d}\\b`).test(a)&&m.add(l);}}e.debug(`Used icons: ${m.size}`);let h="",S=0;for(let f of u){h+=n.substring(S,f.start);let y=n.substring(f.start,f.end),a;try{a=new Function("return "+y)();}catch{e.warn("Failed to parse map, keeping as is"),h+=y,S=f.end;continue}let l=0;for(let d of f.keys)m.has(d)||(delete a[d],l++);l>0?h+=JSON.stringify(a):h+=y,S=f.end;}h+=n.substring(S),await Bun.write(t,h),e.info("Bundle optimized");}catch(i){e.warn("Failed to optimize bundle",i.message);}}}async function I(s,r){if(!s.style)return r.info("No style config provided, skipping style build"),null;r.info(`Building styles from: ${s.style.entry}...`);try{let e=s.style.output,t=e.lastIndexOf("/"),i=e.substring(0,t),n=e.substring(t+1),{mkdir:u}=await import('fs/promises');await u(i,{recursive:!0});let p=k.compile(s.style.entry,{style:s.style.minify?"compressed":"expanded",sourceMap:!!s.style.sourcemap});return await Bun.write(e,p.css),r.info(`Compiled SCSS to CSS \u2192 ${n}`),r.debug(`Styles built \u2192 ${e}`),{success:!0,output:e}}catch(e){return r.error("Failed to build styles",e.message),{success:false,output:s.style?.output||"unknown"}}}async function z(s,r,e){let t=new Map;if(!s.database&&r.length===0)return e.info("No database config, skipping setup"),t;let i=s.database?Array.isArray(s.database)?s.database:[s.database]:[];e.info("Setting up databases...");for(let n of i){let u=n.name||"default",p=new DB(n.connection);if(n.schema)try{let o=await import(n.schema),w=o.default||o.schema;if(Array.isArray(w))for(let c of w)p.defineSchema(c);}catch(o){throw e.error(`Failed to load schema: ${n.schema}`,o.message),o}for(let o of r)p.defineSchema(o);t.set(u,p),e.debug(`Database '${u}' ready`);}if(t.size===0&&r.length>0){let n=new DB(":memory:");for(let u of r)n.defineSchema(u);t.set("default",n),e.debug("Database 'default' ready (in-memory for plugins)");}return t}async function P(s,r){if(!s.api)return [];r.info(`Loading routes from ${s.api.directory}...`);let e=[];try{let t=await T(s.api.directory);for(let i of t)try{let n=await import(i),u=n.default||n.routes;Array.isArray(u)&&e.push(...u);}catch(n){r.error(`Failed to load routes from ${i}`,n.message);}return r.debug(`Routes loaded \u2192 ${e.length} routes`),e}catch(t){return r.error("Failed to scan route directory",t.message),[]}}async function T(s){let r=[];try{let e=await Array.fromAsync(new Bun.Glob("**/*.{ts,js}").scan(s));for(let t of e)r.push(`${s}/${t}`);}catch{}return r}function Q(s,r){let e=r?.onConfig?r.onConfig(s):s,t=new Logger(e.debug?"debug":"info",true,"CruxJS"),i=new PluginRegistry(t),n=new ResourceMerger(t),u=new Map,p=new Map,o=null,w=null,c={config:e,databases:u,plugins:[]},m={config:e,server:null,databases:u,plugins:[],middlewares:p,start:async()=>{},stop:async()=>{},restart:async()=>{},getContext:()=>c,getMiddleware:a=>p.get(a)};async function h(){if(!e.plugins||e.plugins.length===0){t.info("No plugins to register");return}t.debug("REGISTER");for(let l of e.plugins)await i.register(l,m);c.plugins=i.getAll(),i.collectMiddlewares().forEach((l,d)=>p.set(d,l));}async function S(){t.debug("AWAKE");try{w=await j(e,t),c.clientBuild=w;let a=await I(e,t);c.styleBuild=a;let l=i.collectSchemas(),d=await z(e,l,t);u.clear(),d.forEach((b,C)=>u.set(C,b)),await i.callHook("onAwake",c),await r?.onAwake?.(c);}catch(a){throw await r?.onError?.(c,"AWAKE",a),a}}async function f(){t.debug("START");try{let a=await P(e,t),l=i.collectRoutes(),b=[...e.routes||[],...a],C=n.mergeRoutes(b,l),E=e.static?Array.isArray(e.static)?e.static:[e.static]:[],$=i.collectStatic(),R=n.mergeStatic(E,$);t.info("Creating server...");let x;for(let A of c.plugins){let g=A;if(g.__spaErrorHandler){x=g.__spaErrorHandler,console.log(`[CruxJS] \u2713 Error handler collected from: ${A.name}`);break}}o=await server({port:e.server?.port||3e3,hostname:e.server?.host||"localhost",logging:e.server?.logging,routes:C,static:R.length>0?R:void 0,security:e.security,middlewares:e.middlewares,i18n:e.i18n?{defaultLanguage:e.i18n.defaultLanguage,supportedLanguages:e.i18n.supportedLanguages,basePath:e.i18n.basePath,fileExtension:e.i18n.fileExtension||"json"}:void 0,onError:x}),u.forEach((A,g)=>{o.db.set(g,A);}),c.server=o,m.server=o,t.debug(`Server created \u2192 ${e.server?.host||"localhost"}:${e.server?.port||3e3}`),await i.callHook("onStart",c),await r?.onStart?.(c);}catch(a){throw await r?.onError?.(c,"START",a),a}}async function y(){t.debug("READY");try{await o.start(),t.debug(`Server running on http://${e.server?.host||"localhost"}:${e.server?.port||3e3}`),await i.callHook("onReady",c),await r?.onReady?.(c);}catch(a){throw await r?.onError?.(c,"READY",a),a}}return {config:e,server:o,databases:u,plugins:c.plugins,middlewares:p,async start(){await h(),await S(),await f(),await y();},async stop(){t.info("Stopping server...");try{o&&await o.stop(),u.forEach((a,l)=>{a.close(),t.info(`Database '${l}' closed`);}),await i.callHook("onShutdown",c),await r?.onFinish?.(c),t.debug("Server stopped");}catch(a){throw t.error("Failed to stop server",a.message),a}},async restart(){await this.stop(),await this.start();},getContext(){return c},getMiddleware(a){return p.get(a)}}}
|
|
2
2
|
export{Q as createApp,H as optimizeBundle};//# sourceMappingURL=index.js.map
|
|
3
3
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":["buildClient","config","logger","result","outputs","o","optimizeBundle","err","bundlePath","p","content","maps","allKeys","i","char","quote","chunk","start","braceCount","j","inStr","strChar","c","end","mapContent","keys","keyRegex","match","key","sourceDir","glob","usedIcons","file","filePath","fileContent","icon","escapedIcon","newContent","lastIndex","map","obj","removed","buildStyles","outputPath","lastSlashIndex","outputDir","outputFilename","mkdir","compileResult","setupDatabases","additionalSchemas","databases","dbConfigs","dbConfig","name","db","DB","schemaModule","schema","table","loadRoutes","routes","files","scanDirectory","module","exported","dir","entries","entry","createApp","userConfig","hooks","Logger","pluginRegistry","PluginRegistry","resourceMerger","ResourceMerger","middlewares","serverInstance","clientBuild","ctx","partialApp","phaseRegister","plugin","mw","phaseAwake","styleBuild","pluginSchemas","dbs","phaseStart","userApiRoutes","pluginRoutes","allUserRoutes","mergedRoutes","userStatic","pluginStatic","mergedStatic","errorHandler","pluginWithHandler","createServer","phaseReady"],"mappings":"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","file":"index.js","sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\r\n// src/index.ts\r\n//\r\n// Made with ❤️ by Maysara.\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ PACK ════════════════════════════════════════╗\r\n\r\n import { AppConfig, LifecycleContext, LifecycleHooks, AppInstance, RouteDefinition, AppMiddleware, PluginRegistry, ResourceMerger } from '@cruxjs/base';\r\n import { Logger } from '@minejs/logger';\r\n import { server as createServer } from '@minejs/server';\r\n import { DB } from '@minejs/db';\r\n import type { TableSchema } from '@minejs/db';\r\n import * as sass from 'sass';\r\n import * as path from 'path';\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ CORE ════════════════════════════════════════╗\r\n\r\n /**\r\n * Builds the client bundle using Bun's bundler\r\n *\r\n * Browser.tsx is a template file that:\r\n * - Imports user's client config from ./config.ts\r\n * - Reads i18n config from HTML meta tag (injected by server)\r\n * - Bootstraps ClientManager automatically via signal\r\n *\r\n * @param {AppConfig} config - The application configuration containing client build settings\r\n * @param {Logger} logger - Logger instance for logging build progress and errors\r\n * @returns {Promise<{success: boolean, outputs: string[]} | null>} Build result with output paths, or null if no client config\r\n * @throws {Error} If the build process fails\r\n *\r\n * @example\r\n * const result = await buildClient(config, logger);\r\n * if (result?.success) {\r\n * console.log('Built to:', result.outputs);\r\n * }\r\n */\r\n async function buildClient(config: AppConfig, logger: Logger) {\r\n if (!config.client) return null;\r\n\r\n logger.info('Building client...');\r\n\r\n try {\r\n const result = await Bun.build({\r\n entrypoints: [config.client.entry],\r\n outdir: config.client.output,\r\n target: config.client.target || 'browser',\r\n minify: config.client.minify ?? !config.debug,\r\n sourcemap: config.client.sourcemap ?? config.debug ? 'inline' : 'none',\r\n external: config.client.external || []\r\n });\r\n\r\n if (!result.success) throw new Error('Build failed');\r\n\r\n const outputs = result.outputs.map(o => o.path);\r\n \r\n await optimizeBundle(outputs, config, logger);\r\n\r\n logger.debug(`Client built → ${outputs.join(', ')}`);\r\n\r\n return { success: true, outputs };\r\n } catch (err) {\r\n logger.error('Failed to build client', (err as Error).message);\r\n throw err;\r\n }\r\n }\r\n\r\n export async function optimizeBundle(outputs: string[], config: AppConfig, logger: Logger) {\r\n if (!config.client?.entry) return;\r\n \r\n const bundlePath = outputs.find(p => p.endsWith('.js'));\r\n if (!bundlePath) return;\r\n\r\n logger.info('Optimizing bundle (removing unused icons)...');\r\n \r\n try {\r\n const file = Bun.file(bundlePath);\r\n const content = await file.text();\r\n \r\n // Step 1: Find all maps and keys\r\n const maps: {start: number, end: number, keys: string[]}[] = [];\r\n const allKeys = new Set<string>();\r\n \r\n let i = 0;\r\n while (i < content.length) {\r\n const char = content[i];\r\n // Skip strings\r\n if (char === '\"' || char === \"'\") {\r\n const quote = char;\r\n i++;\r\n while (i < content.length) {\r\n if (content[i] === quote && content[i-1] !== '\\\\') break;\r\n i++;\r\n }\r\n } else if (char === '{') {\r\n // Check if this is an icon map start\r\n // Look ahead for key:{category: or key:{viewBox: or key:{svg:\r\n // We check content AFTER the opening brace\r\n const chunk = content.substring(i + 1, i + 100);\r\n if (/^\\s*(?:[\"'][\\w-]+[\"']|\\w+)\\s*:\\s*\\{\\s*(?:category|viewBox|svg)\\b/.test(chunk)) {\r\n // Found a map!\r\n const start = i;\r\n let braceCount = 1;\r\n let j = i + 1;\r\n let inStr = false;\r\n let strChar = '';\r\n \r\n while (j < content.length) {\r\n const c = content[j];\r\n if (inStr) {\r\n if (c === strChar && content[j-1] !== '\\\\') inStr = false;\r\n } else {\r\n if (c === '\"' || c === \"'\") {\r\n inStr = true;\r\n strChar = c;\r\n } else if (c === '{') {\r\n braceCount++;\r\n } else if (c === '}') {\r\n braceCount--;\r\n if (braceCount === 0) break;\r\n }\r\n }\r\n j++;\r\n }\r\n \r\n const end = j + 1;\r\n const mapContent = content.substring(start, end);\r\n \r\n // Extract keys\r\n const keys: string[] = [];\r\n const keyRegex = /(?:[\"']([\\w-]+)[\"']|(\\w+))\\s*:\\s*\\{/g;\r\n let match;\r\n while ((match = keyRegex.exec(mapContent)) !== null) {\r\n const key = match[1] || match[2];\r\n keys.push(key);\r\n allKeys.add(key);\r\n }\r\n \r\n maps.push({start, end, keys});\r\n \r\n // Advance i to end\r\n i = end - 1; \r\n }\r\n }\r\n i++;\r\n }\r\n \r\n logger.debug(`Found ${maps.length} icon maps with ${allKeys.size} total icons.`);\r\n \r\n if (allKeys.size === 0) return;\r\n\r\n // Step 2: Scan usage\r\n const sourceDir = path.dirname(config.client.entry);\r\n const glob = new Bun.Glob('**/*.{ts,tsx,js,jsx}');\r\n const usedIcons = new Set<string>();\r\n \r\n for await (const file of glob.scan({ cwd: sourceDir })) {\r\n const filePath = path.join(sourceDir, file);\r\n const fileContent = await Bun.file(filePath).text();\r\n \r\n for (const icon of allKeys) {\r\n if (!usedIcons.has(icon)) {\r\n // Use word boundary to avoid partial matches (e.g. \"x\" in \"box\")\r\n // Escape special regex characters in icon name\r\n const escapedIcon = icon.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\r\n const regex = new RegExp(`\\\\b${escapedIcon}\\\\b`);\r\n \r\n if (regex.test(fileContent)) {\r\n usedIcons.add(icon);\r\n }\r\n }\r\n }\r\n }\r\n \r\n logger.debug(`Used icons: ${usedIcons.size}`);\r\n \r\n // Step 3: Rewrite\r\n let newContent = '';\r\n let lastIndex = 0;\r\n \r\n for (const map of maps) {\r\n // Append content before map\r\n newContent += content.substring(lastIndex, map.start);\r\n \r\n // Optimize map\r\n const mapContent = content.substring(map.start, map.end);\r\n \r\n let obj;\r\n try {\r\n const parseFn = new Function('return ' + mapContent);\r\n obj = parseFn();\r\n } catch (e) {\r\n logger.warn('Failed to parse map, keeping as is');\r\n newContent += mapContent;\r\n lastIndex = map.end;\r\n continue;\r\n }\r\n \r\n let removed = 0;\r\n for (const key of map.keys) {\r\n if (!usedIcons.has(key)) {\r\n delete obj[key];\r\n removed++;\r\n }\r\n }\r\n \r\n if (removed > 0) {\r\n newContent += JSON.stringify(obj);\r\n } else {\r\n newContent += mapContent;\r\n }\r\n \r\n lastIndex = map.end;\r\n }\r\n \r\n newContent += content.substring(lastIndex);\r\n \r\n await Bun.write(bundlePath, newContent);\r\n logger.info('Bundle optimized');\r\n \r\n } catch (err) {\r\n logger.warn('Failed to optimize bundle', (err as Error).message);\r\n }\r\n }\r\n\r\n /**\r\n * Builds SCSS/CSS styles using a bundler\r\n *\r\n * Handles:\r\n * - Compiling SCSS to CSS\r\n * - Minifying CSS if configured\r\n * - Generating source maps if configured\r\n * - Outputting to specified directory\r\n *\r\n * @param {AppConfig} config - The application configuration containing style settings\r\n * @param {Logger} logger - Logger instance for logging build progress and errors\r\n * @returns {Promise<{success: boolean, output: string} | null>} Build result with output path, or null if no style config\r\n * @throws {Error} If the build process fails\r\n *\r\n * @example\r\n * const result = await buildStyles(config, logger);\r\n * if (result?.success) {\r\n * console.log('Styles built to:', result.output);\r\n * }\r\n */\r\n async function buildStyles(config: AppConfig, logger: Logger) {\r\n if (!config.style) {\r\n logger.info('No style config provided, skipping style build');\r\n return null;\r\n }\r\n\r\n logger.info(`Building styles from: ${config.style.entry}...`);\r\n\r\n try {\r\n // Parse output path to get directory and filename\r\n const outputPath = config.style.output;\r\n const lastSlashIndex = outputPath.lastIndexOf('/');\r\n const outputDir = outputPath.substring(0, lastSlashIndex);\r\n const outputFilename = outputPath.substring(lastSlashIndex + 1);\r\n\r\n // Ensure output directory exists\r\n const { mkdir } = await import('fs/promises');\r\n await mkdir(outputDir, { recursive: true });\r\n\r\n // Use sass to compile SCSS to CSS\r\n const compileResult = sass.compile(config.style.entry, {\r\n style: config.style.minify ? 'compressed' : 'expanded',\r\n sourceMap: config.style.sourcemap ? true : false\r\n });\r\n\r\n // Write CSS file directly without JS wrapper\r\n await Bun.write(outputPath, compileResult.css);\r\n\r\n logger.info(`Compiled SCSS to CSS → ${outputFilename}`);\r\n logger.debug(`Styles built → ${outputPath}`);\r\n\r\n return { success: true, output: outputPath };\r\n } catch (err) {\r\n logger.error('Failed to build styles', (err as Error).message);\r\n return { success: false, output: config.style?.output || 'unknown' };\r\n }\r\n }\r\n\r\n /**\r\n * Initializes and configures database connections\r\n *\r\n * Handles:\r\n * - Multiple database instances (primary, cache, etc.)\r\n * - User-defined schemas from config\r\n * - Plugin schemas from plugins\r\n * - In-memory databases for plugin-only scenarios\r\n *\r\n * @param {AppConfig} config - Application configuration with database settings\r\n * @param {TableSchema[]} additionalSchemas - Database schemas provided by plugins\r\n * @param {Logger} logger - Logger instance for logging setup progress\r\n * @returns {Promise<Map<string, DB>>} Map of database instances by name\r\n * @throws {Error} If schema loading or database initialization fails\r\n *\r\n * @example\r\n * const databases = await setupDatabases(config, pluginSchemas, logger);\r\n * const db = databases.get('primary');\r\n * const result = db.query('SELECT * FROM users');\r\n */\r\n async function setupDatabases(\r\n config: AppConfig,\r\n additionalSchemas: TableSchema[],\r\n logger: Logger\r\n ) {\r\n const databases = new Map<string, DB>();\r\n\r\n if (!config.database && additionalSchemas.length === 0) {\r\n logger.info('No database config, skipping setup');\r\n return databases;\r\n }\r\n\r\n const dbConfigs = config.database\r\n ? Array.isArray(config.database)\r\n ? config.database\r\n : [config.database]\r\n : [];\r\n\r\n logger.info('Setting up databases...');\r\n\r\n for (const dbConfig of dbConfigs) {\r\n const name = dbConfig.name || 'default';\r\n const db = new DB(dbConfig.connection);\r\n\r\n // Load user schema\r\n if (dbConfig.schema) {\r\n try {\r\n const schemaModule = await import(dbConfig.schema);\r\n const schema = schemaModule.default || schemaModule.schema;\r\n\r\n if (Array.isArray(schema)) {\r\n for (const table of schema) {\r\n db.defineSchema(table);\r\n }\r\n }\r\n } catch (err) {\r\n logger.error(`Failed to load schema: ${dbConfig.schema}`, (err as Error).message);\r\n throw err;\r\n }\r\n }\r\n\r\n // Load plugin schemas for this database\r\n for (const schema of additionalSchemas) {\r\n db.defineSchema(schema);\r\n }\r\n\r\n databases.set(name, db);\r\n logger.debug(`Database '${name}' ready`);\r\n }\r\n\r\n // If no user database but plugins need one, create default\r\n if (databases.size === 0 && additionalSchemas.length > 0) {\r\n const db = new DB(':memory:');\r\n\r\n for (const schema of additionalSchemas) {\r\n db.defineSchema(schema);\r\n }\r\n\r\n databases.set('default', db);\r\n logger.debug(`Database 'default' ready (in-memory for plugins)`);\r\n }\r\n\r\n return databases;\r\n }\r\n\r\n /**\r\n * Initializes internationalization (i18n) support\r\n *\r\n * Loads language files and configures the i18n system based on:\r\n * - Default language\r\n * - Supported languages\r\n * - Base path for translation files\r\n * - File extension (json, cjson, etc.)\r\n *\r\n * @param {AppConfig} config - Application configuration with i18n settings\r\n * @param {Logger} logger - Logger instance for logging setup progress\r\n * @returns {Promise<void>}\r\n * @throws {Error} If i18n setup or language file loading fails\r\n *\r\n * @example\r\n * await setupI18n({\r\n * i18n: {\r\n * defaultLanguage: 'en',\r\n * supportedLanguages: ['en', 'ar'],\r\n * basePath: './src/i18n'\r\n * }\r\n * }, logger);\r\n */\r\n // async function setupI18n(config: AppConfig, logger: Logger) {\r\n // if (!config.i18n) return;\r\n\r\n // logger.info('Setting up i18n...');\r\n\r\n // try {\r\n // await _setupI18n({\r\n // defaultLanguage: config.i18n.defaultLanguage,\r\n // supportedLanguages: config.i18n.supportedLanguages,\r\n // basePath: config.i18n.basePath,\r\n // fileExtension: config.i18n.fileExtension || 'json'\r\n // });\r\n\r\n // logger.debug(`i18n ready → ${config.i18n.supportedLanguages.join(', ')}`);\r\n // } catch (err) {\r\n // logger.error('Failed to setup i18n', (err as Error).message);\r\n // throw err;\r\n // }\r\n // }\r\n\r\n /**\r\n * Dynamically loads route definitions from API directory\r\n *\r\n * Scans the specified directory for route files and imports them.\r\n * Each route file should export either:\r\n * - `routes` property with RouteDefinition array\r\n * - Default export with RouteDefinition array\r\n *\r\n * @param {AppConfig} config - Application configuration with api.directory\r\n * @param {Logger} logger - Logger instance for logging scan progress\r\n * @returns {Promise<RouteDefinition[]>} Array of loaded route definitions\r\n *\r\n * @example\r\n * const routes = await loadRoutes({\r\n * api: { directory: './src/server/api' }\r\n * }, logger);\r\n * console.log(`Loaded ${routes.length} routes`);\r\n */\r\n async function loadRoutes(config: AppConfig, logger: Logger): Promise<RouteDefinition[]> {\r\n if (!config.api) return [];\r\n\r\n logger.info(`Loading routes from ${config.api.directory}...`);\r\n\r\n const routes: RouteDefinition[] = [];\r\n\r\n try {\r\n const files = await scanDirectory(config.api.directory);\r\n\r\n for (const file of files) {\r\n try {\r\n const module = await import(file);\r\n const exported = module.default || module.routes;\r\n\r\n if (Array.isArray(exported)) {\r\n routes.push(...exported);\r\n }\r\n } catch (err) {\r\n logger.error(`Failed to load routes from ${file}`, (err as Error).message);\r\n }\r\n }\r\n\r\n logger.debug(`Routes loaded → ${routes.length} routes`);\r\n return routes;\r\n } catch (err) {\r\n logger.error('Failed to scan route directory', (err as Error).message);\r\n return [];\r\n }\r\n }\r\n\r\n /**\r\n * Scans a directory for TypeScript/JavaScript files\r\n *\r\n * Uses Bun's Glob API to recursively find all .ts and .js files in a directory.\r\n * Gracefully handles non-existent directories (returns empty array).\r\n *\r\n * @param {string} dir - Directory path to scan\r\n * @returns {Promise<string[]>} Array of absolute file paths found\r\n *\r\n * @example\r\n * const files = await scanDirectory('./src/server/api');\r\n * // Returns: ['./src/server/api/users.ts', './src/server/api/posts.ts']\r\n */\r\n async function scanDirectory(dir: string): Promise<string[]> {\r\n const files: string[] = [];\r\n\r\n try {\r\n const entries = await Array.fromAsync(\r\n new Bun.Glob('**/*.{ts,js}').scan(dir)\r\n );\r\n\r\n for (const entry of entries) {\r\n files.push(`${dir}/${entry}`);\r\n }\r\n } catch {\r\n // Directory doesn't exist\r\n }\r\n\r\n return files;\r\n }\r\n\r\n /**\r\n * Creates a CruxJS application instance with full lifecycle management\r\n *\r\n * This is the main entry point for building a CruxJS application. It:\r\n * 1. Registers plugins (Phase 0)\r\n * 2. Builds client, initializes databases, setups i18n (Phase 1: AWAKE)\r\n * 3. Creates server, merges routes and middleware (Phase 2: START)\r\n * 4. Starts the server and enables request handling (Phase 3: READY)\r\n *\r\n * Phases execute sequentially when `app.start()` is called.\r\n *\r\n * @param {AppConfig} userConfig - Application configuration object\r\n * @param {LifecycleHooks} [hooks] - Optional lifecycle event handlers\r\n * @returns {AppInstance} Application instance with control methods\r\n *\r\n * @throws {Error} Will throw if any lifecycle phase fails (unless caught in onError hook)\r\n *\r\n * @example\r\n * // Basic usage\r\n * const app = createApp({\r\n * debug: true,\r\n * server: { port: 3000 },\r\n * api: { directory: './src/api' },\r\n * plugins: [spaPlugin]\r\n * });\r\n * await app.start();\r\n *\r\n * @example\r\n * // With lifecycle hooks\r\n * const app = createApp(config, {\r\n * onAwake: async (ctx) => {\r\n * console.log('⏰ App awoken, databases ready');\r\n * },\r\n * onReady: async (ctx) => {\r\n * console.log('✅ Server ready:', ctx.server.getURL());\r\n * },\r\n * onError: async (ctx, phase, error) => {\r\n * console.error(`Error in ${phase}:`, error.message);\r\n * }\r\n * });\r\n *\r\n * @example\r\n * // With cleanup\r\n * const app = createApp(config);\r\n * await app.start();\r\n * // ... server is running\r\n * await app.stop(); // Cleanup\r\n */\r\n export function createApp(\r\n userConfig: AppConfig,\r\n hooks?: LifecycleHooks\r\n ): AppInstance {\r\n // Apply config hook\r\n const config: AppConfig = hooks?.onConfig\r\n ? hooks.onConfig(userConfig) as AppConfig\r\n : userConfig;\r\n\r\n const logger = new Logger(config.debug ? 'debug' : 'info', true, 'CruxJS');\r\n const pluginRegistry = new PluginRegistry(logger);\r\n const resourceMerger = new ResourceMerger(logger);\r\n\r\n const databases = new Map<string, DB>();\r\n const middlewares = new Map<string, AppMiddleware>();\r\n let serverInstance: any = null;\r\n let clientBuild: any = null;\r\n\r\n const ctx: LifecycleContext = {\r\n config,\r\n databases,\r\n plugins: []\r\n };\r\n\r\n // Create partial app instance for plugin registration\r\n const partialApp: AppInstance = {\r\n config,\r\n server: null,\r\n databases,\r\n plugins: [],\r\n middlewares,\r\n start: async () => { },\r\n stop: async () => { },\r\n restart: async () => { },\r\n getContext: () => ctx,\r\n getMiddleware: (name: string) => middlewares.get(name)\r\n };\r\n\r\n // ─────────────────────────────────────────────────────────\r\n // Phase 0: Plugin Registration\r\n // ─────────────────────────────────────────────────────────\r\n\r\n async function phaseRegister() {\r\n if (!config.plugins || config.plugins.length === 0) {\r\n logger.info('No plugins to register');\r\n return;\r\n }\r\n\r\n logger.debug('REGISTER');\r\n\r\n for (const plugin of config.plugins) {\r\n await pluginRegistry.register(plugin, partialApp);\r\n }\r\n\r\n ctx.plugins = pluginRegistry.getAll();\r\n\r\n // Collect plugin middlewares\r\n const pluginMiddlewares = pluginRegistry.collectMiddlewares();\r\n pluginMiddlewares.forEach((mw, name) => middlewares.set(name, mw));\r\n }\r\n\r\n // ─────────────────────────────────────────────────────────\r\n // Phase 1: AWAKE\r\n // ─────────────────────────────────────────────────────────\r\n\r\n async function phaseAwake() {\r\n logger.debug('AWAKE');\r\n\r\n try {\r\n // // Setup i18n\r\n // await setupI18n(config, logger);\r\n\r\n // Build client\r\n clientBuild = await buildClient(config, logger);\r\n ctx.clientBuild = clientBuild;\r\n\r\n // Build styles if configured\r\n const styleBuild = await buildStyles(config, logger);\r\n ctx.styleBuild = styleBuild;\r\n\r\n // Collect plugin schemas\r\n const pluginSchemas = pluginRegistry.collectSchemas();\r\n\r\n // Setup databases\r\n const dbs = await setupDatabases(config, pluginSchemas, logger);\r\n databases.clear();\r\n dbs.forEach((db, name) => databases.set(name, db));\r\n\r\n // Call plugin hooks\r\n await pluginRegistry.callHook('onAwake', ctx);\r\n\r\n // Call user hook\r\n await hooks?.onAwake?.(ctx);\r\n } catch (err) {\r\n await hooks?.onError?.(ctx, 'AWAKE', err as Error);\r\n throw err;\r\n }\r\n }\r\n\r\n // ─────────────────────────────────────────────────────────\r\n // Phase 2: START\r\n // ─────────────────────────────────────────────────────────\r\n\r\n async function phaseStart() {\r\n logger.debug('START');\r\n\r\n try {\r\n // Load user routes\r\n const userApiRoutes = await loadRoutes(config, logger);\r\n\r\n // Collect plugin routes\r\n const pluginRoutes = pluginRegistry.collectRoutes();\r\n\r\n // Merge user routes + plugin routes\r\n const userRoutes = config.routes || [];\r\n const allUserRoutes = [...userRoutes, ...userApiRoutes];\r\n const mergedRoutes = resourceMerger.mergeRoutes(allUserRoutes, pluginRoutes);\r\n\r\n // Collect static configs\r\n const userStatic = config.static\r\n ? Array.isArray(config.static)\r\n ? config.static\r\n : [config.static]\r\n : [];\r\n const pluginStatic = pluginRegistry.collectStatic();\r\n const mergedStatic = resourceMerger.mergeStatic(userStatic, pluginStatic);\r\n\r\n logger.info('Creating server...');\r\n\r\n // Collect error handlers from plugins (especially SPA plugin)\r\n let errorHandler: ((statusCode: number, path: string) => Response) | undefined;\r\n for (const plugin of ctx.plugins) {\r\n const pluginWithHandler = plugin as any;\r\n if (pluginWithHandler.__spaErrorHandler) {\r\n errorHandler = pluginWithHandler.__spaErrorHandler;\r\n console.log(`[CruxJS] ✓ Error handler collected from: ${plugin.name}`);\r\n break; // Use first error handler found\r\n }\r\n }\r\n\r\n // Create server\r\n serverInstance = await createServer({\r\n port: config.server?.port || 3000,\r\n hostname: config.server?.host || 'localhost',\r\n logging: config.server?.logging,\r\n routes: mergedRoutes,\r\n static: mergedStatic.length > 0 ? mergedStatic : undefined,\r\n security: config.security,\r\n middlewares: config.middlewares,\r\n i18n: config.i18n\r\n ? {\r\n defaultLanguage: config.i18n.defaultLanguage,\r\n supportedLanguages: config.i18n.supportedLanguages,\r\n basePath: config.i18n.basePath,\r\n fileExtension: config.i18n.fileExtension || 'json'\r\n }\r\n : undefined,\r\n onError: errorHandler\r\n });\r\n\r\n // Inject databases\r\n databases.forEach((db, name) => {\r\n serverInstance.db.set(name, db);\r\n });\r\n\r\n ctx.server = serverInstance;\r\n partialApp.server = serverInstance;\r\n\r\n logger.debug(\r\n `Server created → ${config.server?.host || 'localhost'}:${config.server?.port || 3000}`\r\n );\r\n\r\n // Call plugin hooks\r\n await pluginRegistry.callHook('onStart', ctx);\r\n\r\n // Call user hook\r\n await hooks?.onStart?.(ctx);\r\n } catch (err) {\r\n await hooks?.onError?.(ctx, 'START', err as Error);\r\n throw err;\r\n }\r\n }\r\n\r\n // ─────────────────────────────────────────────────────────\r\n // Phase 3: READY\r\n // ─────────────────────────────────────────────────────────\r\n\r\n async function phaseReady() {\r\n logger.debug('READY');\r\n\r\n try {\r\n await serverInstance.start();\r\n\r\n logger.debug(\r\n `Server running on http://${config.server?.host || 'localhost'}:${config.server?.port || 3000}`\r\n );\r\n\r\n // Call plugin hooks\r\n await pluginRegistry.callHook('onReady', ctx);\r\n\r\n // Call user hook\r\n await hooks?.onReady?.(ctx);\r\n } catch (err) {\r\n await hooks?.onError?.(ctx, 'READY', err as Error);\r\n throw err;\r\n }\r\n }\r\n\r\n // ─────────────────────────────────────────────────────────\r\n // App Instance\r\n // ─────────────────────────────────────────────────────────\r\n\r\n return {\r\n config,\r\n server: serverInstance,\r\n databases,\r\n plugins: ctx.plugins,\r\n middlewares,\r\n\r\n async start() {\r\n await phaseRegister();\r\n await phaseAwake();\r\n await phaseStart();\r\n await phaseReady();\r\n },\r\n\r\n async stop() {\r\n logger.info('Stopping server...');\r\n\r\n try {\r\n if (serverInstance) {\r\n await serverInstance.stop();\r\n }\r\n\r\n databases.forEach((db, name) => {\r\n db.close();\r\n logger.info(`Database '${name}' closed`);\r\n });\r\n\r\n // Call plugin hooks\r\n await pluginRegistry.callHook('onShutdown', ctx);\r\n\r\n await hooks?.onFinish?.(ctx);\r\n\r\n logger.debug('Server stopped');\r\n } catch (err) {\r\n logger.error('Failed to stop server', (err as Error).message);\r\n throw err;\r\n }\r\n },\r\n\r\n async restart() {\r\n await this.stop();\r\n await this.start();\r\n },\r\n\r\n getContext() {\r\n return ctx;\r\n },\r\n\r\n getMiddleware(name: string) {\r\n return middlewares.get(name);\r\n }\r\n };\r\n }\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ ════ ════════════════════════════════════════╗\r\n\r\n export * from '@cruxjs/base';\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["buildClient","config","logger","result","outputs","o","optimizeBundle","err","bundlePath","p","content","maps","allKeys","i","char","quote","chunk","start","braceCount","j","inStr","strChar","c","end","mapContent","keys","keyRegex","match","key","sourceDir","glob","usedIcons","file","filePath","fileContent","icon","escapedIcon","newContent","lastIndex","map","obj","removed","buildStyles","outputPath","lastSlashIndex","outputDir","outputFilename","mkdir","compileResult","setupDatabases","additionalSchemas","databases","dbConfigs","dbConfig","name","db","DB","schemaModule","schema","table","loadRoutes","routes","files","scanDirectory","module","exported","dir","entries","entry","createApp","userConfig","hooks","Logger","pluginRegistry","PluginRegistry","resourceMerger","ResourceMerger","middlewares","serverInstance","clientBuild","ctx","partialApp","phaseRegister","plugin","mw","phaseAwake","styleBuild","pluginSchemas","dbs","phaseStart","userApiRoutes","pluginRoutes","allUserRoutes","mergedRoutes","userStatic","pluginStatic","mergedStatic","errorHandler","pluginWithHandler","createServer","phaseReady"],"mappings":"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","file":"index.js","sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\r\n// src/index.ts\r\n//\r\n// Made with ❤️ by Maysara.\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ PACK ════════════════════════════════════════╗\r\n\r\n import { AppConfig, LifecycleContext, LifecycleHooks, AppInstance, RouteDefinition, AppMiddleware, PluginRegistry, ResourceMerger } from '@cruxjs/base';\r\n import { Logger } from '@minejs/logger';\r\n import { server as createServer } from '@minejs/server';\r\n import { DB } from '@minejs/db';\r\n import type { TableSchema } from '@minejs/db';\r\n import * as sass from 'sass';\r\n import * as path from 'path';\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ CORE ════════════════════════════════════════╗\r\n\r\n /**\r\n * Builds the client bundle using Bun's bundler\r\n *\r\n * Browser.tsx is a template file that:\r\n * - Imports user's client config from ./config.ts\r\n * - Reads i18n config from HTML meta tag (injected by server)\r\n * - Bootstraps ClientManager automatically via signal\r\n *\r\n * @param {AppConfig} config - The application configuration containing client build settings\r\n * @param {Logger} logger - Logger instance for logging build progress and errors\r\n * @returns {Promise<{success: boolean, outputs: string[]} | null>} Build result with output paths, or null if no client config\r\n * @throws {Error} If the build process fails\r\n *\r\n * @example\r\n * const result = await buildClient(config, logger);\r\n * if (result?.success) {\r\n * console.log('Built to:', result.outputs);\r\n * }\r\n */\r\n async function buildClient(config: AppConfig, logger: Logger) {\r\n if (!config.client) return null;\r\n\r\n logger.info('Building client...');\r\n\r\n try {\r\n const result = await Bun.build({\r\n entrypoints: [config.client.entry],\r\n outdir: config.client.output,\r\n target: config.client.target || 'browser',\r\n minify: config.client.minify ?? !config.debug,\r\n sourcemap: config.client.sourcemap ?? config.debug ? 'inline' : 'none',\r\n external: config.client.external || []\r\n });\r\n\r\n if (!result.success) throw new Error('Build failed');\r\n\r\n const outputs = result.outputs.map(o => o.path);\r\n \r\n await optimizeBundle(outputs, config, logger);\r\n\r\n logger.debug(`Client built → ${outputs.join(', ')}`);\r\n\r\n return { success: true, outputs };\r\n } catch (err) {\r\n logger.error('Failed to build client', (err as Error).message);\r\n throw err;\r\n }\r\n }\r\n\r\n export async function optimizeBundle(outputs: string[], config: AppConfig, logger: Logger) {\r\n if (!config.client?.entry) return;\r\n \r\n const bundlePath = outputs.find(p => p.endsWith('.js'));\r\n if (!bundlePath) return;\r\n\r\n logger.info('Optimizing bundle (removing unused icons)...');\r\n \r\n try {\r\n const file = Bun.file(bundlePath);\r\n const content = await file.text();\r\n \r\n // Step 1: Find all maps and keys\r\n const maps: {start: number, end: number, keys: string[]}[] = [];\r\n const allKeys = new Set<string>();\r\n \r\n let i = 0;\r\n while (i < content.length) {\r\n const char = content[i];\r\n // Skip strings\r\n if (char === '\"' || char === \"'\") {\r\n const quote = char;\r\n i++;\r\n while (i < content.length) {\r\n if (content[i] === quote && content[i-1] !== '\\\\') break;\r\n i++;\r\n }\r\n } else if (char === '{') {\r\n // Check if this is an icon map start\r\n // Look ahead for key:{category: or key:{viewBox: or key:{svg:\r\n // We check content AFTER the opening brace\r\n const chunk = content.substring(i + 1, i + 100);\r\n if (/^\\s*(?:[\"'][\\w-]+[\"']|\\w+)\\s*:\\s*\\{\\s*(?:category|viewBox|svg)\\b/.test(chunk)) {\r\n // Found a map!\r\n const start = i;\r\n let braceCount = 1;\r\n let j = i + 1;\r\n let inStr = false;\r\n let strChar = '';\r\n \r\n while (j < content.length) {\r\n const c = content[j];\r\n if (inStr) {\r\n if (c === strChar && content[j-1] !== '\\\\') inStr = false;\r\n } else {\r\n if (c === '\"' || c === \"'\") {\r\n inStr = true;\r\n strChar = c;\r\n } else if (c === '{') {\r\n braceCount++;\r\n } else if (c === '}') {\r\n braceCount--;\r\n if (braceCount === 0) break;\r\n }\r\n }\r\n j++;\r\n }\r\n \r\n const end = j + 1;\r\n const mapContent = content.substring(start, end);\r\n \r\n // Extract keys\r\n const keys: string[] = [];\r\n const keyRegex = /(?:[\"']([\\w-]+)[\"']|(\\w+))\\s*:\\s*\\{/g;\r\n let match;\r\n while ((match = keyRegex.exec(mapContent)) !== null) {\r\n const key = match[1] || match[2];\r\n keys.push(key);\r\n allKeys.add(key);\r\n }\r\n \r\n maps.push({start, end, keys});\r\n \r\n // Advance i to end\r\n i = end - 1; \r\n }\r\n }\r\n i++;\r\n }\r\n \r\n logger.debug(`Found ${maps.length} icon maps with ${allKeys.size} total icons.`);\r\n \r\n if (allKeys.size === 0) return;\r\n\r\n // Step 2: Scan usage\r\n const sourceDir = path.dirname(config.client.entry);\r\n const glob = new Bun.Glob('**/*.{ts,tsx,js,jsx}');\r\n const usedIcons = new Set<string>();\r\n\r\n // Always include these icons\r\n usedIcons.add('angle-down');\r\n \r\n for await (const file of glob.scan({ cwd: sourceDir })) {\r\n const filePath = path.join(sourceDir, file);\r\n const fileContent = await Bun.file(filePath).text();\r\n \r\n for (const icon of allKeys) {\r\n if (!usedIcons.has(icon)) {\r\n // Use word boundary to avoid partial matches (e.g. \"x\" in \"box\")\r\n // Escape special regex characters in icon name\r\n const escapedIcon = icon.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\r\n const regex = new RegExp(`\\\\b${escapedIcon}\\\\b`);\r\n \r\n if (regex.test(fileContent)) {\r\n usedIcons.add(icon);\r\n }\r\n }\r\n }\r\n }\r\n \r\n logger.debug(`Used icons: ${usedIcons.size}`);\r\n \r\n // Step 3: Rewrite\r\n let newContent = '';\r\n let lastIndex = 0;\r\n \r\n for (const map of maps) {\r\n // Append content before map\r\n newContent += content.substring(lastIndex, map.start);\r\n \r\n // Optimize map\r\n const mapContent = content.substring(map.start, map.end);\r\n \r\n let obj;\r\n try {\r\n const parseFn = new Function('return ' + mapContent);\r\n obj = parseFn();\r\n } catch (e) {\r\n logger.warn('Failed to parse map, keeping as is');\r\n newContent += mapContent;\r\n lastIndex = map.end;\r\n continue;\r\n }\r\n \r\n let removed = 0;\r\n for (const key of map.keys) {\r\n if (!usedIcons.has(key)) {\r\n delete obj[key];\r\n removed++;\r\n }\r\n }\r\n \r\n if (removed > 0) {\r\n newContent += JSON.stringify(obj);\r\n } else {\r\n newContent += mapContent;\r\n }\r\n \r\n lastIndex = map.end;\r\n }\r\n \r\n newContent += content.substring(lastIndex);\r\n \r\n await Bun.write(bundlePath, newContent);\r\n logger.info('Bundle optimized');\r\n \r\n } catch (err) {\r\n logger.warn('Failed to optimize bundle', (err as Error).message);\r\n }\r\n }\r\n\r\n /**\r\n * Builds SCSS/CSS styles using a bundler\r\n *\r\n * Handles:\r\n * - Compiling SCSS to CSS\r\n * - Minifying CSS if configured\r\n * - Generating source maps if configured\r\n * - Outputting to specified directory\r\n *\r\n * @param {AppConfig} config - The application configuration containing style settings\r\n * @param {Logger} logger - Logger instance for logging build progress and errors\r\n * @returns {Promise<{success: boolean, output: string} | null>} Build result with output path, or null if no style config\r\n * @throws {Error} If the build process fails\r\n *\r\n * @example\r\n * const result = await buildStyles(config, logger);\r\n * if (result?.success) {\r\n * console.log('Styles built to:', result.output);\r\n * }\r\n */\r\n async function buildStyles(config: AppConfig, logger: Logger) {\r\n if (!config.style) {\r\n logger.info('No style config provided, skipping style build');\r\n return null;\r\n }\r\n\r\n logger.info(`Building styles from: ${config.style.entry}...`);\r\n\r\n try {\r\n // Parse output path to get directory and filename\r\n const outputPath = config.style.output;\r\n const lastSlashIndex = outputPath.lastIndexOf('/');\r\n const outputDir = outputPath.substring(0, lastSlashIndex);\r\n const outputFilename = outputPath.substring(lastSlashIndex + 1);\r\n\r\n // Ensure output directory exists\r\n const { mkdir } = await import('fs/promises');\r\n await mkdir(outputDir, { recursive: true });\r\n\r\n // Use sass to compile SCSS to CSS\r\n const compileResult = sass.compile(config.style.entry, {\r\n style: config.style.minify ? 'compressed' : 'expanded',\r\n sourceMap: config.style.sourcemap ? true : false\r\n });\r\n\r\n // Write CSS file directly without JS wrapper\r\n await Bun.write(outputPath, compileResult.css);\r\n\r\n logger.info(`Compiled SCSS to CSS → ${outputFilename}`);\r\n logger.debug(`Styles built → ${outputPath}`);\r\n\r\n return { success: true, output: outputPath };\r\n } catch (err) {\r\n logger.error('Failed to build styles', (err as Error).message);\r\n return { success: false, output: config.style?.output || 'unknown' };\r\n }\r\n }\r\n\r\n /**\r\n * Initializes and configures database connections\r\n *\r\n * Handles:\r\n * - Multiple database instances (primary, cache, etc.)\r\n * - User-defined schemas from config\r\n * - Plugin schemas from plugins\r\n * - In-memory databases for plugin-only scenarios\r\n *\r\n * @param {AppConfig} config - Application configuration with database settings\r\n * @param {TableSchema[]} additionalSchemas - Database schemas provided by plugins\r\n * @param {Logger} logger - Logger instance for logging setup progress\r\n * @returns {Promise<Map<string, DB>>} Map of database instances by name\r\n * @throws {Error} If schema loading or database initialization fails\r\n *\r\n * @example\r\n * const databases = await setupDatabases(config, pluginSchemas, logger);\r\n * const db = databases.get('primary');\r\n * const result = db.query('SELECT * FROM users');\r\n */\r\n async function setupDatabases(\r\n config: AppConfig,\r\n additionalSchemas: TableSchema[],\r\n logger: Logger\r\n ) {\r\n const databases = new Map<string, DB>();\r\n\r\n if (!config.database && additionalSchemas.length === 0) {\r\n logger.info('No database config, skipping setup');\r\n return databases;\r\n }\r\n\r\n const dbConfigs = config.database\r\n ? Array.isArray(config.database)\r\n ? config.database\r\n : [config.database]\r\n : [];\r\n\r\n logger.info('Setting up databases...');\r\n\r\n for (const dbConfig of dbConfigs) {\r\n const name = dbConfig.name || 'default';\r\n const db = new DB(dbConfig.connection);\r\n\r\n // Load user schema\r\n if (dbConfig.schema) {\r\n try {\r\n const schemaModule = await import(dbConfig.schema);\r\n const schema = schemaModule.default || schemaModule.schema;\r\n\r\n if (Array.isArray(schema)) {\r\n for (const table of schema) {\r\n db.defineSchema(table);\r\n }\r\n }\r\n } catch (err) {\r\n logger.error(`Failed to load schema: ${dbConfig.schema}`, (err as Error).message);\r\n throw err;\r\n }\r\n }\r\n\r\n // Load plugin schemas for this database\r\n for (const schema of additionalSchemas) {\r\n db.defineSchema(schema);\r\n }\r\n\r\n databases.set(name, db);\r\n logger.debug(`Database '${name}' ready`);\r\n }\r\n\r\n // If no user database but plugins need one, create default\r\n if (databases.size === 0 && additionalSchemas.length > 0) {\r\n const db = new DB(':memory:');\r\n\r\n for (const schema of additionalSchemas) {\r\n db.defineSchema(schema);\r\n }\r\n\r\n databases.set('default', db);\r\n logger.debug(`Database 'default' ready (in-memory for plugins)`);\r\n }\r\n\r\n return databases;\r\n }\r\n\r\n /**\r\n * Initializes internationalization (i18n) support\r\n *\r\n * Loads language files and configures the i18n system based on:\r\n * - Default language\r\n * - Supported languages\r\n * - Base path for translation files\r\n * - File extension (json, cjson, etc.)\r\n *\r\n * @param {AppConfig} config - Application configuration with i18n settings\r\n * @param {Logger} logger - Logger instance for logging setup progress\r\n * @returns {Promise<void>}\r\n * @throws {Error} If i18n setup or language file loading fails\r\n *\r\n * @example\r\n * await setupI18n({\r\n * i18n: {\r\n * defaultLanguage: 'en',\r\n * supportedLanguages: ['en', 'ar'],\r\n * basePath: './src/i18n'\r\n * }\r\n * }, logger);\r\n */\r\n // async function setupI18n(config: AppConfig, logger: Logger) {\r\n // if (!config.i18n) return;\r\n\r\n // logger.info('Setting up i18n...');\r\n\r\n // try {\r\n // await _setupI18n({\r\n // defaultLanguage: config.i18n.defaultLanguage,\r\n // supportedLanguages: config.i18n.supportedLanguages,\r\n // basePath: config.i18n.basePath,\r\n // fileExtension: config.i18n.fileExtension || 'json'\r\n // });\r\n\r\n // logger.debug(`i18n ready → ${config.i18n.supportedLanguages.join(', ')}`);\r\n // } catch (err) {\r\n // logger.error('Failed to setup i18n', (err as Error).message);\r\n // throw err;\r\n // }\r\n // }\r\n\r\n /**\r\n * Dynamically loads route definitions from API directory\r\n *\r\n * Scans the specified directory for route files and imports them.\r\n * Each route file should export either:\r\n * - `routes` property with RouteDefinition array\r\n * - Default export with RouteDefinition array\r\n *\r\n * @param {AppConfig} config - Application configuration with api.directory\r\n * @param {Logger} logger - Logger instance for logging scan progress\r\n * @returns {Promise<RouteDefinition[]>} Array of loaded route definitions\r\n *\r\n * @example\r\n * const routes = await loadRoutes({\r\n * api: { directory: './src/server/api' }\r\n * }, logger);\r\n * console.log(`Loaded ${routes.length} routes`);\r\n */\r\n async function loadRoutes(config: AppConfig, logger: Logger): Promise<RouteDefinition[]> {\r\n if (!config.api) return [];\r\n\r\n logger.info(`Loading routes from ${config.api.directory}...`);\r\n\r\n const routes: RouteDefinition[] = [];\r\n\r\n try {\r\n const files = await scanDirectory(config.api.directory);\r\n\r\n for (const file of files) {\r\n try {\r\n const module = await import(file);\r\n const exported = module.default || module.routes;\r\n\r\n if (Array.isArray(exported)) {\r\n routes.push(...exported);\r\n }\r\n } catch (err) {\r\n logger.error(`Failed to load routes from ${file}`, (err as Error).message);\r\n }\r\n }\r\n\r\n logger.debug(`Routes loaded → ${routes.length} routes`);\r\n return routes;\r\n } catch (err) {\r\n logger.error('Failed to scan route directory', (err as Error).message);\r\n return [];\r\n }\r\n }\r\n\r\n /**\r\n * Scans a directory for TypeScript/JavaScript files\r\n *\r\n * Uses Bun's Glob API to recursively find all .ts and .js files in a directory.\r\n * Gracefully handles non-existent directories (returns empty array).\r\n *\r\n * @param {string} dir - Directory path to scan\r\n * @returns {Promise<string[]>} Array of absolute file paths found\r\n *\r\n * @example\r\n * const files = await scanDirectory('./src/server/api');\r\n * // Returns: ['./src/server/api/users.ts', './src/server/api/posts.ts']\r\n */\r\n async function scanDirectory(dir: string): Promise<string[]> {\r\n const files: string[] = [];\r\n\r\n try {\r\n const entries = await Array.fromAsync(\r\n new Bun.Glob('**/*.{ts,js}').scan(dir)\r\n );\r\n\r\n for (const entry of entries) {\r\n files.push(`${dir}/${entry}`);\r\n }\r\n } catch {\r\n // Directory doesn't exist\r\n }\r\n\r\n return files;\r\n }\r\n\r\n /**\r\n * Creates a CruxJS application instance with full lifecycle management\r\n *\r\n * This is the main entry point for building a CruxJS application. It:\r\n * 1. Registers plugins (Phase 0)\r\n * 2. Builds client, initializes databases, setups i18n (Phase 1: AWAKE)\r\n * 3. Creates server, merges routes and middleware (Phase 2: START)\r\n * 4. Starts the server and enables request handling (Phase 3: READY)\r\n *\r\n * Phases execute sequentially when `app.start()` is called.\r\n *\r\n * @param {AppConfig} userConfig - Application configuration object\r\n * @param {LifecycleHooks} [hooks] - Optional lifecycle event handlers\r\n * @returns {AppInstance} Application instance with control methods\r\n *\r\n * @throws {Error} Will throw if any lifecycle phase fails (unless caught in onError hook)\r\n *\r\n * @example\r\n * // Basic usage\r\n * const app = createApp({\r\n * debug: true,\r\n * server: { port: 3000 },\r\n * api: { directory: './src/api' },\r\n * plugins: [spaPlugin]\r\n * });\r\n * await app.start();\r\n *\r\n * @example\r\n * // With lifecycle hooks\r\n * const app = createApp(config, {\r\n * onAwake: async (ctx) => {\r\n * console.log('⏰ App awoken, databases ready');\r\n * },\r\n * onReady: async (ctx) => {\r\n * console.log('✅ Server ready:', ctx.server.getURL());\r\n * },\r\n * onError: async (ctx, phase, error) => {\r\n * console.error(`Error in ${phase}:`, error.message);\r\n * }\r\n * });\r\n *\r\n * @example\r\n * // With cleanup\r\n * const app = createApp(config);\r\n * await app.start();\r\n * // ... server is running\r\n * await app.stop(); // Cleanup\r\n */\r\n export function createApp(\r\n userConfig: AppConfig,\r\n hooks?: LifecycleHooks\r\n ): AppInstance {\r\n // Apply config hook\r\n const config: AppConfig = hooks?.onConfig\r\n ? hooks.onConfig(userConfig) as AppConfig\r\n : userConfig;\r\n\r\n const logger = new Logger(config.debug ? 'debug' : 'info', true, 'CruxJS');\r\n const pluginRegistry = new PluginRegistry(logger);\r\n const resourceMerger = new ResourceMerger(logger);\r\n\r\n const databases = new Map<string, DB>();\r\n const middlewares = new Map<string, AppMiddleware>();\r\n let serverInstance: any = null;\r\n let clientBuild: any = null;\r\n\r\n const ctx: LifecycleContext = {\r\n config,\r\n databases,\r\n plugins: []\r\n };\r\n\r\n // Create partial app instance for plugin registration\r\n const partialApp: AppInstance = {\r\n config,\r\n server: null,\r\n databases,\r\n plugins: [],\r\n middlewares,\r\n start: async () => { },\r\n stop: async () => { },\r\n restart: async () => { },\r\n getContext: () => ctx,\r\n getMiddleware: (name: string) => middlewares.get(name)\r\n };\r\n\r\n // ─────────────────────────────────────────────────────────\r\n // Phase 0: Plugin Registration\r\n // ─────────────────────────────────────────────────────────\r\n\r\n async function phaseRegister() {\r\n if (!config.plugins || config.plugins.length === 0) {\r\n logger.info('No plugins to register');\r\n return;\r\n }\r\n\r\n logger.debug('REGISTER');\r\n\r\n for (const plugin of config.plugins) {\r\n await pluginRegistry.register(plugin, partialApp);\r\n }\r\n\r\n ctx.plugins = pluginRegistry.getAll();\r\n\r\n // Collect plugin middlewares\r\n const pluginMiddlewares = pluginRegistry.collectMiddlewares();\r\n pluginMiddlewares.forEach((mw, name) => middlewares.set(name, mw));\r\n }\r\n\r\n // ─────────────────────────────────────────────────────────\r\n // Phase 1: AWAKE\r\n // ─────────────────────────────────────────────────────────\r\n\r\n async function phaseAwake() {\r\n logger.debug('AWAKE');\r\n\r\n try {\r\n // // Setup i18n\r\n // await setupI18n(config, logger);\r\n\r\n // Build client\r\n clientBuild = await buildClient(config, logger);\r\n ctx.clientBuild = clientBuild;\r\n\r\n // Build styles if configured\r\n const styleBuild = await buildStyles(config, logger);\r\n ctx.styleBuild = styleBuild;\r\n\r\n // Collect plugin schemas\r\n const pluginSchemas = pluginRegistry.collectSchemas();\r\n\r\n // Setup databases\r\n const dbs = await setupDatabases(config, pluginSchemas, logger);\r\n databases.clear();\r\n dbs.forEach((db, name) => databases.set(name, db));\r\n\r\n // Call plugin hooks\r\n await pluginRegistry.callHook('onAwake', ctx);\r\n\r\n // Call user hook\r\n await hooks?.onAwake?.(ctx);\r\n } catch (err) {\r\n await hooks?.onError?.(ctx, 'AWAKE', err as Error);\r\n throw err;\r\n }\r\n }\r\n\r\n // ─────────────────────────────────────────────────────────\r\n // Phase 2: START\r\n // ─────────────────────────────────────────────────────────\r\n\r\n async function phaseStart() {\r\n logger.debug('START');\r\n\r\n try {\r\n // Load user routes\r\n const userApiRoutes = await loadRoutes(config, logger);\r\n\r\n // Collect plugin routes\r\n const pluginRoutes = pluginRegistry.collectRoutes();\r\n\r\n // Merge user routes + plugin routes\r\n const userRoutes = config.routes || [];\r\n const allUserRoutes = [...userRoutes, ...userApiRoutes];\r\n const mergedRoutes = resourceMerger.mergeRoutes(allUserRoutes, pluginRoutes);\r\n\r\n // Collect static configs\r\n const userStatic = config.static\r\n ? Array.isArray(config.static)\r\n ? config.static\r\n : [config.static]\r\n : [];\r\n const pluginStatic = pluginRegistry.collectStatic();\r\n const mergedStatic = resourceMerger.mergeStatic(userStatic, pluginStatic);\r\n\r\n logger.info('Creating server...');\r\n\r\n // Collect error handlers from plugins (especially SPA plugin)\r\n let errorHandler: ((statusCode: number, path: string) => Response) | undefined;\r\n for (const plugin of ctx.plugins) {\r\n const pluginWithHandler = plugin as any;\r\n if (pluginWithHandler.__spaErrorHandler) {\r\n errorHandler = pluginWithHandler.__spaErrorHandler;\r\n console.log(`[CruxJS] ✓ Error handler collected from: ${plugin.name}`);\r\n break; // Use first error handler found\r\n }\r\n }\r\n\r\n // Create server\r\n serverInstance = await createServer({\r\n port: config.server?.port || 3000,\r\n hostname: config.server?.host || 'localhost',\r\n logging: config.server?.logging,\r\n routes: mergedRoutes,\r\n static: mergedStatic.length > 0 ? mergedStatic : undefined,\r\n security: config.security,\r\n middlewares: config.middlewares,\r\n i18n: config.i18n\r\n ? {\r\n defaultLanguage: config.i18n.defaultLanguage,\r\n supportedLanguages: config.i18n.supportedLanguages,\r\n basePath: config.i18n.basePath,\r\n fileExtension: config.i18n.fileExtension || 'json'\r\n }\r\n : undefined,\r\n onError: errorHandler\r\n });\r\n\r\n // Inject databases\r\n databases.forEach((db, name) => {\r\n serverInstance.db.set(name, db);\r\n });\r\n\r\n ctx.server = serverInstance;\r\n partialApp.server = serverInstance;\r\n\r\n logger.debug(\r\n `Server created → ${config.server?.host || 'localhost'}:${config.server?.port || 3000}`\r\n );\r\n\r\n // Call plugin hooks\r\n await pluginRegistry.callHook('onStart', ctx);\r\n\r\n // Call user hook\r\n await hooks?.onStart?.(ctx);\r\n } catch (err) {\r\n await hooks?.onError?.(ctx, 'START', err as Error);\r\n throw err;\r\n }\r\n }\r\n\r\n // ─────────────────────────────────────────────────────────\r\n // Phase 3: READY\r\n // ─────────────────────────────────────────────────────────\r\n\r\n async function phaseReady() {\r\n logger.debug('READY');\r\n\r\n try {\r\n await serverInstance.start();\r\n\r\n logger.debug(\r\n `Server running on http://${config.server?.host || 'localhost'}:${config.server?.port || 3000}`\r\n );\r\n\r\n // Call plugin hooks\r\n await pluginRegistry.callHook('onReady', ctx);\r\n\r\n // Call user hook\r\n await hooks?.onReady?.(ctx);\r\n } catch (err) {\r\n await hooks?.onError?.(ctx, 'READY', err as Error);\r\n throw err;\r\n }\r\n }\r\n\r\n // ─────────────────────────────────────────────────────────\r\n // App Instance\r\n // ─────────────────────────────────────────────────────────\r\n\r\n return {\r\n config,\r\n server: serverInstance,\r\n databases,\r\n plugins: ctx.plugins,\r\n middlewares,\r\n\r\n async start() {\r\n await phaseRegister();\r\n await phaseAwake();\r\n await phaseStart();\r\n await phaseReady();\r\n },\r\n\r\n async stop() {\r\n logger.info('Stopping server...');\r\n\r\n try {\r\n if (serverInstance) {\r\n await serverInstance.stop();\r\n }\r\n\r\n databases.forEach((db, name) => {\r\n db.close();\r\n logger.info(`Database '${name}' closed`);\r\n });\r\n\r\n // Call plugin hooks\r\n await pluginRegistry.callHook('onShutdown', ctx);\r\n\r\n await hooks?.onFinish?.(ctx);\r\n\r\n logger.debug('Server stopped');\r\n } catch (err) {\r\n logger.error('Failed to stop server', (err as Error).message);\r\n throw err;\r\n }\r\n },\r\n\r\n async restart() {\r\n await this.stop();\r\n await this.start();\r\n },\r\n\r\n getContext() {\r\n return ctx;\r\n },\r\n\r\n getMiddleware(name: string) {\r\n return middlewares.get(name);\r\n }\r\n };\r\n }\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ ════ ════════════════════════════════════════╗\r\n\r\n export * from '@cruxjs/base';\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝"]}
|
package/package.json
CHANGED