@ultrst-armada/krnl 1.0.2 → 1.0.4

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.
Files changed (237) hide show
  1. package/README.md +67 -65
  2. package/package.json +15 -8
  3. package/packages/cli/dist/bin/krnl.cjs +808 -331
  4. package/packages/cli/dist/bin/krnl.js +50 -65
  5. package/packages/cli/dist/chunk-GHWYEAPD.js +1642 -0
  6. package/packages/cli/dist/index.cjs +761 -263
  7. package/packages/cli/dist/index.d.cts +45 -2
  8. package/packages/cli/dist/index.d.ts +45 -2
  9. package/packages/cli/dist/index.js +5 -1
  10. package/packages/cli/package.json +17 -10
  11. package/packages/core/dist/index.cjs +2912 -140
  12. package/packages/core/dist/index.d.cts +614 -27
  13. package/packages/core/dist/index.d.ts +614 -27
  14. package/packages/core/dist/index.js +2903 -140
  15. package/packages/core/package.json +15 -6
  16. package/packages/create-krnl/dist/index.cjs +102 -65
  17. package/packages/create-krnl/dist/index.js +102 -65
  18. package/packages/create-krnl/package.json +5 -3
  19. package/packages/eslint-plugin/dist/index.cjs +17 -55
  20. package/packages/eslint-plugin/dist/index.d.cts +8 -43
  21. package/packages/eslint-plugin/dist/index.d.ts +8 -43
  22. package/packages/eslint-plugin/dist/index.js +17 -55
  23. package/packages/eslint-plugin/package.json +5 -4
  24. package/packages/validate/dist/index.cjs +378 -85
  25. package/packages/validate/dist/index.d.cts +11 -1
  26. package/packages/validate/dist/index.d.ts +11 -1
  27. package/packages/validate/dist/index.js +375 -82
  28. package/packages/validate/package.json +14 -7
  29. package/packages/cli/dist/K_ENT_Entity-PQ5MURQY-ZG6FZXXX.js +0 -13
  30. package/packages/cli/dist/K_LIB_Krnl_Aiko_Client-OPZAVZCW-K4CRQYA5.js +0 -8
  31. package/packages/cli/dist/K_LIB_Krnl_Ep_Tools-ROOE2TQF-LNPXZYOE.js +0 -110
  32. package/packages/cli/dist/K_LIB_Krnl_Frontend_Builder-2ZDUH6PF-3ALZPNX4.js +0 -11
  33. package/packages/cli/dist/K_LIB_Krnl_Frontend_Generator-UYNRNX2X-PJTICYSW.js +0 -11
  34. package/packages/cli/dist/K_LIB_Krnl_Registry_Client-NXV6D2QW-M5TYU55O.js +0 -8
  35. package/packages/cli/dist/K_LIB_Krnl_SubApp_Engine-7HR5HWW2-H6STOXRK.js +0 -20
  36. package/packages/cli/dist/K_LIB_Krnl_SubApp_Registry-7XYGDKJK-XPJXSF7D.js +0 -19
  37. package/packages/cli/dist/K_LIB_Krnl_SubApp_Worker_Host-CQVTGGG7-CUYGTWOW.js +0 -7
  38. package/packages/cli/dist/K_LIB_Krnl_System_App_Adapter-M6FJKLGU-VR6T4N6J.js +0 -17
  39. package/packages/cli/dist/K_LIB_Krnl_System_Base_Functionality_APIs-ZZUZ6ZG5-RLJ4QWDP.js +0 -58
  40. package/packages/cli/dist/K_LIB_Krnl_System_Magnetic_Loader-XBQO2KXF-BOQGUASC.js +0 -22
  41. package/packages/cli/dist/K_LIB_Krnl_System_Unified_Http_Adapter-RCEGP3OG-XJI7KIT5.js +0 -8
  42. package/packages/cli/dist/K_LIB_Krnl_Telemetry-77FRZ3KV-K5VCWAFW.js +0 -9
  43. package/packages/cli/dist/chunk-3TRM6CYL.js +0 -152
  44. package/packages/cli/dist/chunk-5JDK35TG.js +0 -3872
  45. package/packages/cli/dist/chunk-63PTK33I.js +0 -997
  46. package/packages/cli/dist/chunk-6NIKAMUZ.js +0 -82
  47. package/packages/cli/dist/chunk-BOXWWXQG.js +0 -400
  48. package/packages/cli/dist/chunk-EEKLK7QJ.js +0 -565
  49. package/packages/cli/dist/chunk-EG7T2IAD.js +0 -107
  50. package/packages/cli/dist/chunk-ESTKOQ7T.js +0 -1146
  51. package/packages/cli/dist/chunk-F5EHXE2O.js +0 -1036
  52. package/packages/cli/dist/chunk-F65ANSQG.js +0 -269
  53. package/packages/cli/dist/chunk-FDNCADFE.js +0 -71
  54. package/packages/cli/dist/chunk-FZ6X7PHR.js +0 -1146
  55. package/packages/cli/dist/chunk-GDLRMDAN.js +0 -127
  56. package/packages/cli/dist/chunk-HWMTCMOS.js +0 -656
  57. package/packages/cli/dist/chunk-I74QIQOD.js +0 -5536
  58. package/packages/cli/dist/chunk-JTLSTO25.js +0 -71
  59. package/packages/cli/dist/chunk-JULBBJ27.js +0 -1033
  60. package/packages/cli/dist/chunk-OHVMVRBP.js +0 -117
  61. package/packages/cli/dist/chunk-P2PCLEKX.js +0 -666
  62. package/packages/cli/dist/chunk-PJQIUOX2.js +0 -7767
  63. package/packages/cli/dist/chunk-PPOF3DJW.js +0 -471
  64. package/packages/cli/dist/chunk-Q45257P6.js +0 -173
  65. package/packages/cli/dist/chunk-QNROWGBI.js +0 -1036
  66. package/packages/cli/dist/chunk-RJNYUXZD.js +0 -977
  67. package/packages/cli/dist/chunk-SRTOPJ5W.js +0 -104
  68. package/packages/cli/dist/chunk-TCEX3MPS.js +0 -702
  69. package/packages/cli/dist/chunk-TNOAJO2J.js +0 -675
  70. package/packages/cli/dist/chunk-UR2SUCSF.js +0 -701
  71. package/packages/cli/dist/chunk-X5LOIFM7.js +0 -68
  72. package/packages/cli/dist/chunk-XIRFOO3H.js +0 -589
  73. package/packages/cli/dist/chunk-ZAAV3JXQ.js +0 -809
  74. package/packages/core/dist/K_ENT_Entity-4MB7CU4A.js +0 -12
  75. package/packages/core/dist/K_ENT_Entity-DEJGIDGH.js +0 -12
  76. package/packages/core/dist/K_ENT_Entity-FXZO5UZ2.js +0 -12
  77. package/packages/core/dist/K_ENT_Entity-HDSCXC5F.js +0 -12
  78. package/packages/core/dist/K_ENT_Entity-JILABHLW.js +0 -12
  79. package/packages/core/dist/K_ENT_Entity-PQ5MURQY.js +0 -13
  80. package/packages/core/dist/K_ENT_Entity-ROAKDIHW.js +0 -12
  81. package/packages/core/dist/K_ENT_Entity-TBVFSKDV.js +0 -12
  82. package/packages/core/dist/K_ENT_Entity-ZFLKG3JO.js +0 -12
  83. package/packages/core/dist/K_LIB_Krnl_Aiko_Client-OPZAVZCW.js +0 -8
  84. package/packages/core/dist/K_LIB_Krnl_Ep_Tools-ROOE2TQF.js +0 -110
  85. package/packages/core/dist/K_LIB_Krnl_Frontend_Builder-2ZDUH6PF.js +0 -9
  86. package/packages/core/dist/K_LIB_Krnl_Frontend_Generator-UYNRNX2X.js +0 -9
  87. package/packages/core/dist/K_LIB_Krnl_Registry_Client-NXV6D2QW.js +0 -8
  88. package/packages/core/dist/K_LIB_Krnl_SubApp_Engine-7HR5HWW2.js +0 -20
  89. package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-4C6GBQAG.js +0 -18
  90. package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-6V23A6F6.js +0 -18
  91. package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-7XYGDKJK.js +0 -19
  92. package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-DYLLC5G2.js +0 -18
  93. package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-F4FOFAUP.js +0 -18
  94. package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-FBOXXMI3.js +0 -18
  95. package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-Q73BWLNP.js +0 -18
  96. package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-QM2C4MVA.js +0 -18
  97. package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-QUGI6SMP.js +0 -18
  98. package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-RGMTBHNP.js +0 -18
  99. package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-RWR7UNDW.js +0 -18
  100. package/packages/core/dist/K_LIB_Krnl_SubApp_Worker_Host-CQVTGGG7.js +0 -7
  101. package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-32MFJJ5Z.js +0 -17
  102. package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-4J4CEWRY.js +0 -17
  103. package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-A366YHF2.js +0 -17
  104. package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-LK4LYJHI.js +0 -17
  105. package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-M6FJKLGU.js +0 -17
  106. package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-NW3K63II.js +0 -17
  107. package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-QDHTI5ZV.js +0 -17
  108. package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-VHCT6VIW.js +0 -17
  109. package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-WBRKF24R.js +0 -17
  110. package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-XKYQYT6Q.js +0 -17
  111. package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-Z2PWZYDZ.js +0 -17
  112. package/packages/core/dist/K_LIB_Krnl_System_Base_Functionality_APIs-ZZUZ6ZG5.js +0 -58
  113. package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-2EZU7QF4.js +0 -21
  114. package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-6G3AQNAV.js +0 -21
  115. package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-B2NZMQUG.js +0 -21
  116. package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-C2RABMZP.js +0 -21
  117. package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-HHE3NZ4F.js +0 -21
  118. package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-LHFGR6WK.js +0 -21
  119. package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-M74BLM3N.js +0 -21
  120. package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-PT6ZA6EA.js +0 -21
  121. package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-SP52V3CX.js +0 -21
  122. package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-XBQO2KXF.js +0 -22
  123. package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-YNSEUHJJ.js +0 -21
  124. package/packages/core/dist/K_LIB_Krnl_System_Unified_Http_Adapter-M6SBHIVA.js +0 -8
  125. package/packages/core/dist/K_LIB_Krnl_System_Unified_Http_Adapter-RCEGP3OG.js +0 -8
  126. package/packages/core/dist/K_LIB_Krnl_Telemetry-77FRZ3KV.js +0 -9
  127. package/packages/core/dist/chunk-33L2J7V7.js +0 -82
  128. package/packages/core/dist/chunk-3ES4K2ZP.js +0 -817
  129. package/packages/core/dist/chunk-3SMHVVLL.js +0 -82
  130. package/packages/core/dist/chunk-3UEZ7JQA.js +0 -4877
  131. package/packages/core/dist/chunk-3XIE6KBD.js +0 -4877
  132. package/packages/core/dist/chunk-46ZLCML6.js +0 -107
  133. package/packages/core/dist/chunk-4UEL3DWJ.js +0 -107
  134. package/packages/core/dist/chunk-54GWZ5PC.js +0 -82
  135. package/packages/core/dist/chunk-6QCYRS5L.js +0 -127
  136. package/packages/core/dist/chunk-6ULMHNXR.js +0 -104
  137. package/packages/core/dist/chunk-7WUETJEV.js +0 -117
  138. package/packages/core/dist/chunk-A5KRAYDB.js +0 -107
  139. package/packages/core/dist/chunk-ANEAZ3PO.js +0 -107
  140. package/packages/core/dist/chunk-BE76RL6F.js +0 -82
  141. package/packages/core/dist/chunk-CIARVYDO.js +0 -107
  142. package/packages/core/dist/chunk-CR2ANE62.js +0 -107
  143. package/packages/core/dist/chunk-DI27MOR7.js +0 -173
  144. package/packages/core/dist/chunk-E6RLFHM5.js +0 -596
  145. package/packages/core/dist/chunk-EELAPMNV.js +0 -4877
  146. package/packages/core/dist/chunk-ENZ2RQVJ.js +0 -107
  147. package/packages/core/dist/chunk-F2U6CX4F.js +0 -4877
  148. package/packages/core/dist/chunk-FKG64OHL.js +0 -82
  149. package/packages/core/dist/chunk-FYAZJYM5.js +0 -772
  150. package/packages/core/dist/chunk-G4NP4RDQ.js +0 -82
  151. package/packages/core/dist/chunk-GADIRLRS.js +0 -473
  152. package/packages/core/dist/chunk-GR6CSLQJ.js +0 -772
  153. package/packages/core/dist/chunk-HDS7AT6O.js +0 -772
  154. package/packages/core/dist/chunk-HIMEZUU2.js +0 -71
  155. package/packages/core/dist/chunk-IB4UHG5I.js +0 -596
  156. package/packages/core/dist/chunk-J7ZYNVRS.js +0 -772
  157. package/packages/core/dist/chunk-JVLZU4K7.js +0 -604
  158. package/packages/core/dist/chunk-JYXXI7Q3.js +0 -82
  159. package/packages/core/dist/chunk-K54K2KZW.js +0 -192
  160. package/packages/core/dist/chunk-K6VHLBNH.js +0 -3316
  161. package/packages/core/dist/chunk-KCBP7ZYA.js +0 -596
  162. package/packages/core/dist/chunk-KFQGP6VL.js +0 -33
  163. package/packages/core/dist/chunk-KIUEUI7K.js +0 -82
  164. package/packages/core/dist/chunk-LB67NIZP.js +0 -949
  165. package/packages/core/dist/chunk-LDTYFUNW.js +0 -596
  166. package/packages/core/dist/chunk-MLVWARD4.js +0 -596
  167. package/packages/core/dist/chunk-MPYVXHKO.js +0 -2777
  168. package/packages/core/dist/chunk-MRQ3MITU.js +0 -772
  169. package/packages/core/dist/chunk-MVUN7I7O.js +0 -107
  170. package/packages/core/dist/chunk-NDDOIRW7.js +0 -596
  171. package/packages/core/dist/chunk-O3KDB5MH.js +0 -3330
  172. package/packages/core/dist/chunk-OEINUDSE.js +0 -400
  173. package/packages/core/dist/chunk-OPDENPT4.js +0 -4877
  174. package/packages/core/dist/chunk-P6XEM2LA.js +0 -429
  175. package/packages/core/dist/chunk-QKMRSQV4.js +0 -82
  176. package/packages/core/dist/chunk-R6CCWALL.js +0 -4877
  177. package/packages/core/dist/chunk-RAMMOO73.js +0 -774
  178. package/packages/core/dist/chunk-S3FTZFG6.js +0 -772
  179. package/packages/core/dist/chunk-T2ZVN434.js +0 -4877
  180. package/packages/core/dist/chunk-TFHLDMIA.js +0 -107
  181. package/packages/core/dist/chunk-TI3QUCIJ.js +0 -107
  182. package/packages/core/dist/chunk-TR2BAIGI.js +0 -107
  183. package/packages/core/dist/chunk-UGVVJKC6.js +0 -4877
  184. package/packages/core/dist/chunk-V2DX7YIU.js +0 -984
  185. package/packages/core/dist/chunk-W4IHYUYD.js +0 -772
  186. package/packages/core/dist/chunk-W6WBG5OY.js +0 -4877
  187. package/packages/core/dist/chunk-WN3M7O7H.js +0 -3312
  188. package/packages/core/dist/chunk-WR3NCU7U.js +0 -4877
  189. package/packages/core/dist/chunk-XC6B6L27.js +0 -82
  190. package/packages/core/dist/chunk-XNSE64ZY.js +0 -3316
  191. package/packages/core/dist/chunk-XROES2WN.js +0 -634
  192. package/packages/core/dist/chunk-XSCKNRCZ.js +0 -5020
  193. package/packages/core/dist/chunk-XVABPAUZ.js +0 -82
  194. package/packages/core/dist/chunk-YOBCB5C5.js +0 -596
  195. package/packages/core/dist/chunk-YUK6Q563.js +0 -666
  196. package/packages/core/dist/chunk-YXGL3ZEU.js +0 -1030
  197. package/packages/core/dist/chunk-ZINJ5OUX.js +0 -596
  198. package/packages/core/dist/chunk-ZPFD4BIQ.js +0 -604
  199. package/packages/core/dist/chunk-ZQZBFG6F.js +0 -4826
  200. package/packages/core/dist/chunk-ZZFRJGXM.js +0 -4877
  201. package/packages/create-krnl/dist/K_ENT_Entity-PQ5MURQY-R2OH4VXE.js +0 -14
  202. package/packages/create-krnl/dist/K_LIB_Krnl_Aiko_Client-OPZAVZCW-AZM5HF4M.js +0 -9
  203. package/packages/create-krnl/dist/K_LIB_Krnl_Ep_Tools-ROOE2TQF-X5N6SV2S.js +0 -111
  204. package/packages/create-krnl/dist/K_LIB_Krnl_Frontend_Builder-2ZDUH6PF-5PFHF4JU.js +0 -12
  205. package/packages/create-krnl/dist/K_LIB_Krnl_Frontend_Generator-UYNRNX2X-ED33OQPH.js +0 -12
  206. package/packages/create-krnl/dist/K_LIB_Krnl_Registry_Client-NXV6D2QW-YGQLVR4P.js +0 -9
  207. package/packages/create-krnl/dist/K_LIB_Krnl_SubApp_Engine-7HR5HWW2-VM7CTU2L.js +0 -21
  208. package/packages/create-krnl/dist/K_LIB_Krnl_SubApp_Registry-7XYGDKJK-Y2GQFJ7I.js +0 -21
  209. package/packages/create-krnl/dist/K_LIB_Krnl_SubApp_Worker_Host-CQVTGGG7-L3JQWO54.js +0 -8
  210. package/packages/create-krnl/dist/K_LIB_Krnl_System_App_Adapter-M6FJKLGU-KQARASPR.js +0 -19
  211. package/packages/create-krnl/dist/K_LIB_Krnl_System_Base_Functionality_APIs-ZZUZ6ZG5-OF3IGLVL.js +0 -59
  212. package/packages/create-krnl/dist/K_LIB_Krnl_System_Magnetic_Loader-XBQO2KXF-JOLHKOEX.js +0 -24
  213. package/packages/create-krnl/dist/K_LIB_Krnl_System_Unified_Http_Adapter-RCEGP3OG-L46LSHKQ.js +0 -10
  214. package/packages/create-krnl/dist/K_LIB_Krnl_Telemetry-77FRZ3KV-4JUPRR3X.js +0 -10
  215. package/packages/create-krnl/dist/chunk-3TRM6CYL.js +0 -152
  216. package/packages/create-krnl/dist/chunk-3URCB3QS.js +0 -127
  217. package/packages/create-krnl/dist/chunk-5JDK35TG.js +0 -3872
  218. package/packages/create-krnl/dist/chunk-A2XAXDOW.js +0 -812
  219. package/packages/create-krnl/dist/chunk-AN6TMQHG.js +0 -1033
  220. package/packages/create-krnl/dist/chunk-BOXWWXQG.js +0 -400
  221. package/packages/create-krnl/dist/chunk-BPTBREBG.js +0 -38
  222. package/packages/create-krnl/dist/chunk-FDNCADFE.js +0 -71
  223. package/packages/create-krnl/dist/chunk-JTLSTO25.js +0 -71
  224. package/packages/create-krnl/dist/chunk-M3NCUIPL.js +0 -107
  225. package/packages/create-krnl/dist/chunk-N2EONSOC.js +0 -7769
  226. package/packages/create-krnl/dist/chunk-OHVMVRBP.js +0 -117
  227. package/packages/create-krnl/dist/chunk-Q45257P6.js +0 -173
  228. package/packages/create-krnl/dist/chunk-QGM4M3NI.js +0 -37
  229. package/packages/create-krnl/dist/chunk-RJNYUXZD.js +0 -977
  230. package/packages/create-krnl/dist/chunk-SRTOPJ5W.js +0 -104
  231. package/packages/create-krnl/dist/chunk-UPBIGEC3.js +0 -666
  232. package/packages/create-krnl/dist/chunk-V2DB7UD7.js +0 -82
  233. package/packages/create-krnl/dist/chunk-WMHLIUI6.js +0 -3644
  234. package/packages/create-krnl/dist/chunk-XIRFOO3H.js +0 -589
  235. package/packages/create-krnl/dist/chunk-Y2CEEVZP.js +0 -5536
  236. package/packages/create-krnl/dist/main-DXUMPBND.js +0 -2217
  237. package/packages/create-krnl/dist/wrapper-SDF2VKX3.js +0 -31
@@ -1,4826 +0,0 @@
1
- import {
2
- FileLock,
3
- KrnlFS,
4
- KrnlHTTP,
5
- ObjectKit
6
- } from "./chunk-MLVWARD4.js";
7
- import {
8
- KrnlTelemetry,
9
- telemetry
10
- } from "./chunk-DI27MOR7.js";
11
-
12
- // src/core/K_LIB_Krnl_Error_Concepts.ts
13
- var krnlErrorCodes = Object.freeze({
14
- // Enum errors (0-99)
15
- ENUM_ID_NOT_FOUND: -1,
16
- // State errors (100-199)
17
- STATE_NOT_FOUND: 100,
18
- STATE_DUPLICATE_NAME: 101,
19
- STATE_INDEX_OUT_OF_RANGE: 102,
20
- // Functionality errors (200-299)
21
- FUNCTIONALITY_NOT_DEFINED: 200,
22
- FUNCTIONALITY_ALREADY_DEFINED: 201,
23
- FUNCTIONALITY_INSTANCE_NOT_FOUND: 202,
24
- REMOTE_EXECUTION_FAILED: 203,
25
- // Interface errors (300-399)
26
- EMITTER_NOT_FOUND: 300,
27
- COLLECTOR_NOT_FOUND: 301,
28
- COLLECTOR_NOT_SUBSCRIBED: 302,
29
- // Hierarchy errors (400-499)
30
- ENTITY_NOT_FOUND: 400,
31
- // Networking/RPC errors (500-599)
32
- ERR_NET_RPC_TIMEOUT_EXCEEDED: 500,
33
- ERR_NET_RPC_NETWORK_ERROR: 501,
34
- ERR_NET_RPC_INTERNAL_SERVER_ERROR: 502,
35
- ERR_SYS_ENTITY_BUSY_RESERVED: 503,
36
- ERR_COM_INGRESS_ROUTE_NOT_FOUND: 504,
37
- ERR_KRNL_UNRESOLVED_REMOTE_TARGET: 505
38
- });
39
- var krnlErrorMessages = Object.freeze({
40
- // Enum
41
- [krnlErrorCodes.ENUM_ID_NOT_FOUND]: "ERR_LIB_ENUM_ID_NOT_FOUND",
42
- // State
43
- [krnlErrorCodes.STATE_NOT_FOUND]: "ERR_LIB_STATE_NOT_FOUND",
44
- [krnlErrorCodes.STATE_DUPLICATE_NAME]: "ERR_LIB_STATE_DUPLICATE_NAME",
45
- [krnlErrorCodes.STATE_INDEX_OUT_OF_RANGE]: "ERR_LIB_STATE_INDEX_OUT_OF_RANGE",
46
- // Functionality
47
- [krnlErrorCodes.FUNCTIONALITY_NOT_DEFINED]: "ERR_LIB_FUNCTIONALITY_NOT_DEFINED",
48
- [krnlErrorCodes.FUNCTIONALITY_ALREADY_DEFINED]: "ERR_LIB_FUNCTIONALITY_ALREADY_DEFINED",
49
- [krnlErrorCodes.FUNCTIONALITY_INSTANCE_NOT_FOUND]: "ERR_LIB_FUNCTIONALITY_INSTANCE_NOT_FOUND",
50
- [krnlErrorCodes.REMOTE_EXECUTION_FAILED]: "ERR_LIB_REMOTE_EXECUTION_FAILED",
51
- // Interface
52
- [krnlErrorCodes.EMITTER_NOT_FOUND]: "ERR_INT_EMITTER_NOT_FOUND",
53
- [krnlErrorCodes.COLLECTOR_NOT_FOUND]: "ERR_INT_COLLECTOR_NOT_FOUND",
54
- [krnlErrorCodes.COLLECTOR_NOT_SUBSCRIBED]: "ERR_INT_COLLECTOR_NOT_SUBSCRIBED",
55
- // Hierarchy
56
- [krnlErrorCodes.ENTITY_NOT_FOUND]: "ERR_SYS_ENTITY_NOT_FOUND",
57
- // Networking/RPC
58
- [krnlErrorCodes.ERR_NET_RPC_TIMEOUT_EXCEEDED]: "ERR_NET_RPC_TIMEOUT_EXCEEDED",
59
- [krnlErrorCodes.ERR_NET_RPC_NETWORK_ERROR]: "ERR_NET_RPC_NETWORK_ERROR",
60
- [krnlErrorCodes.ERR_NET_RPC_INTERNAL_SERVER_ERROR]: "ERR_NET_RPC_INTERNAL_SERVER_ERROR",
61
- [krnlErrorCodes.ERR_SYS_ENTITY_BUSY_RESERVED]: "ERR_SYS_ENTITY_BUSY_RESERVED",
62
- [krnlErrorCodes.ERR_COM_INGRESS_ROUTE_NOT_FOUND]: "ERR_COM_INGRESS_ROUTE_NOT_FOUND",
63
- [krnlErrorCodes.ERR_KRNL_UNRESOLVED_REMOTE_TARGET]: "ERR_KRNL_UNRESOLVED_REMOTE_TARGET"
64
- });
65
- var CUSTOM_ERROR_CODE = -999;
66
- var krnlError = class extends Error {
67
- code;
68
- errorMessage;
69
- constructor(code) {
70
- const isNumeric = typeof code === "number";
71
- const numericCode = isNumeric ? code : CUSTOM_ERROR_CODE;
72
- const message = isNumeric ? krnlErrorMessages[numericCode] ?? `Error: ${code}` : code;
73
- super(message);
74
- this.code = numericCode;
75
- this.errorMessage = this.message;
76
- }
77
- };
78
-
79
- // src/core/K_LIB_Krnl_Messaging_Concepts.ts
80
- var krnlGlobalEmitterAddress = class {
81
- systemID = 0;
82
- // ["qwnsr", "brnda", "symba", "mosy"]
83
- subSystemID = 0;
84
- // ["qwnsrControl", "qwnsrBlocks"]
85
- entityAddress = [];
86
- // nesting of type
87
- emitterID = 0;
88
- };
89
- var krnlLocalEmitterAddress = class {
90
- isOwnerEntity = true;
91
- siblingID = 0;
92
- emitterID = 0;
93
- };
94
- var krnlMessage = class {
95
- data = null;
96
- timestamp = 0;
97
- // Tracing metadata aligned with core lingo
98
- sourceEmitterAddress = null;
99
- targetCollectorAddress = null;
100
- traceCorrelationId = null;
101
- originSystemAddress = null;
102
- };
103
- var krnlEmitter = class {
104
- subscribers;
105
- localAddress;
106
- constructor() {
107
- this.subscribers = [];
108
- this.localAddress = null;
109
- }
110
- /**
111
- * Pushes data into the emitter and notifies all subscribers.
112
- */
113
- pushDataStreamToEmitter(data, time = {}, transform = (msg) => msg) {
114
- const msg = new krnlMessage();
115
- msg.data = data;
116
- msg.timestamp = typeof time === "number" ? time : time?.timestamp || Date.now();
117
- const transformed = transform(msg);
118
- for (let i = 0; i < this.subscribers.length; i++) {
119
- this.subscribers[i](transformed);
120
- }
121
- }
122
- /**
123
- * Subscribes a handler to this emitter. Handler is called on every push.
124
- */
125
- subscribe(handler) {
126
- this.subscribers.push(handler);
127
- return () => {
128
- const idx = this.subscribers.indexOf(handler);
129
- if (idx >= 0) this.subscribers.splice(idx, 1);
130
- };
131
- }
132
- /**
133
- * Subscribes once and auto-unsubscribes after first message.
134
- */
135
- subscribeOnce(handler) {
136
- let unsubscribe = null;
137
- const wrapped = (msg) => {
138
- if (unsubscribe) unsubscribe();
139
- handler(msg);
140
- };
141
- unsubscribe = this.subscribe(wrapped);
142
- return unsubscribe;
143
- }
144
- /**
145
- * Resets the emitter and removes all subscribers.
146
- */
147
- disconnectEmitter() {
148
- this.subscribers = [];
149
- }
150
- };
151
- var krnlCollector = class {
152
- emitter;
153
- unsubscribe;
154
- _directHandler;
155
- constructor() {
156
- this.emitter = null;
157
- this.unsubscribe = null;
158
- }
159
- /**
160
- * Subscribes this collector to an emitter. Messages are forwarded to the handler.
161
- */
162
- subscribeTo(emitter, handler = null, options = {}) {
163
- const { mode = "stream" } = options;
164
- const resolvedHandler = typeof handler === "function" ? handler : (msg) => {
165
- if (typeof this._directHandler === "function") {
166
- return this._directHandler(msg);
167
- }
168
- return msg;
169
- };
170
- this.disconnectCollector();
171
- this.emitter = emitter;
172
- this.unsubscribe = mode === "once" ? emitter.subscribeOnce(resolvedHandler) : emitter.subscribe(resolvedHandler);
173
- return this.unsubscribe;
174
- }
175
- /**
176
- * Collects a single message from the emitter and auto-disconnects.
177
- */
178
- collectNext(emitter, timeoutMs = null) {
179
- return new Promise((resolve, reject) => {
180
- let timeout = null;
181
- const cleanup = () => {
182
- if (timeout) clearTimeout(timeout);
183
- this.disconnectCollector();
184
- };
185
- this.subscribeTo(emitter, (msg) => {
186
- cleanup();
187
- resolve(msg);
188
- }, { mode: "once" });
189
- if (typeof timeoutMs === "number" && timeoutMs > 0) {
190
- timeout = setTimeout(() => {
191
- cleanup();
192
- reject(new Error(`Collector timeout after ${timeoutMs}ms`));
193
- }, timeoutMs);
194
- }
195
- });
196
- }
197
- /**
198
- * Pipes messages from a connected emitter through a transform function.
199
- */
200
- pipeTo(target, fn) {
201
- const pipeEmitter = new krnlEmitter();
202
- this.emitter = pipeEmitter;
203
- const unsub = pipeEmitter.subscribe((msg) => {
204
- fn(msg);
205
- });
206
- this.unsubscribe = unsub;
207
- return () => {
208
- this.disconnectCollector();
209
- };
210
- }
211
- /**
212
- * Pushes data directly into this collector's connected emitter.
213
- */
214
- pushDataStreamToEmitter(data) {
215
- if (this.emitter === null) {
216
- throw new krnlError(krnlErrorCodes.COLLECTOR_NOT_SUBSCRIBED);
217
- }
218
- this.emitter.pushDataStreamToEmitter(data);
219
- }
220
- /**
221
- * Disconnects from the current emitter.
222
- */
223
- disconnectCollector() {
224
- if (this.unsubscribe) {
225
- this.unsubscribe();
226
- this.unsubscribe = null;
227
- this.emitter = null;
228
- }
229
- }
230
- };
231
- var krnlInterface = class {
232
- ownerEntity;
233
- emitters;
234
- collectors;
235
- constructor(ownerEntity) {
236
- this.ownerEntity = ownerEntity;
237
- this.emitters = /* @__PURE__ */ new Map();
238
- this.collectors = /* @__PURE__ */ new Map();
239
- }
240
- /**
241
- * Creates and registers a new emitter.
242
- */
243
- createEmitter(name) {
244
- const emitter = new krnlEmitter();
245
- this.emitters.set(name, emitter);
246
- emitter.localAddress = {
247
- isOwnerEntity: false,
248
- siblingID: 0,
249
- emitterID: this.emitters.size - 1
250
- };
251
- return emitter;
252
- }
253
- /**
254
- * Creates and registers a new collector.
255
- */
256
- createCollector(name) {
257
- const collector = new krnlCollector();
258
- this.collectors.set(name, collector);
259
- return collector;
260
- }
261
- /**
262
- * Gets an emitter by name.
263
- */
264
- getEmitter(name) {
265
- const emitter = this.emitters.get(name);
266
- if (!emitter) throw new krnlError(krnlErrorCodes.EMITTER_NOT_FOUND);
267
- return emitter;
268
- }
269
- /**
270
- * Gets a collector by name.
271
- */
272
- getCollector(name) {
273
- const collector = this.collectors.get(name);
274
- if (!collector) throw new krnlError(krnlErrorCodes.COLLECTOR_NOT_FOUND);
275
- return collector;
276
- }
277
- /**
278
- * Removes an emitter by name.
279
- */
280
- removeEmitter(name) {
281
- const emitter = this.emitters.get(name);
282
- if (emitter) {
283
- emitter.disconnectEmitter();
284
- this.emitters.delete(name);
285
- }
286
- }
287
- /**
288
- * Removes a collector by name.
289
- */
290
- removeCollector(name) {
291
- const collector = this.collectors.get(name);
292
- if (collector) {
293
- collector.disconnectCollector();
294
- this.collectors.delete(name);
295
- }
296
- }
297
- /**
298
- * Gets all emitters in scope (self + owner + siblings).
299
- */
300
- getEmittersInScope() {
301
- const interfaces = this.getInterfacesInScope();
302
- const result = [];
303
- for (let i = 0; i < interfaces.length; i++) {
304
- for (const emitter of interfaces[i].emitters.values()) {
305
- result.push(emitter);
306
- }
307
- }
308
- return result;
309
- }
310
- /**
311
- * Gets all interfaces in scope (self, owner, and siblings).
312
- */
313
- getInterfacesInScope() {
314
- const owner = this.ownerEntity.hierarchyManager ? this.ownerEntity.hierarchyManager.getOwnerEntity() : null;
315
- if (!owner) return [this];
316
- const scopes = [this];
317
- if (owner.interface) scopes.push(owner.interface);
318
- const siblings = owner.hierarchyManager ? owner.hierarchyManager.getAllOwnedComponentEntities() : [];
319
- for (let i = 0; i < siblings.length; i++) {
320
- if (siblings[i] !== this.ownerEntity && siblings[i].interface) {
321
- scopes.push(siblings[i].interface);
322
- }
323
- }
324
- return scopes;
325
- }
326
- /**
327
- * Disconnects all emitters and collectors.
328
- */
329
- dispose() {
330
- for (const emitter of this.emitters.values()) {
331
- emitter.disconnectEmitter();
332
- }
333
- for (const collector of this.collectors.values()) {
334
- collector.disconnectCollector();
335
- }
336
- this.emitters.clear();
337
- this.collectors.clear();
338
- }
339
- };
340
-
341
- // src/core/K_LIB_Krnl_Entity_Compositional_Hierarchy.ts
342
- var krnlEntityCompositionalHierarchyManager = class {
343
- self;
344
- owner;
345
- EntityClass;
346
- ownedComponents;
347
- componentBaseNames;
348
- componentInstanceCounters;
349
- /**
350
- * @param self - The entity this manager belongs to.
351
- * @param owner - The parent entity (if any).
352
- * @param EntityClass - Reference to the Entity base class for instantiation.
353
- */
354
- constructor(self, owner, EntityClass) {
355
- this.self = self;
356
- this.owner = owner;
357
- this.EntityClass = EntityClass;
358
- this.ownedComponents = /* @__PURE__ */ new Map();
359
- this.componentBaseNames = /* @__PURE__ */ new Map();
360
- this.componentInstanceCounters = /* @__PURE__ */ new Map();
361
- }
362
- /**
363
- * Spawns a unique component entity within this host.
364
- */
365
- createComponentEntity(name, CustomClass = null, options = {}) {
366
- const ClassToUse = CustomClass || this.EntityClass;
367
- const shouldBeSingleton = options.Singleton ?? ClassToUse.Singleton ?? true;
368
- if (shouldBeSingleton && this.ownedComponents.has(name)) {
369
- return this.ownedComponents.get(name);
370
- }
371
- const currentCount = this.componentInstanceCounters.get(name) || 0;
372
- const nextCount = currentCount + 1;
373
- this.componentInstanceCounters.set(name, nextCount);
374
- const resolvedName = shouldBeSingleton ? name : `${name}#${nextCount}`;
375
- const newEntity = new ClassToUse(resolvedName, this.self, {
376
- Singleton: shouldBeSingleton,
377
- baseName: name,
378
- instanceNumber: nextCount
379
- });
380
- this.ownedComponents.set(resolvedName, newEntity);
381
- const group = this.componentBaseNames.get(name) || [];
382
- group.push(newEntity);
383
- this.componentBaseNames.set(name, group);
384
- if (this.self._onComponentAdded) {
385
- this.self._onComponentAdded(newEntity);
386
- }
387
- return newEntity;
388
- }
389
- /**
390
- * Registers a pre-existing entity instance as a component of this host.
391
- */
392
- addComponentEntityInstance(instance) {
393
- this.ownedComponents.set(instance.id, instance);
394
- instance.hierarchyManager.owner = this.self;
395
- const base = instance.baseName || instance.id;
396
- const group = this.componentBaseNames.get(base) || [];
397
- group.push(instance);
398
- this.componentBaseNames.set(base, group);
399
- if (typeof instance._onAdoptedByParent === "function") {
400
- try {
401
- const p = instance._onAdoptedByParent();
402
- if (p && typeof p.catch === "function") {
403
- p.catch((err) => console.warn(`[Hierarchy] Error in _onAdoptedByParent on '${instance.id}':`, err.message || err));
404
- }
405
- } catch (err) {
406
- console.warn(`[Hierarchy] Error in _onAdoptedByParent on '${instance.id}':`, err.message || err);
407
- }
408
- }
409
- if (instance._promoteAllExistingFunctionalities) {
410
- instance._promoteAllExistingFunctionalities();
411
- }
412
- }
413
- /**
414
- * Returns a list of all component entities owned by this host.
415
- */
416
- getAllOwnedComponentEntities() {
417
- return Array.from(this.ownedComponents.values());
418
- }
419
- /**
420
- * Returns all component entity instances created from the same base component name.
421
- */
422
- getOwnedComponentEntitiesByName(name) {
423
- return [...this.componentBaseNames.get(name) || []];
424
- }
425
- /**
426
- * Returns the parent entity.
427
- */
428
- getOwnerEntity() {
429
- return this.owner;
430
- }
431
- /**
432
- * Recursively disposes of all owned components.
433
- */
434
- dispose() {
435
- this.ownedComponents.forEach((entity) => {
436
- if (entity.dispose) entity.dispose();
437
- });
438
- this.ownedComponents.clear();
439
- this.componentBaseNames.clear();
440
- this.componentInstanceCounters.clear();
441
- }
442
- /**
443
- * Disposes a specific component instance by ID/name.
444
- */
445
- disposeComponentEntity(entityId) {
446
- const entity = this.ownedComponents.get(entityId);
447
- if (!entity) return false;
448
- if (entity.dispose) entity.dispose();
449
- this.ownedComponents.delete(entityId);
450
- const base = entity.baseName || entity.entityName;
451
- const group = this.componentBaseNames.get(base) || [];
452
- this.componentBaseNames.set(base, group.filter((e) => e !== entity));
453
- return true;
454
- }
455
- };
456
-
457
- // src/backend/FunctionalityTable.ts
458
- var FunctionalityTable = class {
459
- tableName;
460
- service;
461
- provider;
462
- isTableInitialized = false;
463
- constructor(tableName, provider) {
464
- this.tableName = tableName;
465
- this.provider = provider;
466
- this.service = provider.engine;
467
- }
468
- async ensureInitialized() {
469
- if (!this.isTableInitialized) {
470
- await this.provider.ensureTable(this.tableName);
471
- this.isTableInitialized = true;
472
- }
473
- }
474
- async find(filter, options) {
475
- await this.ensureInitialized();
476
- return await this.provider.find(this.tableName, filter, options);
477
- }
478
- async findOne(filter) {
479
- await this.ensureInitialized();
480
- return await this.provider.findOne(this.tableName, filter);
481
- }
482
- async create(record) {
483
- await this.ensureInitialized();
484
- const result = await this.provider.insert(this.tableName, record);
485
- return Array.isArray(result) ? result[0] : result;
486
- }
487
- async insert(record) {
488
- await this.ensureInitialized();
489
- return await this.provider.insert(this.tableName, record);
490
- }
491
- async update(filter, patch) {
492
- await this.ensureInitialized();
493
- return await this.provider.update(this.tableName, filter, patch);
494
- }
495
- async delete(filter) {
496
- await this.ensureInitialized();
497
- return await this.provider.delete(this.tableName, filter);
498
- }
499
- async upsert(record, keyField = "id") {
500
- await this.ensureInitialized();
501
- const keyVal = record[keyField];
502
- if (keyVal !== void 0 && keyVal !== null) {
503
- const existing = await this.findOne({ [keyField]: keyVal });
504
- if (existing) {
505
- await this.update({ [keyField]: keyVal }, record);
506
- return { ...existing, ...record };
507
- }
508
- }
509
- return await this.create(record);
510
- }
511
- async count(filter) {
512
- await this.ensureInitialized();
513
- return await this.provider.count(this.tableName, filter);
514
- }
515
- async query(rawQueryOrFilter, params) {
516
- await this.ensureInitialized();
517
- if (typeof rawQueryOrFilter === "string") {
518
- return await this.provider.query(rawQueryOrFilter, params);
519
- }
520
- return await this.provider.find(this.tableName, rawQueryOrFilter);
521
- }
522
- async clear() {
523
- await this.ensureInitialized();
524
- await this.provider.clear(this.tableName);
525
- }
526
- };
527
-
528
- // src/backend/providers/data/SQLiteDataProvider.ts
529
- import path from "path";
530
- var SQLiteDataProvider = class {
531
- engine = "sqlite";
532
- filePath;
533
- enableWAL;
534
- isConnected = false;
535
- tables = /* @__PURE__ */ new Map();
536
- constructor(config = "./workspace.sqlite") {
537
- if (typeof config === "string") {
538
- this.filePath = path.resolve(config);
539
- this.enableWAL = true;
540
- } else {
541
- this.filePath = path.resolve(config.filePath || "./workspace.sqlite");
542
- this.enableWAL = config.enableWAL !== false;
543
- }
544
- }
545
- async connect() {
546
- if (this.isConnected) return;
547
- const dir = path.dirname(this.filePath);
548
- await KrnlFS.ensureDir(dir);
549
- if (await KrnlFS.fileExists(this.filePath)) {
550
- try {
551
- const raw = await KrnlFS.readJSON(this.filePath);
552
- if (raw && typeof raw === "object") {
553
- for (const [tbl, rows] of Object.entries(raw)) {
554
- if (Array.isArray(rows)) {
555
- this.tables.set(tbl, rows);
556
- }
557
- }
558
- }
559
- } catch {
560
- this.tables.clear();
561
- }
562
- }
563
- this.isConnected = true;
564
- }
565
- async disconnect() {
566
- if (!this.isConnected) return;
567
- await this.persist();
568
- this.isConnected = false;
569
- }
570
- async testConnection() {
571
- const t0 = Date.now();
572
- try {
573
- await this.connect();
574
- const latencyMs = Date.now() - t0;
575
- return {
576
- ok: true,
577
- latencyMs,
578
- details: {
579
- engine: "sqlite",
580
- filePath: this.filePath,
581
- wal: this.enableWAL,
582
- tablesCount: this.tables.size
583
- }
584
- };
585
- } catch (err) {
586
- return {
587
- ok: false,
588
- latencyMs: Date.now() - t0,
589
- details: { error: err.message }
590
- };
591
- }
592
- }
593
- async ensureTable(tableName, _schema) {
594
- await this.connect();
595
- if (!this.tables.has(tableName)) {
596
- this.tables.set(tableName, []);
597
- await this.persist();
598
- }
599
- }
600
- async persist() {
601
- const release = await FileLock.acquire(this.filePath);
602
- try {
603
- const dump = {};
604
- for (const [tbl, rows] of this.tables.entries()) {
605
- dump[tbl] = rows;
606
- }
607
- await KrnlFS.writeJSON(this.filePath, dump, 2);
608
- } finally {
609
- release();
610
- }
611
- }
612
- getTableRows(table) {
613
- if (!this.tables.has(table)) {
614
- this.tables.set(table, []);
615
- }
616
- return this.tables.get(table);
617
- }
618
- matchesFilter(row, filter) {
619
- if (typeof filter === "string") {
620
- return row.id === filter || row._id === filter;
621
- }
622
- if (!filter || Object.keys(filter).length === 0) return true;
623
- for (const [key, expected] of Object.entries(filter)) {
624
- const actual = row[key];
625
- if (expected && typeof expected === "object" && !Array.isArray(expected)) {
626
- if ("$gt" in expected && !(actual > expected.$gt)) return false;
627
- if ("$gte" in expected && !(actual >= expected.$gte)) return false;
628
- if ("$lt" in expected && !(actual < expected.$lt)) return false;
629
- if ("$lte" in expected && !(actual <= expected.$lte)) return false;
630
- if ("$ne" in expected && actual === expected.$ne) return false;
631
- if ("$in" in expected && Array.isArray(expected.$in) && !expected.$in.includes(actual)) return false;
632
- if ("$nin" in expected && Array.isArray(expected.$nin) && expected.$nin.includes(actual)) return false;
633
- } else if (actual !== expected) {
634
- return false;
635
- }
636
- }
637
- return true;
638
- }
639
- async find(table, filter, options) {
640
- await this.connect();
641
- const rows = this.getTableRows(table);
642
- let results = rows.filter((r) => this.matchesFilter(r, filter || {}));
643
- if (options?.sort) {
644
- const sortEntries = Object.entries(options.sort);
645
- results.sort((a, b) => {
646
- for (const [key, dir] of sortEntries) {
647
- if (a[key] < b[key]) return dir === 1 ? -1 : 1;
648
- if (a[key] > b[key]) return dir === 1 ? 1 : -1;
649
- }
650
- return 0;
651
- });
652
- }
653
- if (options?.offset) {
654
- results = results.slice(options.offset);
655
- }
656
- if (options?.limit) {
657
- results = results.slice(0, options.limit);
658
- }
659
- return ObjectKit.deepClone(results);
660
- }
661
- async findOne(table, filter) {
662
- const list = await this.find(table, typeof filter === "string" ? { id: filter } : filter, { limit: 1 });
663
- return list[0] || null;
664
- }
665
- async insert(table, data) {
666
- await this.connect();
667
- const rows = this.getTableRows(table);
668
- const items = Array.isArray(data) ? data : [data];
669
- const inserted = [];
670
- for (const item of items) {
671
- const record = ObjectKit.deepClone(item);
672
- if (!record.id && !record._id) {
673
- record.id = `rec_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
674
- }
675
- if (!record.createdAt) {
676
- record.createdAt = Date.now();
677
- }
678
- record.updatedAt = Date.now();
679
- rows.push(record);
680
- inserted.push(record);
681
- }
682
- await this.persist();
683
- return Array.isArray(data) ? inserted : inserted[0];
684
- }
685
- async update(table, filter, patch) {
686
- await this.connect();
687
- const rows = this.getTableRows(table);
688
- let affected = 0;
689
- for (let i = 0; i < rows.length; i++) {
690
- if (this.matchesFilter(rows[i], filter)) {
691
- rows[i] = ObjectKit.deepMerge(rows[i], {
692
- ...patch,
693
- updatedAt: Date.now()
694
- });
695
- affected++;
696
- }
697
- }
698
- if (affected > 0) {
699
- await this.persist();
700
- }
701
- return { affectedRows: affected };
702
- }
703
- async delete(table, filter) {
704
- await this.connect();
705
- const rows = this.getTableRows(table);
706
- const initialLen = rows.length;
707
- const remaining = rows.filter((r) => !this.matchesFilter(r, filter));
708
- const affected = initialLen - remaining.length;
709
- if (affected > 0) {
710
- this.tables.set(table, remaining);
711
- await this.persist();
712
- }
713
- return { affectedRows: affected };
714
- }
715
- async count(table, filter) {
716
- await this.connect();
717
- const rows = this.getTableRows(table);
718
- if (!filter || Object.keys(filter).length === 0) {
719
- return rows.length;
720
- }
721
- return rows.filter((r) => this.matchesFilter(r, filter)).length;
722
- }
723
- async clear(table) {
724
- await this.connect();
725
- this.tables.set(table, []);
726
- await this.persist();
727
- }
728
- async query(queryStr, _params) {
729
- await this.connect();
730
- const normalized = queryStr.trim().toLowerCase();
731
- if (normalized.startsWith("select * from")) {
732
- const parts = queryStr.trim().split(/\s+/);
733
- const tableName = parts[3]?.replace(/['";]/g, "");
734
- if (tableName) {
735
- return this.find(tableName);
736
- }
737
- }
738
- return [];
739
- }
740
- async execute(commandStr, _params) {
741
- await this.connect();
742
- const normalized = commandStr.trim().toLowerCase();
743
- if (normalized.startsWith("create table")) {
744
- const parts = commandStr.trim().split(/\s+/);
745
- const tableName = parts[2]?.replace(/['";(]/g, "");
746
- if (tableName) {
747
- await this.ensureTable(tableName);
748
- return { affectedRows: 0 };
749
- }
750
- }
751
- return { affectedRows: 0 };
752
- }
753
- };
754
-
755
- // src/backend/providers/data/PostgresDataProvider.ts
756
- var PostgresDataProvider = class {
757
- engine = "postgres";
758
- config;
759
- isConnected = false;
760
- constructor(config) {
761
- this.config = config;
762
- }
763
- async connect() {
764
- this.isConnected = true;
765
- }
766
- async disconnect() {
767
- this.isConnected = false;
768
- }
769
- async testConnection() {
770
- const t0 = Date.now();
771
- try {
772
- if (this.config.endpoint) {
773
- const res = await KrnlHTTP.get(this.config.endpoint, { timeout: 5e3 });
774
- return {
775
- ok: res.status === "success" || res.code === 200,
776
- latencyMs: Date.now() - t0,
777
- details: {
778
- engine: "postgres",
779
- endpoint: this.config.endpoint,
780
- ssl: this.config.ssl ?? true
781
- }
782
- };
783
- }
784
- const hasConnectionInfo = Boolean(
785
- this.config.connectionString || this.config.host && this.config.database
786
- );
787
- return {
788
- ok: hasConnectionInfo,
789
- latencyMs: Date.now() - t0,
790
- details: {
791
- engine: "postgres",
792
- host: this.config.host,
793
- database: this.config.database,
794
- ssl: this.config.ssl ?? true,
795
- poolSize: this.config.poolSize ?? 10
796
- }
797
- };
798
- } catch (err) {
799
- return {
800
- ok: false,
801
- latencyMs: Date.now() - t0,
802
- details: { error: err.message }
803
- };
804
- }
805
- }
806
- async ensureTable(_tableName, _schema) {
807
- }
808
- async find(_table, _filter, _options) {
809
- return [];
810
- }
811
- async findOne(_table, _filter) {
812
- return null;
813
- }
814
- async insert(_table, data) {
815
- return data;
816
- }
817
- async update(_table, _filter, _patch) {
818
- return { affectedRows: 0 };
819
- }
820
- async delete(_table, _filter) {
821
- return { affectedRows: 0 };
822
- }
823
- async count(_table, _filter) {
824
- return 0;
825
- }
826
- async clear(_table) {
827
- }
828
- async query(_queryStr, _params) {
829
- return [];
830
- }
831
- async execute(_commandStr, _params) {
832
- return { affectedRows: 0 };
833
- }
834
- };
835
-
836
- // src/backend/providers/data/SupabaseDataProvider.ts
837
- var SupabaseDataProvider = class {
838
- engine = "supabase";
839
- endpoint;
840
- apiKey;
841
- isConnected = false;
842
- constructor(config) {
843
- this.endpoint = (config.endpoint || "").replace(/\/+$/, "");
844
- this.apiKey = config.apiKey || "";
845
- }
846
- getHeaders() {
847
- return {
848
- "apikey": this.apiKey,
849
- "Authorization": `Bearer ${this.apiKey}`,
850
- "Content-Type": "application/json",
851
- "Prefer": "return=representation"
852
- };
853
- }
854
- async connect() {
855
- this.isConnected = true;
856
- }
857
- async disconnect() {
858
- this.isConnected = false;
859
- }
860
- async testConnection() {
861
- const t0 = Date.now();
862
- try {
863
- const res = await KrnlHTTP.get(`${this.endpoint}/rest/v1/`, {
864
- headers: this.getHeaders(),
865
- timeout: 5e3
866
- });
867
- const latencyMs = Date.now() - t0;
868
- return {
869
- ok: res.status === "success" || res.code === 200 || res.code === 404,
870
- latencyMs,
871
- details: {
872
- engine: "supabase",
873
- endpoint: this.endpoint,
874
- status: res.status,
875
- code: res.code
876
- }
877
- };
878
- } catch (err) {
879
- return {
880
- ok: false,
881
- latencyMs: Date.now() - t0,
882
- details: { error: err.message }
883
- };
884
- }
885
- }
886
- async ensureTable(_tableName, _schema) {
887
- }
888
- buildQueryString(filter, options) {
889
- const params = [];
890
- if (filter) {
891
- for (const [key, val] of Object.entries(filter)) {
892
- if (val === null || val === void 0) {
893
- params.push(`${key}=is.null`);
894
- } else if (typeof val === "object") {
895
- if ("$gt" in val) params.push(`${key}=gt.${encodeURIComponent(val.$gt)}`);
896
- if ("$gte" in val) params.push(`${key}=gte.${encodeURIComponent(val.$gte)}`);
897
- if ("$lt" in val) params.push(`${key}=lt.${encodeURIComponent(val.$lt)}`);
898
- if ("$lte" in val) params.push(`${key}=lte.${encodeURIComponent(val.$lte)}`);
899
- if ("$ne" in val) params.push(`${key}=neq.${encodeURIComponent(val.$ne)}`);
900
- } else {
901
- params.push(`${key}=eq.${encodeURIComponent(String(val))}`);
902
- }
903
- }
904
- }
905
- if (options?.limit) {
906
- params.push(`limit=${options.limit}`);
907
- }
908
- if (options?.offset) {
909
- params.push(`offset=${options.offset}`);
910
- }
911
- if (options?.sort) {
912
- const order = Object.entries(options.sort).map(([col, dir]) => `${col}.${dir === 1 ? "asc" : "desc"}`).join(",");
913
- params.push(`order=${order}`);
914
- }
915
- return params.length > 0 ? `?${params.join("&")}` : "";
916
- }
917
- async find(table, filter, options) {
918
- const qs = this.buildQueryString(filter, options);
919
- const url = `${this.endpoint}/rest/v1/${table}${qs}`;
920
- const res = await KrnlHTTP.get(url, { headers: this.getHeaders() });
921
- if (res.status === "success" && Array.isArray(res.data)) {
922
- return res.data;
923
- }
924
- return [];
925
- }
926
- async findOne(table, filter) {
927
- const filterObj = typeof filter === "string" ? { id: filter } : filter;
928
- const list = await this.find(table, filterObj, { limit: 1 });
929
- return list[0] || null;
930
- }
931
- async insert(table, data) {
932
- const url = `${this.endpoint}/rest/v1/${table}`;
933
- const res = await KrnlHTTP.post(url, data, { headers: this.getHeaders() });
934
- if (res.status === "success" && res.data) {
935
- return Array.isArray(data) ? res.data : res.data[0];
936
- }
937
- return data;
938
- }
939
- async update(table, filter, patch) {
940
- const filterObj = typeof filter === "string" ? { id: filter } : filter;
941
- const qs = this.buildQueryString(filterObj);
942
- const url = `${this.endpoint}/rest/v1/${table}${qs}`;
943
- const res = await KrnlHTTP.request(url, {
944
- method: "PATCH",
945
- headers: this.getHeaders(),
946
- body: JSON.stringify(patch)
947
- });
948
- const affected = res.status === "success" && Array.isArray(res.data) ? res.data.length : 0;
949
- return { affectedRows: affected };
950
- }
951
- async delete(table, filter) {
952
- const filterObj = typeof filter === "string" ? { id: filter } : filter;
953
- const qs = this.buildQueryString(filterObj);
954
- const url = `${this.endpoint}/rest/v1/${table}${qs}`;
955
- const res = await KrnlHTTP.del(url, { headers: this.getHeaders() });
956
- const affected = res.status === "success" && Array.isArray(res.data) ? res.data.length : 0;
957
- return { affectedRows: affected };
958
- }
959
- async count(table, filter) {
960
- const qs = this.buildQueryString(filter);
961
- const url = `${this.endpoint}/rest/v1/${table}${qs}`;
962
- const headers = { ...this.getHeaders(), "Prefer": "count=exact", "Range-Unit": "items", "Range": "0-0" };
963
- const res = await KrnlHTTP.get(url, { headers });
964
- return res.status === "success" && Array.isArray(res.data) ? res.data.length : 0;
965
- }
966
- async clear(table) {
967
- await this.delete(table, {});
968
- }
969
- async query(queryStr, _params) {
970
- return this.find(queryStr);
971
- }
972
- async execute(_commandStr, _params) {
973
- return { affectedRows: 0 };
974
- }
975
- };
976
-
977
- // src/backend/providers/data/PocketBaseDataProvider.ts
978
- var PocketBaseDataProvider = class {
979
- engine = "pocketbase";
980
- endpoint;
981
- apiKey;
982
- isConnected = false;
983
- constructor(config) {
984
- this.endpoint = (config.endpoint || "http://127.0.0.1:8090").replace(/\/+$/, "");
985
- this.apiKey = config.apiKey;
986
- }
987
- getHeaders() {
988
- const headers = {
989
- "Content-Type": "application/json"
990
- };
991
- if (this.apiKey) {
992
- headers["Authorization"] = this.apiKey;
993
- }
994
- return headers;
995
- }
996
- async connect() {
997
- this.isConnected = true;
998
- }
999
- async disconnect() {
1000
- this.isConnected = false;
1001
- }
1002
- async testConnection() {
1003
- const t0 = Date.now();
1004
- try {
1005
- const res = await KrnlHTTP.get(`${this.endpoint}/api/health`, {
1006
- headers: this.getHeaders(),
1007
- timeout: 5e3
1008
- });
1009
- const latencyMs = Date.now() - t0;
1010
- return {
1011
- ok: res.status === "success" && (res.data?.code === 200 || res.code === 200),
1012
- latencyMs,
1013
- details: {
1014
- engine: "pocketbase",
1015
- endpoint: this.endpoint,
1016
- data: res.data
1017
- }
1018
- };
1019
- } catch (err) {
1020
- return {
1021
- ok: false,
1022
- latencyMs: Date.now() - t0,
1023
- details: { error: err.message }
1024
- };
1025
- }
1026
- }
1027
- async ensureTable(_tableName, _schema) {
1028
- }
1029
- buildFilterParam(filter) {
1030
- if (!filter || Object.keys(filter).length === 0) return "";
1031
- const expressions = [];
1032
- for (const [key, val] of Object.entries(filter)) {
1033
- if (typeof val === "string") {
1034
- expressions.push(`${key}='${val.replace(/'/g, "\\'")}'`);
1035
- } else if (typeof val === "number" || typeof val === "boolean") {
1036
- expressions.push(`${key}=${val}`);
1037
- }
1038
- }
1039
- return expressions.length > 0 ? encodeURIComponent(expressions.join(" && ")) : "";
1040
- }
1041
- async find(table, filter, options) {
1042
- const params = [];
1043
- const filterStr = this.buildFilterParam(filter);
1044
- if (filterStr) params.push(`filter=${filterStr}`);
1045
- const perPage = options?.limit || 50;
1046
- const page = options?.offset ? Math.floor(options.offset / perPage) + 1 : 1;
1047
- params.push(`page=${page}&perPage=${perPage}`);
1048
- if (options?.sort) {
1049
- const sortParts = Object.entries(options.sort).map(([col, dir]) => `${dir === -1 ? "-" : "+"}${col}`);
1050
- params.push(`sort=${encodeURIComponent(sortParts.join(","))}`);
1051
- }
1052
- const qs = params.length > 0 ? `?${params.join("&")}` : "";
1053
- const url = `${this.endpoint}/api/collections/${table}/records${qs}`;
1054
- const res = await KrnlHTTP.get(url, { headers: this.getHeaders() });
1055
- if (res.status === "success" && res.data?.items) {
1056
- return res.data.items;
1057
- }
1058
- return [];
1059
- }
1060
- async findOne(table, filter) {
1061
- if (typeof filter === "string") {
1062
- const url = `${this.endpoint}/api/collections/${table}/records/${filter}`;
1063
- const res = await KrnlHTTP.get(url, { headers: this.getHeaders() });
1064
- return res.status === "success" && res.data ? res.data : null;
1065
- }
1066
- const list = await this.find(table, filter, { limit: 1 });
1067
- return list[0] || null;
1068
- }
1069
- async insert(table, data) {
1070
- const url = `${this.endpoint}/api/collections/${table}/records`;
1071
- if (Array.isArray(data)) {
1072
- const results = [];
1073
- for (const item of data) {
1074
- const res = await KrnlHTTP.post(url, item, { headers: this.getHeaders() });
1075
- if (res.status === "success" && res.data) results.push(res.data);
1076
- }
1077
- return results;
1078
- } else {
1079
- const res = await KrnlHTTP.post(url, data, { headers: this.getHeaders() });
1080
- return res.status === "success" && res.data ? res.data : data;
1081
- }
1082
- }
1083
- async update(table, filter, patch) {
1084
- let targetId = null;
1085
- if (typeof filter === "string") {
1086
- targetId = filter;
1087
- } else {
1088
- const existing = await this.findOne(table, filter);
1089
- targetId = existing?.id || null;
1090
- }
1091
- if (!targetId) return { affectedRows: 0 };
1092
- const url = `${this.endpoint}/api/collections/${table}/records/${targetId}`;
1093
- const res = await KrnlHTTP.request(url, {
1094
- method: "PATCH",
1095
- headers: this.getHeaders(),
1096
- body: JSON.stringify(patch)
1097
- });
1098
- return { affectedRows: res.status === "success" ? 1 : 0 };
1099
- }
1100
- async delete(table, filter) {
1101
- let targetId = null;
1102
- if (typeof filter === "string") {
1103
- targetId = filter;
1104
- } else {
1105
- const existing = await this.findOne(table, filter);
1106
- targetId = existing?.id || null;
1107
- }
1108
- if (!targetId) return { affectedRows: 0 };
1109
- const url = `${this.endpoint}/api/collections/${table}/records/${targetId}`;
1110
- const res = await KrnlHTTP.del(url, { headers: this.getHeaders() });
1111
- return { affectedRows: res.status === "success" ? 1 : 0 };
1112
- }
1113
- async count(table, filter) {
1114
- const filterStr = this.buildFilterParam(filter);
1115
- const qs = `?page=1&perPage=1${filterStr ? `&filter=${filterStr}` : ""}`;
1116
- const url = `${this.endpoint}/api/collections/${table}/records${qs}`;
1117
- const res = await KrnlHTTP.get(url, { headers: this.getHeaders() });
1118
- return res.status === "success" && typeof res.data?.totalItems === "number" ? res.data.totalItems : 0;
1119
- }
1120
- async clear(table) {
1121
- const all = await this.find(table);
1122
- for (const item of all) {
1123
- if (item.id) await this.delete(table, item.id);
1124
- }
1125
- }
1126
- async query(queryStr, _params) {
1127
- return this.find(queryStr);
1128
- }
1129
- async execute(_commandStr, _params) {
1130
- return { affectedRows: 0 };
1131
- }
1132
- };
1133
-
1134
- // src/backend/providers/data/FirebaseDataProvider.ts
1135
- var FirebaseDataProvider = class {
1136
- engine = "firebase";
1137
- projectId;
1138
- apiKey;
1139
- isConnected = false;
1140
- constructor(config) {
1141
- this.projectId = config.projectId || "";
1142
- this.apiKey = config.apiKey;
1143
- }
1144
- get baseUrl() {
1145
- return `https://firestore.googleapis.com/v1/projects/${this.projectId}/databases/(default)/documents`;
1146
- }
1147
- get urlSuffix() {
1148
- return this.apiKey ? `?key=${this.apiKey}` : "";
1149
- }
1150
- async connect() {
1151
- this.isConnected = true;
1152
- }
1153
- async disconnect() {
1154
- this.isConnected = false;
1155
- }
1156
- async testConnection() {
1157
- const t0 = Date.now();
1158
- try {
1159
- const url = `${this.baseUrl}${this.urlSuffix}`;
1160
- const res = await KrnlHTTP.get(url, { timeout: 5e3 });
1161
- const latencyMs = Date.now() - t0;
1162
- return {
1163
- ok: res.status === "success" || res.code === 200 || res.code === 404,
1164
- latencyMs,
1165
- details: {
1166
- engine: "firebase",
1167
- projectId: this.projectId,
1168
- status: res.status
1169
- }
1170
- };
1171
- } catch (err) {
1172
- return {
1173
- ok: false,
1174
- latencyMs: Date.now() - t0,
1175
- details: { error: err.message }
1176
- };
1177
- }
1178
- }
1179
- async ensureTable(_tableName, _schema) {
1180
- }
1181
- /**
1182
- * Translates plain JavaScript object into Firestore document value structure
1183
- */
1184
- toFirestoreFields(obj) {
1185
- const fields = {};
1186
- for (const [key, val] of Object.entries(obj)) {
1187
- fields[key] = this.toFirestoreValue(val);
1188
- }
1189
- return fields;
1190
- }
1191
- toFirestoreValue(val) {
1192
- if (val === null || val === void 0) return { nullValue: null };
1193
- if (typeof val === "boolean") return { booleanValue: val };
1194
- if (typeof val === "number") {
1195
- return Number.isInteger(val) ? { integerValue: String(val) } : { doubleValue: val };
1196
- }
1197
- if (typeof val === "string") return { stringValue: val };
1198
- if (Array.isArray(val)) {
1199
- return { arrayValue: { values: val.map((v) => this.toFirestoreValue(v)) } };
1200
- }
1201
- if (typeof val === "object") {
1202
- return { mapValue: { fields: this.toFirestoreFields(val) } };
1203
- }
1204
- return { stringValue: String(val) };
1205
- }
1206
- /**
1207
- * Translates Firestore document into standard JavaScript object
1208
- */
1209
- fromFirestoreDoc(doc) {
1210
- if (!doc || !doc.fields) return null;
1211
- const result = {};
1212
- const docName = doc.name || "";
1213
- const id = docName.split("/").pop();
1214
- if (id) result.id = id;
1215
- for (const [key, fieldObj] of Object.entries(doc.fields)) {
1216
- result[key] = this.fromFirestoreValue(fieldObj);
1217
- }
1218
- return result;
1219
- }
1220
- fromFirestoreValue(fieldObj) {
1221
- if (!fieldObj || typeof fieldObj !== "object") return null;
1222
- if ("stringValue" in fieldObj) return fieldObj.stringValue;
1223
- if ("integerValue" in fieldObj) return parseInt(fieldObj.integerValue, 10);
1224
- if ("doubleValue" in fieldObj) return fieldObj.doubleValue;
1225
- if ("booleanValue" in fieldObj) return fieldObj.booleanValue;
1226
- if ("nullValue" in fieldObj) return null;
1227
- if ("arrayValue" in fieldObj) {
1228
- return (fieldObj.arrayValue.values || []).map((v) => this.fromFirestoreValue(v));
1229
- }
1230
- if ("mapValue" in fieldObj) {
1231
- const obj = {};
1232
- for (const [k, v] of Object.entries(fieldObj.mapValue.fields || {})) {
1233
- obj[k] = this.fromFirestoreValue(v);
1234
- }
1235
- return obj;
1236
- }
1237
- return null;
1238
- }
1239
- async find(table, filter, options) {
1240
- const url = `${this.baseUrl}/${table}${this.urlSuffix}`;
1241
- const res = await KrnlHTTP.get(url);
1242
- if (res.status === "success" && Array.isArray(res.data?.documents)) {
1243
- let items = res.data.documents.map((d) => this.fromFirestoreDoc(d)).filter(Boolean);
1244
- if (filter && Object.keys(filter).length > 0) {
1245
- items = items.filter((item) => {
1246
- for (const [k, v] of Object.entries(filter)) {
1247
- if (item[k] !== v) return false;
1248
- }
1249
- return true;
1250
- });
1251
- }
1252
- if (options?.offset) items = items.slice(options.offset);
1253
- if (options?.limit) items = items.slice(0, options.limit);
1254
- return items;
1255
- }
1256
- return [];
1257
- }
1258
- async findOne(table, filter) {
1259
- if (typeof filter === "string") {
1260
- const url = `${this.baseUrl}/${table}/${filter}${this.urlSuffix}`;
1261
- const res = await KrnlHTTP.get(url);
1262
- return res.status === "success" && res.data ? this.fromFirestoreDoc(res.data) : null;
1263
- }
1264
- const list = await this.find(table, filter, { limit: 1 });
1265
- return list[0] || null;
1266
- }
1267
- async insert(table, data) {
1268
- const items = Array.isArray(data) ? data : [data];
1269
- const results = [];
1270
- for (const item of items) {
1271
- const docObj = { ...item };
1272
- const docId = docObj.id || docObj._id || `doc_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
1273
- delete docObj.id;
1274
- delete docObj._id;
1275
- const url = `${this.baseUrl}/${table}?documentId=${docId}${this.apiKey ? `&key=${this.apiKey}` : ""}`;
1276
- const body = { fields: this.toFirestoreFields(docObj) };
1277
- const res = await KrnlHTTP.post(url, body);
1278
- if (res.status === "success" && res.data) {
1279
- results.push(this.fromFirestoreDoc(res.data));
1280
- } else {
1281
- results.push({ id: docId, ...docObj });
1282
- }
1283
- }
1284
- return Array.isArray(data) ? results : results[0];
1285
- }
1286
- async update(table, filter, patch) {
1287
- let docId = null;
1288
- if (typeof filter === "string") {
1289
- docId = filter;
1290
- } else {
1291
- const existing = await this.findOne(table, filter);
1292
- docId = existing?.id || null;
1293
- }
1294
- if (!docId) return { affectedRows: 0 };
1295
- const patchFields = this.toFirestoreFields(patch);
1296
- const updateMask = Object.keys(patch).map((k) => `updateMask.fieldPaths=${k}`).join("&");
1297
- const url = `${this.baseUrl}/${table}/${docId}?${updateMask}${this.apiKey ? `&key=${this.apiKey}` : ""}`;
1298
- const res = await KrnlHTTP.request(url, {
1299
- method: "PATCH",
1300
- headers: { "Content-Type": "application/json" },
1301
- body: JSON.stringify({ fields: patchFields })
1302
- });
1303
- return { affectedRows: res.status === "success" ? 1 : 0 };
1304
- }
1305
- async delete(table, filter) {
1306
- let docId = null;
1307
- if (typeof filter === "string") {
1308
- docId = filter;
1309
- } else {
1310
- const existing = await this.findOne(table, filter);
1311
- docId = existing?.id || null;
1312
- }
1313
- if (!docId) return { affectedRows: 0 };
1314
- const url = `${this.baseUrl}/${table}/${docId}${this.urlSuffix}`;
1315
- const res = await KrnlHTTP.del(url);
1316
- return { affectedRows: res.status === "success" ? 1 : 0 };
1317
- }
1318
- async count(table, filter) {
1319
- const list = await this.find(table, filter);
1320
- return list.length;
1321
- }
1322
- async clear(table) {
1323
- const all = await this.find(table);
1324
- for (const item of all) {
1325
- if (item.id) await this.delete(table, item.id);
1326
- }
1327
- }
1328
- async query(queryStr, _params) {
1329
- return this.find(queryStr);
1330
- }
1331
- async execute(_commandStr, _params) {
1332
- return { affectedRows: 0 };
1333
- }
1334
- };
1335
-
1336
- // src/backend/providers/data/TursoLibSQLDataProvider.ts
1337
- var TursoLibSQLDataProvider = class {
1338
- engine = "turso";
1339
- config;
1340
- isConnected = false;
1341
- constructor(config) {
1342
- this.config = config;
1343
- }
1344
- async connect() {
1345
- this.isConnected = true;
1346
- }
1347
- async disconnect() {
1348
- this.isConnected = false;
1349
- }
1350
- async testConnection() {
1351
- const startTime = Date.now();
1352
- try {
1353
- if (!this.config.endpoint) {
1354
- return { ok: false, latencyMs: 0, details: "Missing Turso endpoint" };
1355
- }
1356
- return { ok: true, latencyMs: Date.now() - startTime };
1357
- } catch (err) {
1358
- return { ok: false, latencyMs: Date.now() - startTime, details: err.message };
1359
- }
1360
- }
1361
- async ensureTable(tableName) {
1362
- await this.execute(
1363
- `CREATE TABLE IF NOT EXISTS "${tableName}" (id TEXT PRIMARY KEY, data TEXT, updated_at INTEGER)`
1364
- );
1365
- }
1366
- async query(queryStr, params = []) {
1367
- if (!this.config.endpoint) return [];
1368
- try {
1369
- const url = `${this.config.endpoint.replace(/\/$/, "")}/v2/pipeline`;
1370
- const response = await KrnlHTTP.request(url, {
1371
- method: "POST",
1372
- headers: {
1373
- Authorization: `Bearer ${this.config.authToken || this.config.apiKey || ""}`,
1374
- "Content-Type": "application/json"
1375
- },
1376
- body: JSON.stringify({
1377
- requests: [
1378
- {
1379
- type: "execute",
1380
- stmt: { sql: queryStr, args: params.map((p) => ({ type: "text", value: String(p) })) }
1381
- },
1382
- { type: "close" }
1383
- ]
1384
- })
1385
- });
1386
- const rows = response.data?.results?.[0]?.response?.result?.rows || [];
1387
- return rows.map((r) => r[1]?.value ? JSON.parse(r[1].value) : r[0]?.value || r);
1388
- } catch {
1389
- return [];
1390
- }
1391
- }
1392
- async execute(commandStr, params = []) {
1393
- const res = await this.query(commandStr, params);
1394
- return { affectedRows: res.length || 1 };
1395
- }
1396
- async find(table, filter) {
1397
- return this.query(`SELECT data FROM "${table}"`);
1398
- }
1399
- async findOne(table, filter) {
1400
- const id = typeof filter === "string" ? filter : filter.id;
1401
- if (!id) return null;
1402
- const res = await this.query(`SELECT data FROM "${table}" WHERE id = ? LIMIT 1`, [id]);
1403
- return res[0] || null;
1404
- }
1405
- async insert(table, data) {
1406
- await this.ensureTable(table);
1407
- const items = Array.isArray(data) ? data : [data];
1408
- for (const item of items) {
1409
- const id = item.id || `turso_${Date.now()}`;
1410
- await this.execute(
1411
- `INSERT OR REPLACE INTO "${table}" (id, data, updated_at) VALUES (?, ?, ?)`,
1412
- [id, JSON.stringify(item), Date.now()]
1413
- );
1414
- }
1415
- return data;
1416
- }
1417
- async update(table, filter, patch) {
1418
- const existing = await this.findOne(table, filter);
1419
- if (!existing) return { affectedRows: 0 };
1420
- const updated = { ...existing, ...patch };
1421
- await this.insert(table, updated);
1422
- return { affectedRows: 1 };
1423
- }
1424
- async delete(table, filter) {
1425
- const id = typeof filter === "string" ? filter : filter.id;
1426
- if (!id) return { affectedRows: 0 };
1427
- return this.execute(`DELETE FROM "${table}" WHERE id = ?`, [id]);
1428
- }
1429
- async count(table) {
1430
- const all = await this.find(table);
1431
- return all.length;
1432
- }
1433
- async clear(table) {
1434
- await this.execute(`DELETE FROM "${table}"`);
1435
- }
1436
- };
1437
-
1438
- // src/backend/providers/data/MongoAtlasDataProvider.ts
1439
- var MongoAtlasDataProvider = class {
1440
- engine = "mongodb";
1441
- config;
1442
- isConnected = false;
1443
- constructor(config) {
1444
- this.config = config;
1445
- }
1446
- async connect() {
1447
- this.isConnected = true;
1448
- }
1449
- async disconnect() {
1450
- this.isConnected = false;
1451
- }
1452
- async testConnection() {
1453
- const startTime = Date.now();
1454
- try {
1455
- if (!this.config.endpoint) {
1456
- return { ok: false, latencyMs: 0, details: "Missing Mongo Atlas endpoint" };
1457
- }
1458
- return { ok: true, latencyMs: Date.now() - startTime };
1459
- } catch (err) {
1460
- return { ok: false, latencyMs: Date.now() - startTime, details: err.message };
1461
- }
1462
- }
1463
- async ensureTable(tableName) {
1464
- }
1465
- async callAtlasAction(action, payload) {
1466
- if (!this.config.endpoint) return null;
1467
- const url = `${this.config.endpoint.replace(/\/$/, "")}/action/${action}`;
1468
- try {
1469
- const response = await KrnlHTTP.request(url, {
1470
- method: "POST",
1471
- headers: {
1472
- "api-key": this.config.apiKey || "",
1473
- "Content-Type": "application/json"
1474
- },
1475
- body: JSON.stringify({
1476
- dataSource: "Cluster0",
1477
- database: this.config.database || "krnl_vault",
1478
- ...payload
1479
- })
1480
- });
1481
- return response.data;
1482
- } catch {
1483
- return null;
1484
- }
1485
- }
1486
- async query(queryStr) {
1487
- return [];
1488
- }
1489
- async execute() {
1490
- return { affectedRows: 1 };
1491
- }
1492
- async find(table, filter = {}) {
1493
- const res = await this.callAtlasAction("find", { collection: table, filter });
1494
- return res?.documents || [];
1495
- }
1496
- async findOne(table, filter) {
1497
- const queryFilter = typeof filter === "string" ? { id: filter } : filter;
1498
- const res = await this.callAtlasAction("findOne", { collection: table, filter: queryFilter });
1499
- return res?.document || null;
1500
- }
1501
- async insert(table, data) {
1502
- if (Array.isArray(data)) {
1503
- await this.callAtlasAction("insertMany", { collection: table, documents: data });
1504
- } else {
1505
- await this.callAtlasAction("insertOne", { collection: table, document: data });
1506
- }
1507
- return data;
1508
- }
1509
- async update(table, filter, patch) {
1510
- const queryFilter = typeof filter === "string" ? { id: filter } : filter;
1511
- const res = await this.callAtlasAction("updateOne", {
1512
- collection: table,
1513
- filter: queryFilter,
1514
- update: { $set: patch }
1515
- });
1516
- return { affectedRows: res?.modifiedCount || 0 };
1517
- }
1518
- async delete(table, filter) {
1519
- const queryFilter = typeof filter === "string" ? { id: filter } : filter;
1520
- const res = await this.callAtlasAction("deleteOne", { collection: table, filter: queryFilter });
1521
- return { affectedRows: res?.deletedCount || 0 };
1522
- }
1523
- async count(table, filter = {}) {
1524
- const res = await this.find(table, filter);
1525
- return res.length;
1526
- }
1527
- async clear(table) {
1528
- await this.callAtlasAction("deleteMany", { collection: table, filter: {} });
1529
- }
1530
- };
1531
-
1532
- // src/backend/providers/storage/FilesystemStorageProvider.ts
1533
- import path2 from "path";
1534
- import fsp from "fs/promises";
1535
- var FilesystemStorageProvider = class {
1536
- driver = "filesystem";
1537
- basePath;
1538
- constructor(config = "./storage") {
1539
- if (typeof config === "string") {
1540
- this.basePath = path2.resolve(config);
1541
- } else {
1542
- this.basePath = path2.resolve(config.basePath || "./storage");
1543
- }
1544
- }
1545
- async uploadFile(targetPath, content, _mimeType) {
1546
- const fullPath = path2.join(this.basePath, targetPath);
1547
- await KrnlFS.ensureDir(path2.dirname(fullPath));
1548
- let buf;
1549
- if (Buffer.isBuffer(content)) {
1550
- buf = content;
1551
- } else if (typeof content === "string") {
1552
- buf = Buffer.from(content, "utf8");
1553
- } else if (content instanceof Uint8Array) {
1554
- buf = Buffer.from(content);
1555
- } else {
1556
- buf = Buffer.from(await content.arrayBuffer());
1557
- }
1558
- await KrnlFS.writeFileAtomic(fullPath, buf);
1559
- const size = buf.byteLength;
1560
- return {
1561
- url: `file://${fullPath}`,
1562
- size
1563
- };
1564
- }
1565
- async downloadFile(targetPath) {
1566
- const fullPath = path2.join(this.basePath, targetPath);
1567
- return await fsp.readFile(fullPath);
1568
- }
1569
- async deleteFile(targetPath) {
1570
- const fullPath = path2.join(this.basePath, targetPath);
1571
- try {
1572
- await KrnlFS.deleteFile(fullPath);
1573
- return true;
1574
- } catch {
1575
- return false;
1576
- }
1577
- }
1578
- getPublicUrl(targetPath) {
1579
- return `file://${path2.join(this.basePath, targetPath)}`;
1580
- }
1581
- async listFiles(prefix = "") {
1582
- const startDir = path2.join(this.basePath, prefix);
1583
- if (!await KrnlFS.dirExists(startDir)) {
1584
- return [];
1585
- }
1586
- const results = [];
1587
- for await (const file of KrnlFS.walkDir(startDir)) {
1588
- const stat = await KrnlFS.getFileStat(file);
1589
- results.push({
1590
- path: path2.relative(this.basePath, file),
1591
- size: stat.size,
1592
- updatedAt: stat.modifiedAt.getTime()
1593
- });
1594
- }
1595
- return results;
1596
- }
1597
- };
1598
-
1599
- // src/backend/providers/storage/SupabaseStorageProvider.ts
1600
- var SupabaseStorageProvider = class {
1601
- driver = "supabase_storage";
1602
- endpoint;
1603
- apiKey;
1604
- bucket;
1605
- constructor(config) {
1606
- this.endpoint = (config.endpoint || "").replace(/\/+$/, "");
1607
- this.apiKey = config.apiKey || "";
1608
- this.bucket = config.bucket || "default";
1609
- }
1610
- getHeaders(mimeType) {
1611
- const headers = {
1612
- "apikey": this.apiKey,
1613
- "Authorization": `Bearer ${this.apiKey}`
1614
- };
1615
- if (mimeType) {
1616
- headers["Content-Type"] = mimeType;
1617
- }
1618
- return headers;
1619
- }
1620
- async uploadFile(targetPath, content, mimeType = "application/octet-stream") {
1621
- const cleanPath = targetPath.replace(/^\/+/, "");
1622
- const url = `${this.endpoint}/storage/v1/object/${this.bucket}/${cleanPath}`;
1623
- let body = content;
1624
- let size = 0;
1625
- if (Buffer.isBuffer(content)) {
1626
- size = content.byteLength;
1627
- } else if (typeof content === "string") {
1628
- size = Buffer.byteLength(content);
1629
- }
1630
- await KrnlHTTP.request(url, {
1631
- method: "POST",
1632
- headers: this.getHeaders(mimeType),
1633
- body
1634
- });
1635
- return {
1636
- url: this.getPublicUrl(cleanPath),
1637
- size
1638
- };
1639
- }
1640
- async downloadFile(targetPath) {
1641
- const cleanPath = targetPath.replace(/^\/+/, "");
1642
- const url = `${this.endpoint}/storage/v1/object/authenticated/${this.bucket}/${cleanPath}`;
1643
- const res = await fetch(url, { headers: this.getHeaders() });
1644
- const arrayBuf = await res.arrayBuffer();
1645
- return Buffer.from(arrayBuf);
1646
- }
1647
- async deleteFile(targetPath) {
1648
- const cleanPath = targetPath.replace(/^\/+/, "");
1649
- const url = `${this.endpoint}/storage/v1/object/${this.bucket}/${cleanPath}`;
1650
- const res = await KrnlHTTP.del(url, { headers: this.getHeaders() });
1651
- return res.status === "success";
1652
- }
1653
- getPublicUrl(targetPath) {
1654
- const cleanPath = targetPath.replace(/^\/+/, "");
1655
- return `${this.endpoint}/storage/v1/object/public/${this.bucket}/${cleanPath}`;
1656
- }
1657
- async listFiles(prefix = "") {
1658
- const url = `${this.endpoint}/storage/v1/object/list/${this.bucket}`;
1659
- const res = await KrnlHTTP.post(url, { prefix, limit: 100 }, { headers: this.getHeaders("application/json") });
1660
- if (res.status === "success" && Array.isArray(res.data)) {
1661
- return res.data.map((item) => ({
1662
- path: item.name,
1663
- size: item.metadata?.size || 0,
1664
- updatedAt: item.updated_at ? new Date(item.updated_at).getTime() : Date.now()
1665
- }));
1666
- }
1667
- return [];
1668
- }
1669
- };
1670
-
1671
- // src/backend/providers/storage/S3StorageProvider.ts
1672
- var S3StorageProvider = class {
1673
- driver = "s3";
1674
- endpoint;
1675
- bucket;
1676
- region;
1677
- accessKey;
1678
- secretKey;
1679
- constructor(config) {
1680
- this.bucket = config.bucket || "default-bucket";
1681
- this.region = config.region || "us-east-1";
1682
- this.accessKey = config.accessKey;
1683
- this.secretKey = config.secretKey;
1684
- this.endpoint = config.endpoint ? config.endpoint.replace(/\/+$/, "") : `https://${this.bucket}.s3.${this.region}.amazonaws.com`;
1685
- }
1686
- getBaseUrl() {
1687
- return this.endpoint.includes(this.bucket) ? this.endpoint : `${this.endpoint}/${this.bucket}`;
1688
- }
1689
- async uploadFile(targetPath, content, mimeType = "application/octet-stream") {
1690
- const cleanPath = targetPath.replace(/^\/+/, "");
1691
- const url = `${this.getBaseUrl()}/${cleanPath}`;
1692
- let body = content;
1693
- let size = 0;
1694
- if (Buffer.isBuffer(content)) {
1695
- size = content.byteLength;
1696
- } else if (typeof content === "string") {
1697
- size = Buffer.byteLength(content);
1698
- }
1699
- await KrnlHTTP.request(url, {
1700
- method: "PUT",
1701
- headers: {
1702
- "Content-Type": mimeType
1703
- },
1704
- body
1705
- });
1706
- return {
1707
- url: this.getPublicUrl(cleanPath),
1708
- size
1709
- };
1710
- }
1711
- async downloadFile(targetPath) {
1712
- const cleanPath = targetPath.replace(/^\/+/, "");
1713
- const url = `${this.getBaseUrl()}/${cleanPath}`;
1714
- const res = await fetch(url);
1715
- const arrayBuf = await res.arrayBuffer();
1716
- return Buffer.from(arrayBuf);
1717
- }
1718
- async deleteFile(targetPath) {
1719
- const cleanPath = targetPath.replace(/^\/+/, "");
1720
- const url = `${this.getBaseUrl()}/${cleanPath}`;
1721
- const res = await KrnlHTTP.del(url);
1722
- return res.status === "success";
1723
- }
1724
- getPublicUrl(targetPath) {
1725
- const cleanPath = targetPath.replace(/^\/+/, "");
1726
- return `${this.getBaseUrl()}/${cleanPath}`;
1727
- }
1728
- async listFiles(_prefix = "") {
1729
- return [];
1730
- }
1731
- };
1732
-
1733
- // src/backend/providers/storage/PocketBaseStorageProvider.ts
1734
- var PocketBaseStorageProvider = class {
1735
- driver = "pocketbase_storage";
1736
- endpoint;
1737
- bucket;
1738
- constructor(config) {
1739
- this.endpoint = (config.endpoint || "http://127.0.0.1:8090").replace(/\/+$/, "");
1740
- this.bucket = config.bucket || "files";
1741
- }
1742
- async uploadFile(targetPath, content, mimeType = "application/octet-stream") {
1743
- const cleanPath = targetPath.replace(/^\/+/, "");
1744
- const url = `${this.endpoint}/api/collections/${this.bucket}/records`;
1745
- let size = 0;
1746
- if (Buffer.isBuffer(content)) size = content.byteLength;
1747
- else if (typeof content === "string") size = Buffer.byteLength(content);
1748
- const formData = new FormData();
1749
- const blob = new Blob([content], { type: mimeType });
1750
- formData.append("file", blob, cleanPath);
1751
- await fetch(url, {
1752
- method: "POST",
1753
- body: formData
1754
- });
1755
- return {
1756
- url: this.getPublicUrl(cleanPath),
1757
- size
1758
- };
1759
- }
1760
- async downloadFile(targetPath) {
1761
- const cleanPath = targetPath.replace(/^\/+/, "");
1762
- const url = `${this.endpoint}/api/files/${this.bucket}/${cleanPath}`;
1763
- const res = await fetch(url);
1764
- const arrayBuf = await res.arrayBuffer();
1765
- return Buffer.from(arrayBuf);
1766
- }
1767
- async deleteFile(targetPath) {
1768
- const cleanPath = targetPath.replace(/^\/+/, "");
1769
- const url = `${this.endpoint}/api/collections/${this.bucket}/records/${cleanPath}`;
1770
- const res = await KrnlHTTP.del(url);
1771
- return res.status === "success";
1772
- }
1773
- getPublicUrl(targetPath) {
1774
- const cleanPath = targetPath.replace(/^\/+/, "");
1775
- return `${this.endpoint}/api/files/${this.bucket}/${cleanPath}`;
1776
- }
1777
- async listFiles(_prefix = "") {
1778
- return [];
1779
- }
1780
- };
1781
-
1782
- // src/backend/providers/storage/FirebaseStorageProvider.ts
1783
- var FirebaseStorageProvider = class {
1784
- driver = "firebase_storage";
1785
- bucket;
1786
- apiKey;
1787
- constructor(config) {
1788
- this.bucket = config.bucket || `${config.projectId || "default"}.appspot.com`;
1789
- this.apiKey = config.apiKey;
1790
- }
1791
- get baseUrl() {
1792
- return `https://firebasestorage.googleapis.com/v0/b/${this.bucket}/o`;
1793
- }
1794
- async uploadFile(targetPath, content, mimeType = "application/octet-stream") {
1795
- const cleanPath = encodeURIComponent(targetPath.replace(/^\/+/, ""));
1796
- const url = `${this.baseUrl}?name=${cleanPath}${this.apiKey ? `&key=${this.apiKey}` : ""}`;
1797
- let body = content;
1798
- let size = 0;
1799
- if (Buffer.isBuffer(content)) size = content.byteLength;
1800
- else if (typeof content === "string") size = Buffer.byteLength(content);
1801
- await fetch(url, {
1802
- method: "POST",
1803
- headers: {
1804
- "Content-Type": mimeType
1805
- },
1806
- body
1807
- });
1808
- return {
1809
- url: this.getPublicUrl(targetPath),
1810
- size
1811
- };
1812
- }
1813
- async downloadFile(targetPath) {
1814
- const cleanPath = encodeURIComponent(targetPath.replace(/^\/+/, ""));
1815
- const url = `${this.baseUrl}/${cleanPath}?alt=media${this.apiKey ? `&key=${this.apiKey}` : ""}`;
1816
- const res = await fetch(url);
1817
- const arrayBuf = await res.arrayBuffer();
1818
- return Buffer.from(arrayBuf);
1819
- }
1820
- async deleteFile(targetPath) {
1821
- const cleanPath = encodeURIComponent(targetPath.replace(/^\/+/, ""));
1822
- const url = `${this.baseUrl}/${cleanPath}${this.apiKey ? `&key=${this.apiKey}` : ""}`;
1823
- const res = await KrnlHTTP.del(url);
1824
- return res.status === "success";
1825
- }
1826
- getPublicUrl(targetPath) {
1827
- const cleanPath = encodeURIComponent(targetPath.replace(/^\/+/, ""));
1828
- return `${this.baseUrl}/${cleanPath}?alt=media`;
1829
- }
1830
- async listFiles(_prefix = "") {
1831
- return [];
1832
- }
1833
- };
1834
-
1835
- // src/backend/providers/storage/VercelBlobStorageProvider.ts
1836
- var VercelBlobStorageProvider = class {
1837
- driver = "vercel_blob";
1838
- config;
1839
- constructor(config) {
1840
- this.config = config;
1841
- }
1842
- async uploadFile(path7, content, mimeType = "application/octet-stream") {
1843
- const token = this.config.token || this.config.apiKey;
1844
- const cleanPath = path7.replace(/^\//, "");
1845
- const size = typeof content === "string" ? Buffer.byteLength(content) : content.byteLength;
1846
- if (!token) {
1847
- return { url: `https://mock.blob.vercel-storage.com/${cleanPath}`, size };
1848
- }
1849
- try {
1850
- const response = await KrnlHTTP.request(`https://blob.vercel-storage.com/${cleanPath}`, {
1851
- method: "PUT",
1852
- headers: {
1853
- Authorization: `Bearer ${token}`,
1854
- "x-content-type": mimeType
1855
- },
1856
- body: content
1857
- });
1858
- return {
1859
- url: response.data?.url || `https://blob.vercel-storage.com/${cleanPath}`,
1860
- size
1861
- };
1862
- } catch {
1863
- return { url: `https://blob.vercel-storage.com/${cleanPath}`, size };
1864
- }
1865
- }
1866
- async downloadFile(path7) {
1867
- const publicUrl = this.getPublicUrl(path7);
1868
- const response = await fetch(publicUrl);
1869
- const arrayBuffer = await response.arrayBuffer();
1870
- return Buffer.from(arrayBuffer);
1871
- }
1872
- async deleteFile(path7) {
1873
- const token = this.config.token || this.config.apiKey;
1874
- const cleanPath = path7.replace(/^\//, "");
1875
- if (!token) return true;
1876
- try {
1877
- await KrnlHTTP.request(`https://blob.vercel-storage.com/delete`, {
1878
- method: "POST",
1879
- headers: {
1880
- Authorization: `Bearer ${token}`,
1881
- "Content-Type": "application/json"
1882
- },
1883
- body: JSON.stringify({ urls: [this.getPublicUrl(cleanPath)] })
1884
- });
1885
- return true;
1886
- } catch {
1887
- return false;
1888
- }
1889
- }
1890
- getPublicUrl(path7) {
1891
- const cleanPath = path7.replace(/^\//, "");
1892
- return `https://blob.vercel-storage.com/${cleanPath}`;
1893
- }
1894
- async listFiles() {
1895
- return [];
1896
- }
1897
- };
1898
-
1899
- // src/backend/WorkspaceBackendFactory.ts
1900
- var WorkspaceBackendFactory = class _WorkspaceBackendFactory {
1901
- static create(config) {
1902
- const dataProvider = _WorkspaceBackendFactory.resolveDataProvider(config);
1903
- const storageProvider = _WorkspaceBackendFactory.resolveStorageProvider(config);
1904
- return {
1905
- id: config.id,
1906
- mode: config.mode,
1907
- data: dataProvider,
1908
- storage: storageProvider,
1909
- async initialize() {
1910
- await dataProvider.connect();
1911
- },
1912
- async dispose() {
1913
- await dataProvider.disconnect();
1914
- }
1915
- };
1916
- }
1917
- static createLocal(filePath = "./workspace.sqlite", storagePath = "./storage") {
1918
- return _WorkspaceBackendFactory.create({
1919
- id: "local-workspace",
1920
- provider: "sqlite",
1921
- mode: "local",
1922
- database: { type: "sqlite", filePath, enableWAL: true },
1923
- storage: { type: "filesystem", basePath: storagePath }
1924
- });
1925
- }
1926
- /**
1927
- * Resolves the credential cascade hierarchy:
1928
- * 1. Feature-level explicit credentials
1929
- * 2. Host entity configured credentials
1930
- * 3. Fallback to local SQLite
1931
- */
1932
- static resolveCredentialsCascade(functionalityConfig, entitySettings, defaultFallback = { type: "sqlite", filePath: "./workspace.sqlite", enableWAL: true }) {
1933
- if (typeof functionalityConfig === "object" && functionalityConfig?.credentials) {
1934
- return functionalityConfig.credentials;
1935
- }
1936
- const requestedService = (typeof functionalityConfig === "string" ? functionalityConfig : typeof functionalityConfig === "object" && functionalityConfig?.service ? functionalityConfig.service : entitySettings?.defaultService || "sqlite") || "sqlite";
1937
- if (entitySettings && entitySettings[requestedService]) {
1938
- return entitySettings[requestedService];
1939
- }
1940
- return defaultFallback;
1941
- }
1942
- static resolveDataProvider(config) {
1943
- const dbConfig = config.database || { type: config.provider };
1944
- switch (dbConfig.type) {
1945
- case "sqlite":
1946
- return new SQLiteDataProvider(dbConfig);
1947
- case "postgres":
1948
- return new PostgresDataProvider(dbConfig);
1949
- case "supabase":
1950
- return new SupabaseDataProvider({
1951
- ...dbConfig,
1952
- endpoint: dbConfig.endpoint || config.endpoint,
1953
- apiKey: dbConfig.apiKey || config.auth?.apiKey
1954
- });
1955
- case "pocketbase":
1956
- return new PocketBaseDataProvider({
1957
- ...dbConfig,
1958
- endpoint: dbConfig.endpoint || config.endpoint,
1959
- apiKey: dbConfig.apiKey || config.auth?.token
1960
- });
1961
- case "firebase":
1962
- return new FirebaseDataProvider({
1963
- ...dbConfig,
1964
- apiKey: dbConfig.apiKey || config.auth?.apiKey
1965
- });
1966
- case "turso":
1967
- return new TursoLibSQLDataProvider({
1968
- ...dbConfig,
1969
- endpoint: dbConfig.endpoint || config.endpoint,
1970
- authToken: dbConfig.authToken || config.auth?.token
1971
- });
1972
- case "mongodb":
1973
- return new MongoAtlasDataProvider({
1974
- ...dbConfig,
1975
- endpoint: dbConfig.endpoint || config.endpoint,
1976
- apiKey: dbConfig.apiKey || config.auth?.apiKey
1977
- });
1978
- default:
1979
- return new SQLiteDataProvider({ type: "sqlite", filePath: "./workspace.sqlite", enableWAL: true });
1980
- }
1981
- }
1982
- static resolveStorageProvider(config) {
1983
- const storageConfig = config.storage || { type: "filesystem" };
1984
- switch (storageConfig.type) {
1985
- case "filesystem":
1986
- return new FilesystemStorageProvider(storageConfig);
1987
- case "supabase_storage":
1988
- return new SupabaseStorageProvider({
1989
- ...storageConfig,
1990
- endpoint: storageConfig.endpoint || config.endpoint,
1991
- apiKey: storageConfig.apiKey || config.auth?.apiKey
1992
- });
1993
- case "s3":
1994
- return new S3StorageProvider({
1995
- ...storageConfig,
1996
- endpoint: storageConfig.endpoint || config.endpoint
1997
- });
1998
- case "pocketbase_storage":
1999
- return new PocketBaseStorageProvider({
2000
- ...storageConfig,
2001
- endpoint: storageConfig.endpoint || config.endpoint
2002
- });
2003
- case "firebase_storage":
2004
- return new FirebaseStorageProvider({
2005
- ...storageConfig,
2006
- apiKey: storageConfig.apiKey || config.auth?.apiKey
2007
- });
2008
- case "vercel_blob":
2009
- return new VercelBlobStorageProvider(storageConfig);
2010
- default:
2011
- return new FilesystemStorageProvider("./storage");
2012
- }
2013
- }
2014
- };
2015
-
2016
- // src/backend/control_plane/LocalSQLiteAccountProvider.ts
2017
- import path3 from "path";
2018
- var LocalSQLiteAccountProvider = class {
2019
- filePath;
2020
- isLoaded = false;
2021
- db = { users: {}, workspaces: {} };
2022
- constructor(filePath = "./accounts.sqlite") {
2023
- this.filePath = path3.resolve(filePath);
2024
- }
2025
- async load() {
2026
- if (this.isLoaded) return;
2027
- await KrnlFS.ensureDir(path3.dirname(this.filePath));
2028
- if (await KrnlFS.fileExists(this.filePath)) {
2029
- try {
2030
- const raw = await KrnlFS.readJSON(this.filePath);
2031
- if (raw && typeof raw === "object") {
2032
- this.db = {
2033
- users: raw.users || {},
2034
- workspaces: raw.workspaces || {}
2035
- };
2036
- }
2037
- } catch {
2038
- this.db = { users: {}, workspaces: {} };
2039
- }
2040
- }
2041
- this.isLoaded = true;
2042
- }
2043
- async persist() {
2044
- const release = await FileLock.acquire(this.filePath);
2045
- try {
2046
- await KrnlFS.writeJSON(this.filePath, this.db, 2);
2047
- } finally {
2048
- release();
2049
- }
2050
- }
2051
- async getUser(userId) {
2052
- await this.load();
2053
- const user = this.db.users[userId];
2054
- if (!user) return null;
2055
- const { passwordHash, ...profile } = user;
2056
- return profile;
2057
- }
2058
- async getFullUser(userId) {
2059
- await this.load();
2060
- const user = this.db.users[userId];
2061
- if (!user) return null;
2062
- const { passwordHash, ...profile } = user;
2063
- return {
2064
- id: user.id,
2065
- credentials: { username: user.username, email: user.email },
2066
- identity: user.metadata || {},
2067
- role: user.role,
2068
- apps: user.apps || {},
2069
- createdAt: user.createdAt,
2070
- updatedAt: Date.now()
2071
- };
2072
- }
2073
- async registerUser(credentials) {
2074
- await this.load();
2075
- const userId = credentials.username || credentials.email || `usr_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`;
2076
- const profile = {
2077
- id: userId,
2078
- email: credentials.email,
2079
- username: credentials.username || credentials.email,
2080
- role: credentials.role || "USER",
2081
- passwordHash: credentials.password,
2082
- metadata: credentials.metadata || {},
2083
- apps: {},
2084
- createdAt: Date.now()
2085
- };
2086
- this.db.users[userId] = profile;
2087
- if (!this.db.workspaces[userId]) {
2088
- this.db.workspaces[userId] = [];
2089
- }
2090
- await this.persist();
2091
- const { passwordHash, ...safeProfile } = profile;
2092
- return safeProfile;
2093
- }
2094
- async authenticate(credentials) {
2095
- await this.load();
2096
- const userId = credentials.username || credentials.email;
2097
- let user = userId ? this.db.users[userId] : null;
2098
- if (!user && (credentials.email || credentials.username)) {
2099
- const isFirst = Object.keys(this.db.users).length === 0;
2100
- user = await this.registerUser({
2101
- ...credentials,
2102
- role: isFirst ? "ADMIN" : "USER"
2103
- });
2104
- }
2105
- if (!user) {
2106
- throw new Error("Invalid user credentials.");
2107
- }
2108
- const token = `sess::${user.id}::${Date.now()}::${Math.random().toString(36).substring(2, 9)}`;
2109
- const { passwordHash, ...profile } = user;
2110
- return {
2111
- userId: user.id,
2112
- role: user.role,
2113
- token,
2114
- expiresAt: Date.now() + 864e5,
2115
- user: profile
2116
- };
2117
- }
2118
- async verifySession(token) {
2119
- if (!token) return null;
2120
- await this.load();
2121
- const parts = token.split("::");
2122
- if (parts.length >= 4 && parts[0] === "sess") {
2123
- const userId = parts[1];
2124
- const user = this.db.users[userId];
2125
- if (user) {
2126
- const { passwordHash, ...profile } = user;
2127
- return {
2128
- userId: user.id,
2129
- role: user.role,
2130
- token,
2131
- expiresAt: Date.now() + 864e5,
2132
- user: profile
2133
- };
2134
- }
2135
- }
2136
- return null;
2137
- }
2138
- async getAppUserContext(userId, appId) {
2139
- await this.load();
2140
- const user = this.db.users[userId];
2141
- if (!user) return null;
2142
- if (!user.apps) user.apps = {};
2143
- if (!user.apps[appId]) {
2144
- user.apps[appId] = {
2145
- appId,
2146
- enabled: true,
2147
- preferences: { workspaceOrder: [] },
2148
- workspaces: [],
2149
- createdAt: Date.now(),
2150
- updatedAt: Date.now()
2151
- };
2152
- await this.persist();
2153
- }
2154
- return user.apps[appId];
2155
- }
2156
- async updateAppPreferences(userId, appId, preferences) {
2157
- await this.load();
2158
- const user = this.db.users[userId];
2159
- if (!user) throw new Error(`User not found: ${userId}`);
2160
- if (!user.apps) user.apps = {};
2161
- if (!user.apps[appId]) {
2162
- user.apps[appId] = {
2163
- appId,
2164
- enabled: true,
2165
- preferences: {},
2166
- workspaces: [],
2167
- createdAt: Date.now(),
2168
- updatedAt: Date.now()
2169
- };
2170
- }
2171
- user.apps[appId].preferences = {
2172
- ...user.apps[appId].preferences,
2173
- ...preferences
2174
- };
2175
- user.apps[appId].updatedAt = Date.now();
2176
- await this.persist();
2177
- return user.apps[appId].preferences;
2178
- }
2179
- async listWorkspaces(userId, appId) {
2180
- await this.load();
2181
- if (appId) {
2182
- const appCtx = await this.getAppUserContext(userId, appId);
2183
- if (appCtx && appCtx.workspaces.length > 0) {
2184
- return appCtx.workspaces.map((w) => ({
2185
- id: w.id,
2186
- ownerId: userId,
2187
- name: w.name,
2188
- workspaceSpec: w,
2189
- createdAt: w.createdAt,
2190
- updatedAt: w.updatedAt
2191
- }));
2192
- }
2193
- }
2194
- return this.db.workspaces[userId] || [];
2195
- }
2196
- async createWorkspace(userId, descriptor, appId) {
2197
- await this.load();
2198
- if (!this.db.workspaces[userId]) {
2199
- this.db.workspaces[userId] = [];
2200
- }
2201
- const wsId = descriptor.id || `ws_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`;
2202
- const newWs = {
2203
- id: wsId,
2204
- ownerId: userId,
2205
- name: descriptor.name || "Default Workspace",
2206
- backendConfig: descriptor.backendConfig || {
2207
- id: "default",
2208
- provider: "sqlite",
2209
- mode: "local",
2210
- database: { type: "sqlite", filePath: "./workspace.sqlite" },
2211
- storage: { type: "filesystem", basePath: "./storage" }
2212
- },
2213
- workspaceSpec: descriptor.workspaceSpec,
2214
- createdAt: Date.now(),
2215
- updatedAt: Date.now()
2216
- };
2217
- this.db.workspaces[userId].push(newWs);
2218
- if (appId) {
2219
- const user = this.db.users[userId];
2220
- if (user && user.apps && user.apps[appId]) {
2221
- const spec = descriptor.workspaceSpec || {
2222
- id: wsId,
2223
- name: newWs.name,
2224
- primaryStorage: {
2225
- id: `endpoint_local_${wsId}`,
2226
- provider: "local",
2227
- configuration: { provider: "local", basePath: "./storage" },
2228
- enabled: true,
2229
- status: "CONNECTED",
2230
- syncState: "SYNCED"
2231
- },
2232
- backupStorages: [],
2233
- sync: {
2234
- enabled: true,
2235
- mode: "on_write",
2236
- conflictStrategy: "primary_wins",
2237
- deletePropagation: true,
2238
- retry: { maxAttempts: 3, backoffMs: 1e3 },
2239
- verification: { verifyChecksums: true }
2240
- },
2241
- preferences: {},
2242
- metadata: {},
2243
- createdAt: Date.now(),
2244
- updatedAt: Date.now()
2245
- };
2246
- user.apps[appId].workspaces.push(spec);
2247
- if (!user.apps[appId].preferences.workspaceOrder) {
2248
- user.apps[appId].preferences.workspaceOrder = [];
2249
- }
2250
- user.apps[appId].preferences.workspaceOrder.push(wsId);
2251
- }
2252
- }
2253
- await this.persist();
2254
- return newWs;
2255
- }
2256
- async deleteWorkspace(userId, workspaceId, appId) {
2257
- await this.load();
2258
- const list = this.db.workspaces[userId] || [];
2259
- const idx = list.findIndex((w) => w.id === workspaceId);
2260
- let removed = false;
2261
- if (idx !== -1) {
2262
- list.splice(idx, 1);
2263
- removed = true;
2264
- }
2265
- if (appId) {
2266
- const user = this.db.users[userId];
2267
- if (user && user.apps && user.apps[appId]) {
2268
- const wIdx = user.apps[appId].workspaces.findIndex((w) => w.id === workspaceId);
2269
- if (wIdx !== -1) {
2270
- user.apps[appId].workspaces.splice(wIdx, 1);
2271
- removed = true;
2272
- }
2273
- if (user.apps[appId].preferences.workspaceOrder) {
2274
- user.apps[appId].preferences.workspaceOrder = user.apps[appId].preferences.workspaceOrder.filter((id) => id !== workspaceId);
2275
- }
2276
- }
2277
- }
2278
- if (removed) {
2279
- await this.persist();
2280
- }
2281
- return removed;
2282
- }
2283
- };
2284
-
2285
- // src/backend/control_plane/HostedAccountProvider.ts
2286
- var HostedAccountProvider = class {
2287
- endpoint;
2288
- apiKey;
2289
- constructor(config) {
2290
- this.endpoint = (config.endpoint || "").replace(/\/+$/, "");
2291
- this.apiKey = config.apiKey || config.token;
2292
- }
2293
- getHeaders() {
2294
- const headers = { "Content-Type": "application/json" };
2295
- if (this.apiKey) {
2296
- headers["Authorization"] = `Bearer ${this.apiKey}`;
2297
- headers["apikey"] = this.apiKey;
2298
- }
2299
- return headers;
2300
- }
2301
- async getUser(userId) {
2302
- const res = await KrnlHTTP.get(`${this.endpoint}/api/auth/users/${userId}`, { headers: this.getHeaders() });
2303
- return res.status === "success" && res.data ? res.data : null;
2304
- }
2305
- async authenticate(credentials) {
2306
- const res = await KrnlHTTP.post(`${this.endpoint}/api/auth/login`, credentials, { headers: this.getHeaders() });
2307
- if (res.status === "success" && res.data) {
2308
- return res.data;
2309
- }
2310
- throw new Error(res.message || "Authentication failed against hosted account backend.");
2311
- }
2312
- async verifySession(token) {
2313
- if (!token) return null;
2314
- const res = await KrnlHTTP.post(`${this.endpoint}/api/auth/verify`, { token }, { headers: this.getHeaders() });
2315
- return res.status === "success" && res.data ? res.data : null;
2316
- }
2317
- async listWorkspaces(userId) {
2318
- const res = await KrnlHTTP.get(`${this.endpoint}/api/workspaces?userId=${userId}`, { headers: this.getHeaders() });
2319
- return res.status === "success" && Array.isArray(res.data) ? res.data : [];
2320
- }
2321
- async createWorkspace(userId, descriptor) {
2322
- const res = await KrnlHTTP.post(`${this.endpoint}/api/workspaces`, { userId, ...descriptor }, { headers: this.getHeaders() });
2323
- if (res.status === "success" && res.data) {
2324
- return res.data;
2325
- }
2326
- throw new Error(res.message || "Failed to create workspace on hosted account backend.");
2327
- }
2328
- async deleteWorkspace(_userId, workspaceId) {
2329
- const res = await KrnlHTTP.del(`${this.endpoint}/api/workspaces/${workspaceId}`, { headers: this.getHeaders() });
2330
- return res.status === "success";
2331
- }
2332
- };
2333
-
2334
- // src/backend/control_plane/ControlPlaneManager.ts
2335
- var ControlPlaneManager = class _ControlPlaneManager {
2336
- accountProvider;
2337
- constructor(providerOrPath) {
2338
- if (!providerOrPath || typeof providerOrPath === "string") {
2339
- this.accountProvider = new LocalSQLiteAccountProvider(providerOrPath || "./accounts.sqlite");
2340
- } else {
2341
- this.accountProvider = providerOrPath;
2342
- }
2343
- }
2344
- static createLocal(filePath = "./accounts.sqlite") {
2345
- return new _ControlPlaneManager(new LocalSQLiteAccountProvider(filePath));
2346
- }
2347
- static createHosted(endpoint, apiKey) {
2348
- return new _ControlPlaneManager(new HostedAccountProvider({ endpoint, apiKey }));
2349
- }
2350
- async login(credentials) {
2351
- return await this.accountProvider.authenticate(credentials);
2352
- }
2353
- async verifyToken(token) {
2354
- return await this.accountProvider.verifySession(token);
2355
- }
2356
- async getUser(userId) {
2357
- return await this.accountProvider.getUser(userId);
2358
- }
2359
- async getUserWorkspaces(userId) {
2360
- return await this.accountProvider.listWorkspaces(userId);
2361
- }
2362
- async registerWorkspace(userId, descriptor) {
2363
- return await this.accountProvider.createWorkspace(userId, descriptor);
2364
- }
2365
- };
2366
-
2367
- // src/backend/control_plane/AdminGuard.ts
2368
- var AdminGuard = class _AdminGuard {
2369
- /**
2370
- * Verifies that the session token is valid and belongs to an ADMIN.
2371
- */
2372
- static async verify(authHeaderOrToken, accountProvider) {
2373
- return _AdminGuard.verifyRole(authHeaderOrToken, accountProvider, ["ADMIN", "admin"]);
2374
- }
2375
- /**
2376
- * Verifies that the session token is valid and belongs to either a MODERATOR or ADMIN.
2377
- */
2378
- static async verifyModeratorOrAdmin(authHeaderOrToken, accountProvider) {
2379
- return _AdminGuard.verifyRole(authHeaderOrToken, accountProvider, ["ADMIN", "MODERATOR", "admin", "moderator"]);
2380
- }
2381
- /**
2382
- * Generalized RBAC verification against a list of allowed roles.
2383
- */
2384
- static async verifyRole(authHeaderOrToken, accountProvider, allowedRoles = ["ADMIN"]) {
2385
- const token = authHeaderOrToken?.startsWith("Bearer ") ? authHeaderOrToken.substring(7).trim() : authHeaderOrToken?.trim() || null;
2386
- const session = await accountProvider.verifySession(token);
2387
- if (!session) {
2388
- throw new Error("[AccessDenied] Authentication required: Invalid or missing session token.");
2389
- }
2390
- const currentRoleNormalized = String(session.role).toUpperCase();
2391
- const isAllowed = allowedRoles.some((r) => String(r).toUpperCase() === currentRoleNormalized);
2392
- if (!isAllowed) {
2393
- throw new Error(
2394
- `[AccessDenied] Forbidden: Elevated privileges required (${allowedRoles.join(", ")}). Current role: "${session.role}".`
2395
- );
2396
- }
2397
- return session;
2398
- }
2399
- /**
2400
- * Synchronous assertion on pre-resolved session.
2401
- */
2402
- static enforce(session, allowedRoles = ["ADMIN"]) {
2403
- if (!session) {
2404
- throw new Error("[AccessDenied] Authentication required: Session is null.");
2405
- }
2406
- const currentRoleNormalized = String(session.role).toUpperCase();
2407
- const isAllowed = allowedRoles.some((r) => String(r).toUpperCase() === currentRoleNormalized);
2408
- if (!isAllowed) {
2409
- throw new Error(
2410
- `[AccessDenied] Forbidden: Insufficient role permissions. Current role: "${session.role}".`
2411
- );
2412
- }
2413
- }
2414
- };
2415
-
2416
- // src/backend/control_plane/AuditLogger.ts
2417
- import path4 from "path";
2418
- var AuditLogger = class {
2419
- filePath;
2420
- constructor(filePath = "./.krnl/control_plane/audit_trail.jsonl") {
2421
- this.filePath = filePath;
2422
- }
2423
- /**
2424
- * Records an immutable audit log entry.
2425
- */
2426
- async log(params) {
2427
- const entry = {
2428
- id: `aud_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`,
2429
- timestamp: Date.now(),
2430
- actorUserId: params.actorUserId,
2431
- actorRole: params.actorRole,
2432
- action: params.action,
2433
- resource: params.resource,
2434
- details: params.details || {},
2435
- ipAddress: params.ipAddress,
2436
- status: params.status || "SUCCESS"
2437
- };
2438
- const line = JSON.stringify(entry) + "\n";
2439
- try {
2440
- await KrnlFS.ensureDir(path4.dirname(this.filePath));
2441
- await KrnlFS.appendFile(this.filePath, line);
2442
- } catch {
2443
- }
2444
- return entry;
2445
- }
2446
- /**
2447
- * Retrieves historical audit entries with optional filtering.
2448
- */
2449
- async query(filter) {
2450
- if (!await KrnlFS.fileExists(this.filePath)) {
2451
- return [];
2452
- }
2453
- try {
2454
- const raw = await KrnlFS.readFile(this.filePath);
2455
- const lines = raw.split("\n").filter((l) => l.trim().length > 0);
2456
- let entries = lines.map((l) => {
2457
- try {
2458
- return JSON.parse(l);
2459
- } catch {
2460
- return null;
2461
- }
2462
- }).filter((e) => e !== null);
2463
- if (filter?.actorUserId) {
2464
- entries = entries.filter((e) => e.actorUserId === filter.actorUserId);
2465
- }
2466
- if (filter?.action) {
2467
- entries = entries.filter((e) => e.action === filter.action);
2468
- }
2469
- if (filter?.status) {
2470
- entries = entries.filter((e) => e.status === filter.status);
2471
- }
2472
- if (filter?.actorRole) {
2473
- entries = entries.filter((e) => String(e.actorRole).toUpperCase() === String(filter.actorRole).toUpperCase());
2474
- }
2475
- entries.sort((a, b) => b.timestamp - a.timestamp);
2476
- if (filter?.limit && filter.limit > 0) {
2477
- entries = entries.slice(0, filter.limit);
2478
- }
2479
- return entries;
2480
- } catch {
2481
- return [];
2482
- }
2483
- }
2484
- };
2485
-
2486
- // src/backend/control_plane/DatabaseClusterManager.ts
2487
- import path5 from "path";
2488
- var DatabaseClusterManager = class {
2489
- vaultPath;
2490
- auditLogger;
2491
- inMemoryVault;
2492
- constructor(vaultPath = "./.krnl/control_plane/database_cluster.json", auditLogger) {
2493
- this.vaultPath = path5.resolve(vaultPath);
2494
- this.auditLogger = auditLogger || new AuditLogger();
2495
- this.inMemoryVault = {
2496
- version: "1.0.0",
2497
- primaryDatabaseId: "default_primary_sqlite",
2498
- clusters: [
2499
- {
2500
- id: "default_primary_sqlite",
2501
- name: "Primary Local SQLite",
2502
- role: "primary",
2503
- enabled: true,
2504
- config: {
2505
- type: "sqlite",
2506
- filePath: "./accounts.sqlite",
2507
- enableWAL: true
2508
- },
2509
- healthStatus: "HEALTHY",
2510
- lastSyncTimestamp: Date.now()
2511
- }
2512
- ],
2513
- updatedAt: Date.now()
2514
- };
2515
- this.init();
2516
- }
2517
- async init() {
2518
- try {
2519
- await KrnlFS.ensureDir(path5.dirname(this.vaultPath));
2520
- if (await KrnlFS.fileExists(this.vaultPath)) {
2521
- const loaded = await KrnlFS.readJSON(this.vaultPath);
2522
- if (loaded) this.inMemoryVault = loaded;
2523
- } else {
2524
- await KrnlFS.writeJSON(this.vaultPath, this.inMemoryVault, 2);
2525
- }
2526
- } catch {
2527
- }
2528
- }
2529
- getVault() {
2530
- return this.inMemoryVault;
2531
- }
2532
- async saveVault() {
2533
- this.inMemoryVault.updatedAt = Date.now();
2534
- try {
2535
- await KrnlFS.ensureDir(path5.dirname(this.vaultPath));
2536
- await KrnlFS.writeJSON(this.vaultPath, this.inMemoryVault, 2);
2537
- } catch {
2538
- }
2539
- }
2540
- /**
2541
- * Tests a database connection and measures real-time round-trip latency in milliseconds.
2542
- */
2543
- async testDatabaseConnection(config) {
2544
- const startTime = Date.now();
2545
- try {
2546
- if (config.type === "sqlite") {
2547
- const testPath = config.filePath || "./test_probe.sqlite";
2548
- await KrnlFS.fileExists(testPath);
2549
- return { ok: true, latencyMs: Date.now() - startTime };
2550
- } else if (config.type === "supabase" || config.type === "turso" || config.type === "custom_api") {
2551
- if (!config.endpoint) {
2552
- return { ok: false, latencyMs: 0, errorDetails: "Missing endpoint for remote connection test" };
2553
- }
2554
- return { ok: true, latencyMs: Date.now() - startTime };
2555
- }
2556
- return { ok: true, latencyMs: Date.now() - startTime };
2557
- } catch (err) {
2558
- return { ok: false, latencyMs: Date.now() - startTime, errorDetails: err.message };
2559
- }
2560
- }
2561
- /**
2562
- * Promotes a redundant clone to the active primary role, auto-demoting the existing primary.
2563
- */
2564
- async promoteToPrimary(targetDatabaseId, actorUserId = "system_admin") {
2565
- const target = this.inMemoryVault.clusters.find((c) => c.id === targetDatabaseId);
2566
- if (!target) {
2567
- throw new Error(`Database cluster entry [${targetDatabaseId}] not found.`);
2568
- }
2569
- const previousPrimaryId = this.inMemoryVault.primaryDatabaseId;
2570
- for (const cluster of this.inMemoryVault.clusters) {
2571
- if (cluster.id === previousPrimaryId) {
2572
- cluster.role = "redundant_clone";
2573
- }
2574
- }
2575
- target.role = "primary";
2576
- target.enabled = true;
2577
- this.inMemoryVault.primaryDatabaseId = target.id;
2578
- await this.saveVault();
2579
- await this.auditLogger.log({
2580
- actorUserId,
2581
- actorRole: "ADMIN",
2582
- action: "FAILOVER_PROMOTE_PRIMARY",
2583
- resource: target.id,
2584
- details: { previousPrimaryId, newPrimaryId: target.id }
2585
- });
2586
- return this.inMemoryVault;
2587
- }
2588
- /**
2589
- * Adds or updates a database cluster entry.
2590
- */
2591
- async upsertClusterEntry(entry, actorUserId = "system_admin") {
2592
- const existingIndex = this.inMemoryVault.clusters.findIndex((c) => c.id === entry.id);
2593
- if (existingIndex >= 0) {
2594
- this.inMemoryVault.clusters[existingIndex] = { ...this.inMemoryVault.clusters[existingIndex], ...entry };
2595
- } else {
2596
- this.inMemoryVault.clusters.push(entry);
2597
- }
2598
- await this.saveVault();
2599
- await this.auditLogger.log({
2600
- actorUserId,
2601
- actorRole: "ADMIN",
2602
- action: "UPSERT_DATABASE_CLUSTER",
2603
- resource: entry.id,
2604
- details: { role: entry.role, type: entry.config.type }
2605
- });
2606
- return this.inMemoryVault;
2607
- }
2608
- /**
2609
- * Synchronizes data snapshots across all enabled redundant backup clones.
2610
- */
2611
- async syncRedundantDatabases() {
2612
- const primary = this.inMemoryVault.clusters.find((c) => c.id === this.inMemoryVault.primaryDatabaseId);
2613
- if (!primary) {
2614
- return { syncedCount: 0, timestamp: Date.now() };
2615
- }
2616
- let syncedCount = 0;
2617
- const now = Date.now();
2618
- for (const cluster of this.inMemoryVault.clusters) {
2619
- if (cluster.id !== primary.id && cluster.enabled && cluster.role === "redundant_clone") {
2620
- cluster.lastSyncTimestamp = now;
2621
- cluster.healthStatus = "HEALTHY";
2622
- syncedCount++;
2623
- }
2624
- }
2625
- await this.saveVault();
2626
- return { syncedCount, timestamp: now };
2627
- }
2628
- };
2629
-
2630
- // src/backend/storage_adapters/WorkspaceStorageAdapter.ts
2631
- import crypto from "crypto";
2632
- function calculateContentHash(data) {
2633
- const buffer = typeof data === "string" ? Buffer.from(data, "utf8") : Buffer.from(data);
2634
- return crypto.createHash("sha256").update(buffer).digest("hex");
2635
- }
2636
- function normalizeWorkspacePath(pathStr) {
2637
- if (!pathStr || pathStr === "." || pathStr === "/") return "";
2638
- let clean = pathStr.replace(/\\/g, "/").replace(/^\/+/, "").replace(/\/+$/, "");
2639
- clean = clean.replace(/\/+/g, "/");
2640
- return clean;
2641
- }
2642
- var BaseWorkspaceStorageAdapter = class {
2643
- watchers = /* @__PURE__ */ new Map();
2644
- isInitialized = false;
2645
- watch(pathStr, callback) {
2646
- const norm = normalizeWorkspacePath(pathStr);
2647
- if (!this.watchers.has(norm)) {
2648
- this.watchers.set(norm, /* @__PURE__ */ new Set());
2649
- }
2650
- this.watchers.get(norm).add(callback);
2651
- return () => {
2652
- const set = this.watchers.get(norm);
2653
- if (set) {
2654
- set.delete(callback);
2655
- if (set.size === 0) {
2656
- this.watchers.delete(norm);
2657
- }
2658
- }
2659
- };
2660
- }
2661
- notifyWatchers(event) {
2662
- const norm = normalizeWorkspacePath(event.path);
2663
- const directSet = this.watchers.get(norm);
2664
- if (directSet) {
2665
- for (const cb of directSet) {
2666
- try {
2667
- cb(event);
2668
- } catch {
2669
- }
2670
- }
2671
- }
2672
- const rootSet = this.watchers.get("");
2673
- if (rootSet && norm !== "") {
2674
- for (const cb of rootSet) {
2675
- try {
2676
- cb(event);
2677
- } catch {
2678
- }
2679
- }
2680
- }
2681
- }
2682
- async disconnect() {
2683
- this.watchers.clear();
2684
- this.isInitialized = false;
2685
- }
2686
- };
2687
-
2688
- // src/backend/storage_adapters/LocalStorageAdapter.ts
2689
- import fs from "fs/promises";
2690
- import path6 from "path";
2691
- var LocalStorageAdapter = class extends BaseWorkspaceStorageAdapter {
2692
- provider = "local";
2693
- endpointId;
2694
- basePath;
2695
- constructor(endpointId, config) {
2696
- super();
2697
- this.endpointId = endpointId;
2698
- this.basePath = path6.resolve(config.basePath || "./storage");
2699
- }
2700
- getFullPath(relPath) {
2701
- const norm = normalizeWorkspacePath(relPath);
2702
- return path6.join(this.basePath, norm);
2703
- }
2704
- async initialize() {
2705
- if (this.isInitialized) return;
2706
- await fs.mkdir(this.basePath, { recursive: true });
2707
- this.isInitialized = true;
2708
- }
2709
- async exists(pathStr) {
2710
- await this.initialize();
2711
- const full = this.getFullPath(pathStr);
2712
- try {
2713
- await fs.access(full);
2714
- return true;
2715
- } catch {
2716
- return false;
2717
- }
2718
- }
2719
- async stat(pathStr) {
2720
- await this.initialize();
2721
- const norm = normalizeWorkspacePath(pathStr);
2722
- const full = this.getFullPath(pathStr);
2723
- const st = await fs.stat(full);
2724
- let contentHash;
2725
- if (!st.isDirectory()) {
2726
- const data = await fs.readFile(full);
2727
- contentHash = calculateContentHash(data);
2728
- }
2729
- return {
2730
- path: norm,
2731
- type: st.isDirectory() ? "directory" : "file",
2732
- size: st.size,
2733
- modifiedAt: Math.floor(st.mtimeMs),
2734
- contentHash,
2735
- revision: String(Math.floor(st.mtimeMs))
2736
- };
2737
- }
2738
- async list(pathStr = "") {
2739
- await this.initialize();
2740
- const norm = normalizeWorkspacePath(pathStr);
2741
- const full = this.getFullPath(norm);
2742
- const entries = [];
2743
- const scanDir = async (dirFull, prefix) => {
2744
- let items = [];
2745
- try {
2746
- items = await fs.readdir(dirFull);
2747
- } catch {
2748
- return;
2749
- }
2750
- for (const item of items) {
2751
- const itemFull = path6.join(dirFull, item);
2752
- const itemRel = prefix ? `${prefix}/${item}` : item;
2753
- try {
2754
- const st = await fs.stat(itemFull);
2755
- if (st.isDirectory()) {
2756
- entries.push({
2757
- path: itemRel,
2758
- name: item,
2759
- type: "directory",
2760
- size: 0,
2761
- modifiedAt: Math.floor(st.mtimeMs)
2762
- });
2763
- await scanDir(itemFull, itemRel);
2764
- } else {
2765
- entries.push({
2766
- path: itemRel,
2767
- name: item,
2768
- type: "file",
2769
- size: st.size,
2770
- modifiedAt: Math.floor(st.mtimeMs)
2771
- });
2772
- }
2773
- } catch {
2774
- }
2775
- }
2776
- };
2777
- await scanDir(full, norm);
2778
- return entries;
2779
- }
2780
- async read(pathStr) {
2781
- await this.initialize();
2782
- const full = this.getFullPath(pathStr);
2783
- const buffer = await fs.readFile(full);
2784
- return new Uint8Array(buffer);
2785
- }
2786
- async write(pathStr, data) {
2787
- await this.initialize();
2788
- const norm = normalizeWorkspacePath(pathStr);
2789
- const full = this.getFullPath(norm);
2790
- await fs.mkdir(path6.dirname(full), { recursive: true });
2791
- const buffer = typeof data === "string" ? Buffer.from(data, "utf8") : Buffer.from(data);
2792
- await fs.writeFile(full, buffer);
2793
- const hash = calculateContentHash(buffer);
2794
- this.notifyWatchers({
2795
- type: "update",
2796
- path: norm,
2797
- metadata: {
2798
- path: norm,
2799
- type: "file",
2800
- size: buffer.length,
2801
- modifiedAt: Date.now(),
2802
- contentHash: hash,
2803
- revision: String(Date.now())
2804
- }
2805
- });
2806
- }
2807
- async mkdir(pathStr) {
2808
- await this.initialize();
2809
- const norm = normalizeWorkspacePath(pathStr);
2810
- const full = this.getFullPath(norm);
2811
- await fs.mkdir(full, { recursive: true });
2812
- this.notifyWatchers({
2813
- type: "create",
2814
- path: norm,
2815
- metadata: {
2816
- path: norm,
2817
- type: "directory",
2818
- size: 0,
2819
- modifiedAt: Date.now()
2820
- }
2821
- });
2822
- }
2823
- async delete(pathStr) {
2824
- await this.initialize();
2825
- const norm = normalizeWorkspacePath(pathStr);
2826
- const full = this.getFullPath(norm);
2827
- try {
2828
- const st = await fs.stat(full);
2829
- if (st.isDirectory()) {
2830
- await fs.rm(full, { recursive: true, force: true });
2831
- } else {
2832
- await fs.unlink(full);
2833
- }
2834
- this.notifyWatchers({ type: "delete", path: norm });
2835
- } catch {
2836
- }
2837
- }
2838
- async move(source, destination) {
2839
- await this.initialize();
2840
- const srcNorm = normalizeWorkspacePath(source);
2841
- const destNorm = normalizeWorkspacePath(destination);
2842
- const srcFull = this.getFullPath(srcNorm);
2843
- const destFull = this.getFullPath(destNorm);
2844
- await fs.mkdir(path6.dirname(destFull), { recursive: true });
2845
- await fs.rename(srcFull, destFull);
2846
- this.notifyWatchers({ type: "delete", path: srcNorm });
2847
- this.notifyWatchers({ type: "create", path: destNorm });
2848
- }
2849
- async copy(source, destination) {
2850
- await this.initialize();
2851
- const srcNorm = normalizeWorkspacePath(source);
2852
- const destNorm = normalizeWorkspacePath(destination);
2853
- const srcFull = this.getFullPath(srcNorm);
2854
- const destFull = this.getFullPath(destNorm);
2855
- await fs.mkdir(path6.dirname(destFull), { recursive: true });
2856
- await fs.copyFile(srcFull, destFull);
2857
- const st = await fs.stat(destFull);
2858
- this.notifyWatchers({
2859
- type: "create",
2860
- path: destNorm,
2861
- metadata: {
2862
- path: destNorm,
2863
- type: "file",
2864
- size: st.size,
2865
- modifiedAt: Math.floor(st.mtimeMs)
2866
- }
2867
- });
2868
- }
2869
- };
2870
-
2871
- // src/backend/storage_adapters/GoogleDriveStorageAdapter.ts
2872
- var GoogleDriveStorageAdapter = class extends BaseWorkspaceStorageAdapter {
2873
- provider = "google_drive";
2874
- endpointId;
2875
- rootFolderId;
2876
- accountId;
2877
- virtualDrive = /* @__PURE__ */ new Map();
2878
- constructor(endpointId, config) {
2879
- super();
2880
- this.endpointId = endpointId;
2881
- this.rootFolderId = config.rootFolderId || "root";
2882
- this.accountId = config.accountId || "default";
2883
- }
2884
- async initialize() {
2885
- if (this.isInitialized) return;
2886
- this.isInitialized = true;
2887
- }
2888
- async exists(pathStr) {
2889
- await this.initialize();
2890
- const norm = normalizeWorkspacePath(pathStr);
2891
- if (!norm) return true;
2892
- return this.virtualDrive.has(norm);
2893
- }
2894
- async stat(pathStr) {
2895
- await this.initialize();
2896
- const norm = normalizeWorkspacePath(pathStr);
2897
- if (!norm) {
2898
- return {
2899
- path: "",
2900
- type: "directory",
2901
- size: 0,
2902
- modifiedAt: Date.now()
2903
- };
2904
- }
2905
- const node = this.virtualDrive.get(norm);
2906
- if (!node) {
2907
- throw new Error(`[GoogleDrive] File not found: ${pathStr}`);
2908
- }
2909
- return {
2910
- path: norm,
2911
- type: node.type,
2912
- size: node.size,
2913
- modifiedAt: node.modifiedAt,
2914
- contentHash: node.contentHash,
2915
- revision: node.revision
2916
- };
2917
- }
2918
- async list(pathStr = "") {
2919
- await this.initialize();
2920
- const norm = normalizeWorkspacePath(pathStr);
2921
- const results = [];
2922
- for (const [key, node] of this.virtualDrive.entries()) {
2923
- if (norm === "" || key.startsWith(`${norm}/`)) {
2924
- const rel = norm === "" ? key : key.slice(norm.length + 1);
2925
- results.push({
2926
- path: key,
2927
- name: node.name,
2928
- type: node.type,
2929
- size: node.size,
2930
- modifiedAt: node.modifiedAt
2931
- });
2932
- }
2933
- }
2934
- return results;
2935
- }
2936
- async read(pathStr) {
2937
- await this.initialize();
2938
- const norm = normalizeWorkspacePath(pathStr);
2939
- const node = this.virtualDrive.get(norm);
2940
- if (!node || node.type !== "file") {
2941
- throw new Error(`[GoogleDrive] File not found or is a directory: ${pathStr}`);
2942
- }
2943
- return node.content || new Uint8Array(0);
2944
- }
2945
- async write(pathStr, data) {
2946
- await this.initialize();
2947
- const norm = normalizeWorkspacePath(pathStr);
2948
- const buffer = typeof data === "string" ? Buffer.from(data, "utf8") : Buffer.from(data);
2949
- const content = new Uint8Array(buffer);
2950
- const hash = calculateContentHash(buffer);
2951
- const now = Date.now();
2952
- const name = norm.split("/").pop() || norm;
2953
- const parts = norm.split("/");
2954
- for (let i = 1; i < parts.length; i++) {
2955
- const parentPath = parts.slice(0, i).join("/");
2956
- if (!this.virtualDrive.has(parentPath)) {
2957
- this.virtualDrive.set(parentPath, {
2958
- id: `gdir_${Date.now()}_${i}`,
2959
- name: parts[i - 1],
2960
- type: "directory",
2961
- size: 0,
2962
- modifiedAt: now,
2963
- revision: String(now)
2964
- });
2965
- }
2966
- }
2967
- const node = {
2968
- id: `gfile_${Date.now()}`,
2969
- name,
2970
- type: "file",
2971
- content,
2972
- size: content.length,
2973
- modifiedAt: now,
2974
- contentHash: hash,
2975
- revision: String(now)
2976
- };
2977
- this.virtualDrive.set(norm, node);
2978
- this.notifyWatchers({
2979
- type: "update",
2980
- path: norm,
2981
- metadata: {
2982
- path: norm,
2983
- type: "file",
2984
- size: content.length,
2985
- modifiedAt: now,
2986
- contentHash: hash,
2987
- revision: String(now)
2988
- }
2989
- });
2990
- }
2991
- async mkdir(pathStr) {
2992
- await this.initialize();
2993
- const norm = normalizeWorkspacePath(pathStr);
2994
- if (!norm) return;
2995
- const now = Date.now();
2996
- const name = norm.split("/").pop() || norm;
2997
- this.virtualDrive.set(norm, {
2998
- id: `gdir_${Date.now()}`,
2999
- name,
3000
- type: "directory",
3001
- size: 0,
3002
- modifiedAt: now,
3003
- revision: String(now)
3004
- });
3005
- this.notifyWatchers({
3006
- type: "create",
3007
- path: norm,
3008
- metadata: {
3009
- path: norm,
3010
- type: "directory",
3011
- size: 0,
3012
- modifiedAt: now
3013
- }
3014
- });
3015
- }
3016
- async delete(pathStr) {
3017
- await this.initialize();
3018
- const norm = normalizeWorkspacePath(pathStr);
3019
- for (const key of Array.from(this.virtualDrive.keys())) {
3020
- if (key === norm || key.startsWith(`${norm}/`)) {
3021
- this.virtualDrive.delete(key);
3022
- }
3023
- }
3024
- this.notifyWatchers({ type: "delete", path: norm });
3025
- }
3026
- async move(source, destination) {
3027
- await this.initialize();
3028
- const srcNorm = normalizeWorkspacePath(source);
3029
- const destNorm = normalizeWorkspacePath(destination);
3030
- const node = this.virtualDrive.get(srcNorm);
3031
- if (!node) throw new Error(`[GoogleDrive] Source not found: ${source}`);
3032
- this.virtualDrive.delete(srcNorm);
3033
- node.name = destNorm.split("/").pop() || destNorm;
3034
- this.virtualDrive.set(destNorm, node);
3035
- this.notifyWatchers({ type: "delete", path: srcNorm });
3036
- this.notifyWatchers({ type: "create", path: destNorm });
3037
- }
3038
- async copy(source, destination) {
3039
- await this.initialize();
3040
- const srcNorm = normalizeWorkspacePath(source);
3041
- const destNorm = normalizeWorkspacePath(destination);
3042
- const node = this.virtualDrive.get(srcNorm);
3043
- if (!node) throw new Error(`[GoogleDrive] Source not found: ${source}`);
3044
- const now = Date.now();
3045
- this.virtualDrive.set(destNorm, {
3046
- ...node,
3047
- id: `gfile_${Date.now()}`,
3048
- name: destNorm.split("/").pop() || destNorm,
3049
- modifiedAt: now,
3050
- revision: String(now)
3051
- });
3052
- this.notifyWatchers({
3053
- type: "create",
3054
- path: destNorm,
3055
- metadata: {
3056
- path: destNorm,
3057
- type: node.type,
3058
- size: node.size,
3059
- modifiedAt: now,
3060
- contentHash: node.contentHash
3061
- }
3062
- });
3063
- }
3064
- };
3065
-
3066
- // src/backend/storage_adapters/OneDriveStorageAdapter.ts
3067
- var OneDriveStorageAdapter = class extends BaseWorkspaceStorageAdapter {
3068
- provider = "onedrive";
3069
- endpointId;
3070
- accountId;
3071
- rootFolderId;
3072
- items = /* @__PURE__ */ new Map();
3073
- constructor(endpointId, config) {
3074
- super();
3075
- this.endpointId = endpointId;
3076
- this.accountId = config.accountId || "default";
3077
- this.rootFolderId = config.rootFolderId || "root";
3078
- }
3079
- async initialize() {
3080
- if (this.isInitialized) return;
3081
- this.isInitialized = true;
3082
- }
3083
- async exists(pathStr) {
3084
- await this.initialize();
3085
- const norm = normalizeWorkspacePath(pathStr);
3086
- if (!norm) return true;
3087
- return this.items.has(norm);
3088
- }
3089
- async stat(pathStr) {
3090
- await this.initialize();
3091
- const norm = normalizeWorkspacePath(pathStr);
3092
- if (!norm) {
3093
- return {
3094
- path: "",
3095
- type: "directory",
3096
- size: 0,
3097
- modifiedAt: Date.now()
3098
- };
3099
- }
3100
- const item = this.items.get(norm);
3101
- if (!item) {
3102
- throw new Error(`[OneDrive] Item not found: ${pathStr}`);
3103
- }
3104
- return {
3105
- path: norm,
3106
- type: item.type,
3107
- size: item.size,
3108
- modifiedAt: item.modifiedAt,
3109
- contentHash: item.contentHash,
3110
- revision: item.eTag
3111
- };
3112
- }
3113
- async list(pathStr = "") {
3114
- await this.initialize();
3115
- const norm = normalizeWorkspacePath(pathStr);
3116
- const results = [];
3117
- for (const [key, item] of this.items.entries()) {
3118
- if (norm === "" || key.startsWith(`${norm}/`)) {
3119
- results.push({
3120
- path: key,
3121
- name: item.name,
3122
- type: item.type,
3123
- size: item.size,
3124
- modifiedAt: item.modifiedAt
3125
- });
3126
- }
3127
- }
3128
- return results;
3129
- }
3130
- async read(pathStr) {
3131
- await this.initialize();
3132
- const norm = normalizeWorkspacePath(pathStr);
3133
- const item = this.items.get(norm);
3134
- if (!item || item.type !== "file") {
3135
- throw new Error(`[OneDrive] File not found: ${pathStr}`);
3136
- }
3137
- return item.content || new Uint8Array(0);
3138
- }
3139
- async write(pathStr, data) {
3140
- await this.initialize();
3141
- const norm = normalizeWorkspacePath(pathStr);
3142
- const buffer = typeof data === "string" ? Buffer.from(data, "utf8") : Buffer.from(data);
3143
- const content = new Uint8Array(buffer);
3144
- const hash = calculateContentHash(buffer);
3145
- const now = Date.now();
3146
- const name = norm.split("/").pop() || norm;
3147
- const parts = norm.split("/");
3148
- for (let i = 1; i < parts.length; i++) {
3149
- const parent = parts.slice(0, i).join("/");
3150
- if (!this.items.has(parent)) {
3151
- this.items.set(parent, {
3152
- id: `onedrive_dir_${now}_${i}`,
3153
- name: parts[i - 1],
3154
- type: "directory",
3155
- size: 0,
3156
- modifiedAt: now,
3157
- eTag: `"{etag_${now}}"`
3158
- });
3159
- }
3160
- }
3161
- const item = {
3162
- id: `onedrive_file_${now}`,
3163
- name,
3164
- type: "file",
3165
- content,
3166
- size: content.length,
3167
- modifiedAt: now,
3168
- contentHash: hash,
3169
- eTag: `"{etag_${now}_${hash.slice(0, 8)}}"`
3170
- };
3171
- this.items.set(norm, item);
3172
- this.notifyWatchers({
3173
- type: "update",
3174
- path: norm,
3175
- metadata: {
3176
- path: norm,
3177
- type: "file",
3178
- size: content.length,
3179
- modifiedAt: now,
3180
- contentHash: hash,
3181
- revision: item.eTag
3182
- }
3183
- });
3184
- }
3185
- async mkdir(pathStr) {
3186
- await this.initialize();
3187
- const norm = normalizeWorkspacePath(pathStr);
3188
- if (!norm) return;
3189
- const now = Date.now();
3190
- this.items.set(norm, {
3191
- id: `onedrive_dir_${now}`,
3192
- name: norm.split("/").pop() || norm,
3193
- type: "directory",
3194
- size: 0,
3195
- modifiedAt: now,
3196
- eTag: `"{etag_${now}}"`
3197
- });
3198
- this.notifyWatchers({
3199
- type: "create",
3200
- path: norm,
3201
- metadata: { path: norm, type: "directory", size: 0, modifiedAt: now }
3202
- });
3203
- }
3204
- async delete(pathStr) {
3205
- await this.initialize();
3206
- const norm = normalizeWorkspacePath(pathStr);
3207
- for (const key of Array.from(this.items.keys())) {
3208
- if (key === norm || key.startsWith(`${norm}/`)) {
3209
- this.items.delete(key);
3210
- }
3211
- }
3212
- this.notifyWatchers({ type: "delete", path: norm });
3213
- }
3214
- async move(source, destination) {
3215
- await this.initialize();
3216
- const srcNorm = normalizeWorkspacePath(source);
3217
- const destNorm = normalizeWorkspacePath(destination);
3218
- const item = this.items.get(srcNorm);
3219
- if (!item) throw new Error(`[OneDrive] Source not found: ${source}`);
3220
- this.items.delete(srcNorm);
3221
- item.name = destNorm.split("/").pop() || destNorm;
3222
- this.items.set(destNorm, item);
3223
- this.notifyWatchers({ type: "delete", path: srcNorm });
3224
- this.notifyWatchers({ type: "create", path: destNorm });
3225
- }
3226
- async copy(source, destination) {
3227
- await this.initialize();
3228
- const srcNorm = normalizeWorkspacePath(source);
3229
- const destNorm = normalizeWorkspacePath(destination);
3230
- const item = this.items.get(srcNorm);
3231
- if (!item) throw new Error(`[OneDrive] Source not found: ${source}`);
3232
- const now = Date.now();
3233
- this.items.set(destNorm, {
3234
- ...item,
3235
- id: `onedrive_file_${now}`,
3236
- name: destNorm.split("/").pop() || destNorm,
3237
- modifiedAt: now,
3238
- eTag: `"{etag_${now}}"`
3239
- });
3240
- this.notifyWatchers({
3241
- type: "create",
3242
- path: destNorm,
3243
- metadata: {
3244
- path: destNorm,
3245
- type: item.type,
3246
- size: item.size,
3247
- modifiedAt: now,
3248
- contentHash: item.contentHash
3249
- }
3250
- });
3251
- }
3252
- };
3253
-
3254
- // src/backend/storage_adapters/SupabaseStorageAdapter.ts
3255
- var SupabaseStorageAdapter = class extends BaseWorkspaceStorageAdapter {
3256
- provider = "supabase_storage";
3257
- endpointId;
3258
- projectUrl;
3259
- bucket;
3260
- objects = /* @__PURE__ */ new Map();
3261
- constructor(endpointId, config) {
3262
- super();
3263
- this.endpointId = endpointId;
3264
- this.projectUrl = config.projectUrl || "https://supabase.co";
3265
- this.bucket = config.bucket || "workspace";
3266
- }
3267
- async initialize() {
3268
- if (this.isInitialized) return;
3269
- this.isInitialized = true;
3270
- }
3271
- async exists(pathStr) {
3272
- await this.initialize();
3273
- const norm = normalizeWorkspacePath(pathStr);
3274
- if (!norm) return true;
3275
- return this.objects.has(norm);
3276
- }
3277
- async stat(pathStr) {
3278
- await this.initialize();
3279
- const norm = normalizeWorkspacePath(pathStr);
3280
- if (!norm) {
3281
- return { path: "", type: "directory", size: 0, modifiedAt: Date.now() };
3282
- }
3283
- const obj = this.objects.get(norm);
3284
- if (!obj) {
3285
- throw new Error(`[SupabaseStorage] Object not found in bucket '${this.bucket}': ${pathStr}`);
3286
- }
3287
- return {
3288
- path: norm,
3289
- type: "file",
3290
- size: obj.size,
3291
- modifiedAt: obj.updatedAt,
3292
- contentHash: obj.contentHash,
3293
- revision: String(obj.updatedAt)
3294
- };
3295
- }
3296
- async list(pathStr = "") {
3297
- await this.initialize();
3298
- const norm = normalizeWorkspacePath(pathStr);
3299
- const results = [];
3300
- for (const [key, obj] of this.objects.entries()) {
3301
- if (norm === "" || key.startsWith(`${norm}/`)) {
3302
- results.push({
3303
- path: key,
3304
- name: obj.name,
3305
- type: "file",
3306
- size: obj.size,
3307
- modifiedAt: obj.updatedAt
3308
- });
3309
- }
3310
- }
3311
- return results;
3312
- }
3313
- async read(pathStr) {
3314
- await this.initialize();
3315
- const norm = normalizeWorkspacePath(pathStr);
3316
- const obj = this.objects.get(norm);
3317
- if (!obj) {
3318
- throw new Error(`[SupabaseStorage] Object not found: ${pathStr}`);
3319
- }
3320
- return obj.content || new Uint8Array(0);
3321
- }
3322
- async write(pathStr, data) {
3323
- await this.initialize();
3324
- const norm = normalizeWorkspacePath(pathStr);
3325
- const buffer = typeof data === "string" ? Buffer.from(data, "utf8") : Buffer.from(data);
3326
- const content = new Uint8Array(buffer);
3327
- const hash = calculateContentHash(buffer);
3328
- const now = Date.now();
3329
- const name = norm.split("/").pop() || norm;
3330
- const obj = {
3331
- name,
3332
- id: `sb_obj_${now}_${Math.random().toString(36).substring(2, 7)}`,
3333
- bucketId: this.bucket,
3334
- content,
3335
- size: content.length,
3336
- updatedAt: now,
3337
- contentHash: hash
3338
- };
3339
- this.objects.set(norm, obj);
3340
- this.notifyWatchers({
3341
- type: "update",
3342
- path: norm,
3343
- metadata: {
3344
- path: norm,
3345
- type: "file",
3346
- size: content.length,
3347
- modifiedAt: now,
3348
- contentHash: hash,
3349
- revision: String(now)
3350
- }
3351
- });
3352
- }
3353
- async mkdir(pathStr) {
3354
- await this.initialize();
3355
- }
3356
- async delete(pathStr) {
3357
- await this.initialize();
3358
- const norm = normalizeWorkspacePath(pathStr);
3359
- for (const key of Array.from(this.objects.keys())) {
3360
- if (key === norm || key.startsWith(`${norm}/`)) {
3361
- this.objects.delete(key);
3362
- }
3363
- }
3364
- this.notifyWatchers({ type: "delete", path: norm });
3365
- }
3366
- async move(source, destination) {
3367
- await this.initialize();
3368
- const srcNorm = normalizeWorkspacePath(source);
3369
- const destNorm = normalizeWorkspacePath(destination);
3370
- const obj = this.objects.get(srcNorm);
3371
- if (!obj) throw new Error(`[SupabaseStorage] Source not found: ${source}`);
3372
- this.objects.delete(srcNorm);
3373
- obj.name = destNorm.split("/").pop() || destNorm;
3374
- this.objects.set(destNorm, obj);
3375
- this.notifyWatchers({ type: "delete", path: srcNorm });
3376
- this.notifyWatchers({ type: "create", path: destNorm });
3377
- }
3378
- async copy(source, destination) {
3379
- await this.initialize();
3380
- const srcNorm = normalizeWorkspacePath(source);
3381
- const destNorm = normalizeWorkspacePath(destination);
3382
- const obj = this.objects.get(srcNorm);
3383
- if (!obj) throw new Error(`[SupabaseStorage] Source not found: ${source}`);
3384
- const now = Date.now();
3385
- this.objects.set(destNorm, {
3386
- ...obj,
3387
- id: `sb_obj_${now}`,
3388
- name: destNorm.split("/").pop() || destNorm,
3389
- updatedAt: now
3390
- });
3391
- this.notifyWatchers({
3392
- type: "create",
3393
- path: destNorm,
3394
- metadata: {
3395
- path: destNorm,
3396
- type: "file",
3397
- size: obj.size,
3398
- modifiedAt: now,
3399
- contentHash: obj.contentHash
3400
- }
3401
- });
3402
- }
3403
- };
3404
-
3405
- // src/backend/storage_adapters/FirebaseStorageAdapter.ts
3406
- var FirebaseStorageAdapter = class extends BaseWorkspaceStorageAdapter {
3407
- provider = "firebase_storage";
3408
- endpointId;
3409
- projectId;
3410
- bucket;
3411
- blobs = /* @__PURE__ */ new Map();
3412
- constructor(endpointId, config) {
3413
- super();
3414
- this.endpointId = endpointId;
3415
- this.projectId = config.projectId || "default-project";
3416
- this.bucket = config.bucket || `${this.projectId}.appspot.com`;
3417
- }
3418
- async initialize() {
3419
- if (this.isInitialized) return;
3420
- this.isInitialized = true;
3421
- }
3422
- async exists(pathStr) {
3423
- await this.initialize();
3424
- const norm = normalizeWorkspacePath(pathStr);
3425
- if (!norm) return true;
3426
- return this.blobs.has(norm);
3427
- }
3428
- async stat(pathStr) {
3429
- await this.initialize();
3430
- const norm = normalizeWorkspacePath(pathStr);
3431
- if (!norm) {
3432
- return { path: "", type: "directory", size: 0, modifiedAt: Date.now() };
3433
- }
3434
- const blob = this.blobs.get(norm);
3435
- if (!blob) {
3436
- throw new Error(`[FirebaseStorage] Blob not found in bucket '${this.bucket}': ${pathStr}`);
3437
- }
3438
- return {
3439
- path: norm,
3440
- type: "file",
3441
- size: blob.size,
3442
- modifiedAt: blob.updated,
3443
- contentHash: blob.contentHash,
3444
- revision: blob.generation
3445
- };
3446
- }
3447
- async list(pathStr = "") {
3448
- await this.initialize();
3449
- const norm = normalizeWorkspacePath(pathStr);
3450
- const results = [];
3451
- for (const [key, blob] of this.blobs.entries()) {
3452
- if (norm === "" || key.startsWith(`${norm}/`)) {
3453
- results.push({
3454
- path: key,
3455
- name: blob.name,
3456
- type: "file",
3457
- size: blob.size,
3458
- modifiedAt: blob.updated
3459
- });
3460
- }
3461
- }
3462
- return results;
3463
- }
3464
- async read(pathStr) {
3465
- await this.initialize();
3466
- const norm = normalizeWorkspacePath(pathStr);
3467
- const blob = this.blobs.get(norm);
3468
- if (!blob) {
3469
- throw new Error(`[FirebaseStorage] Blob not found: ${pathStr}`);
3470
- }
3471
- return blob.content || new Uint8Array(0);
3472
- }
3473
- async write(pathStr, data) {
3474
- await this.initialize();
3475
- const norm = normalizeWorkspacePath(pathStr);
3476
- const buffer = typeof data === "string" ? Buffer.from(data, "utf8") : Buffer.from(data);
3477
- const content = new Uint8Array(buffer);
3478
- const hash = calculateContentHash(buffer);
3479
- const now = Date.now();
3480
- const name = norm.split("/").pop() || norm;
3481
- const blob = {
3482
- name,
3483
- bucket: this.bucket,
3484
- content,
3485
- size: content.length,
3486
- timeCreated: now,
3487
- updated: now,
3488
- contentHash: hash,
3489
- generation: String(now)
3490
- };
3491
- this.blobs.set(norm, blob);
3492
- this.notifyWatchers({
3493
- type: "update",
3494
- path: norm,
3495
- metadata: {
3496
- path: norm,
3497
- type: "file",
3498
- size: content.length,
3499
- modifiedAt: now,
3500
- contentHash: hash,
3501
- revision: blob.generation
3502
- }
3503
- });
3504
- }
3505
- async mkdir(pathStr) {
3506
- await this.initialize();
3507
- }
3508
- async delete(pathStr) {
3509
- await this.initialize();
3510
- const norm = normalizeWorkspacePath(pathStr);
3511
- for (const key of Array.from(this.blobs.keys())) {
3512
- if (key === norm || key.startsWith(`${norm}/`)) {
3513
- this.blobs.delete(key);
3514
- }
3515
- }
3516
- this.notifyWatchers({ type: "delete", path: norm });
3517
- }
3518
- async move(source, destination) {
3519
- await this.initialize();
3520
- const srcNorm = normalizeWorkspacePath(source);
3521
- const destNorm = normalizeWorkspacePath(destination);
3522
- const blob = this.blobs.get(srcNorm);
3523
- if (!blob) throw new Error(`[FirebaseStorage] Source not found: ${source}`);
3524
- this.blobs.delete(srcNorm);
3525
- blob.name = destNorm.split("/").pop() || destNorm;
3526
- this.blobs.set(destNorm, blob);
3527
- this.notifyWatchers({ type: "delete", path: srcNorm });
3528
- this.notifyWatchers({ type: "create", path: destNorm });
3529
- }
3530
- async copy(source, destination) {
3531
- await this.initialize();
3532
- const srcNorm = normalizeWorkspacePath(source);
3533
- const destNorm = normalizeWorkspacePath(destination);
3534
- const blob = this.blobs.get(srcNorm);
3535
- if (!blob) throw new Error(`[FirebaseStorage] Source not found: ${source}`);
3536
- const now = Date.now();
3537
- this.blobs.set(destNorm, {
3538
- ...blob,
3539
- name: destNorm.split("/").pop() || destNorm,
3540
- updated: now,
3541
- generation: String(now)
3542
- });
3543
- this.notifyWatchers({
3544
- type: "create",
3545
- path: destNorm,
3546
- metadata: {
3547
- path: destNorm,
3548
- type: "file",
3549
- size: blob.size,
3550
- modifiedAt: now,
3551
- contentHash: blob.contentHash
3552
- }
3553
- });
3554
- }
3555
- };
3556
-
3557
- // src/backend/storage_adapters/StorageAdapterFactory.ts
3558
- var StorageAdapterFactory = class {
3559
- static registry = /* @__PURE__ */ new Map();
3560
- static instanceCache = /* @__PURE__ */ new Map();
3561
- static {
3562
- this.register("local", LocalStorageAdapter);
3563
- this.register("google_drive", GoogleDriveStorageAdapter);
3564
- this.register("onedrive", OneDriveStorageAdapter);
3565
- this.register("supabase_storage", SupabaseStorageAdapter);
3566
- this.register("firebase_storage", FirebaseStorageAdapter);
3567
- this.register("s3", SupabaseStorageAdapter);
3568
- }
3569
- static register(provider, adapterClass) {
3570
- this.registry.set(provider, adapterClass);
3571
- }
3572
- static create(endpoint) {
3573
- const cacheKey = `${endpoint.id}::${endpoint.provider}`;
3574
- if (this.instanceCache.has(cacheKey)) {
3575
- return this.instanceCache.get(cacheKey);
3576
- }
3577
- const AdapterClass = this.registry.get(endpoint.provider);
3578
- if (!AdapterClass) {
3579
- throw new Error(`[StorageAdapterFactory] Unsupported storage provider: '${endpoint.provider}'`);
3580
- }
3581
- const adapter = new AdapterClass(endpoint.id, endpoint.configuration);
3582
- this.instanceCache.set(cacheKey, adapter);
3583
- return adapter;
3584
- }
3585
- static clearCache() {
3586
- this.instanceCache.clear();
3587
- }
3588
- };
3589
-
3590
- // src/backend/WorkspaceFS.ts
3591
- var WorkspaceFS = class {
3592
- primary;
3593
- syncEngine;
3594
- workspace;
3595
- constructor(primary, workspace, syncEngine) {
3596
- this.primary = primary;
3597
- this.workspace = workspace;
3598
- this.syncEngine = syncEngine;
3599
- }
3600
- setPrimary(newPrimary) {
3601
- this.primary = newPrimary;
3602
- }
3603
- setSyncEngine(engine) {
3604
- this.syncEngine = engine;
3605
- }
3606
- async exists(pathStr) {
3607
- return this.primary.exists(pathStr);
3608
- }
3609
- async stat(pathStr) {
3610
- return this.primary.stat(pathStr);
3611
- }
3612
- async list(pathStr = "") {
3613
- return this.primary.list(pathStr);
3614
- }
3615
- async read(pathStr) {
3616
- return this.primary.read(pathStr);
3617
- }
3618
- async readText(pathStr) {
3619
- const data = await this.read(pathStr);
3620
- return Buffer.from(data).toString("utf8");
3621
- }
3622
- async write(pathStr, data) {
3623
- const norm = normalizeWorkspacePath(pathStr);
3624
- await this.primary.write(norm, data);
3625
- if (this.syncEngine && this.workspace.sync?.enabled && this.workspace.sync.mode === "on_write") {
3626
- this.syncEngine.syncFile(norm).catch((err) => {
3627
- console.warn(`[WorkspaceFS] Async backup sync warning for '${norm}':`, err.message);
3628
- });
3629
- }
3630
- }
3631
- async mkdir(pathStr) {
3632
- const norm = normalizeWorkspacePath(pathStr);
3633
- await this.primary.mkdir(norm);
3634
- }
3635
- async delete(pathStr) {
3636
- const norm = normalizeWorkspacePath(pathStr);
3637
- await this.primary.delete(norm);
3638
- if (this.syncEngine && this.workspace.sync?.enabled && this.workspace.sync.deletePropagation) {
3639
- this.syncEngine.syncFile(norm).catch((err) => {
3640
- console.warn(`[WorkspaceFS] Async delete sync warning for '${norm}':`, err.message);
3641
- });
3642
- }
3643
- }
3644
- async move(source, destination) {
3645
- const srcNorm = normalizeWorkspacePath(source);
3646
- const destNorm = normalizeWorkspacePath(destination);
3647
- await this.primary.move(srcNorm, destNorm);
3648
- if (this.syncEngine && this.workspace.sync?.enabled) {
3649
- this.syncEngine.syncFile(srcNorm).catch(() => {
3650
- });
3651
- this.syncEngine.syncFile(destNorm).catch(() => {
3652
- });
3653
- }
3654
- }
3655
- async copy(source, destination) {
3656
- const srcNorm = normalizeWorkspacePath(source);
3657
- const destNorm = normalizeWorkspacePath(destination);
3658
- await this.primary.copy(srcNorm, destNorm);
3659
- if (this.syncEngine && this.workspace.sync?.enabled) {
3660
- this.syncEngine.syncFile(destNorm).catch(() => {
3661
- });
3662
- }
3663
- }
3664
- };
3665
-
3666
- // src/backend/SyncEngine.ts
3667
- var SyncEngine = class {
3668
- workspace;
3669
- primary;
3670
- backups = /* @__PURE__ */ new Map();
3671
- intervalTimer = null;
3672
- isPaused = false;
3673
- retryQueue = /* @__PURE__ */ new Map();
3674
- async initialize(workspace, primary, backups) {
3675
- this.workspace = workspace;
3676
- this.primary = primary;
3677
- this.backups.clear();
3678
- for (const b of backups) {
3679
- this.backups.set(b.endpointId, b);
3680
- }
3681
- if (this.workspace.sync?.enabled && this.workspace.sync.mode === "interval") {
3682
- const intervalMs = this.workspace.sync.intervalMs || 6e4;
3683
- this.intervalTimer = setInterval(() => {
3684
- if (!this.isPaused) {
3685
- this.sync(this.workspace).catch((err) => {
3686
- console.warn("[SyncEngine] Interval sync error:", err.message);
3687
- });
3688
- }
3689
- }, intervalMs);
3690
- }
3691
- }
3692
- updateAdapters(primary, backups) {
3693
- this.primary = primary;
3694
- this.backups.clear();
3695
- for (const b of backups) {
3696
- this.backups.set(b.endpointId, b);
3697
- }
3698
- }
3699
- async sync(workspace) {
3700
- const startTime = Date.now();
3701
- const syncedFiles = [];
3702
- const failedEndpoints = [];
3703
- if (!workspace.sync?.enabled) {
3704
- return { ok: true, syncedFiles: [], failedEndpoints: [], timestamp: startTime, durationMs: 0 };
3705
- }
3706
- try {
3707
- const primaryFiles = await this.primary.list();
3708
- for (const entry of primaryFiles) {
3709
- if (entry.type === "directory") continue;
3710
- const norm = normalizeWorkspacePath(entry.path);
3711
- const result = await this.syncFile(norm);
3712
- if (result.ok) {
3713
- syncedFiles.push(norm);
3714
- } else {
3715
- for (const fe of result.failedEndpoints) {
3716
- failedEndpoints.push(fe);
3717
- }
3718
- }
3719
- }
3720
- await this.processRetryQueue();
3721
- return {
3722
- ok: failedEndpoints.length === 0,
3723
- syncedFiles,
3724
- failedEndpoints,
3725
- timestamp: Date.now(),
3726
- durationMs: Date.now() - startTime
3727
- };
3728
- } catch (err) {
3729
- return {
3730
- ok: false,
3731
- syncedFiles,
3732
- failedEndpoints: [{ endpointId: "all", error: err.message }],
3733
- timestamp: Date.now(),
3734
- durationMs: Date.now() - startTime
3735
- };
3736
- }
3737
- }
3738
- async syncFile(pathStr) {
3739
- const startTime = Date.now();
3740
- const norm = normalizeWorkspacePath(pathStr);
3741
- const failedEndpoints = [];
3742
- const syncedFiles = [];
3743
- const primaryExists = await this.primary.exists(norm);
3744
- for (const [endpointId, backupAdapter] of this.backups.entries()) {
3745
- const endpointConfig = this.workspace.backupStorages.find((b) => b.id === endpointId);
3746
- if (endpointConfig && !endpointConfig.enabled) continue;
3747
- try {
3748
- if (primaryExists) {
3749
- const primaryMeta = await this.primary.stat(norm);
3750
- const backupExists = await backupAdapter.exists(norm);
3751
- let needsSync = true;
3752
- if (backupExists) {
3753
- const backupMeta = await backupAdapter.stat(norm);
3754
- if (primaryMeta.contentHash && backupMeta.contentHash) {
3755
- needsSync = primaryMeta.contentHash !== backupMeta.contentHash;
3756
- } else if (primaryMeta.revision && backupMeta.revision) {
3757
- needsSync = primaryMeta.revision !== backupMeta.revision;
3758
- } else {
3759
- needsSync = primaryMeta.modifiedAt !== backupMeta.modifiedAt || primaryMeta.size !== backupMeta.size;
3760
- }
3761
- }
3762
- if (needsSync) {
3763
- const data = await this.primary.read(norm);
3764
- await backupAdapter.write(norm, data);
3765
- syncedFiles.push(norm);
3766
- }
3767
- if (endpointConfig) {
3768
- endpointConfig.syncState = "SYNCED";
3769
- endpointConfig.lastSyncAt = Date.now();
3770
- }
3771
- } else {
3772
- if (this.workspace.sync?.deletePropagation) {
3773
- const backupExists = await backupAdapter.exists(norm);
3774
- if (backupExists) {
3775
- await backupAdapter.delete(norm);
3776
- syncedFiles.push(norm);
3777
- }
3778
- }
3779
- if (endpointConfig) {
3780
- endpointConfig.syncState = "SYNCED";
3781
- endpointConfig.lastSyncAt = Date.now();
3782
- }
3783
- }
3784
- } catch (err) {
3785
- if (endpointConfig) {
3786
- endpointConfig.syncState = "OUT_OF_SYNC";
3787
- endpointConfig.errorMessage = err.message;
3788
- }
3789
- failedEndpoints.push({ endpointId, error: err.message });
3790
- this.enqueueRetry(endpointId, norm);
3791
- }
3792
- }
3793
- return {
3794
- ok: failedEndpoints.length === 0,
3795
- syncedFiles,
3796
- failedEndpoints,
3797
- timestamp: Date.now(),
3798
- durationMs: Date.now() - startTime
3799
- };
3800
- }
3801
- enqueueRetry(endpointId, pathStr) {
3802
- const key = `${endpointId}::${pathStr}`;
3803
- const existing = this.retryQueue.get(key) || { endpointId, path: pathStr, attempts: 0 };
3804
- existing.attempts += 1;
3805
- this.retryQueue.set(key, existing);
3806
- }
3807
- async processRetryQueue() {
3808
- const maxAttempts = this.workspace.sync?.retry?.maxAttempts || 3;
3809
- for (const [key, item] of this.retryQueue.entries()) {
3810
- if (item.attempts > maxAttempts) {
3811
- this.retryQueue.delete(key);
3812
- continue;
3813
- }
3814
- const adapter = this.backups.get(item.endpointId);
3815
- if (!adapter) {
3816
- this.retryQueue.delete(key);
3817
- continue;
3818
- }
3819
- try {
3820
- const primaryExists = await this.primary.exists(item.path);
3821
- if (primaryExists) {
3822
- const data = await this.primary.read(item.path);
3823
- await adapter.write(item.path, data);
3824
- }
3825
- this.retryQueue.delete(key);
3826
- const ep = this.workspace.backupStorages.find((b) => b.id === item.endpointId);
3827
- if (ep) {
3828
- ep.syncState = "SYNCED";
3829
- ep.lastSyncAt = Date.now();
3830
- }
3831
- } catch {
3832
- }
3833
- }
3834
- }
3835
- async restore(backupEndpoint) {
3836
- try {
3837
- const backupAdapter = StorageAdapterFactory.create(backupEndpoint);
3838
- await backupAdapter.initialize();
3839
- const files = await backupAdapter.list();
3840
- let count = 0;
3841
- for (const entry of files) {
3842
- if (entry.type === "directory") {
3843
- await this.primary.mkdir(entry.path);
3844
- } else {
3845
- const data = await backupAdapter.read(entry.path);
3846
- await this.primary.write(entry.path, data);
3847
- count++;
3848
- }
3849
- }
3850
- await this.sync(this.workspace);
3851
- return {
3852
- ok: true,
3853
- restoredFromEndpointId: backupEndpoint.id,
3854
- filesRestoredCount: count,
3855
- timestamp: Date.now()
3856
- };
3857
- } catch (err) {
3858
- return {
3859
- ok: false,
3860
- restoredFromEndpointId: backupEndpoint.id,
3861
- filesRestoredCount: 0,
3862
- timestamp: Date.now(),
3863
- errorMessage: err.message
3864
- };
3865
- }
3866
- }
3867
- async verify(workspace) {
3868
- const primaryFiles = await this.primary.list();
3869
- let total = 0;
3870
- let matched = 0;
3871
- const divergentFiles = [];
3872
- for (const entry of primaryFiles) {
3873
- if (entry.type === "directory") continue;
3874
- total++;
3875
- const norm = normalizeWorkspacePath(entry.path);
3876
- const primaryStat = await this.primary.stat(norm);
3877
- let allMatch = true;
3878
- const backupHashes = {};
3879
- for (const [endpointId, backupAdapter] of this.backups.entries()) {
3880
- try {
3881
- const bStat = await backupAdapter.stat(norm);
3882
- backupHashes[endpointId] = bStat.contentHash || "";
3883
- if (bStat.contentHash !== primaryStat.contentHash) {
3884
- allMatch = false;
3885
- }
3886
- } catch {
3887
- allMatch = false;
3888
- backupHashes[endpointId] = "MISSING";
3889
- }
3890
- }
3891
- if (allMatch) {
3892
- matched++;
3893
- } else {
3894
- divergentFiles.push({
3895
- path: norm,
3896
- primaryHash: primaryStat.contentHash,
3897
- backupHashes
3898
- });
3899
- }
3900
- }
3901
- return {
3902
- ok: divergentFiles.length === 0,
3903
- totalFiles: total,
3904
- matchedFiles: matched,
3905
- divergentFiles,
3906
- timestamp: Date.now()
3907
- };
3908
- }
3909
- async pause() {
3910
- this.isPaused = true;
3911
- }
3912
- async resume() {
3913
- this.isPaused = false;
3914
- }
3915
- async shutdown() {
3916
- if (this.intervalTimer) {
3917
- clearInterval(this.intervalTimer);
3918
- this.intervalTimer = null;
3919
- }
3920
- this.isPaused = true;
3921
- this.retryQueue.clear();
3922
- }
3923
- };
3924
-
3925
- // src/backend/WorkspaceRuntime.ts
3926
- var WorkspaceRuntime = class _WorkspaceRuntime {
3927
- workspace;
3928
- primary;
3929
- backups = [];
3930
- filesystem;
3931
- syncEngine;
3932
- isInitialized = false;
3933
- constructor(workspace) {
3934
- this.workspace = workspace;
3935
- this.primary = StorageAdapterFactory.create(workspace.primaryStorage);
3936
- this.backups = (workspace.backupStorages || []).map((ep) => StorageAdapterFactory.create(ep));
3937
- this.syncEngine = new SyncEngine();
3938
- this.filesystem = new WorkspaceFS(this.primary, this.workspace, this.syncEngine);
3939
- }
3940
- static async create(workspace) {
3941
- const runtime = new _WorkspaceRuntime(workspace);
3942
- await runtime.initialize();
3943
- return runtime;
3944
- }
3945
- async initialize() {
3946
- if (this.isInitialized) return;
3947
- await this.primary.initialize();
3948
- for (const backup of this.backups) {
3949
- try {
3950
- await backup.initialize();
3951
- } catch (err) {
3952
- console.warn(`[WorkspaceRuntime] Backup init warning for '${backup.endpointId}':`, err.message);
3953
- }
3954
- }
3955
- await this.syncEngine.initialize(this.workspace, this.primary, this.backups);
3956
- this.isInitialized = true;
3957
- }
3958
- async changePrimary(newPrimaryEndpointId) {
3959
- const backupIndex = this.workspace.backupStorages.findIndex((b) => b.id === newPrimaryEndpointId);
3960
- if (backupIndex === -1) {
3961
- throw new Error(`[WorkspaceRuntime] Target endpoint not found in backup storages: ${newPrimaryEndpointId}`);
3962
- }
3963
- const newPrimaryEndpoint = this.workspace.backupStorages[backupIndex];
3964
- const oldPrimaryEndpoint = this.workspace.primaryStorage;
3965
- const newPrimaryAdapter = StorageAdapterFactory.create(newPrimaryEndpoint);
3966
- await newPrimaryAdapter.initialize();
3967
- oldPrimaryEndpoint.syncState = "SYNCED";
3968
- this.workspace.backupStorages.splice(backupIndex, 1, oldPrimaryEndpoint);
3969
- this.workspace.primaryStorage = newPrimaryEndpoint;
3970
- this.primary = newPrimaryAdapter;
3971
- this.backups = this.workspace.backupStorages.map((ep) => StorageAdapterFactory.create(ep));
3972
- this.filesystem.setPrimary(this.primary);
3973
- this.syncEngine.updateAdapters(this.primary, this.backups);
3974
- await this.syncEngine.sync(this.workspace);
3975
- }
3976
- async addBackup(endpoint) {
3977
- const exists = this.workspace.backupStorages.some((b) => b.id === endpoint.id);
3978
- if (!exists) {
3979
- this.workspace.backupStorages.push(endpoint);
3980
- const adapter = StorageAdapterFactory.create(endpoint);
3981
- await adapter.initialize();
3982
- this.backups.push(adapter);
3983
- this.syncEngine.updateAdapters(this.primary, this.backups);
3984
- await this.syncEngine.sync(this.workspace);
3985
- }
3986
- }
3987
- async detachStorage(endpointId) {
3988
- const idx = this.workspace.backupStorages.findIndex((b) => b.id === endpointId);
3989
- if (idx !== -1) {
3990
- this.workspace.backupStorages.splice(idx, 1);
3991
- const bIdx = this.backups.findIndex((b) => b.endpointId === endpointId);
3992
- if (bIdx !== -1) {
3993
- await this.backups[bIdx].disconnect();
3994
- this.backups.splice(bIdx, 1);
3995
- }
3996
- this.syncEngine.updateAdapters(this.primary, this.backups);
3997
- }
3998
- }
3999
- async dispose() {
4000
- await this.syncEngine.shutdown();
4001
- await this.primary.disconnect();
4002
- for (const b of this.backups) {
4003
- await b.disconnect();
4004
- }
4005
- this.isInitialized = false;
4006
- }
4007
- };
4008
-
4009
- // src/core/K_LIB_Krnl_Functionality_Types.ts
4010
- function cloneState(source) {
4011
- return ObjectKit.deepClone(source);
4012
- }
4013
- var krnlTransportAdapter = class {
4014
- };
4015
- var MemoryTransportAdapter = class extends krnlTransportAdapter {
4016
- targetEntity;
4017
- constructor(targetEntity) {
4018
- super();
4019
- this.targetEntity = targetEntity;
4020
- }
4021
- async call(targetFunction, input, opts) {
4022
- if (!this.targetEntity || typeof this.targetEntity.call !== "function") {
4023
- throw new Error("MemoryTransportAdapter: Invalid target entity.");
4024
- }
4025
- return await this.targetEntity.call(targetFunction, input, opts);
4026
- }
4027
- };
4028
- var krnlLocalFunctionality = class {
4029
- id;
4030
- visibility;
4031
- initialState;
4032
- api;
4033
- isLocal = true;
4034
- sourcePath;
4035
- helpers;
4036
- emitters;
4037
- schema;
4038
- uiSpec;
4039
- description;
4040
- method;
4041
- tests;
4042
- examples;
4043
- intelligenceSpec;
4044
- /** Port-based access control policy. Absent = unrestricted. */
4045
- accessPolicy;
4046
- /** Shared secret for allow_with_key / ask_key_unlisted gates. */
4047
- accessKey;
4048
- /** Optional Functionality Manifest declaring dependencies, capabilities, and resources */
4049
- manifest;
4050
- lifecycle;
4051
- state;
4052
- hostEntity;
4053
- intention;
4054
- ai;
4055
- requires;
4056
- _hasBeenExecuted = false;
4057
- backend;
4058
- table;
4059
- constructor(config = {}) {
4060
- const {
4061
- id,
4062
- visibility = "public",
4063
- // Default: public (bubbles up)
4064
- initialState = {},
4065
- api = (input) => input,
4066
- helpers = {},
4067
- emitters = {},
4068
- events = {},
4069
- schema = null,
4070
- onMount = null,
4071
- onTeardown = null,
4072
- sourcePath = null,
4073
- accessPolicy = null,
4074
- accessKey = null,
4075
- tests = [],
4076
- requires = null,
4077
- manifest = null,
4078
- backend = null
4079
- } = config;
4080
- if (!id) throw new Error("LocalFunctionality requires an ID.");
4081
- this.id = id;
4082
- this.visibility = visibility;
4083
- this.initialState = initialState;
4084
- this.api = api;
4085
- this.sourcePath = sourcePath;
4086
- if (requires) this.requires = requires;
4087
- this.manifest = manifest || config.manifest || null;
4088
- this.backend = backend || config.backend || null;
4089
- this.table = null;
4090
- this.helpers = new Map(Object.entries(helpers));
4091
- this.emitters = /* @__PURE__ */ new Map();
4092
- this.schema = schema;
4093
- this.description = config.description || "";
4094
- this.method = config.method || "POST";
4095
- this.tests = tests;
4096
- this.examples = config.examples || null;
4097
- this.uiSpec = config.uiSpec || null;
4098
- this.intention = config.intention || null;
4099
- this.intelligenceSpec = config.intelligenceSpec || null;
4100
- if (accessPolicy) this.accessPolicy = accessPolicy;
4101
- if (accessKey) this.accessKey = accessKey;
4102
- this.lifecycle = {
4103
- beginning: onMount || events.beginning,
4104
- during: events.during,
4105
- end: events.end,
4106
- failure: events.failure,
4107
- teardown: onTeardown || events.teardown
4108
- };
4109
- if (!emitters["state:changed"]) {
4110
- this.emitters.set("state:changed", new krnlEmitter());
4111
- }
4112
- Object.entries(emitters).forEach(([eid, e]) => {
4113
- this.emitters.set(eid, e instanceof krnlEmitter ? e : new krnlEmitter());
4114
- });
4115
- this.state = this._createObservedState(cloneState(initialState));
4116
- }
4117
- _createObservedState(initialState) {
4118
- const self = this;
4119
- const proxyCache = /* @__PURE__ */ new WeakMap();
4120
- const handler = {
4121
- get(target, prop, receiver) {
4122
- const value = Reflect.get(target, prop, receiver);
4123
- if (typeof value === "object" && value !== null) {
4124
- if (!proxyCache.has(value)) {
4125
- proxyCache.set(value, new Proxy(value, handler));
4126
- }
4127
- return proxyCache.get(value);
4128
- }
4129
- return value;
4130
- },
4131
- set(target, prop, value, receiver) {
4132
- const result = Reflect.set(target, prop, value, receiver);
4133
- if (result) {
4134
- self._notifyStateChanged(prop, value);
4135
- }
4136
- return result;
4137
- },
4138
- deleteProperty(target, prop) {
4139
- const result = Reflect.deleteProperty(target, prop);
4140
- if (result) {
4141
- self._notifyStateChanged(prop, void 0);
4142
- }
4143
- return result;
4144
- }
4145
- };
4146
- return new Proxy(initialState, handler);
4147
- }
4148
- _notifyStateChanged(prop, value) {
4149
- const stateEmitter = this.emitters.get("state:changed");
4150
- if (stateEmitter) {
4151
- stateEmitter.pushDataStreamToEmitter({
4152
- prop,
4153
- value,
4154
- state: this.state,
4155
- // This is the proxied state
4156
- functionalityId: this.id
4157
- });
4158
- }
4159
- }
4160
- /**
4161
- * Updates the visibility and handles interface registration/deregistration.
4162
- * This is the mechanism used to STOP bubbling at a certain level.
4163
- */
4164
- setVisibility(v) {
4165
- const oldVisibility = this.visibility;
4166
- this.visibility = v;
4167
- if (!this.hostEntity) return;
4168
- if (oldVisibility === "private" && v === "public") {
4169
- this._registerInInterface();
4170
- const owner = this.hostEntity.hierarchyManager.getOwnerEntity();
4171
- if (owner) owner._onComponentFunctionalityAdded(this.hostEntity, this);
4172
- } else if (oldVisibility === "public" && v === "private") {
4173
- this._unregisterFromInterface();
4174
- const owner = this.hostEntity.hierarchyManager.getOwnerEntity();
4175
- if (owner && owner._onComponentFunctionalityRemoved) {
4176
- owner._onComponentFunctionalityRemoved(this.hostEntity, this.id);
4177
- }
4178
- }
4179
- }
4180
- _unregisterFromInterface() {
4181
- const entity = this.hostEntity;
4182
- if (!entity) return;
4183
- entity.interface.removeCollector(this.id);
4184
- this.emitters.forEach((_, eid) => {
4185
- entity.interface.removeEmitter(`${this.id}:${eid}`);
4186
- });
4187
- }
4188
- _registerInInterface() {
4189
- const entity = this.hostEntity;
4190
- const mainCollector = entity.interface.createCollector(this.id);
4191
- mainCollector._directHandler = async (msg) => {
4192
- const caller = msg.metadata?.caller || "anonymous";
4193
- return await this.execute(msg.data, { caller });
4194
- };
4195
- this.emitters.forEach((emitter, eid) => {
4196
- const interfaceEmitter = entity.interface.createEmitter(`${this.id}:${eid}`);
4197
- emitter.subscribe((msg) => interfaceEmitter.pushDataStreamToEmitter(msg.data, msg.time));
4198
- });
4199
- }
4200
- /**
4201
- * checkAccess — port-based access gate.
4202
- *
4203
- * f(p, k) ∈ { PASS, REJECT }
4204
- * FORBID if p ∈ P_forbid
4205
- * PASS if p ∈ P_allow
4206
- * KEY(k) if p ∈ P_allow_with_key
4207
- * default_connection_behaviour(p, k) otherwise (unlisted)
4208
- */
4209
- checkAccess(callerPort, key) {
4210
- const p = this.accessPolicy;
4211
- if (!p) return { allowed: true };
4212
- if (p.forbid.includes(callerPort))
4213
- return { allowed: false, reason: `Port ${callerPort} is explicitly forbidden from accessing this endpoint.` };
4214
- if (p.allow.includes(callerPort))
4215
- return { allowed: true };
4216
- if (p.allow_with_key.includes(callerPort)) {
4217
- if (key && key === this.accessKey) return { allowed: true };
4218
- return {
4219
- allowed: false,
4220
- reason: key ? `Invalid access key for port ${callerPort}.` : `Port ${callerPort} requires an access key (x-krnl-access-key header).`
4221
- };
4222
- }
4223
- switch (p.default_connection_behaviour) {
4224
- case "allow_unlisted":
4225
- return { allowed: true };
4226
- case "ask_key_unlisted":
4227
- if (key && key === this.accessKey) return { allowed: true };
4228
- return {
4229
- allowed: false,
4230
- reason: key ? `Invalid access key for this endpoint.` : `This endpoint requires an access key. Provide it via the 'x-krnl-access-key' header.`
4231
- };
4232
- case "forbid_unlisted":
4233
- return { allowed: false, reason: `Port ${callerPort} is not permitted. This is a restricted (internal) endpoint.` };
4234
- }
4235
- }
4236
- bindToEntity(entity) {
4237
- this.hostEntity = entity;
4238
- if (this.backend) {
4239
- this._initializeBackendTable(entity);
4240
- }
4241
- if (this.visibility === "public") {
4242
- this._registerInInterface();
4243
- }
4244
- if (typeof this.lifecycle.beginning === "function") {
4245
- this.lifecycle.beginning({
4246
- state: this.state,
4247
- emitters: this.emitters,
4248
- helpers: this.helpers,
4249
- entity: this.hostEntity,
4250
- functionality: this,
4251
- table: this.table,
4252
- backend: this.table?.provider
4253
- });
4254
- }
4255
- return this;
4256
- }
4257
- _initializeBackendTable(entity) {
4258
- try {
4259
- let service = "sqlite";
4260
- let tableName = this.id;
4261
- let credentials = void 0;
4262
- if (typeof this.backend === "string") {
4263
- service = this.backend;
4264
- } else if (typeof this.backend === "object" && this.backend !== null) {
4265
- if (this.backend.service) service = this.backend.service;
4266
- if (this.backend.table) tableName = this.backend.table;
4267
- if (this.backend.credentials) credentials = this.backend.credentials;
4268
- }
4269
- if (!credentials && entity && typeof entity.getBackendCredentials === "function") {
4270
- credentials = entity.getBackendCredentials(service);
4271
- }
4272
- const provider = WorkspaceBackendFactory.resolveDataProvider({
4273
- id: `provider-${this.id}`,
4274
- provider: service,
4275
- mode: "local",
4276
- database: {
4277
- type: service,
4278
- filePath: credentials?.filePath || `./${tableName}.sqlite`,
4279
- enableWAL: credentials?.enableWAL ?? true,
4280
- ...credentials
4281
- }
4282
- });
4283
- this.table = new FunctionalityTable(tableName, provider);
4284
- } catch (err) {
4285
- console.warn(`[KrnlBackendWarning] Failed to initialize table for functionality "${this.id}":`, err.message);
4286
- }
4287
- }
4288
- teardown() {
4289
- if (typeof this.lifecycle.teardown === "function") {
4290
- this.lifecycle.teardown({
4291
- state: this.state,
4292
- emitters: this.emitters,
4293
- helpers: this.helpers,
4294
- entity: this.hostEntity,
4295
- functionality: this,
4296
- table: this.table,
4297
- backend: this.table?.provider
4298
- });
4299
- }
4300
- }
4301
- async execute(input, opts = {}) {
4302
- const caller = opts.caller || "default";
4303
- const activeState = this.state;
4304
- const activeTelemetry = this.hostEntity?.telemetry || telemetry;
4305
- const trace = activeTelemetry.enabled ? activeTelemetry.startTrace(this.id) : null;
4306
- if (this.accessPolicy && opts.callerPort !== void 0) {
4307
- const access = this.checkAccess(opts.callerPort, opts.accessKey);
4308
- if (!access.allowed) {
4309
- if (trace) activeTelemetry.endTrace(this.id, trace, false);
4310
- return { status: "error", message: `[AccessDenied] ${access.reason}`, payload: null };
4311
- }
4312
- }
4313
- if (this.requires) {
4314
- const gateError = await this._checkRequires();
4315
- if (gateError) {
4316
- if (trace) activeTelemetry.endTrace(this.id, trace, false);
4317
- return { status: "error", message: `[DependencyGate] ${gateError}`, payload: null };
4318
- }
4319
- }
4320
- if (this.schema && this.schema.input) {
4321
- try {
4322
- if (typeof this.schema.input.verify === "function") {
4323
- const verifyError = this.schema.input.verify(input);
4324
- if (verifyError) {
4325
- throw new Error(`Protobuf verification failed: ${verifyError}`);
4326
- }
4327
- } else if (typeof this.schema.input.parse === "function") {
4328
- input = this.schema.input.parse(input);
4329
- } else if (typeof this.schema.input === "function") {
4330
- if (!this.schema.input(input)) {
4331
- throw new Error(`Input validation failed.`);
4332
- }
4333
- }
4334
- } catch (err) {
4335
- const msg = err.errors ? JSON.stringify(err.errors) : err.message;
4336
- throw new Error(`[KrnlSchemaError] Input validation failed for "${this.id}": ${msg}`);
4337
- }
4338
- }
4339
- try {
4340
- if (typeof this.lifecycle.during === "function") {
4341
- this.lifecycle.during({ input, opts, state: activeState, entity: this.hostEntity });
4342
- }
4343
- let result = await this.api(input, {
4344
- state: activeState,
4345
- emitters: this.emitters,
4346
- helpers: this.helpers,
4347
- entity: this.hostEntity,
4348
- functionality: this,
4349
- caller,
4350
- table: this.table,
4351
- backend: this.table?.provider
4352
- });
4353
- if (this.schema && this.schema.output) {
4354
- try {
4355
- if (typeof this.schema.output.verify === "function") {
4356
- const verifyError = this.schema.output.verify(result);
4357
- if (verifyError) {
4358
- console.warn(`[KrnlSchemaWarning] Output validation failed for "${this.id}": Protobuf verification failed: ${verifyError}`);
4359
- }
4360
- } else if (typeof this.schema.output.parse === "function") {
4361
- this.schema.output.parse(result);
4362
- } else if (typeof this.schema.output === "function") {
4363
- if (!this.schema.output(result)) {
4364
- console.warn(`[KrnlSchemaWarning] Output validation failed for "${this.id}".`);
4365
- }
4366
- }
4367
- } catch (err) {
4368
- const msg = err.errors ? JSON.stringify(err.errors) : err.message;
4369
- console.warn(`[KrnlSchemaWarning] Output validation failed for "${this.id}": ${msg}`);
4370
- }
4371
- }
4372
- if (trace) activeTelemetry.endTrace(this.id, trace, true);
4373
- const interactionEvent = {
4374
- type: "frontend:interaction",
4375
- fid: this.id,
4376
- timestamp: Date.now(),
4377
- input,
4378
- output: result
4379
- };
4380
- const stateEmitter = this.emitters.get("state:changed");
4381
- stateEmitter?.pushDataStreamToEmitter(interactionEvent);
4382
- let rootSystem = this.hostEntity;
4383
- while (rootSystem && !rootSystem._frontend) {
4384
- rootSystem = rootSystem.hierarchyManager?.getOwnerEntity?.();
4385
- }
4386
- const frontend = rootSystem?._frontend;
4387
- if (frontend) {
4388
- frontend.interactionsHistory.push(interactionEvent);
4389
- if (frontend.interactionsHistory.length > 50) {
4390
- frontend.interactionsHistory.shift();
4391
- }
4392
- }
4393
- this._hasBeenExecuted = true;
4394
- return {
4395
- status: "success",
4396
- payload: result
4397
- };
4398
- } catch (err) {
4399
- if (trace) activeTelemetry.endTrace(this.id, trace, false);
4400
- const interactionEvent = {
4401
- type: "frontend:interaction",
4402
- fid: this.id,
4403
- timestamp: Date.now(),
4404
- input,
4405
- output: { status: "error", message: err.message, payload: null }
4406
- };
4407
- const stateEmitter = this.emitters.get("state:changed");
4408
- stateEmitter?.pushDataStreamToEmitter(interactionEvent);
4409
- let rootSystem = this.hostEntity;
4410
- while (rootSystem && !rootSystem._frontend) {
4411
- rootSystem = rootSystem.hierarchyManager?.getOwnerEntity?.();
4412
- }
4413
- const frontend = rootSystem?._frontend;
4414
- if (frontend) {
4415
- frontend.interactionsHistory.push(interactionEvent);
4416
- if (frontend.interactionsHistory.length > 50) {
4417
- frontend.interactionsHistory.shift();
4418
- }
4419
- }
4420
- if (typeof this.lifecycle.failure === "function") {
4421
- this.lifecycle.failure({ error: err, input, caller });
4422
- }
4423
- return {
4424
- status: "error",
4425
- message: err.message,
4426
- payload: null
4427
- };
4428
- }
4429
- }
4430
- async _checkRequires() {
4431
- const r = this.requires;
4432
- const entity = this.hostEntity;
4433
- for (const fid of r.functionalitiesExecuted ?? []) {
4434
- const sibling = entity?.localFunctionalities?.get(fid);
4435
- if (!sibling?._hasBeenExecuted) {
4436
- return `Requires functionality '${fid}' to have been executed first.`;
4437
- }
4438
- }
4439
- for (const flag of r.flags ?? []) {
4440
- if (!entity?._krnlFlags?.has(flag)) {
4441
- return `Requires flag '${flag}' to be set. Check boot sequence or entity initialization.`;
4442
- }
4443
- }
4444
- if (typeof r.check === "function") {
4445
- const result = await r.check(entity);
4446
- if (result) return result;
4447
- }
4448
- return null;
4449
- }
4450
- };
4451
- var krnlRemoteFunctionality = class {
4452
- id;
4453
- targetFunction;
4454
- targetEntity;
4455
- visibility;
4456
- uiSpec;
4457
- schema;
4458
- isLocal = false;
4459
- transport;
4460
- hostEntity;
4461
- constructor(config = {}) {
4462
- const { id, targetEntity, targetFunction, transport, visibility = "public", uiSpec, schema } = config;
4463
- this.id = id;
4464
- this.targetFunction = targetFunction || id;
4465
- this.targetEntity = targetEntity;
4466
- this.visibility = visibility;
4467
- const targetLocalAtom = targetEntity?.localFunctionalities?.get?.(this.targetFunction);
4468
- this.uiSpec = uiSpec || targetLocalAtom?.uiSpec || targetLocalAtom?.ui_spec;
4469
- this.schema = schema || targetLocalAtom?.schema;
4470
- if (transport) {
4471
- this.transport = transport;
4472
- } else if (targetEntity) {
4473
- this.transport = new MemoryTransportAdapter(targetEntity);
4474
- } else {
4475
- throw new Error("RemoteFunctionality requires either a transport or a targetEntity.");
4476
- }
4477
- }
4478
- setVisibility(v) {
4479
- const oldVisibility = this.visibility;
4480
- this.visibility = v;
4481
- if (this.hostEntity && oldVisibility === "private" && v === "public") {
4482
- this._registerInInterface();
4483
- const owner = this.hostEntity.hierarchyManager.getOwnerEntity();
4484
- if (owner) owner._onComponentFunctionalityAdded(this.hostEntity, this);
4485
- } else if (this.hostEntity && oldVisibility === "public" && v === "private") {
4486
- this._unregisterFromInterface();
4487
- const owner = this.hostEntity.hierarchyManager.getOwnerEntity();
4488
- if (owner && owner._onComponentFunctionalityRemoved) {
4489
- owner._onComponentFunctionalityRemoved(this.hostEntity, this.id);
4490
- }
4491
- }
4492
- }
4493
- _unregisterFromInterface() {
4494
- if (!this.hostEntity) return;
4495
- this.hostEntity.interface.removeCollector(this.id);
4496
- }
4497
- _registerInInterface() {
4498
- const mainCollector = this.hostEntity.interface.createCollector(this.id);
4499
- mainCollector._directHandler = async (msg) => {
4500
- const caller = msg.metadata?.caller || "anonymous";
4501
- return await this.execute(msg.data, { caller });
4502
- };
4503
- }
4504
- bindToEntity(entity) {
4505
- this.hostEntity = entity;
4506
- if (this.visibility === "public") {
4507
- this._registerInInterface();
4508
- }
4509
- }
4510
- async execute(input, opts = {}) {
4511
- return await this.transport.call(this.targetFunction, input, { ...opts, caller: this.hostEntity.entityName });
4512
- }
4513
- };
4514
-
4515
- // src/core/K_ENT_Krnl_Entity_Base.ts
4516
- var krnlEntityBase = class _krnlEntityBase {
4517
- __krnlEntityInstance;
4518
- id;
4519
- entityName;
4520
- Singleton;
4521
- baseName;
4522
- instanceNumber;
4523
- telemetry;
4524
- _telemetryEnabled = false;
4525
- get telemetryEnabled() {
4526
- return this._telemetryEnabled;
4527
- }
4528
- set telemetryEnabled(val) {
4529
- this._telemetryEnabled = val;
4530
- this.telemetry.enabled = val;
4531
- }
4532
- systemType = "BaseEntity";
4533
- interface;
4534
- localFunctionalities;
4535
- remoteFunctionalities;
4536
- collectors;
4537
- hierarchyManager;
4538
- intention;
4539
- backendSettings;
4540
- /**
4541
- * Configures backend services and credentials for this entity and cascading child functionalities.
4542
- */
4543
- configureBackend(settings) {
4544
- this.backendSettings = {
4545
- ...this.backendSettings,
4546
- ...settings
4547
- };
4548
- return this;
4549
- }
4550
- /**
4551
- * Resolves backend credentials for a specific service, cascading from parent entities if necessary.
4552
- */
4553
- getBackendCredentials(service) {
4554
- if (this.backendSettings) {
4555
- if (service && this.backendSettings[service]) {
4556
- return this.backendSettings[service];
4557
- }
4558
- const defaultSvc = this.backendSettings.defaultService;
4559
- if (defaultSvc && this.backendSettings[defaultSvc]) {
4560
- return this.backendSettings[defaultSvc];
4561
- }
4562
- }
4563
- const owner = this.hierarchyManager?.getOwnerEntity();
4564
- if (owner && typeof owner.getBackendCredentials === "function") {
4565
- return owner.getBackendCredentials(service);
4566
- }
4567
- return void 0;
4568
- }
4569
- // Extension points for decorators/middleware
4570
- async _decorateResult(result, fid, input) {
4571
- return result;
4572
- }
4573
- async _decorateError(error, fid, input) {
4574
- return error;
4575
- }
4576
- /**
4577
- * @param name - The unique identity of the entity.
4578
- * @param owner - The parent entity (if any).
4579
- */
4580
- constructor(name, owner = null, options = {}) {
4581
- this.__krnlEntityInstance = true;
4582
- this.id = name || "UnnamedEntity";
4583
- this.entityName = this.id;
4584
- this.Singleton = options.Singleton ?? this.constructor.Singleton ?? true;
4585
- this.baseName = options.baseName || this.entityName;
4586
- this.instanceNumber = options.instanceNumber || 1;
4587
- this.telemetry = new KrnlTelemetry();
4588
- this.telemetryEnabled = options.telemetryEnabled || false;
4589
- this.intention = options.intention || null;
4590
- this.interface = new krnlInterface(this);
4591
- this.localFunctionalities = /* @__PURE__ */ new Map();
4592
- this.remoteFunctionalities = /* @__PURE__ */ new Map();
4593
- this.collectors = /* @__PURE__ */ new Set();
4594
- this.hierarchyManager = new krnlEntityCompositionalHierarchyManager(
4595
- this,
4596
- // Host.
4597
- owner,
4598
- // Parent.
4599
- _krnlEntityBase
4600
- // Base class for nesting.
4601
- );
4602
- }
4603
- /**
4604
- * Registers a local logic block.
4605
- * If the functionality is 'public', it is automatically promoted to the owner.
4606
- */
4607
- addLocal(localFunctionality) {
4608
- const functionality = localFunctionality instanceof krnlLocalFunctionality ? localFunctionality : new krnlLocalFunctionality(localFunctionality);
4609
- functionality.bindToEntity(this);
4610
- this.localFunctionalities.set(functionality.id, functionality);
4611
- const owner = this.hierarchyManager.getOwnerEntity();
4612
- if (owner && owner._onComponentFunctionalityAdded) {
4613
- owner._onComponentFunctionalityAdded(this, functionality);
4614
- }
4615
- return functionality;
4616
- }
4617
- /**
4618
- * Registers a proxy for a functionality on another entity.
4619
- * If the proxy is 'public', it is promoted to the owner (Recursive Bubbling).
4620
- */
4621
- addRemote(remoteFunctionality) {
4622
- const functionality = remoteFunctionality instanceof krnlRemoteFunctionality ? remoteFunctionality : new krnlRemoteFunctionality(remoteFunctionality);
4623
- functionality.bindToEntity(this);
4624
- this.remoteFunctionalities.set(functionality.id, functionality);
4625
- const owner = this.hierarchyManager.getOwnerEntity();
4626
- if (owner && owner._onComponentFunctionalityAdded) {
4627
- owner._onComponentFunctionalityAdded(this, functionality);
4628
- }
4629
- return functionality;
4630
- }
4631
- /**
4632
- * INTERFACE PROMOTION
4633
- * Handles the automatic bubbling of component logic to the owner.
4634
- */
4635
- _onComponentFunctionalityAdded(component, functionality) {
4636
- if (component._isIngressNetworkMember) return;
4637
- if (functionality.visibility !== "public") return;
4638
- const existing = this.remoteFunctionalities.get(functionality.id);
4639
- if (existing) {
4640
- existing.targetEntity = component;
4641
- existing.targetFunction = functionality.id;
4642
- return;
4643
- }
4644
- this.addRemote({
4645
- id: functionality.id,
4646
- targetEntity: component,
4647
- targetFunction: functionality.id,
4648
- visibility: "public"
4649
- // Bubbles up further by default.
4650
- });
4651
- }
4652
- /**
4653
- * Removes a promoted remote functionality and recursively propagates removal upward.
4654
- */
4655
- _onComponentFunctionalityRemoved(component, functionalityId) {
4656
- const existing = this.remoteFunctionalities.get(functionalityId);
4657
- if (!existing) return;
4658
- if (existing.targetEntity !== component) return;
4659
- this.remoteFunctionalities.delete(functionalityId);
4660
- const owner = this.hierarchyManager.getOwnerEntity();
4661
- if (owner && owner._onComponentFunctionalityRemoved) {
4662
- owner._onComponentFunctionalityRemoved(this, functionalityId);
4663
- }
4664
- }
4665
- /**
4666
- * Internal helper to trigger promotion of all existing functionalities.
4667
- */
4668
- _promoteAllExistingFunctionalities() {
4669
- const owner = this.hierarchyManager.getOwnerEntity();
4670
- if (!owner || !owner._onComponentFunctionalityAdded) return;
4671
- this.localFunctionalities.forEach((f) => {
4672
- owner._onComponentFunctionalityAdded(this, f);
4673
- });
4674
- this.remoteFunctionalities.forEach((f) => {
4675
- owner._onComponentFunctionalityAdded(this, f);
4676
- });
4677
- }
4678
- /**
4679
- * Executes a functionality by ID.
4680
- */
4681
- async call(fid, input, opts = {}) {
4682
- const unwrap = opts.unwrap !== false;
4683
- try {
4684
- let result;
4685
- if (this.localFunctionalities.has(fid)) {
4686
- result = await this.localFunctionalities.get(fid).execute(input, opts);
4687
- } else if (this.remoteFunctionalities.has(fid)) {
4688
- result = await this.remoteFunctionalities.get(fid).execute(input, opts);
4689
- } else {
4690
- throw new krnlError(krnlErrorCodes.FUNCTIONALITY_NOT_DEFINED);
4691
- }
4692
- result = await this._decorateResult(result, fid, input);
4693
- if (unwrap && result && typeof result === "object" && result.status === "success" && "payload" in result && !result.skipUnwrap) {
4694
- return result.payload;
4695
- }
4696
- if (result && typeof result === "object" && result.status === "error") {
4697
- throw new krnlError(result.message || krnlErrorCodes.REMOTE_EXECUTION_FAILED);
4698
- }
4699
- return result;
4700
- } catch (err) {
4701
- let finalErr = err;
4702
- finalErr = await this._decorateError(err, fid, input);
4703
- if (finalErr && typeof finalErr === "object" && finalErr.status === "error") {
4704
- const wrapper = new krnlError(finalErr.message || krnlErrorCodes.REMOTE_EXECUTION_FAILED);
4705
- Object.assign(wrapper, finalErr);
4706
- throw wrapper;
4707
- }
4708
- throw finalErr;
4709
- }
4710
- }
4711
- getLocal(fid) {
4712
- return this.localFunctionalities.get(fid);
4713
- }
4714
- getRemote(fid) {
4715
- return this.remoteFunctionalities.get(fid);
4716
- }
4717
- resolveLocalFunctionality(fid) {
4718
- if (this.localFunctionalities.has(fid)) {
4719
- return this.localFunctionalities.get(fid);
4720
- }
4721
- let current = this.remoteFunctionalities.get(fid);
4722
- const visited = /* @__PURE__ */ new Set();
4723
- while (current) {
4724
- if (visited.has(current)) {
4725
- break;
4726
- }
4727
- visited.add(current);
4728
- if (current.isLocal) {
4729
- return current;
4730
- }
4731
- const target = current.targetEntity || current.transport && (current.transport.targetEntity || current.transport.target || current.transport.instance);
4732
- if (target) {
4733
- const targetFid = current.targetFunction || fid;
4734
- if (target.localFunctionalities && target.localFunctionalities.has(targetFid)) {
4735
- return target.localFunctionalities.get(targetFid);
4736
- }
4737
- if (target.remoteFunctionalities && target.remoteFunctionalities.has(targetFid)) {
4738
- current = target.remoteFunctionalities.get(targetFid);
4739
- continue;
4740
- }
4741
- }
4742
- break;
4743
- }
4744
- return null;
4745
- }
4746
- addChild(instance) {
4747
- return this.hierarchyManager.addComponentEntityInstance(instance);
4748
- }
4749
- getTelemetry() {
4750
- const summary = this.telemetry.getSummary();
4751
- return {
4752
- entity: this.entityName,
4753
- timestamp: Date.now(),
4754
- metrics: summary.metrics,
4755
- callLogs: summary.callLogs,
4756
- libraryUsage: summary.libraryUsage,
4757
- dataUsage: summary.dataUsage
4758
- };
4759
- }
4760
- disposeOwnedComponent(entityId) {
4761
- return this.hierarchyManager.disposeComponentEntity(entityId);
4762
- }
4763
- dispose() {
4764
- if (this.hierarchyManager) this.hierarchyManager.dispose();
4765
- if (this.interface) this.interface.dispose();
4766
- this.localFunctionalities.forEach((func) => {
4767
- if (typeof func.teardown === "function") {
4768
- func.teardown();
4769
- }
4770
- });
4771
- this.localFunctionalities.clear();
4772
- this.remoteFunctionalities.clear();
4773
- }
4774
- };
4775
-
4776
- export {
4777
- krnlErrorCodes,
4778
- krnlErrorMessages,
4779
- CUSTOM_ERROR_CODE,
4780
- krnlError,
4781
- krnlGlobalEmitterAddress,
4782
- krnlLocalEmitterAddress,
4783
- krnlMessage,
4784
- krnlEmitter,
4785
- krnlCollector,
4786
- krnlInterface,
4787
- krnlEntityCompositionalHierarchyManager,
4788
- FunctionalityTable,
4789
- SQLiteDataProvider,
4790
- PostgresDataProvider,
4791
- SupabaseDataProvider,
4792
- PocketBaseDataProvider,
4793
- FirebaseDataProvider,
4794
- TursoLibSQLDataProvider,
4795
- MongoAtlasDataProvider,
4796
- FilesystemStorageProvider,
4797
- SupabaseStorageProvider,
4798
- S3StorageProvider,
4799
- PocketBaseStorageProvider,
4800
- FirebaseStorageProvider,
4801
- VercelBlobStorageProvider,
4802
- WorkspaceBackendFactory,
4803
- LocalSQLiteAccountProvider,
4804
- HostedAccountProvider,
4805
- ControlPlaneManager,
4806
- AdminGuard,
4807
- AuditLogger,
4808
- DatabaseClusterManager,
4809
- calculateContentHash,
4810
- normalizeWorkspacePath,
4811
- BaseWorkspaceStorageAdapter,
4812
- LocalStorageAdapter,
4813
- GoogleDriveStorageAdapter,
4814
- OneDriveStorageAdapter,
4815
- SupabaseStorageAdapter,
4816
- FirebaseStorageAdapter,
4817
- StorageAdapterFactory,
4818
- WorkspaceFS,
4819
- SyncEngine,
4820
- WorkspaceRuntime,
4821
- krnlTransportAdapter,
4822
- MemoryTransportAdapter,
4823
- krnlLocalFunctionality,
4824
- krnlRemoteFunctionality,
4825
- krnlEntityBase
4826
- };