@almadar/server 1.0.13 → 1.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{index--XhXIuTh.d.ts → index-BGC2yptv.d.ts} +12 -1
- package/dist/index.d.ts +26 -2
- package/dist/index.js +583 -2
- package/dist/index.js.map +1 -1
- package/dist/lib/index.d.ts +1 -1
- package/dist/lib/index.js +43 -0
- package/dist/lib/index.js.map +1 -1
- package/dist/middleware/index.js +21 -1
- package/dist/middleware/index.js.map +1 -1
- package/dist/stores/index.d.ts +167 -0
- package/dist/stores/index.js +594 -0
- package/dist/stores/index.js.map +1 -0
- package/package.json +7 -2
package/dist/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/lib/env.ts","../../src/lib/db.ts","../../src/lib/logger.ts","../../src/lib/eventBus.ts","../../src/lib/websocket.ts"],"names":["db"],"mappings":";;;;;;;;;;;;AAIA,MAAA,CAAO,MAAA,EAAO;AAEd,IAAM,SAAA,GAAY,EAAE,MAAA,CAAO;AAAA,EACzB,QAAA,EAAU,CAAA,CAAE,IAAA,CAAK,CAAC,aAAA,EAAe,cAAc,MAAM,CAAC,CAAA,CAAE,OAAA,CAAQ,aAAa,CAAA;AAAA,EAC7E,IAAA,EAAM,CAAA,CACH,MAAA,EAAO,CACP,OAAA,CAAQ,MAAM,CAAA,CACd,SAAA,CAAU,CAAC,GAAA,KAAQ,QAAA,CAAS,GAAA,EAAK,EAAE,CAAC,CAAA;AAAA,EACvC,WAAA,EAAa,CAAA,CACV,MAAA,EAAO,CACP,OAAA,CAAQ,uBAAuB,CAAA,CAC/B,SAAA,CAAU,CAAC,GAAA,KAAS,GAAA,CAAI,QAAA,CAAS,GAAG,CAAA,GAAI,GAAA,CAAI,KAAA,CAAM,GAAG,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,EAAM,CAAA,GAAI,GAAI,CAAA;AAAA;AAAA,EAGrF,YAAA,EAAc,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA;AAAA,EAGlC,mBAAA,EAAqB,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EACzC,qBAAA,EAAuB,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EAC3C,oBAAA,EAAsB,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EAC1C,6BAAA,EAA+B,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EACnD,uBAAA,EAAyB,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EAC7C,2BAAA,EAA6B,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA;AAAA,EAGjD,UAAA,EAAY,CAAA,CAAE,MAAA,EAAO,CAAE,QAAQ,MAAM,CAAA;AAAA;AAAA,EAGrC,aAAA,EAAe,CAAA,CAAE,MAAA,EAAO,CAAE,OAAA,CAAQ,MAAM,CAAA,CAAE,SAAA,CAAU,CAAC,CAAA,KAAM,CAAA,KAAM,MAAM,CAAA;AAAA,EACvE,SAAA,EAAW,CAAA,CACR,MAAA,EAAO,CACP,UAAS,CACT,SAAA,CAAU,CAAC,CAAA,KAAO,CAAA,GAAI,QAAA,CAAS,CAAA,EAAG,EAAE,IAAI,MAAU;AACvD,CAAC,CAAA;AAED,IAAM,MAAA,GAAS,SAAA,CAAU,SAAA,CAAU,OAAA,CAAQ,GAAG,CAAA;AAE9C,IAAI,CAAC,OAAO,OAAA,EAAS;AACnB,EAAA,OAAA,CAAQ,MAAM,uCAAA,EAAoC,MAAA,CAAO,KAAA,CAAM,OAAA,GAAU,WAAW,CAAA;AACpF,EAAA,MAAM,IAAI,MAAM,+BAA+B,CAAA;AACjD;AAEO,IAAM,MAAM,MAAA,CAAO;AClC1B,IAAI,WAAA,GAAoC,IAAA;AAKxC,SAAS,kBAAA,GAAoC;AAC3C,EAAA,IAAI,WAAA,EAAa;AACf,IAAA,OAAO,WAAA;AAAA,EACT;AAGA,EAAA,IAAI,KAAA,CAAM,IAAA,CAAK,MAAA,GAAS,CAAA,EAAG;AACzB,IAAA,WAAA,GAAc,KAAA,CAAM,KAAK,CAAC,CAAA;AAC1B,IAAA,OAAO,WAAA;AAAA,EACT;AAGA,EAAA,IAAI,IAAI,uBAAA,EAAyB;AAE/B,IAAA,WAAA,GAAc,MAAM,aAAA,CAAc;AAAA,MAChC,SAAA,EAAW,IAAI,mBAAA,IAAuB;AAAA,KACvC,CAAA;AACD,IAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,mDAAA,EAA+C,GAAA,CAAI,uBAAuB,CAAA,CAAE,CAAA;AACxF,IAAA,OAAO,WAAA;AAAA,EACT;AAGA,EAAA,MAAM,qBAAqB,GAAA,CAAI,6BAAA;AAE/B,EAAA,IAAI,kBAAA,EAAoB;AAGtB,IAAA,MAAM,cAAA,GAAiB,UAAQ,kBAAkB,CAAA;AACjD,IAAA,WAAA,GAAc,MAAM,aAAA,CAAc;AAAA,MAChC,UAAA,EAAY,KAAA,CAAM,UAAA,CAAW,IAAA,CAAK,cAAc,CAAA;AAAA,MAChD,WAAW,GAAA,CAAI;AAAA,KAChB,CAAA;AAAA,EACH,WAAW,GAAA,CAAI,mBAAA,IAAuB,GAAA,CAAI,qBAAA,IAAyB,IAAI,oBAAA,EAAsB;AAE3F,IAAA,WAAA,GAAc,MAAM,aAAA,CAAc;AAAA,MAChC,UAAA,EAAY,KAAA,CAAM,UAAA,CAAW,IAAA,CAAK;AAAA,QAChC,WAAW,GAAA,CAAI,mBAAA;AAAA,QACf,aAAa,GAAA,CAAI,qBAAA;AAAA,QACjB,UAAA,EAAY,GAAA,CAAI,oBAAA,CAAqB,OAAA,CAAQ,QAAQ,IAAI;AAAA,OAC1D,CAAA;AAAA,MACD,WAAW,GAAA,CAAI;AAAA,KAChB,CAAA;AAAA,EACH,CAAA,MAAA,IAAW,IAAI,mBAAA,EAAqB;AAElC,IAAA,WAAA,GAAc,MAAM,aAAA,CAAc;AAAA,MAChC,UAAA,EAAY,KAAA,CAAM,UAAA,CAAW,kBAAA,EAAmB;AAAA,MAChD,WAAW,GAAA,CAAI;AAAA,KAChB,CAAA;AAAA,EACH,CAAA,MAAO;AAEL,IAAA,WAAA,GAAc,MAAM,aAAA,CAAc;AAAA,MAChC,SAAA,EAAW;AAAA,KACZ,CAAA;AAAA,EACH;AAEA,EAAA,OAAO,WAAA;AACT;AAKO,SAAS,YAAA,GAA0C;AACxD,EAAA,MAAM,MAAM,kBAAA,EAAmB;AAC/B,EAAA,MAAMA,GAAAA,GAAK,IAAI,SAAA,EAAU;AAGzB,EAAA,IAAI,IAAI,uBAAA,EAAyB;AAC/B,IAAAA,IAAG,QAAA,CAAS;AAAA,MACV,MAAM,GAAA,CAAI,uBAAA;AAAA,MACV,GAAA,EAAK;AAAA,KACN,CAAA;AAAA,EACH;AAEA,EAAA,OAAOA,GAAAA;AACT;AAKO,SAAS,OAAA,GAA2B;AACzC,EAAA,MAAM,MAAM,kBAAA,EAAmB;AAC/B,EAAA,OAAO,IAAI,IAAA,EAAK;AAClB;AA8BkB,YAAA;;;AC7HlB,IAAM,MAAA,GAAS;AAAA,EACb,KAAA,EAAO,UAAA;AAAA;AAAA,EACP,IAAA,EAAM,UAAA;AAAA;AAAA,EACN,IAAA,EAAM,UAAA;AAAA;AAAA,EACN,KAAA,EAAO,UAAA;AAAA;AAAA,EACP,KAAA,EAAO;AACT,CAAA;AAEA,IAAM,SAAA,GAAY,CAAC,KAAA,KAA6B;AAC9C,EAAA,MAAM,MAAA,GAAqB,CAAC,OAAA,EAAS,MAAA,EAAQ,QAAQ,OAAO,CAAA;AAC5D,EAAA,MAAM,QAAA,GAAW,GAAA,CAAI,QAAA,KAAa,YAAA,GAAe,MAAA,GAAS,OAAA;AAC1D,EAAA,OAAO,OAAO,OAAA,CAAQ,KAAK,CAAA,IAAK,MAAA,CAAO,QAAQ,QAAQ,CAAA;AACzD,CAAA;AAEA,IAAM,aAAA,GAAgB,CAAC,KAAA,EAAiB,OAAA,EAAiB,IAAA,KAA2B;AAClF,EAAA,MAAM,SAAA,GAAA,iBAAY,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AACzC,EAAA,MAAM,KAAA,GAAQ,OAAO,KAAK,CAAA;AAC1B,EAAA,MAAM,MAAA,GAAS,GAAG,KAAK,CAAA,CAAA,EAAI,MAAM,WAAA,EAAa,CAAA,CAAA,EAAI,MAAA,CAAO,KAAK,CAAA,CAAA;AAC9D,EAAA,MAAM,UAAU,IAAA,GAAO,CAAA,CAAA,EAAI,KAAK,SAAA,CAAU,IAAI,CAAC,CAAA,CAAA,GAAK,EAAA;AACpD,EAAA,OAAO,GAAG,SAAS,CAAA,CAAA,EAAI,MAAM,CAAA,CAAA,EAAI,OAAO,GAAG,OAAO,CAAA,CAAA;AACpD,CAAA;AAEO,IAAM,MAAA,GAAS;AAAA,EACpB,KAAA,EAAO,CAAC,OAAA,EAAiB,IAAA,KAAmB;AAC1C,IAAA,IAAI,SAAA,CAAU,OAAO,CAAA,EAAG;AACtB,MAAA,OAAA,CAAQ,GAAA,CAAI,aAAA,CAAc,OAAA,EAAS,OAAA,EAAS,IAAI,CAAC,CAAA;AAAA,IACnD;AAAA,EACF,CAAA;AAAA,EACA,IAAA,EAAM,CAAC,OAAA,EAAiB,IAAA,KAAmB;AACzC,IAAA,IAAI,SAAA,CAAU,MAAM,CAAA,EAAG;AACrB,MAAA,OAAA,CAAQ,GAAA,CAAI,aAAA,CAAc,MAAA,EAAQ,OAAA,EAAS,IAAI,CAAC,CAAA;AAAA,IAClD;AAAA,EACF,CAAA;AAAA,EACA,IAAA,EAAM,CAAC,OAAA,EAAiB,IAAA,KAAmB;AACzC,IAAA,IAAI,SAAA,CAAU,MAAM,CAAA,EAAG;AACrB,MAAA,OAAA,CAAQ,IAAA,CAAK,aAAA,CAAc,MAAA,EAAQ,OAAA,EAAS,IAAI,CAAC,CAAA;AAAA,IACnD;AAAA,EACF,CAAA;AAAA,EACA,KAAA,EAAO,CAAC,OAAA,EAAiB,IAAA,KAAmB;AAC1C,IAAA,IAAI,SAAA,CAAU,OAAO,CAAA,EAAG;AACtB,MAAA,OAAA,CAAQ,KAAA,CAAM,aAAA,CAAc,OAAA,EAAS,OAAA,EAAS,IAAI,CAAC,CAAA;AAAA,IACrD;AAAA,EACF;AACF;;;AC/BO,IAAM,WAAN,MAAe;AAAA,EACZ,QAAA,uBAA+C,GAAA,EAAI;AAAA,EACnD,KAAA;AAAA,EAER,YAAY,OAAA,EAA+B;AACzC,IAAA,IAAA,CAAK,KAAA,GAAQ,SAAS,KAAA,IAAS,KAAA;AAAA,EACjC;AAAA,EAEA,EAAA,CAAG,OAAe,OAAA,EAAmC;AACnD,IAAA,IAAI,CAAC,IAAA,CAAK,QAAA,CAAS,GAAA,CAAI,KAAK,CAAA,EAAG;AAC7B,MAAA,IAAA,CAAK,QAAA,CAAS,GAAA,CAAI,KAAA,kBAAO,IAAI,KAAK,CAAA;AAAA,IACpC;AACA,IAAA,IAAA,CAAK,QAAA,CAAS,GAAA,CAAI,KAAK,CAAA,CAAG,IAAI,OAAO,CAAA;AAGrC,IAAA,OAAO,MAAM;AACX,MAAA,IAAA,CAAK,QAAA,CAAS,GAAA,CAAI,KAAK,CAAA,EAAG,OAAO,OAAO,CAAA;AAAA,IAC1C,CAAA;AAAA,EACF;AAAA,EAEA,GAAA,CAAI,OAAe,OAAA,EAA6B;AAC9C,IAAA,IAAA,CAAK,QAAA,CAAS,GAAA,CAAI,KAAK,CAAA,EAAG,OAAO,OAAO,CAAA;AAAA,EAC1C;AAAA,EAEA,IAAA,CAAK,KAAA,EAAe,OAAA,EAAmB,IAAA,EAAsC;AAC3E,IAAA,IAAI,KAAK,KAAA,EAAO;AACd,MAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,oBAAA,EAAuB,KAAK,CAAA,CAAA,CAAA,EAAK,OAAO,CAAA;AAAA,IACtD;AAEA,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,QAAA,CAAS,GAAA,CAAI,KAAK,CAAA;AACxC,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,QAAA,CAAS,QAAQ,CAAA,OAAA,KAAW;AAC1B,QAAA,IAAI;AACF,UAAA,OAAA,CAAQ,SAAS,IAAI,CAAA;AAAA,QACvB,SAAS,GAAA,EAAK;AACZ,UAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,gCAAA,EAAmC,KAAK,CAAA,CAAA,CAAA,EAAK,GAAG,CAAA;AAAA,QAChE;AAAA,MACF,CAAC,CAAA;AAAA,IACH;AAAA,EACF;AAAA,EAEA,KAAA,GAAc;AACZ,IAAA,IAAA,CAAK,SAAS,KAAA,EAAM;AAAA,EACtB;AACF,CAAA;AAKO,IAAM,cAAA,GAAiB,IAAI,QAAA,CAAS;AAAA,EACzC,KAAA,EAAO,OAAA,CAAQ,GAAA,CAAI,QAAA,KAAa;AAClC,CAAC;AAKM,SAAS,eAAA,CACd,UAAA,EACA,MAAA,EACA,OAAA,EACM;AACN,EAAA,MAAM,YAAY,CAAA,EAAG,UAAA,CAAW,WAAA,EAAa,IAAI,MAAM,CAAA,CAAA;AACvD,EAAA,cAAA,CAAe,KAAK,SAAA,EAAW,OAAA,EAAS,EAAE,OAAA,EAAS,YAAY,CAAA;AACjE;ACpDA,IAAI,GAAA,GAA8B,IAAA;AAmB3B,SAAS,mBAAA,CAAoB,MAAA,EAAgB,IAAA,GAAe,YAAA,EAA+B;AAChG,EAAA,IAAI,GAAA,EAAK;AACP,IAAA,MAAA,CAAO,KAAK,wCAAwC,CAAA;AACpD,IAAA,OAAO,GAAA;AAAA,EACT;AAEA,EAAA,GAAA,GAAM,IAAI,eAAA,CAAgB,EAAE,MAAA,EAAQ,MAAM,CAAA;AAE1C,EAAA,MAAA,CAAO,IAAA,CAAK,CAAA,gCAAA,EAAmC,IAAI,CAAA,CAAE,CAAA;AAGrD,EAAA,GAAA,CAAI,EAAA,CAAG,YAAA,EAAc,CAAC,EAAA,EAAe,GAAA,KAAyB;AAC5D,IAAA,MAAM,QAAA,GAAW,GAAA,CAAI,OAAA,CAAQ,mBAAmB,CAAA,IAAK,SAAA;AACrD,IAAA,MAAA,CAAO,KAAA,CAAM,CAAA,8BAAA,EAAiC,QAAQ,CAAA,CAAE,CAAA;AAGxD,IAAA,EAAA,CAAG,IAAA;AAAA,MACD,KAAK,SAAA,CAAU;AAAA,QACb,IAAA,EAAM,WAAA;AAAA,QACN,SAAA,EAAW,KAAK,GAAA,EAAI;AAAA,QACpB,OAAA,EAAS;AAAA,OACV;AAAA,KACH;AAGA,IAAA,EAAA,CAAG,EAAA,CAAG,SAAA,EAAW,CAAC,IAAA,KAAkB;AAClC,MAAA,IAAI;AACF,QAAA,MAAM,OAAA,GAAU,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,UAAU,CAAA;AAC1C,QAAA,MAAA,CAAO,KAAA,CAAM,CAAA,0BAAA,EAA6B,QAAQ,CAAA,CAAA,CAAA,EAAK,OAAO,CAAA;AAG9D,QAAA,IAAI,OAAA,CAAQ,IAAA,IAAQ,OAAA,CAAQ,OAAA,EAAS;AAEnC,UAAA,cAAA,CAAe,IAAA,CAAK,OAAA,CAAQ,IAAA,EAAM,OAAA,CAAQ,OAAA,EAAS;AAAA,YACjD,OAAA,EAAS,QAAA;AAAA,YACT,MAAA,EAAQ;AAAA,WACT,CAAA;AAAA,QACH;AAAA,MACF,SAAS,KAAA,EAAO;AACd,QAAA,MAAA,CAAO,KAAA,CAAM,wCAAwC,KAAK,CAAA;AAAA,MAC5D;AAAA,IACF,CAAC,CAAA;AAED,IAAA,EAAA,CAAG,EAAA,CAAG,SAAS,MAAM;AACnB,MAAA,MAAA,CAAO,KAAA,CAAM,CAAA,iCAAA,EAAoC,QAAQ,CAAA,CAAE,CAAA;AAAA,IAC7D,CAAC,CAAA;AAED,IAAA,EAAA,CAAG,EAAA,CAAG,OAAA,EAAS,CAAC,KAAA,KAAiB;AAC/B,MAAA,MAAA,CAAO,KAAA,CAAM,6BAA6B,KAAK,CAAA;AAAA,IACjD,CAAC,CAAA;AAAA,EACH,CAAC,CAAA;AAGD,EAAA,cAAA,CAAe,EAAA,CAAG,GAAA,EAAK,CAAC,KAAA,KAAmB;AACzC,IAAA,IAAI,CAAC,GAAA,EAAK;AAEV,IAAA,MAAM,UAAA,GAAa,KAAA;AACnB,IAAA,MAAM,OAAA,GAAU,KAAK,SAAA,CAAU;AAAA,MAC7B,MAAM,UAAA,CAAW,IAAA;AAAA,MACjB,SAAS,UAAA,CAAW,OAAA;AAAA,MACpB,WAAW,UAAA,CAAW,SAAA;AAAA,MACtB,QAAQ,UAAA,CAAW;AAAA,KACpB,CAAA;AAED,IAAA,IAAI,cAAA,GAAiB,CAAA;AACrB,IAAA,GAAA,CAAI,OAAA,CAAQ,OAAA,CAAQ,CAAC,MAAA,KAAsB;AACzC,MAAA,IAAI,MAAA,CAAO,UAAA,KAAe,SAAA,CAAU,IAAA,EAAM;AACxC,QAAA,MAAA,CAAO,KAAK,OAAO,CAAA;AACnB,QAAA,cAAA,EAAA;AAAA,MACF;AAAA,IACF,CAAC,CAAA;AAED,IAAA,IAAI,iBAAiB,CAAA,EAAG;AACtB,MAAA,MAAA,CAAO,MAAM,CAAA,sBAAA,EAAyB,UAAA,CAAW,IAAI,CAAA,IAAA,EAAO,cAAc,CAAA,UAAA,CAAY,CAAA;AAAA,IACxF;AAAA,EACF,CAAC,CAAA;AAED,EAAA,OAAO,GAAA;AACT;AAKO,SAAS,kBAAA,GAA6C;AAC3D,EAAA,OAAO,GAAA;AACT;AAKO,SAAS,oBAAA,GAAsC;AACpD,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,EAAS,MAAA,KAAW;AACtC,IAAA,IAAI,CAAC,GAAA,EAAK;AACR,MAAA,OAAA,EAAQ;AACR,MAAA;AAAA,IACF;AAEA,IAAA,GAAA,CAAI,KAAA,CAAM,CAAC,GAAA,KAAgB;AACzB,MAAA,IAAI,GAAA,EAAK;AACP,QAAA,MAAA,CAAO,GAAG,CAAA;AAAA,MACZ,CAAA,MAAO;AACL,QAAA,GAAA,GAAM,IAAA;AACN,QAAA,OAAA,EAAQ;AAAA,MACV;AAAA,IACF,CAAC,CAAA;AAAA,EACH,CAAC,CAAA;AACH;AAKO,SAAS,uBAAA,GAAkC;AAChD,EAAA,IAAI,CAAC,KAAK,OAAO,CAAA;AACjB,EAAA,OAAO,IAAI,OAAA,CAAQ,IAAA;AACrB","file":"index.js","sourcesContent":["import { z } from 'zod';\nimport dotenv from 'dotenv';\n\n// Load environment variables\ndotenv.config();\n\nconst envSchema = z.object({\n NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),\n PORT: z\n .string()\n .default('3030')\n .transform((val) => parseInt(val, 10)),\n CORS_ORIGIN: z\n .string()\n .default('http://localhost:5173')\n .transform((val) => (val.includes(',') ? val.split(',').map((s) => s.trim()) : val)),\n \n // Database (Prisma/SQL) - optional\n DATABASE_URL: z.string().optional(),\n \n // Firebase/Firestore configuration\n FIREBASE_PROJECT_ID: z.string().optional(),\n FIREBASE_CLIENT_EMAIL: z.string().optional(),\n FIREBASE_PRIVATE_KEY: z.string().optional(),\n FIREBASE_SERVICE_ACCOUNT_PATH: z.string().optional(),\n FIRESTORE_EMULATOR_HOST: z.string().optional(),\n FIREBASE_AUTH_EMULATOR_HOST: z.string().optional(),\n \n // API configuration\n API_PREFIX: z.string().default('/api'),\n\n // Mock data configuration\n USE_MOCK_DATA: z.string().default('true').transform((v) => v === 'true'),\n MOCK_SEED: z\n .string()\n .optional()\n .transform((v) => (v ? parseInt(v, 10) : undefined)),\n});\n\nconst parsed = envSchema.safeParse(process.env);\n\nif (!parsed.success) {\n console.error('❌ Invalid environment variables:', parsed.error.flatten().fieldErrors);\n throw new Error('Invalid environment variables');\n}\n\nexport const env = parsed.data;\n","/**\n * Database Configuration\n * \n * Supports both Prisma (SQL) and Firebase Admin (Firestore)\n * Generated apps can use either depending on the database option\n */\n\nimport admin from 'firebase-admin';\nimport { env } from './env.js';\n\n// ============ Firebase Admin / Firestore ============\n\nlet firebaseApp: admin.app.App | null = null;\n\n/**\n * Initialize Firebase Admin SDK\n */\nfunction initializeFirebase(): admin.app.App {\n if (firebaseApp) {\n return firebaseApp;\n }\n\n // Check if already initialized\n if (admin.apps.length > 0) {\n firebaseApp = admin.apps[0]!;\n return firebaseApp;\n }\n\n // Check for emulator mode FIRST (no credentials needed)\n if (env.FIRESTORE_EMULATOR_HOST) {\n // Emulator mode - no credentials needed\n firebaseApp = admin.initializeApp({\n projectId: env.FIREBASE_PROJECT_ID || 'demo-project',\n });\n console.log(`🔧 Firebase Admin initialized for emulator: ${env.FIRESTORE_EMULATOR_HOST}`);\n return firebaseApp;\n }\n\n // Production mode - need credentials\n const serviceAccountPath = env.FIREBASE_SERVICE_ACCOUNT_PATH;\n \n if (serviceAccountPath) {\n // Use service account file\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const serviceAccount = require(serviceAccountPath);\n firebaseApp = admin.initializeApp({\n credential: admin.credential.cert(serviceAccount),\n projectId: env.FIREBASE_PROJECT_ID,\n });\n } else if (env.FIREBASE_PROJECT_ID && env.FIREBASE_CLIENT_EMAIL && env.FIREBASE_PRIVATE_KEY) {\n // Use inline service account credentials\n firebaseApp = admin.initializeApp({\n credential: admin.credential.cert({\n projectId: env.FIREBASE_PROJECT_ID,\n clientEmail: env.FIREBASE_CLIENT_EMAIL,\n privateKey: env.FIREBASE_PRIVATE_KEY.replace(/\\\\n/g, '\\n'),\n }),\n projectId: env.FIREBASE_PROJECT_ID,\n });\n } else if (env.FIREBASE_PROJECT_ID) {\n // Use application default credentials (for Cloud Run, etc.)\n firebaseApp = admin.initializeApp({\n credential: admin.credential.applicationDefault(),\n projectId: env.FIREBASE_PROJECT_ID,\n });\n } else {\n // Emulator mode - use default credentials\n firebaseApp = admin.initializeApp({\n projectId: 'demo-project',\n });\n }\n\n return firebaseApp;\n}\n\n/**\n * Get Firestore instance\n */\nexport function getFirestore(): admin.firestore.Firestore {\n const app = initializeFirebase();\n const db = app.firestore();\n \n // Connect to emulator if configured\n if (env.FIRESTORE_EMULATOR_HOST) {\n db.settings({\n host: env.FIRESTORE_EMULATOR_HOST,\n ssl: false,\n });\n }\n \n return db;\n}\n\n/**\n * Get Firebase Auth instance\n */\nexport function getAuth(): admin.auth.Auth {\n const app = initializeFirebase();\n return app.auth();\n}\n\n// ============ Prisma (Optional - for SQL databases) ============\n\n// Uncomment if using Prisma with SQL database\n/*\nimport { PrismaClient } from '@prisma/client';\n\ndeclare global {\n // eslint-disable-next-line no-var\n var __db: PrismaClient | undefined;\n}\n\nconst createPrismaClient = () => {\n return new PrismaClient({\n log: env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'],\n });\n};\n\nexport const db = globalThis.__db ?? createPrismaClient();\n\nif (env.NODE_ENV !== 'production') {\n globalThis.__db = db;\n}\n*/\n\n// Re-export admin for convenience\nexport { admin };\n\n// Export db instance for handler convenience\nexport const db = getFirestore();\n","import { env } from './env.js';\n\ntype LogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nconst colors = {\n debug: '\\x1b[36m', // Cyan\n info: '\\x1b[32m', // Green\n warn: '\\x1b[33m', // Yellow\n error: '\\x1b[31m', // Red\n reset: '\\x1b[0m',\n};\n\nconst shouldLog = (level: LogLevel): boolean => {\n const levels: LogLevel[] = ['debug', 'info', 'warn', 'error'];\n const minLevel = env.NODE_ENV === 'production' ? 'info' : 'debug';\n return levels.indexOf(level) >= levels.indexOf(minLevel);\n};\n\nconst formatMessage = (level: LogLevel, message: string, meta?: unknown): string => {\n const timestamp = new Date().toISOString();\n const color = colors[level];\n const prefix = `${color}[${level.toUpperCase()}]${colors.reset}`;\n const metaStr = meta ? ` ${JSON.stringify(meta)}` : '';\n return `${timestamp} ${prefix} ${message}${metaStr}`;\n};\n\nexport const logger = {\n debug: (message: string, meta?: unknown) => {\n if (shouldLog('debug')) {\n console.log(formatMessage('debug', message, meta));\n }\n },\n info: (message: string, meta?: unknown) => {\n if (shouldLog('info')) {\n console.log(formatMessage('info', message, meta));\n }\n },\n warn: (message: string, meta?: unknown) => {\n if (shouldLog('warn')) {\n console.warn(formatMessage('warn', message, meta));\n }\n },\n error: (message: string, meta?: unknown) => {\n if (shouldLog('error')) {\n console.error(formatMessage('error', message, meta));\n }\n },\n};\n","/**\n * Server EventBus - Singleton for server-side cross-trait communication\n *\n * This EventBus enables:\n * - Server-side trait event emission after CRUD operations\n * - Server-side trait listeners responding to events\n * - Cross-client event broadcast via WebSocket\n *\n * @packageDocumentation\n */\n\ntype EventHandler = (payload: unknown, meta?: Record<string, unknown>) => void;\n\n/**\n * Simple EventBus implementation for server-side events\n */\nexport class EventBus {\n private handlers: Map<string, Set<EventHandler>> = new Map();\n private debug: boolean;\n\n constructor(options?: { debug?: boolean }) {\n this.debug = options?.debug ?? false;\n }\n\n on(event: string, handler: EventHandler): () => void {\n if (!this.handlers.has(event)) {\n this.handlers.set(event, new Set());\n }\n this.handlers.get(event)!.add(handler);\n\n // Return unsubscribe function\n return () => {\n this.handlers.get(event)?.delete(handler);\n };\n }\n\n off(event: string, handler: EventHandler): void {\n this.handlers.get(event)?.delete(handler);\n }\n\n emit(event: string, payload?: unknown, meta?: Record<string, unknown>): void {\n if (this.debug) {\n console.log(`[EventBus] Emitting ${event}:`, payload);\n }\n\n const handlers = this.handlers.get(event);\n if (handlers) {\n handlers.forEach(handler => {\n try {\n handler(payload, meta);\n } catch (err) {\n console.error(`[EventBus] Error in handler for ${event}:`, err);\n }\n });\n }\n }\n\n clear(): void {\n this.handlers.clear();\n }\n}\n\n/**\n * Singleton EventBus instance for server-side event communication.\n */\nexport const serverEventBus = new EventBus({\n debug: process.env.NODE_ENV === 'development',\n});\n\n/**\n * Type-safe event emission helper\n */\nexport function emitEntityEvent(\n entityType: string,\n action: 'CREATED' | 'UPDATED' | 'DELETED',\n payload: Record<string, unknown>\n): void {\n const eventType = `${entityType.toUpperCase()}_${action}`;\n serverEventBus.emit(eventType, payload, { orbital: entityType });\n}\n","/**\n * WebSocket Event Broadcast - Cross-client event synchronization\n *\n * Broadcasts server-side events to all connected clients via WebSocket.\n * This enables real-time updates across multiple browser clients.\n *\n * @packageDocumentation\n */\n\nimport { WebSocketServer, WebSocket, type RawData } from 'ws';\nimport type { Server, IncomingMessage } from 'http';\nimport { serverEventBus } from './eventBus.js';\nimport { logger } from './logger.js';\n\n/**\n * Event structure for broadcasting\n */\ninterface BroadcastEvent {\n type: string;\n payload?: unknown;\n timestamp?: number;\n source?: Record<string, unknown>;\n}\n\n/**\n * WebSocket server instance (singleton)\n */\nlet wss: WebSocketServer | null = null;\n\n/**\n * Setup WebSocket server for event broadcasting.\n *\n * Listens to all server events via wildcard and broadcasts to connected clients.\n *\n * @param server - HTTP server to attach WebSocket to\n * @param path - WebSocket endpoint path (default: '/ws/events')\n *\n * @example\n * ```typescript\n * import { createServer } from 'http';\n * import { setupEventBroadcast } from '@/lib/websocket';\n *\n * const server = createServer(app);\n * setupEventBroadcast(server);\n * ```\n */\nexport function setupEventBroadcast(server: Server, path: string = '/ws/events'): WebSocketServer {\n if (wss) {\n logger.warn('[WebSocket] Server already initialized');\n return wss;\n }\n\n wss = new WebSocketServer({ server, path });\n\n logger.info(`[WebSocket] Server listening at ${path}`);\n\n // Handle new connections\n wss.on('connection', (ws: WebSocket, req: IncomingMessage) => {\n const clientId = req.headers['sec-websocket-key'] || 'unknown';\n logger.debug(`[WebSocket] Client connected: ${clientId}`);\n\n // Send welcome message\n ws.send(\n JSON.stringify({\n type: 'CONNECTED',\n timestamp: Date.now(),\n message: 'Connected to event stream',\n })\n );\n\n // Handle client messages (for future bidirectional communication)\n ws.on('message', (data: RawData) => {\n try {\n const message = JSON.parse(data.toString());\n logger.debug(`[WebSocket] Received from ${clientId}:`, message);\n\n // Handle client-to-server events if needed\n if (message.type && message.payload) {\n // Emit to server event bus with client source\n serverEventBus.emit(message.type, message.payload, {\n orbital: 'client',\n entity: clientId,\n });\n }\n } catch (error) {\n logger.error(`[WebSocket] Failed to parse message:`, error);\n }\n });\n\n ws.on('close', () => {\n logger.debug(`[WebSocket] Client disconnected: ${clientId}`);\n });\n\n ws.on('error', (error: Error) => {\n logger.error(`[WebSocket] Client error:`, error);\n });\n });\n\n // Subscribe to all server events and broadcast to clients\n serverEventBus.on('*', (event: unknown) => {\n if (!wss) return;\n\n const typedEvent = event as BroadcastEvent;\n const message = JSON.stringify({\n type: typedEvent.type,\n payload: typedEvent.payload,\n timestamp: typedEvent.timestamp,\n source: typedEvent.source,\n });\n\n let broadcastCount = 0;\n wss.clients.forEach((client: WebSocket) => {\n if (client.readyState === WebSocket.OPEN) {\n client.send(message);\n broadcastCount++;\n }\n });\n\n if (broadcastCount > 0) {\n logger.debug(`[WebSocket] Broadcast ${typedEvent.type} to ${broadcastCount} client(s)`);\n }\n });\n\n return wss;\n}\n\n/**\n * Get the WebSocket server instance (for testing or advanced usage)\n */\nexport function getWebSocketServer(): WebSocketServer | null {\n return wss;\n}\n\n/**\n * Close the WebSocket server\n */\nexport function closeWebSocketServer(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!wss) {\n resolve();\n return;\n }\n\n wss.close((err?: Error) => {\n if (err) {\n reject(err);\n } else {\n wss = null;\n resolve();\n }\n });\n });\n}\n\n/**\n * Get connected client count\n */\nexport function getConnectedClientCount(): number {\n if (!wss) return 0;\n return wss.clients.size;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/lib/env.ts","../../src/lib/db.ts","../../src/lib/logger.ts","../../src/lib/eventBus.ts","../../src/lib/websocket.ts"],"names":["db"],"mappings":"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","file":"index.js","sourcesContent":["import { z } from 'zod';\nimport dotenv from 'dotenv';\n\n// Load environment variables\ndotenv.config();\n\nconst envSchema = z.object({\n NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),\n PORT: z\n .string()\n .default('3030')\n .transform((val) => parseInt(val, 10)),\n CORS_ORIGIN: z\n .string()\n .default('http://localhost:5173')\n .transform((val) => (val.includes(',') ? val.split(',').map((s) => s.trim()) : val)),\n \n // Database (Prisma/SQL) - optional\n DATABASE_URL: z.string().optional(),\n \n // Firebase/Firestore configuration\n FIREBASE_PROJECT_ID: z.string().optional(),\n FIREBASE_CLIENT_EMAIL: z.string().optional(),\n FIREBASE_PRIVATE_KEY: z.string().optional(),\n FIREBASE_SERVICE_ACCOUNT_PATH: z.string().optional(),\n FIRESTORE_EMULATOR_HOST: z.string().optional(),\n FIREBASE_AUTH_EMULATOR_HOST: z.string().optional(),\n \n // API configuration\n API_PREFIX: z.string().default('/api'),\n\n // Mock data configuration\n USE_MOCK_DATA: z.string().default('true').transform((v) => v === 'true'),\n MOCK_SEED: z\n .string()\n .optional()\n .transform((v) => (v ? parseInt(v, 10) : undefined)),\n});\n\nconst parsed = envSchema.safeParse(process.env);\n\nif (!parsed.success) {\n console.error('❌ Invalid environment variables:', parsed.error.flatten().fieldErrors);\n throw new Error('Invalid environment variables');\n}\n\nexport const env = parsed.data;\n","/**\n * Database Configuration\n * \n * Supports both Prisma (SQL) and Firebase Admin (Firestore)\n * Generated apps can use either depending on the database option\n */\n\nimport admin from 'firebase-admin';\nimport { env } from './env.js';\n\n// ============ Firebase Admin / Firestore ============\n\nlet firebaseApp: admin.app.App | null = null;\n\n/**\n * Initialize Firebase Admin SDK\n */\nfunction initializeFirebase(): admin.app.App {\n if (firebaseApp) {\n return firebaseApp;\n }\n\n // Check if already initialized\n if (admin.apps.length > 0) {\n firebaseApp = admin.apps[0]!;\n return firebaseApp;\n }\n\n // Check for emulator mode FIRST (no credentials needed)\n if (env.FIRESTORE_EMULATOR_HOST) {\n // Emulator mode - no credentials needed\n firebaseApp = admin.initializeApp({\n projectId: env.FIREBASE_PROJECT_ID || 'demo-project',\n });\n console.log(`🔧 Firebase Admin initialized for emulator: ${env.FIRESTORE_EMULATOR_HOST}`);\n return firebaseApp;\n }\n\n // Production mode - need credentials\n const serviceAccountPath = env.FIREBASE_SERVICE_ACCOUNT_PATH;\n \n if (serviceAccountPath) {\n // Use service account file\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const serviceAccount = require(serviceAccountPath);\n firebaseApp = admin.initializeApp({\n credential: admin.credential.cert(serviceAccount),\n projectId: env.FIREBASE_PROJECT_ID,\n });\n } else if (env.FIREBASE_PROJECT_ID && env.FIREBASE_CLIENT_EMAIL && env.FIREBASE_PRIVATE_KEY) {\n // Use inline service account credentials\n firebaseApp = admin.initializeApp({\n credential: admin.credential.cert({\n projectId: env.FIREBASE_PROJECT_ID,\n clientEmail: env.FIREBASE_CLIENT_EMAIL,\n privateKey: env.FIREBASE_PRIVATE_KEY.replace(/\\\\n/g, '\\n'),\n }),\n projectId: env.FIREBASE_PROJECT_ID,\n });\n } else if (env.FIREBASE_PROJECT_ID) {\n // Use application default credentials (for Cloud Run, etc.)\n firebaseApp = admin.initializeApp({\n credential: admin.credential.applicationDefault(),\n projectId: env.FIREBASE_PROJECT_ID,\n });\n } else {\n // Emulator mode - use default credentials\n firebaseApp = admin.initializeApp({\n projectId: 'demo-project',\n });\n }\n\n return firebaseApp;\n}\n\n/**\n * Get Firestore instance\n */\nexport function getFirestore(): admin.firestore.Firestore {\n const app = initializeFirebase();\n const db = app.firestore();\n \n // Connect to emulator if configured\n if (env.FIRESTORE_EMULATOR_HOST) {\n db.settings({\n host: env.FIRESTORE_EMULATOR_HOST,\n ssl: false,\n });\n }\n \n return db;\n}\n\n/**\n * Get Firebase Auth instance\n */\nexport function getAuth(): admin.auth.Auth {\n const app = initializeFirebase();\n return app.auth();\n}\n\n// ============ Prisma (Optional - for SQL databases) ============\n\n// Uncomment if using Prisma with SQL database\n/*\nimport { PrismaClient } from '@prisma/client';\n\ndeclare global {\n // eslint-disable-next-line no-var\n var __db: PrismaClient | undefined;\n}\n\nconst createPrismaClient = () => {\n return new PrismaClient({\n log: env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'],\n });\n};\n\nexport const db = globalThis.__db ?? createPrismaClient();\n\nif (env.NODE_ENV !== 'production') {\n globalThis.__db = db;\n}\n*/\n\n// Re-export admin for convenience\nexport { admin };\n\n// Export db instance for handler convenience\nexport const db = getFirestore();\n","import { env } from './env.js';\n\ntype LogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nconst colors = {\n debug: '\\x1b[36m', // Cyan\n info: '\\x1b[32m', // Green\n warn: '\\x1b[33m', // Yellow\n error: '\\x1b[31m', // Red\n reset: '\\x1b[0m',\n};\n\nconst shouldLog = (level: LogLevel): boolean => {\n const levels: LogLevel[] = ['debug', 'info', 'warn', 'error'];\n const minLevel = env.NODE_ENV === 'production' ? 'info' : 'debug';\n return levels.indexOf(level) >= levels.indexOf(minLevel);\n};\n\nconst formatMessage = (level: LogLevel, message: string, meta?: unknown): string => {\n const timestamp = new Date().toISOString();\n const color = colors[level];\n const prefix = `${color}[${level.toUpperCase()}]${colors.reset}`;\n const metaStr = meta ? ` ${JSON.stringify(meta)}` : '';\n return `${timestamp} ${prefix} ${message}${metaStr}`;\n};\n\nexport const logger = {\n debug: (message: string, meta?: unknown) => {\n if (shouldLog('debug')) {\n console.log(formatMessage('debug', message, meta));\n }\n },\n info: (message: string, meta?: unknown) => {\n if (shouldLog('info')) {\n console.log(formatMessage('info', message, meta));\n }\n },\n warn: (message: string, meta?: unknown) => {\n if (shouldLog('warn')) {\n console.warn(formatMessage('warn', message, meta));\n }\n },\n error: (message: string, meta?: unknown) => {\n if (shouldLog('error')) {\n console.error(formatMessage('error', message, meta));\n }\n },\n};\n","/**\n * Server EventBus - Singleton for server-side cross-trait communication\n *\n * This EventBus enables:\n * - Server-side trait event emission after CRUD operations\n * - Server-side trait listeners responding to events\n * - Cross-client event broadcast via WebSocket\n *\n * @packageDocumentation\n */\n\ntype EventHandler = (payload: unknown, meta?: Record<string, unknown>) => void;\n\nexport interface EventLogEntry {\n event: string;\n payload: unknown;\n timestamp: number;\n listenerCount: number;\n wildcardListenerCount: number;\n}\n\nconst MAX_EVENT_LOG = 200;\n\n/**\n * Simple EventBus implementation for server-side events\n */\nexport class EventBus {\n private handlers: Map<string, Set<EventHandler>> = new Map();\n private debug: boolean;\n private eventLog: EventLogEntry[] = [];\n\n constructor(options?: { debug?: boolean }) {\n this.debug = options?.debug ?? false;\n }\n\n on(event: string, handler: EventHandler): () => void {\n if (!this.handlers.has(event)) {\n this.handlers.set(event, new Set());\n }\n this.handlers.get(event)!.add(handler);\n\n // Return unsubscribe function\n return () => {\n this.handlers.get(event)?.delete(handler);\n };\n }\n\n off(event: string, handler: EventHandler): void {\n this.handlers.get(event)?.delete(handler);\n }\n\n emit(event: string, payload?: unknown, meta?: Record<string, unknown>): void {\n if (this.debug) {\n console.log(`[EventBus] Emitting ${event}:`, payload);\n }\n\n const handlers = this.handlers.get(event);\n const listenerCount = handlers?.size ?? 0;\n if (handlers) {\n handlers.forEach(handler => {\n try {\n handler(payload, meta);\n } catch (err) {\n console.error(`[EventBus] Error in handler for ${event}:`, err);\n }\n });\n }\n\n // Wildcard subscribers receive all events (used by WebSocket broadcast)\n let wildcardListenerCount = 0;\n if (event !== '*') {\n const wildcardHandlers = this.handlers.get('*');\n wildcardListenerCount = wildcardHandlers?.size ?? 0;\n if (wildcardHandlers) {\n wildcardHandlers.forEach(handler => {\n try {\n handler({ type: event, payload, timestamp: Date.now() }, meta);\n } catch (err) {\n console.error(`[EventBus] Error in wildcard handler for ${event}:`, err);\n }\n });\n }\n }\n\n // Record event in log (dev diagnostics)\n if (this.debug) {\n this.eventLog.push({\n event,\n payload,\n timestamp: Date.now(),\n listenerCount,\n wildcardListenerCount,\n });\n if (this.eventLog.length > MAX_EVENT_LOG) {\n this.eventLog.splice(0, this.eventLog.length - MAX_EVENT_LOG);\n }\n }\n }\n\n getRecentEvents(limit = 50): EventLogEntry[] {\n return this.eventLog.slice(-limit);\n }\n\n clearEventLog(): void {\n this.eventLog.length = 0;\n }\n\n getListenerCounts(): Record<string, number> {\n const counts: Record<string, number> = {};\n this.handlers.forEach((handlers, event) => {\n counts[event] = handlers.size;\n });\n return counts;\n }\n\n clear(): void {\n this.handlers.clear();\n this.eventLog.length = 0;\n }\n}\n\n/**\n * Singleton EventBus instance for server-side event communication.\n */\nexport const serverEventBus = new EventBus({\n debug: process.env.NODE_ENV === 'development',\n});\n\n/**\n * Type-safe event emission helper\n */\nexport function emitEntityEvent(\n entityType: string,\n action: 'CREATED' | 'UPDATED' | 'DELETED',\n payload: Record<string, unknown>\n): void {\n const eventType = `${entityType.toUpperCase()}_${action}`;\n serverEventBus.emit(eventType, payload, { orbital: entityType });\n}\n","/**\n * WebSocket Event Broadcast - Cross-client event synchronization\n *\n * Broadcasts server-side events to all connected clients via WebSocket.\n * This enables real-time updates across multiple browser clients.\n *\n * @packageDocumentation\n */\n\nimport { WebSocketServer, WebSocket, type RawData } from 'ws';\nimport type { Server, IncomingMessage } from 'http';\nimport { serverEventBus } from './eventBus.js';\nimport { logger } from './logger.js';\n\n/**\n * Event structure for broadcasting\n */\ninterface BroadcastEvent {\n type: string;\n payload?: unknown;\n timestamp?: number;\n source?: Record<string, unknown>;\n}\n\n/**\n * WebSocket server instance (singleton)\n */\nlet wss: WebSocketServer | null = null;\n\n/**\n * Setup WebSocket server for event broadcasting.\n *\n * Listens to all server events via wildcard and broadcasts to connected clients.\n *\n * @param server - HTTP server to attach WebSocket to\n * @param path - WebSocket endpoint path (default: '/ws/events')\n *\n * @example\n * ```typescript\n * import { createServer } from 'http';\n * import { setupEventBroadcast } from '@/lib/websocket';\n *\n * const server = createServer(app);\n * setupEventBroadcast(server);\n * ```\n */\nexport function setupEventBroadcast(server: Server, path: string = '/ws/events'): WebSocketServer {\n if (wss) {\n logger.warn('[WebSocket] Server already initialized');\n return wss;\n }\n\n wss = new WebSocketServer({ server, path });\n\n logger.info(`[WebSocket] Server listening at ${path}`);\n\n // Handle new connections\n wss.on('connection', (ws: WebSocket, req: IncomingMessage) => {\n const clientId = req.headers['sec-websocket-key'] || 'unknown';\n logger.debug(`[WebSocket] Client connected: ${clientId}`);\n\n // Send welcome message\n ws.send(\n JSON.stringify({\n type: 'CONNECTED',\n timestamp: Date.now(),\n message: 'Connected to event stream',\n })\n );\n\n // Handle client messages (for future bidirectional communication)\n ws.on('message', (data: RawData) => {\n try {\n const message = JSON.parse(data.toString());\n logger.debug(`[WebSocket] Received from ${clientId}:`, message);\n\n // Handle client-to-server events if needed\n if (message.type && message.payload) {\n // Emit to server event bus with client source\n serverEventBus.emit(message.type, message.payload, {\n orbital: 'client',\n entity: clientId,\n });\n }\n } catch (error) {\n logger.error(`[WebSocket] Failed to parse message:`, error);\n }\n });\n\n ws.on('close', () => {\n logger.debug(`[WebSocket] Client disconnected: ${clientId}`);\n });\n\n ws.on('error', (error: Error) => {\n logger.error(`[WebSocket] Client error:`, error);\n });\n });\n\n // Subscribe to all server events and broadcast to clients\n serverEventBus.on('*', (event: unknown) => {\n if (!wss) return;\n\n const typedEvent = event as BroadcastEvent;\n const message = JSON.stringify({\n type: typedEvent.type,\n payload: typedEvent.payload,\n timestamp: typedEvent.timestamp,\n source: typedEvent.source,\n });\n\n let broadcastCount = 0;\n wss.clients.forEach((client: WebSocket) => {\n if (client.readyState === WebSocket.OPEN) {\n client.send(message);\n broadcastCount++;\n }\n });\n\n if (broadcastCount > 0) {\n logger.debug(`[WebSocket] Broadcast ${typedEvent.type} to ${broadcastCount} client(s)`);\n }\n });\n\n return wss;\n}\n\n/**\n * Get the WebSocket server instance (for testing or advanced usage)\n */\nexport function getWebSocketServer(): WebSocketServer | null {\n return wss;\n}\n\n/**\n * Close the WebSocket server\n */\nexport function closeWebSocketServer(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!wss) {\n resolve();\n return;\n }\n\n wss.close((err?: Error) => {\n if (err) {\n reject(err);\n } else {\n wss = null;\n resolve();\n }\n });\n });\n}\n\n/**\n * Get connected client count\n */\nexport function getConnectedClientCount(): number {\n if (!wss) return 0;\n return wss.clients.size;\n}\n"]}
|
package/dist/middleware/index.js
CHANGED
|
@@ -286,9 +286,29 @@ getFirestore();
|
|
|
286
286
|
|
|
287
287
|
// src/middleware/authenticateFirebase.ts
|
|
288
288
|
var BEARER_PREFIX = "Bearer ";
|
|
289
|
+
var DEV_USER = {
|
|
290
|
+
uid: "dev-user-001",
|
|
291
|
+
email: "dev@localhost",
|
|
292
|
+
email_verified: true,
|
|
293
|
+
aud: "dev-project",
|
|
294
|
+
auth_time: Math.floor(Date.now() / 1e3),
|
|
295
|
+
exp: Math.floor(Date.now() / 1e3) + 3600,
|
|
296
|
+
iat: Math.floor(Date.now() / 1e3),
|
|
297
|
+
iss: "https://securetoken.google.com/dev-project",
|
|
298
|
+
sub: "dev-user-001",
|
|
299
|
+
firebase: {
|
|
300
|
+
identities: {},
|
|
301
|
+
sign_in_provider: "custom"
|
|
302
|
+
}
|
|
303
|
+
};
|
|
289
304
|
async function authenticateFirebase(req, res, next) {
|
|
305
|
+
const authorization = req.headers.authorization;
|
|
306
|
+
if (env.NODE_ENV === "development" && (!authorization || !authorization.startsWith(BEARER_PREFIX))) {
|
|
307
|
+
req.firebaseUser = DEV_USER;
|
|
308
|
+
res.locals.firebaseUser = DEV_USER;
|
|
309
|
+
return next();
|
|
310
|
+
}
|
|
290
311
|
try {
|
|
291
|
-
const authorization = req.headers.authorization;
|
|
292
312
|
if (!authorization || !authorization.startsWith(BEARER_PREFIX)) {
|
|
293
313
|
return res.status(401).json({ error: "Authorization header missing or malformed" });
|
|
294
314
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/lib/env.ts","../../src/lib/logger.ts","../../src/middleware/errorHandler.ts","../../src/middleware/validation.ts","../../src/lib/db.ts","../../src/middleware/authenticateFirebase.ts"],"names":["ZodError","db"],"mappings":"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","file":"index.js","sourcesContent":["import { z } from 'zod';\nimport dotenv from 'dotenv';\n\n// Load environment variables\ndotenv.config();\n\nconst envSchema = z.object({\n NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),\n PORT: z\n .string()\n .default('3030')\n .transform((val) => parseInt(val, 10)),\n CORS_ORIGIN: z\n .string()\n .default('http://localhost:5173')\n .transform((val) => (val.includes(',') ? val.split(',').map((s) => s.trim()) : val)),\n \n // Database (Prisma/SQL) - optional\n DATABASE_URL: z.string().optional(),\n \n // Firebase/Firestore configuration\n FIREBASE_PROJECT_ID: z.string().optional(),\n FIREBASE_CLIENT_EMAIL: z.string().optional(),\n FIREBASE_PRIVATE_KEY: z.string().optional(),\n FIREBASE_SERVICE_ACCOUNT_PATH: z.string().optional(),\n FIRESTORE_EMULATOR_HOST: z.string().optional(),\n FIREBASE_AUTH_EMULATOR_HOST: z.string().optional(),\n \n // API configuration\n API_PREFIX: z.string().default('/api'),\n\n // Mock data configuration\n USE_MOCK_DATA: z.string().default('true').transform((v) => v === 'true'),\n MOCK_SEED: z\n .string()\n .optional()\n .transform((v) => (v ? parseInt(v, 10) : undefined)),\n});\n\nconst parsed = envSchema.safeParse(process.env);\n\nif (!parsed.success) {\n console.error('❌ Invalid environment variables:', parsed.error.flatten().fieldErrors);\n throw new Error('Invalid environment variables');\n}\n\nexport const env = parsed.data;\n","import { env } from './env.js';\n\ntype LogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nconst colors = {\n debug: '\\x1b[36m', // Cyan\n info: '\\x1b[32m', // Green\n warn: '\\x1b[33m', // Yellow\n error: '\\x1b[31m', // Red\n reset: '\\x1b[0m',\n};\n\nconst shouldLog = (level: LogLevel): boolean => {\n const levels: LogLevel[] = ['debug', 'info', 'warn', 'error'];\n const minLevel = env.NODE_ENV === 'production' ? 'info' : 'debug';\n return levels.indexOf(level) >= levels.indexOf(minLevel);\n};\n\nconst formatMessage = (level: LogLevel, message: string, meta?: unknown): string => {\n const timestamp = new Date().toISOString();\n const color = colors[level];\n const prefix = `${color}[${level.toUpperCase()}]${colors.reset}`;\n const metaStr = meta ? ` ${JSON.stringify(meta)}` : '';\n return `${timestamp} ${prefix} ${message}${metaStr}`;\n};\n\nexport const logger = {\n debug: (message: string, meta?: unknown) => {\n if (shouldLog('debug')) {\n console.log(formatMessage('debug', message, meta));\n }\n },\n info: (message: string, meta?: unknown) => {\n if (shouldLog('info')) {\n console.log(formatMessage('info', message, meta));\n }\n },\n warn: (message: string, meta?: unknown) => {\n if (shouldLog('warn')) {\n console.warn(formatMessage('warn', message, meta));\n }\n },\n error: (message: string, meta?: unknown) => {\n if (shouldLog('error')) {\n console.error(formatMessage('error', message, meta));\n }\n },\n};\n","import { Request, Response, NextFunction } from 'express';\nimport { ZodError } from 'zod';\nimport { logger } from '../lib/logger.js';\n\n/**\n * Base application error class\n */\nexport class AppError extends Error {\n constructor(\n public statusCode: number,\n public message: string,\n public code?: string\n ) {\n super(message);\n this.name = 'AppError';\n }\n}\n\n/**\n * 404 Not Found error\n */\nexport class NotFoundError extends AppError {\n constructor(message: string = 'Resource not found') {\n super(404, message, 'NOT_FOUND');\n }\n}\n\n/**\n * 400 Bad Request / Validation error\n */\nexport class ValidationError extends AppError {\n constructor(message: string = 'Validation failed') {\n super(400, message, 'VALIDATION_ERROR');\n }\n}\n\n/**\n * 401 Unauthorized error\n */\nexport class UnauthorizedError extends AppError {\n constructor(message: string = 'Unauthorized') {\n super(401, message, 'UNAUTHORIZED');\n }\n}\n\n/**\n * 403 Forbidden error\n */\nexport class ForbiddenError extends AppError {\n constructor(message: string = 'Forbidden') {\n super(403, message, 'FORBIDDEN');\n }\n}\n\n/**\n * 409 Conflict error\n */\nexport class ConflictError extends AppError {\n constructor(message: string = 'Resource conflict') {\n super(409, message, 'CONFLICT');\n }\n}\n\n/**\n * Global error handler middleware\n */\nexport const errorHandler = (\n err: Error,\n _req: Request,\n res: Response,\n _next: NextFunction\n): void => {\n logger.error('Error:', { name: err.name, message: err.message, stack: err.stack });\n\n // Zod validation errors\n if (err instanceof ZodError) {\n res.status(400).json({\n success: false,\n error: 'Validation failed',\n code: 'VALIDATION_ERROR',\n details: err.errors.map((e) => ({\n path: e.path.join('.'),\n message: e.message,\n })),\n });\n return;\n }\n\n // Custom application errors\n if (err instanceof AppError) {\n res.status(err.statusCode).json({\n success: false,\n error: err.message,\n code: err.code,\n });\n return;\n }\n\n // Firebase/Firestore errors\n if (err.name === 'FirebaseError' || err.name === 'FirestoreError') {\n res.status(500).json({\n success: false,\n error: 'Database error',\n code: 'DATABASE_ERROR',\n });\n return;\n }\n\n // Unknown errors\n res.status(500).json({\n success: false,\n error: 'Internal server error',\n code: 'INTERNAL_ERROR',\n });\n};\n\n/**\n * Async handler wrapper to catch errors in async route handlers\n */\nexport const asyncHandler =\n (fn: (req: Request, res: Response, next: NextFunction) => Promise<unknown>) =>\n (req: Request, res: Response, next: NextFunction) => {\n Promise.resolve(fn(req, res, next)).catch(next);\n };\n\n/**\n * 404 handler for unmatched routes\n */\nexport const notFoundHandler = (req: Request, res: Response): void => {\n res.status(404).json({\n success: false,\n error: `Route ${req.method} ${req.path} not found`,\n code: 'ROUTE_NOT_FOUND',\n });\n};\n","import { Request, Response, NextFunction } from 'express';\nimport { AnyZodObject, ZodError } from 'zod';\n\n/**\n * Middleware to validate request body against a Zod schema\n */\nexport const validateBody =\n (schema: AnyZodObject) => async (req: Request, res: Response, next: NextFunction): Promise<void> => {\n try {\n req.body = await schema.parseAsync(req.body);\n next();\n } catch (error) {\n if (error instanceof ZodError) {\n res.status(400).json({\n success: false,\n error: 'Validation failed',\n code: 'VALIDATION_ERROR',\n details: error.errors.map((e) => ({\n path: e.path.join('.'),\n message: e.message,\n })),\n });\n return;\n }\n next(error);\n }\n };\n\n/**\n * Middleware to validate request query parameters against a Zod schema\n */\nexport const validateQuery =\n (schema: AnyZodObject) => async (req: Request, res: Response, next: NextFunction): Promise<void> => {\n try {\n req.query = await schema.parseAsync(req.query);\n next();\n } catch (error) {\n if (error instanceof ZodError) {\n res.status(400).json({\n success: false,\n error: 'Invalid query parameters',\n code: 'VALIDATION_ERROR',\n details: error.errors.map((e) => ({\n path: e.path.join('.'),\n message: e.message,\n })),\n });\n return;\n }\n next(error);\n }\n };\n\n/**\n * Middleware to validate request params against a Zod schema\n */\nexport const validateParams =\n (schema: AnyZodObject) => async (req: Request, res: Response, next: NextFunction): Promise<void> => {\n try {\n req.params = await schema.parseAsync(req.params);\n next();\n } catch (error) {\n if (error instanceof ZodError) {\n res.status(400).json({\n success: false,\n error: 'Invalid path parameters',\n code: 'VALIDATION_ERROR',\n details: error.errors.map((e) => ({\n path: e.path.join('.'),\n message: e.message,\n })),\n });\n return;\n }\n next(error);\n }\n };\n","/**\n * Database Configuration\n * \n * Supports both Prisma (SQL) and Firebase Admin (Firestore)\n * Generated apps can use either depending on the database option\n */\n\nimport admin from 'firebase-admin';\nimport { env } from './env.js';\n\n// ============ Firebase Admin / Firestore ============\n\nlet firebaseApp: admin.app.App | null = null;\n\n/**\n * Initialize Firebase Admin SDK\n */\nfunction initializeFirebase(): admin.app.App {\n if (firebaseApp) {\n return firebaseApp;\n }\n\n // Check if already initialized\n if (admin.apps.length > 0) {\n firebaseApp = admin.apps[0]!;\n return firebaseApp;\n }\n\n // Check for emulator mode FIRST (no credentials needed)\n if (env.FIRESTORE_EMULATOR_HOST) {\n // Emulator mode - no credentials needed\n firebaseApp = admin.initializeApp({\n projectId: env.FIREBASE_PROJECT_ID || 'demo-project',\n });\n console.log(`🔧 Firebase Admin initialized for emulator: ${env.FIRESTORE_EMULATOR_HOST}`);\n return firebaseApp;\n }\n\n // Production mode - need credentials\n const serviceAccountPath = env.FIREBASE_SERVICE_ACCOUNT_PATH;\n \n if (serviceAccountPath) {\n // Use service account file\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const serviceAccount = require(serviceAccountPath);\n firebaseApp = admin.initializeApp({\n credential: admin.credential.cert(serviceAccount),\n projectId: env.FIREBASE_PROJECT_ID,\n });\n } else if (env.FIREBASE_PROJECT_ID && env.FIREBASE_CLIENT_EMAIL && env.FIREBASE_PRIVATE_KEY) {\n // Use inline service account credentials\n firebaseApp = admin.initializeApp({\n credential: admin.credential.cert({\n projectId: env.FIREBASE_PROJECT_ID,\n clientEmail: env.FIREBASE_CLIENT_EMAIL,\n privateKey: env.FIREBASE_PRIVATE_KEY.replace(/\\\\n/g, '\\n'),\n }),\n projectId: env.FIREBASE_PROJECT_ID,\n });\n } else if (env.FIREBASE_PROJECT_ID) {\n // Use application default credentials (for Cloud Run, etc.)\n firebaseApp = admin.initializeApp({\n credential: admin.credential.applicationDefault(),\n projectId: env.FIREBASE_PROJECT_ID,\n });\n } else {\n // Emulator mode - use default credentials\n firebaseApp = admin.initializeApp({\n projectId: 'demo-project',\n });\n }\n\n return firebaseApp;\n}\n\n/**\n * Get Firestore instance\n */\nexport function getFirestore(): admin.firestore.Firestore {\n const app = initializeFirebase();\n const db = app.firestore();\n \n // Connect to emulator if configured\n if (env.FIRESTORE_EMULATOR_HOST) {\n db.settings({\n host: env.FIRESTORE_EMULATOR_HOST,\n ssl: false,\n });\n }\n \n return db;\n}\n\n/**\n * Get Firebase Auth instance\n */\nexport function getAuth(): admin.auth.Auth {\n const app = initializeFirebase();\n return app.auth();\n}\n\n// ============ Prisma (Optional - for SQL databases) ============\n\n// Uncomment if using Prisma with SQL database\n/*\nimport { PrismaClient } from '@prisma/client';\n\ndeclare global {\n // eslint-disable-next-line no-var\n var __db: PrismaClient | undefined;\n}\n\nconst createPrismaClient = () => {\n return new PrismaClient({\n log: env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'],\n });\n};\n\nexport const db = globalThis.__db ?? createPrismaClient();\n\nif (env.NODE_ENV !== 'production') {\n globalThis.__db = db;\n}\n*/\n\n// Re-export admin for convenience\nexport { admin };\n\n// Export db instance for handler convenience\nexport const db = getFirestore();\n","import { NextFunction, Request, Response } from 'express';\nimport { getAuth } from '../lib/db.js';\n\nconst BEARER_PREFIX = 'Bearer ';\n\nexport async function authenticateFirebase(req: Request, res: Response, next: NextFunction) {\n try {\n const authorization = req.headers.authorization;\n\n if (!authorization || !authorization.startsWith(BEARER_PREFIX)) {\n return res.status(401).json({ error: 'Authorization header missing or malformed' });\n }\n\n const token = authorization.slice(BEARER_PREFIX.length);\n const decodedToken = await getAuth().verifyIdToken(token);\n\n req.firebaseUser = decodedToken;\n res.locals.firebaseUser = decodedToken;\n\n return next();\n } catch (error) {\n console.error('Firebase authentication failed:', error);\n return res.status(401).json({ error: 'Unauthorized' });\n }\n}\n\nexport default authenticateFirebase;\n\n\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/lib/env.ts","../../src/lib/logger.ts","../../src/middleware/errorHandler.ts","../../src/middleware/validation.ts","../../src/lib/db.ts","../../src/middleware/authenticateFirebase.ts"],"names":["ZodError","db"],"mappings":"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","file":"index.js","sourcesContent":["import { z } from 'zod';\nimport dotenv from 'dotenv';\n\n// Load environment variables\ndotenv.config();\n\nconst envSchema = z.object({\n NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),\n PORT: z\n .string()\n .default('3030')\n .transform((val) => parseInt(val, 10)),\n CORS_ORIGIN: z\n .string()\n .default('http://localhost:5173')\n .transform((val) => (val.includes(',') ? val.split(',').map((s) => s.trim()) : val)),\n \n // Database (Prisma/SQL) - optional\n DATABASE_URL: z.string().optional(),\n \n // Firebase/Firestore configuration\n FIREBASE_PROJECT_ID: z.string().optional(),\n FIREBASE_CLIENT_EMAIL: z.string().optional(),\n FIREBASE_PRIVATE_KEY: z.string().optional(),\n FIREBASE_SERVICE_ACCOUNT_PATH: z.string().optional(),\n FIRESTORE_EMULATOR_HOST: z.string().optional(),\n FIREBASE_AUTH_EMULATOR_HOST: z.string().optional(),\n \n // API configuration\n API_PREFIX: z.string().default('/api'),\n\n // Mock data configuration\n USE_MOCK_DATA: z.string().default('true').transform((v) => v === 'true'),\n MOCK_SEED: z\n .string()\n .optional()\n .transform((v) => (v ? parseInt(v, 10) : undefined)),\n});\n\nconst parsed = envSchema.safeParse(process.env);\n\nif (!parsed.success) {\n console.error('❌ Invalid environment variables:', parsed.error.flatten().fieldErrors);\n throw new Error('Invalid environment variables');\n}\n\nexport const env = parsed.data;\n","import { env } from './env.js';\n\ntype LogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nconst colors = {\n debug: '\\x1b[36m', // Cyan\n info: '\\x1b[32m', // Green\n warn: '\\x1b[33m', // Yellow\n error: '\\x1b[31m', // Red\n reset: '\\x1b[0m',\n};\n\nconst shouldLog = (level: LogLevel): boolean => {\n const levels: LogLevel[] = ['debug', 'info', 'warn', 'error'];\n const minLevel = env.NODE_ENV === 'production' ? 'info' : 'debug';\n return levels.indexOf(level) >= levels.indexOf(minLevel);\n};\n\nconst formatMessage = (level: LogLevel, message: string, meta?: unknown): string => {\n const timestamp = new Date().toISOString();\n const color = colors[level];\n const prefix = `${color}[${level.toUpperCase()}]${colors.reset}`;\n const metaStr = meta ? ` ${JSON.stringify(meta)}` : '';\n return `${timestamp} ${prefix} ${message}${metaStr}`;\n};\n\nexport const logger = {\n debug: (message: string, meta?: unknown) => {\n if (shouldLog('debug')) {\n console.log(formatMessage('debug', message, meta));\n }\n },\n info: (message: string, meta?: unknown) => {\n if (shouldLog('info')) {\n console.log(formatMessage('info', message, meta));\n }\n },\n warn: (message: string, meta?: unknown) => {\n if (shouldLog('warn')) {\n console.warn(formatMessage('warn', message, meta));\n }\n },\n error: (message: string, meta?: unknown) => {\n if (shouldLog('error')) {\n console.error(formatMessage('error', message, meta));\n }\n },\n};\n","import { Request, Response, NextFunction } from 'express';\nimport { ZodError } from 'zod';\nimport { logger } from '../lib/logger.js';\n\n/**\n * Base application error class\n */\nexport class AppError extends Error {\n constructor(\n public statusCode: number,\n public message: string,\n public code?: string\n ) {\n super(message);\n this.name = 'AppError';\n }\n}\n\n/**\n * 404 Not Found error\n */\nexport class NotFoundError extends AppError {\n constructor(message: string = 'Resource not found') {\n super(404, message, 'NOT_FOUND');\n }\n}\n\n/**\n * 400 Bad Request / Validation error\n */\nexport class ValidationError extends AppError {\n constructor(message: string = 'Validation failed') {\n super(400, message, 'VALIDATION_ERROR');\n }\n}\n\n/**\n * 401 Unauthorized error\n */\nexport class UnauthorizedError extends AppError {\n constructor(message: string = 'Unauthorized') {\n super(401, message, 'UNAUTHORIZED');\n }\n}\n\n/**\n * 403 Forbidden error\n */\nexport class ForbiddenError extends AppError {\n constructor(message: string = 'Forbidden') {\n super(403, message, 'FORBIDDEN');\n }\n}\n\n/**\n * 409 Conflict error\n */\nexport class ConflictError extends AppError {\n constructor(message: string = 'Resource conflict') {\n super(409, message, 'CONFLICT');\n }\n}\n\n/**\n * Global error handler middleware\n */\nexport const errorHandler = (\n err: Error,\n _req: Request,\n res: Response,\n _next: NextFunction\n): void => {\n logger.error('Error:', { name: err.name, message: err.message, stack: err.stack });\n\n // Zod validation errors\n if (err instanceof ZodError) {\n res.status(400).json({\n success: false,\n error: 'Validation failed',\n code: 'VALIDATION_ERROR',\n details: err.errors.map((e) => ({\n path: e.path.join('.'),\n message: e.message,\n })),\n });\n return;\n }\n\n // Custom application errors\n if (err instanceof AppError) {\n res.status(err.statusCode).json({\n success: false,\n error: err.message,\n code: err.code,\n });\n return;\n }\n\n // Firebase/Firestore errors\n if (err.name === 'FirebaseError' || err.name === 'FirestoreError') {\n res.status(500).json({\n success: false,\n error: 'Database error',\n code: 'DATABASE_ERROR',\n });\n return;\n }\n\n // Unknown errors\n res.status(500).json({\n success: false,\n error: 'Internal server error',\n code: 'INTERNAL_ERROR',\n });\n};\n\n/**\n * Async handler wrapper to catch errors in async route handlers\n */\nexport const asyncHandler =\n (fn: (req: Request, res: Response, next: NextFunction) => Promise<unknown>) =>\n (req: Request, res: Response, next: NextFunction) => {\n Promise.resolve(fn(req, res, next)).catch(next);\n };\n\n/**\n * 404 handler for unmatched routes\n */\nexport const notFoundHandler = (req: Request, res: Response): void => {\n res.status(404).json({\n success: false,\n error: `Route ${req.method} ${req.path} not found`,\n code: 'ROUTE_NOT_FOUND',\n });\n};\n","import { Request, Response, NextFunction } from 'express';\nimport { AnyZodObject, ZodError } from 'zod';\n\n/**\n * Middleware to validate request body against a Zod schema\n */\nexport const validateBody =\n (schema: AnyZodObject) => async (req: Request, res: Response, next: NextFunction): Promise<void> => {\n try {\n req.body = await schema.parseAsync(req.body);\n next();\n } catch (error) {\n if (error instanceof ZodError) {\n res.status(400).json({\n success: false,\n error: 'Validation failed',\n code: 'VALIDATION_ERROR',\n details: error.errors.map((e) => ({\n path: e.path.join('.'),\n message: e.message,\n })),\n });\n return;\n }\n next(error);\n }\n };\n\n/**\n * Middleware to validate request query parameters against a Zod schema\n */\nexport const validateQuery =\n (schema: AnyZodObject) => async (req: Request, res: Response, next: NextFunction): Promise<void> => {\n try {\n req.query = await schema.parseAsync(req.query);\n next();\n } catch (error) {\n if (error instanceof ZodError) {\n res.status(400).json({\n success: false,\n error: 'Invalid query parameters',\n code: 'VALIDATION_ERROR',\n details: error.errors.map((e) => ({\n path: e.path.join('.'),\n message: e.message,\n })),\n });\n return;\n }\n next(error);\n }\n };\n\n/**\n * Middleware to validate request params against a Zod schema\n */\nexport const validateParams =\n (schema: AnyZodObject) => async (req: Request, res: Response, next: NextFunction): Promise<void> => {\n try {\n req.params = await schema.parseAsync(req.params);\n next();\n } catch (error) {\n if (error instanceof ZodError) {\n res.status(400).json({\n success: false,\n error: 'Invalid path parameters',\n code: 'VALIDATION_ERROR',\n details: error.errors.map((e) => ({\n path: e.path.join('.'),\n message: e.message,\n })),\n });\n return;\n }\n next(error);\n }\n };\n","/**\n * Database Configuration\n * \n * Supports both Prisma (SQL) and Firebase Admin (Firestore)\n * Generated apps can use either depending on the database option\n */\n\nimport admin from 'firebase-admin';\nimport { env } from './env.js';\n\n// ============ Firebase Admin / Firestore ============\n\nlet firebaseApp: admin.app.App | null = null;\n\n/**\n * Initialize Firebase Admin SDK\n */\nfunction initializeFirebase(): admin.app.App {\n if (firebaseApp) {\n return firebaseApp;\n }\n\n // Check if already initialized\n if (admin.apps.length > 0) {\n firebaseApp = admin.apps[0]!;\n return firebaseApp;\n }\n\n // Check for emulator mode FIRST (no credentials needed)\n if (env.FIRESTORE_EMULATOR_HOST) {\n // Emulator mode - no credentials needed\n firebaseApp = admin.initializeApp({\n projectId: env.FIREBASE_PROJECT_ID || 'demo-project',\n });\n console.log(`🔧 Firebase Admin initialized for emulator: ${env.FIRESTORE_EMULATOR_HOST}`);\n return firebaseApp;\n }\n\n // Production mode - need credentials\n const serviceAccountPath = env.FIREBASE_SERVICE_ACCOUNT_PATH;\n \n if (serviceAccountPath) {\n // Use service account file\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const serviceAccount = require(serviceAccountPath);\n firebaseApp = admin.initializeApp({\n credential: admin.credential.cert(serviceAccount),\n projectId: env.FIREBASE_PROJECT_ID,\n });\n } else if (env.FIREBASE_PROJECT_ID && env.FIREBASE_CLIENT_EMAIL && env.FIREBASE_PRIVATE_KEY) {\n // Use inline service account credentials\n firebaseApp = admin.initializeApp({\n credential: admin.credential.cert({\n projectId: env.FIREBASE_PROJECT_ID,\n clientEmail: env.FIREBASE_CLIENT_EMAIL,\n privateKey: env.FIREBASE_PRIVATE_KEY.replace(/\\\\n/g, '\\n'),\n }),\n projectId: env.FIREBASE_PROJECT_ID,\n });\n } else if (env.FIREBASE_PROJECT_ID) {\n // Use application default credentials (for Cloud Run, etc.)\n firebaseApp = admin.initializeApp({\n credential: admin.credential.applicationDefault(),\n projectId: env.FIREBASE_PROJECT_ID,\n });\n } else {\n // Emulator mode - use default credentials\n firebaseApp = admin.initializeApp({\n projectId: 'demo-project',\n });\n }\n\n return firebaseApp;\n}\n\n/**\n * Get Firestore instance\n */\nexport function getFirestore(): admin.firestore.Firestore {\n const app = initializeFirebase();\n const db = app.firestore();\n \n // Connect to emulator if configured\n if (env.FIRESTORE_EMULATOR_HOST) {\n db.settings({\n host: env.FIRESTORE_EMULATOR_HOST,\n ssl: false,\n });\n }\n \n return db;\n}\n\n/**\n * Get Firebase Auth instance\n */\nexport function getAuth(): admin.auth.Auth {\n const app = initializeFirebase();\n return app.auth();\n}\n\n// ============ Prisma (Optional - for SQL databases) ============\n\n// Uncomment if using Prisma with SQL database\n/*\nimport { PrismaClient } from '@prisma/client';\n\ndeclare global {\n // eslint-disable-next-line no-var\n var __db: PrismaClient | undefined;\n}\n\nconst createPrismaClient = () => {\n return new PrismaClient({\n log: env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'],\n });\n};\n\nexport const db = globalThis.__db ?? createPrismaClient();\n\nif (env.NODE_ENV !== 'production') {\n globalThis.__db = db;\n}\n*/\n\n// Re-export admin for convenience\nexport { admin };\n\n// Export db instance for handler convenience\nexport const db = getFirestore();\n","import { NextFunction, Request, Response } from 'express';\nimport type { DecodedIdToken } from 'firebase-admin/auth';\nimport { getAuth } from '../lib/db.js';\nimport { env } from '../lib/env.js';\n\nconst BEARER_PREFIX = 'Bearer ';\n\n/** Fake dev user injected when NODE_ENV=development and no auth header is present */\nconst DEV_USER: DecodedIdToken = {\n uid: 'dev-user-001',\n email: 'dev@localhost',\n email_verified: true,\n aud: 'dev-project',\n auth_time: Math.floor(Date.now() / 1000),\n exp: Math.floor(Date.now() / 1000) + 3600,\n iat: Math.floor(Date.now() / 1000),\n iss: 'https://securetoken.google.com/dev-project',\n sub: 'dev-user-001',\n firebase: {\n identities: {},\n sign_in_provider: 'custom',\n },\n};\n\nexport async function authenticateFirebase(req: Request, res: Response, next: NextFunction) {\n const authorization = req.headers.authorization;\n\n // Dev bypass: in development mode, skip auth if no token is provided\n if (env.NODE_ENV === 'development' && (!authorization || !authorization.startsWith(BEARER_PREFIX))) {\n req.firebaseUser = DEV_USER;\n res.locals.firebaseUser = DEV_USER;\n return next();\n }\n\n try {\n if (!authorization || !authorization.startsWith(BEARER_PREFIX)) {\n return res.status(401).json({ error: 'Authorization header missing or malformed' });\n }\n\n const token = authorization.slice(BEARER_PREFIX.length);\n const decodedToken = await getAuth().verifyIdToken(token);\n\n req.firebaseUser = decodedToken;\n res.locals.firebaseUser = decodedToken;\n\n return next();\n } catch (error) {\n console.error('Firebase authentication failed:', error);\n return res.status(401).json({ error: 'Unauthorized' });\n }\n}\n\nexport default authenticateFirebase;\n\n\n"]}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { OrbitalSchema, SnapshotDocument, SaveOptions, SaveResult, AppSummary, StatsView, ChangeSetDocument, ValidationResults, CategorizedRemovals, PageContentReduction } from '@almadar/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Firestore Serialization Utilities
|
|
5
|
+
*
|
|
6
|
+
* Handles the Firestore 20-level depth limit by serializing deeply nested
|
|
7
|
+
* OrbitalSchema fields (orbitals, traits, services) to JSON strings.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Convert OrbitalSchema to Firestore-safe format.
|
|
12
|
+
*
|
|
13
|
+
* Serializes orbitals, traits, and services to JSON strings to avoid
|
|
14
|
+
* Firestore's 20-level nesting limit.
|
|
15
|
+
*/
|
|
16
|
+
declare function toFirestoreFormat(schema: OrbitalSchema): Record<string, unknown>;
|
|
17
|
+
/**
|
|
18
|
+
* Convert Firestore document back to OrbitalSchema.
|
|
19
|
+
*
|
|
20
|
+
* Deserializes JSON strings back to arrays.
|
|
21
|
+
*/
|
|
22
|
+
declare function fromFirestoreFormat(data: Record<string, unknown>): OrbitalSchema;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* SnapshotStore - Firestore CRUD for schema snapshots
|
|
26
|
+
*
|
|
27
|
+
* Snapshots are full copies of the schema at a point in time,
|
|
28
|
+
* stored in subcollection: users/{uid}/apps/{appId}/snapshots/{snapshotId}
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
declare class SnapshotStore {
|
|
32
|
+
private appsCollection;
|
|
33
|
+
constructor(appsCollection?: string);
|
|
34
|
+
private getCollectionPath;
|
|
35
|
+
private getAppDocPath;
|
|
36
|
+
/** Create a snapshot of the current schema */
|
|
37
|
+
create(uid: string, appId: string, schema: OrbitalSchema, reason: string): Promise<string>;
|
|
38
|
+
/** Get all snapshots for an app (ordered by timestamp desc) */
|
|
39
|
+
getAll(uid: string, appId: string): Promise<SnapshotDocument[]>;
|
|
40
|
+
/** Get a specific snapshot by ID */
|
|
41
|
+
get(uid: string, appId: string, snapshotId: string): Promise<SnapshotDocument | null>;
|
|
42
|
+
/** Delete a snapshot */
|
|
43
|
+
delete(uid: string, appId: string, snapshotId: string): Promise<boolean>;
|
|
44
|
+
/** Get schema snapshot at a specific version */
|
|
45
|
+
getByVersion(uid: string, appId: string, version: number): Promise<OrbitalSchema | null>;
|
|
46
|
+
/** Get the schema from a snapshot (deserialized) */
|
|
47
|
+
getSchemaFromSnapshot(snapshot: SnapshotDocument): OrbitalSchema;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* SchemaStore - Firestore CRUD for OrbitalSchema documents
|
|
52
|
+
*
|
|
53
|
+
* Handles schema get/save/create/delete/list with caching
|
|
54
|
+
* and Firestore serialization.
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
declare class SchemaStore {
|
|
58
|
+
private appsCollection;
|
|
59
|
+
private schemaCache;
|
|
60
|
+
private listCache;
|
|
61
|
+
private protectionService;
|
|
62
|
+
private snapshotStore;
|
|
63
|
+
constructor(appsCollection?: string);
|
|
64
|
+
/** Set snapshot store for auto-snapshot on destructive saves */
|
|
65
|
+
setSnapshotStore(store: SnapshotStore): void;
|
|
66
|
+
/** Get a schema by app ID */
|
|
67
|
+
get(uid: string, appId: string): Promise<OrbitalSchema | null>;
|
|
68
|
+
/**
|
|
69
|
+
* Save a schema (create or full replace).
|
|
70
|
+
*
|
|
71
|
+
* Features:
|
|
72
|
+
* - Detects destructive changes (removals)
|
|
73
|
+
* - Requires confirmation for critical removals
|
|
74
|
+
* - Auto-creates snapshots before destructive changes (if SnapshotStore attached)
|
|
75
|
+
*/
|
|
76
|
+
save(uid: string, appId: string, schema: OrbitalSchema, options?: SaveOptions): Promise<SaveResult>;
|
|
77
|
+
/** Create a new app with initial schema */
|
|
78
|
+
create(uid: string, metadata: {
|
|
79
|
+
name: string;
|
|
80
|
+
description?: string;
|
|
81
|
+
}): Promise<{
|
|
82
|
+
appId: string;
|
|
83
|
+
schema: OrbitalSchema;
|
|
84
|
+
}>;
|
|
85
|
+
/** Delete an app */
|
|
86
|
+
delete(uid: string, appId: string): Promise<boolean>;
|
|
87
|
+
/** List all apps for a user */
|
|
88
|
+
list(uid: string): Promise<AppSummary[]>;
|
|
89
|
+
/** Compute stats from OrbitalSchema */
|
|
90
|
+
computeStats(schema: OrbitalSchema): StatsView;
|
|
91
|
+
/** Invalidate caches for a specific app */
|
|
92
|
+
invalidateCache(uid: string, appId: string): void;
|
|
93
|
+
/** Clear all caches */
|
|
94
|
+
clearCaches(): void;
|
|
95
|
+
/** Get the collection path for an app */
|
|
96
|
+
getAppDocPath(uid: string, appId: string): string;
|
|
97
|
+
/** Expose apps collection name for subcollection stores */
|
|
98
|
+
getAppsCollection(): string;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* ChangeSetStore - Firestore CRUD for schema changesets
|
|
103
|
+
*
|
|
104
|
+
* Changesets track changes made to the schema over time,
|
|
105
|
+
* stored in subcollection: users/{uid}/apps/{appId}/changesets/{changeSetId}
|
|
106
|
+
*/
|
|
107
|
+
|
|
108
|
+
declare class ChangeSetStore {
|
|
109
|
+
private appsCollection;
|
|
110
|
+
constructor(appsCollection?: string);
|
|
111
|
+
private getCollectionPath;
|
|
112
|
+
private getAppDocPath;
|
|
113
|
+
/** Append a changeset to history */
|
|
114
|
+
append(uid: string, appId: string, changeSet: ChangeSetDocument): Promise<void>;
|
|
115
|
+
/** Get change history for an app (ordered by version desc) */
|
|
116
|
+
getHistory(uid: string, appId: string): Promise<ChangeSetDocument[]>;
|
|
117
|
+
/** Get a specific changeset by ID */
|
|
118
|
+
get(uid: string, appId: string, changeSetId: string): Promise<ChangeSetDocument | null>;
|
|
119
|
+
/** Update a changeset's status */
|
|
120
|
+
updateStatus(uid: string, appId: string, changeSetId: string, status: 'applied' | 'reverted' | 'pending'): Promise<void>;
|
|
121
|
+
/** Delete a changeset */
|
|
122
|
+
delete(uid: string, appId: string, changeSetId: string): Promise<boolean>;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* ValidationStore - Firestore CRUD for validation results
|
|
127
|
+
*
|
|
128
|
+
* Validation results stored at: users/{uid}/apps/{appId}/validation/current
|
|
129
|
+
*/
|
|
130
|
+
|
|
131
|
+
declare class ValidationStore {
|
|
132
|
+
private appsCollection;
|
|
133
|
+
constructor(appsCollection?: string);
|
|
134
|
+
private getDocPath;
|
|
135
|
+
private getAppDocPath;
|
|
136
|
+
/** Save validation results */
|
|
137
|
+
save(uid: string, appId: string, results: ValidationResults): Promise<void>;
|
|
138
|
+
/** Get validation results */
|
|
139
|
+
get(uid: string, appId: string): Promise<ValidationResults | null>;
|
|
140
|
+
/** Clear validation results */
|
|
141
|
+
clear(uid: string, appId: string): Promise<void>;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* SchemaProtectionService - Detects destructive schema changes
|
|
146
|
+
*
|
|
147
|
+
* Uses real diff functions from @almadar/core to detect and categorize
|
|
148
|
+
* removals, page content reductions, and other destructive changes.
|
|
149
|
+
*/
|
|
150
|
+
|
|
151
|
+
declare class SchemaProtectionService {
|
|
152
|
+
/**
|
|
153
|
+
* Compare two schemas and detect destructive changes.
|
|
154
|
+
*
|
|
155
|
+
* Returns categorized removals including page content reductions.
|
|
156
|
+
*/
|
|
157
|
+
compareSchemas(before: OrbitalSchema, after: OrbitalSchema): {
|
|
158
|
+
isDestructive: boolean;
|
|
159
|
+
removals: CategorizedRemovals;
|
|
160
|
+
};
|
|
161
|
+
/** Check if critical removals require confirmation */
|
|
162
|
+
requiresConfirmation(removals: CategorizedRemovals): boolean;
|
|
163
|
+
/** Check for significant page content reductions */
|
|
164
|
+
hasSignificantContentReduction(reductions: PageContentReduction[]): boolean;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export { ChangeSetStore, SchemaProtectionService, SchemaStore, SnapshotStore, ValidationStore, fromFirestoreFormat, toFirestoreFormat };
|