@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.
- package/README.md +67 -65
- package/package.json +15 -8
- package/packages/cli/dist/bin/krnl.cjs +808 -331
- package/packages/cli/dist/bin/krnl.js +50 -65
- package/packages/cli/dist/chunk-GHWYEAPD.js +1642 -0
- package/packages/cli/dist/index.cjs +761 -263
- package/packages/cli/dist/index.d.cts +45 -2
- package/packages/cli/dist/index.d.ts +45 -2
- package/packages/cli/dist/index.js +5 -1
- package/packages/cli/package.json +17 -10
- package/packages/core/dist/index.cjs +2912 -140
- package/packages/core/dist/index.d.cts +614 -27
- package/packages/core/dist/index.d.ts +614 -27
- package/packages/core/dist/index.js +2903 -140
- package/packages/core/package.json +15 -6
- package/packages/create-krnl/dist/index.cjs +102 -65
- package/packages/create-krnl/dist/index.js +102 -65
- package/packages/create-krnl/package.json +5 -3
- package/packages/eslint-plugin/dist/index.cjs +17 -55
- package/packages/eslint-plugin/dist/index.d.cts +8 -43
- package/packages/eslint-plugin/dist/index.d.ts +8 -43
- package/packages/eslint-plugin/dist/index.js +17 -55
- package/packages/eslint-plugin/package.json +5 -4
- package/packages/validate/dist/index.cjs +378 -85
- package/packages/validate/dist/index.d.cts +11 -1
- package/packages/validate/dist/index.d.ts +11 -1
- package/packages/validate/dist/index.js +375 -82
- package/packages/validate/package.json +14 -7
- package/packages/cli/dist/K_ENT_Entity-PQ5MURQY-ZG6FZXXX.js +0 -13
- package/packages/cli/dist/K_LIB_Krnl_Aiko_Client-OPZAVZCW-K4CRQYA5.js +0 -8
- package/packages/cli/dist/K_LIB_Krnl_Ep_Tools-ROOE2TQF-LNPXZYOE.js +0 -110
- package/packages/cli/dist/K_LIB_Krnl_Frontend_Builder-2ZDUH6PF-3ALZPNX4.js +0 -11
- package/packages/cli/dist/K_LIB_Krnl_Frontend_Generator-UYNRNX2X-PJTICYSW.js +0 -11
- package/packages/cli/dist/K_LIB_Krnl_Registry_Client-NXV6D2QW-M5TYU55O.js +0 -8
- package/packages/cli/dist/K_LIB_Krnl_SubApp_Engine-7HR5HWW2-H6STOXRK.js +0 -20
- package/packages/cli/dist/K_LIB_Krnl_SubApp_Registry-7XYGDKJK-XPJXSF7D.js +0 -19
- package/packages/cli/dist/K_LIB_Krnl_SubApp_Worker_Host-CQVTGGG7-CUYGTWOW.js +0 -7
- package/packages/cli/dist/K_LIB_Krnl_System_App_Adapter-M6FJKLGU-VR6T4N6J.js +0 -17
- package/packages/cli/dist/K_LIB_Krnl_System_Base_Functionality_APIs-ZZUZ6ZG5-RLJ4QWDP.js +0 -58
- package/packages/cli/dist/K_LIB_Krnl_System_Magnetic_Loader-XBQO2KXF-BOQGUASC.js +0 -22
- package/packages/cli/dist/K_LIB_Krnl_System_Unified_Http_Adapter-RCEGP3OG-XJI7KIT5.js +0 -8
- package/packages/cli/dist/K_LIB_Krnl_Telemetry-77FRZ3KV-K5VCWAFW.js +0 -9
- package/packages/cli/dist/chunk-3TRM6CYL.js +0 -152
- package/packages/cli/dist/chunk-5JDK35TG.js +0 -3872
- package/packages/cli/dist/chunk-63PTK33I.js +0 -997
- package/packages/cli/dist/chunk-6NIKAMUZ.js +0 -82
- package/packages/cli/dist/chunk-BOXWWXQG.js +0 -400
- package/packages/cli/dist/chunk-EEKLK7QJ.js +0 -565
- package/packages/cli/dist/chunk-EG7T2IAD.js +0 -107
- package/packages/cli/dist/chunk-ESTKOQ7T.js +0 -1146
- package/packages/cli/dist/chunk-F5EHXE2O.js +0 -1036
- package/packages/cli/dist/chunk-F65ANSQG.js +0 -269
- package/packages/cli/dist/chunk-FDNCADFE.js +0 -71
- package/packages/cli/dist/chunk-FZ6X7PHR.js +0 -1146
- package/packages/cli/dist/chunk-GDLRMDAN.js +0 -127
- package/packages/cli/dist/chunk-HWMTCMOS.js +0 -656
- package/packages/cli/dist/chunk-I74QIQOD.js +0 -5536
- package/packages/cli/dist/chunk-JTLSTO25.js +0 -71
- package/packages/cli/dist/chunk-JULBBJ27.js +0 -1033
- package/packages/cli/dist/chunk-OHVMVRBP.js +0 -117
- package/packages/cli/dist/chunk-P2PCLEKX.js +0 -666
- package/packages/cli/dist/chunk-PJQIUOX2.js +0 -7767
- package/packages/cli/dist/chunk-PPOF3DJW.js +0 -471
- package/packages/cli/dist/chunk-Q45257P6.js +0 -173
- package/packages/cli/dist/chunk-QNROWGBI.js +0 -1036
- package/packages/cli/dist/chunk-RJNYUXZD.js +0 -977
- package/packages/cli/dist/chunk-SRTOPJ5W.js +0 -104
- package/packages/cli/dist/chunk-TCEX3MPS.js +0 -702
- package/packages/cli/dist/chunk-TNOAJO2J.js +0 -675
- package/packages/cli/dist/chunk-UR2SUCSF.js +0 -701
- package/packages/cli/dist/chunk-X5LOIFM7.js +0 -68
- package/packages/cli/dist/chunk-XIRFOO3H.js +0 -589
- package/packages/cli/dist/chunk-ZAAV3JXQ.js +0 -809
- package/packages/core/dist/K_ENT_Entity-4MB7CU4A.js +0 -12
- package/packages/core/dist/K_ENT_Entity-DEJGIDGH.js +0 -12
- package/packages/core/dist/K_ENT_Entity-FXZO5UZ2.js +0 -12
- package/packages/core/dist/K_ENT_Entity-HDSCXC5F.js +0 -12
- package/packages/core/dist/K_ENT_Entity-JILABHLW.js +0 -12
- package/packages/core/dist/K_ENT_Entity-PQ5MURQY.js +0 -13
- package/packages/core/dist/K_ENT_Entity-ROAKDIHW.js +0 -12
- package/packages/core/dist/K_ENT_Entity-TBVFSKDV.js +0 -12
- package/packages/core/dist/K_ENT_Entity-ZFLKG3JO.js +0 -12
- package/packages/core/dist/K_LIB_Krnl_Aiko_Client-OPZAVZCW.js +0 -8
- package/packages/core/dist/K_LIB_Krnl_Ep_Tools-ROOE2TQF.js +0 -110
- package/packages/core/dist/K_LIB_Krnl_Frontend_Builder-2ZDUH6PF.js +0 -9
- package/packages/core/dist/K_LIB_Krnl_Frontend_Generator-UYNRNX2X.js +0 -9
- package/packages/core/dist/K_LIB_Krnl_Registry_Client-NXV6D2QW.js +0 -8
- package/packages/core/dist/K_LIB_Krnl_SubApp_Engine-7HR5HWW2.js +0 -20
- package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-4C6GBQAG.js +0 -18
- package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-6V23A6F6.js +0 -18
- package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-7XYGDKJK.js +0 -19
- package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-DYLLC5G2.js +0 -18
- package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-F4FOFAUP.js +0 -18
- package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-FBOXXMI3.js +0 -18
- package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-Q73BWLNP.js +0 -18
- package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-QM2C4MVA.js +0 -18
- package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-QUGI6SMP.js +0 -18
- package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-RGMTBHNP.js +0 -18
- package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-RWR7UNDW.js +0 -18
- package/packages/core/dist/K_LIB_Krnl_SubApp_Worker_Host-CQVTGGG7.js +0 -7
- package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-32MFJJ5Z.js +0 -17
- package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-4J4CEWRY.js +0 -17
- package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-A366YHF2.js +0 -17
- package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-LK4LYJHI.js +0 -17
- package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-M6FJKLGU.js +0 -17
- package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-NW3K63II.js +0 -17
- package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-QDHTI5ZV.js +0 -17
- package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-VHCT6VIW.js +0 -17
- package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-WBRKF24R.js +0 -17
- package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-XKYQYT6Q.js +0 -17
- package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-Z2PWZYDZ.js +0 -17
- package/packages/core/dist/K_LIB_Krnl_System_Base_Functionality_APIs-ZZUZ6ZG5.js +0 -58
- package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-2EZU7QF4.js +0 -21
- package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-6G3AQNAV.js +0 -21
- package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-B2NZMQUG.js +0 -21
- package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-C2RABMZP.js +0 -21
- package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-HHE3NZ4F.js +0 -21
- package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-LHFGR6WK.js +0 -21
- package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-M74BLM3N.js +0 -21
- package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-PT6ZA6EA.js +0 -21
- package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-SP52V3CX.js +0 -21
- package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-XBQO2KXF.js +0 -22
- package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-YNSEUHJJ.js +0 -21
- package/packages/core/dist/K_LIB_Krnl_System_Unified_Http_Adapter-M6SBHIVA.js +0 -8
- package/packages/core/dist/K_LIB_Krnl_System_Unified_Http_Adapter-RCEGP3OG.js +0 -8
- package/packages/core/dist/K_LIB_Krnl_Telemetry-77FRZ3KV.js +0 -9
- package/packages/core/dist/chunk-33L2J7V7.js +0 -82
- package/packages/core/dist/chunk-3ES4K2ZP.js +0 -817
- package/packages/core/dist/chunk-3SMHVVLL.js +0 -82
- package/packages/core/dist/chunk-3UEZ7JQA.js +0 -4877
- package/packages/core/dist/chunk-3XIE6KBD.js +0 -4877
- package/packages/core/dist/chunk-46ZLCML6.js +0 -107
- package/packages/core/dist/chunk-4UEL3DWJ.js +0 -107
- package/packages/core/dist/chunk-54GWZ5PC.js +0 -82
- package/packages/core/dist/chunk-6QCYRS5L.js +0 -127
- package/packages/core/dist/chunk-6ULMHNXR.js +0 -104
- package/packages/core/dist/chunk-7WUETJEV.js +0 -117
- package/packages/core/dist/chunk-A5KRAYDB.js +0 -107
- package/packages/core/dist/chunk-ANEAZ3PO.js +0 -107
- package/packages/core/dist/chunk-BE76RL6F.js +0 -82
- package/packages/core/dist/chunk-CIARVYDO.js +0 -107
- package/packages/core/dist/chunk-CR2ANE62.js +0 -107
- package/packages/core/dist/chunk-DI27MOR7.js +0 -173
- package/packages/core/dist/chunk-E6RLFHM5.js +0 -596
- package/packages/core/dist/chunk-EELAPMNV.js +0 -4877
- package/packages/core/dist/chunk-ENZ2RQVJ.js +0 -107
- package/packages/core/dist/chunk-F2U6CX4F.js +0 -4877
- package/packages/core/dist/chunk-FKG64OHL.js +0 -82
- package/packages/core/dist/chunk-FYAZJYM5.js +0 -772
- package/packages/core/dist/chunk-G4NP4RDQ.js +0 -82
- package/packages/core/dist/chunk-GADIRLRS.js +0 -473
- package/packages/core/dist/chunk-GR6CSLQJ.js +0 -772
- package/packages/core/dist/chunk-HDS7AT6O.js +0 -772
- package/packages/core/dist/chunk-HIMEZUU2.js +0 -71
- package/packages/core/dist/chunk-IB4UHG5I.js +0 -596
- package/packages/core/dist/chunk-J7ZYNVRS.js +0 -772
- package/packages/core/dist/chunk-JVLZU4K7.js +0 -604
- package/packages/core/dist/chunk-JYXXI7Q3.js +0 -82
- package/packages/core/dist/chunk-K54K2KZW.js +0 -192
- package/packages/core/dist/chunk-K6VHLBNH.js +0 -3316
- package/packages/core/dist/chunk-KCBP7ZYA.js +0 -596
- package/packages/core/dist/chunk-KFQGP6VL.js +0 -33
- package/packages/core/dist/chunk-KIUEUI7K.js +0 -82
- package/packages/core/dist/chunk-LB67NIZP.js +0 -949
- package/packages/core/dist/chunk-LDTYFUNW.js +0 -596
- package/packages/core/dist/chunk-MLVWARD4.js +0 -596
- package/packages/core/dist/chunk-MPYVXHKO.js +0 -2777
- package/packages/core/dist/chunk-MRQ3MITU.js +0 -772
- package/packages/core/dist/chunk-MVUN7I7O.js +0 -107
- package/packages/core/dist/chunk-NDDOIRW7.js +0 -596
- package/packages/core/dist/chunk-O3KDB5MH.js +0 -3330
- package/packages/core/dist/chunk-OEINUDSE.js +0 -400
- package/packages/core/dist/chunk-OPDENPT4.js +0 -4877
- package/packages/core/dist/chunk-P6XEM2LA.js +0 -429
- package/packages/core/dist/chunk-QKMRSQV4.js +0 -82
- package/packages/core/dist/chunk-R6CCWALL.js +0 -4877
- package/packages/core/dist/chunk-RAMMOO73.js +0 -774
- package/packages/core/dist/chunk-S3FTZFG6.js +0 -772
- package/packages/core/dist/chunk-T2ZVN434.js +0 -4877
- package/packages/core/dist/chunk-TFHLDMIA.js +0 -107
- package/packages/core/dist/chunk-TI3QUCIJ.js +0 -107
- package/packages/core/dist/chunk-TR2BAIGI.js +0 -107
- package/packages/core/dist/chunk-UGVVJKC6.js +0 -4877
- package/packages/core/dist/chunk-V2DX7YIU.js +0 -984
- package/packages/core/dist/chunk-W4IHYUYD.js +0 -772
- package/packages/core/dist/chunk-W6WBG5OY.js +0 -4877
- package/packages/core/dist/chunk-WN3M7O7H.js +0 -3312
- package/packages/core/dist/chunk-WR3NCU7U.js +0 -4877
- package/packages/core/dist/chunk-XC6B6L27.js +0 -82
- package/packages/core/dist/chunk-XNSE64ZY.js +0 -3316
- package/packages/core/dist/chunk-XROES2WN.js +0 -634
- package/packages/core/dist/chunk-XSCKNRCZ.js +0 -5020
- package/packages/core/dist/chunk-XVABPAUZ.js +0 -82
- package/packages/core/dist/chunk-YOBCB5C5.js +0 -596
- package/packages/core/dist/chunk-YUK6Q563.js +0 -666
- package/packages/core/dist/chunk-YXGL3ZEU.js +0 -1030
- package/packages/core/dist/chunk-ZINJ5OUX.js +0 -596
- package/packages/core/dist/chunk-ZPFD4BIQ.js +0 -604
- package/packages/core/dist/chunk-ZQZBFG6F.js +0 -4826
- package/packages/core/dist/chunk-ZZFRJGXM.js +0 -4877
- package/packages/create-krnl/dist/K_ENT_Entity-PQ5MURQY-R2OH4VXE.js +0 -14
- package/packages/create-krnl/dist/K_LIB_Krnl_Aiko_Client-OPZAVZCW-AZM5HF4M.js +0 -9
- package/packages/create-krnl/dist/K_LIB_Krnl_Ep_Tools-ROOE2TQF-X5N6SV2S.js +0 -111
- package/packages/create-krnl/dist/K_LIB_Krnl_Frontend_Builder-2ZDUH6PF-5PFHF4JU.js +0 -12
- package/packages/create-krnl/dist/K_LIB_Krnl_Frontend_Generator-UYNRNX2X-ED33OQPH.js +0 -12
- package/packages/create-krnl/dist/K_LIB_Krnl_Registry_Client-NXV6D2QW-YGQLVR4P.js +0 -9
- package/packages/create-krnl/dist/K_LIB_Krnl_SubApp_Engine-7HR5HWW2-VM7CTU2L.js +0 -21
- package/packages/create-krnl/dist/K_LIB_Krnl_SubApp_Registry-7XYGDKJK-Y2GQFJ7I.js +0 -21
- package/packages/create-krnl/dist/K_LIB_Krnl_SubApp_Worker_Host-CQVTGGG7-L3JQWO54.js +0 -8
- package/packages/create-krnl/dist/K_LIB_Krnl_System_App_Adapter-M6FJKLGU-KQARASPR.js +0 -19
- package/packages/create-krnl/dist/K_LIB_Krnl_System_Base_Functionality_APIs-ZZUZ6ZG5-OF3IGLVL.js +0 -59
- package/packages/create-krnl/dist/K_LIB_Krnl_System_Magnetic_Loader-XBQO2KXF-JOLHKOEX.js +0 -24
- package/packages/create-krnl/dist/K_LIB_Krnl_System_Unified_Http_Adapter-RCEGP3OG-L46LSHKQ.js +0 -10
- package/packages/create-krnl/dist/K_LIB_Krnl_Telemetry-77FRZ3KV-4JUPRR3X.js +0 -10
- package/packages/create-krnl/dist/chunk-3TRM6CYL.js +0 -152
- package/packages/create-krnl/dist/chunk-3URCB3QS.js +0 -127
- package/packages/create-krnl/dist/chunk-5JDK35TG.js +0 -3872
- package/packages/create-krnl/dist/chunk-A2XAXDOW.js +0 -812
- package/packages/create-krnl/dist/chunk-AN6TMQHG.js +0 -1033
- package/packages/create-krnl/dist/chunk-BOXWWXQG.js +0 -400
- package/packages/create-krnl/dist/chunk-BPTBREBG.js +0 -38
- package/packages/create-krnl/dist/chunk-FDNCADFE.js +0 -71
- package/packages/create-krnl/dist/chunk-JTLSTO25.js +0 -71
- package/packages/create-krnl/dist/chunk-M3NCUIPL.js +0 -107
- package/packages/create-krnl/dist/chunk-N2EONSOC.js +0 -7769
- package/packages/create-krnl/dist/chunk-OHVMVRBP.js +0 -117
- package/packages/create-krnl/dist/chunk-Q45257P6.js +0 -173
- package/packages/create-krnl/dist/chunk-QGM4M3NI.js +0 -37
- package/packages/create-krnl/dist/chunk-RJNYUXZD.js +0 -977
- package/packages/create-krnl/dist/chunk-SRTOPJ5W.js +0 -104
- package/packages/create-krnl/dist/chunk-UPBIGEC3.js +0 -666
- package/packages/create-krnl/dist/chunk-V2DB7UD7.js +0 -82
- package/packages/create-krnl/dist/chunk-WMHLIUI6.js +0 -3644
- package/packages/create-krnl/dist/chunk-XIRFOO3H.js +0 -589
- package/packages/create-krnl/dist/chunk-Y2CEEVZP.js +0 -5536
- package/packages/create-krnl/dist/main-DXUMPBND.js +0 -2217
- package/packages/create-krnl/dist/wrapper-SDF2VKX3.js +0 -31
|
@@ -1,3316 +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({
|
|
1371
|
-
url,
|
|
1372
|
-
method: "POST",
|
|
1373
|
-
headers: {
|
|
1374
|
-
Authorization: `Bearer ${this.config.authToken || this.config.apiKey || ""}`,
|
|
1375
|
-
"Content-Type": "application/json"
|
|
1376
|
-
},
|
|
1377
|
-
body: JSON.stringify({
|
|
1378
|
-
requests: [
|
|
1379
|
-
{
|
|
1380
|
-
type: "execute",
|
|
1381
|
-
stmt: { sql: queryStr, args: params.map((p) => ({ type: "text", value: String(p) })) }
|
|
1382
|
-
},
|
|
1383
|
-
{ type: "close" }
|
|
1384
|
-
]
|
|
1385
|
-
})
|
|
1386
|
-
});
|
|
1387
|
-
const rows = response.data?.results?.[0]?.response?.result?.rows || [];
|
|
1388
|
-
return rows.map((r) => r[1]?.value ? JSON.parse(r[1].value) : r[0]?.value || r);
|
|
1389
|
-
} catch {
|
|
1390
|
-
return [];
|
|
1391
|
-
}
|
|
1392
|
-
}
|
|
1393
|
-
async execute(commandStr, params = []) {
|
|
1394
|
-
const res = await this.query(commandStr, params);
|
|
1395
|
-
return { affectedRows: res.length || 1 };
|
|
1396
|
-
}
|
|
1397
|
-
async find(table, filter) {
|
|
1398
|
-
return this.query(`SELECT data FROM "${table}"`);
|
|
1399
|
-
}
|
|
1400
|
-
async findOne(table, filter) {
|
|
1401
|
-
const id = typeof filter === "string" ? filter : filter.id;
|
|
1402
|
-
if (!id) return null;
|
|
1403
|
-
const res = await this.query(`SELECT data FROM "${table}" WHERE id = ? LIMIT 1`, [id]);
|
|
1404
|
-
return res[0] || null;
|
|
1405
|
-
}
|
|
1406
|
-
async insert(table, data) {
|
|
1407
|
-
await this.ensureTable(table);
|
|
1408
|
-
const items = Array.isArray(data) ? data : [data];
|
|
1409
|
-
for (const item of items) {
|
|
1410
|
-
const id = item.id || `turso_${Date.now()}`;
|
|
1411
|
-
await this.execute(
|
|
1412
|
-
`INSERT OR REPLACE INTO "${table}" (id, data, updated_at) VALUES (?, ?, ?)`,
|
|
1413
|
-
[id, JSON.stringify(item), Date.now()]
|
|
1414
|
-
);
|
|
1415
|
-
}
|
|
1416
|
-
return data;
|
|
1417
|
-
}
|
|
1418
|
-
async update(table, filter, patch) {
|
|
1419
|
-
const existing = await this.findOne(table, filter);
|
|
1420
|
-
if (!existing) return { affectedRows: 0 };
|
|
1421
|
-
const updated = { ...existing, ...patch };
|
|
1422
|
-
await this.insert(table, updated);
|
|
1423
|
-
return { affectedRows: 1 };
|
|
1424
|
-
}
|
|
1425
|
-
async delete(table, filter) {
|
|
1426
|
-
const id = typeof filter === "string" ? filter : filter.id;
|
|
1427
|
-
if (!id) return { affectedRows: 0 };
|
|
1428
|
-
return this.execute(`DELETE FROM "${table}" WHERE id = ?`, [id]);
|
|
1429
|
-
}
|
|
1430
|
-
async count(table) {
|
|
1431
|
-
const all = await this.find(table);
|
|
1432
|
-
return all.length;
|
|
1433
|
-
}
|
|
1434
|
-
async clear(table) {
|
|
1435
|
-
await this.execute(`DELETE FROM "${table}"`);
|
|
1436
|
-
}
|
|
1437
|
-
};
|
|
1438
|
-
|
|
1439
|
-
// src/backend/providers/data/MongoAtlasDataProvider.ts
|
|
1440
|
-
var MongoAtlasDataProvider = class {
|
|
1441
|
-
engine = "mongodb";
|
|
1442
|
-
config;
|
|
1443
|
-
isConnected = false;
|
|
1444
|
-
constructor(config) {
|
|
1445
|
-
this.config = config;
|
|
1446
|
-
}
|
|
1447
|
-
async connect() {
|
|
1448
|
-
this.isConnected = true;
|
|
1449
|
-
}
|
|
1450
|
-
async disconnect() {
|
|
1451
|
-
this.isConnected = false;
|
|
1452
|
-
}
|
|
1453
|
-
async testConnection() {
|
|
1454
|
-
const startTime = Date.now();
|
|
1455
|
-
try {
|
|
1456
|
-
if (!this.config.endpoint) {
|
|
1457
|
-
return { ok: false, latencyMs: 0, details: "Missing Mongo Atlas endpoint" };
|
|
1458
|
-
}
|
|
1459
|
-
return { ok: true, latencyMs: Date.now() - startTime };
|
|
1460
|
-
} catch (err) {
|
|
1461
|
-
return { ok: false, latencyMs: Date.now() - startTime, details: err.message };
|
|
1462
|
-
}
|
|
1463
|
-
}
|
|
1464
|
-
async ensureTable(tableName) {
|
|
1465
|
-
}
|
|
1466
|
-
async callAtlasAction(action, payload) {
|
|
1467
|
-
if (!this.config.endpoint) return null;
|
|
1468
|
-
const url = `${this.config.endpoint.replace(/\/$/, "")}/action/${action}`;
|
|
1469
|
-
try {
|
|
1470
|
-
const response = await KrnlHTTP.request({
|
|
1471
|
-
url,
|
|
1472
|
-
method: "POST",
|
|
1473
|
-
headers: {
|
|
1474
|
-
"api-key": this.config.apiKey || "",
|
|
1475
|
-
"Content-Type": "application/json"
|
|
1476
|
-
},
|
|
1477
|
-
body: JSON.stringify({
|
|
1478
|
-
dataSource: "Cluster0",
|
|
1479
|
-
database: this.config.database || "krnl_vault",
|
|
1480
|
-
...payload
|
|
1481
|
-
})
|
|
1482
|
-
});
|
|
1483
|
-
return response.data;
|
|
1484
|
-
} catch {
|
|
1485
|
-
return null;
|
|
1486
|
-
}
|
|
1487
|
-
}
|
|
1488
|
-
async query(queryStr) {
|
|
1489
|
-
return [];
|
|
1490
|
-
}
|
|
1491
|
-
async execute() {
|
|
1492
|
-
return { affectedRows: 1 };
|
|
1493
|
-
}
|
|
1494
|
-
async find(table, filter = {}) {
|
|
1495
|
-
const res = await this.callAtlasAction("find", { collection: table, filter });
|
|
1496
|
-
return res?.documents || [];
|
|
1497
|
-
}
|
|
1498
|
-
async findOne(table, filter) {
|
|
1499
|
-
const queryFilter = typeof filter === "string" ? { id: filter } : filter;
|
|
1500
|
-
const res = await this.callAtlasAction("findOne", { collection: table, filter: queryFilter });
|
|
1501
|
-
return res?.document || null;
|
|
1502
|
-
}
|
|
1503
|
-
async insert(table, data) {
|
|
1504
|
-
if (Array.isArray(data)) {
|
|
1505
|
-
await this.callAtlasAction("insertMany", { collection: table, documents: data });
|
|
1506
|
-
} else {
|
|
1507
|
-
await this.callAtlasAction("insertOne", { collection: table, document: data });
|
|
1508
|
-
}
|
|
1509
|
-
return data;
|
|
1510
|
-
}
|
|
1511
|
-
async update(table, filter, patch) {
|
|
1512
|
-
const queryFilter = typeof filter === "string" ? { id: filter } : filter;
|
|
1513
|
-
const res = await this.callAtlasAction("updateOne", {
|
|
1514
|
-
collection: table,
|
|
1515
|
-
filter: queryFilter,
|
|
1516
|
-
update: { $set: patch }
|
|
1517
|
-
});
|
|
1518
|
-
return { affectedRows: res?.modifiedCount || 0 };
|
|
1519
|
-
}
|
|
1520
|
-
async delete(table, filter) {
|
|
1521
|
-
const queryFilter = typeof filter === "string" ? { id: filter } : filter;
|
|
1522
|
-
const res = await this.callAtlasAction("deleteOne", { collection: table, filter: queryFilter });
|
|
1523
|
-
return { affectedRows: res?.deletedCount || 0 };
|
|
1524
|
-
}
|
|
1525
|
-
async count(table, filter = {}) {
|
|
1526
|
-
const res = await this.find(table, filter);
|
|
1527
|
-
return res.length;
|
|
1528
|
-
}
|
|
1529
|
-
async clear(table) {
|
|
1530
|
-
await this.callAtlasAction("deleteMany", { collection: table, filter: {} });
|
|
1531
|
-
}
|
|
1532
|
-
};
|
|
1533
|
-
|
|
1534
|
-
// src/backend/providers/storage/FilesystemStorageProvider.ts
|
|
1535
|
-
import path2 from "path";
|
|
1536
|
-
import fsp from "fs/promises";
|
|
1537
|
-
var FilesystemStorageProvider = class {
|
|
1538
|
-
driver = "filesystem";
|
|
1539
|
-
basePath;
|
|
1540
|
-
constructor(config = "./storage") {
|
|
1541
|
-
if (typeof config === "string") {
|
|
1542
|
-
this.basePath = path2.resolve(config);
|
|
1543
|
-
} else {
|
|
1544
|
-
this.basePath = path2.resolve(config.basePath || "./storage");
|
|
1545
|
-
}
|
|
1546
|
-
}
|
|
1547
|
-
async uploadFile(targetPath, content, _mimeType) {
|
|
1548
|
-
const fullPath = path2.join(this.basePath, targetPath);
|
|
1549
|
-
await KrnlFS.ensureDir(path2.dirname(fullPath));
|
|
1550
|
-
let buf;
|
|
1551
|
-
if (Buffer.isBuffer(content)) {
|
|
1552
|
-
buf = content;
|
|
1553
|
-
} else if (typeof content === "string") {
|
|
1554
|
-
buf = Buffer.from(content, "utf8");
|
|
1555
|
-
} else if (content instanceof Uint8Array) {
|
|
1556
|
-
buf = Buffer.from(content);
|
|
1557
|
-
} else {
|
|
1558
|
-
buf = Buffer.from(await content.arrayBuffer());
|
|
1559
|
-
}
|
|
1560
|
-
await KrnlFS.writeFileAtomic(fullPath, buf);
|
|
1561
|
-
const size = buf.byteLength;
|
|
1562
|
-
return {
|
|
1563
|
-
url: `file://${fullPath}`,
|
|
1564
|
-
size
|
|
1565
|
-
};
|
|
1566
|
-
}
|
|
1567
|
-
async downloadFile(targetPath) {
|
|
1568
|
-
const fullPath = path2.join(this.basePath, targetPath);
|
|
1569
|
-
return await fsp.readFile(fullPath);
|
|
1570
|
-
}
|
|
1571
|
-
async deleteFile(targetPath) {
|
|
1572
|
-
const fullPath = path2.join(this.basePath, targetPath);
|
|
1573
|
-
try {
|
|
1574
|
-
await KrnlFS.deleteFile(fullPath);
|
|
1575
|
-
return true;
|
|
1576
|
-
} catch {
|
|
1577
|
-
return false;
|
|
1578
|
-
}
|
|
1579
|
-
}
|
|
1580
|
-
getPublicUrl(targetPath) {
|
|
1581
|
-
return `file://${path2.join(this.basePath, targetPath)}`;
|
|
1582
|
-
}
|
|
1583
|
-
async listFiles(prefix = "") {
|
|
1584
|
-
const startDir = path2.join(this.basePath, prefix);
|
|
1585
|
-
if (!await KrnlFS.dirExists(startDir)) {
|
|
1586
|
-
return [];
|
|
1587
|
-
}
|
|
1588
|
-
const results = [];
|
|
1589
|
-
for await (const file of KrnlFS.walkDir(startDir)) {
|
|
1590
|
-
const stat = await KrnlFS.getFileStat(file);
|
|
1591
|
-
results.push({
|
|
1592
|
-
path: path2.relative(this.basePath, file),
|
|
1593
|
-
size: stat.size,
|
|
1594
|
-
updatedAt: stat.modifiedAt.getTime()
|
|
1595
|
-
});
|
|
1596
|
-
}
|
|
1597
|
-
return results;
|
|
1598
|
-
}
|
|
1599
|
-
};
|
|
1600
|
-
|
|
1601
|
-
// src/backend/providers/storage/SupabaseStorageProvider.ts
|
|
1602
|
-
var SupabaseStorageProvider = class {
|
|
1603
|
-
driver = "supabase_storage";
|
|
1604
|
-
endpoint;
|
|
1605
|
-
apiKey;
|
|
1606
|
-
bucket;
|
|
1607
|
-
constructor(config) {
|
|
1608
|
-
this.endpoint = (config.endpoint || "").replace(/\/+$/, "");
|
|
1609
|
-
this.apiKey = config.apiKey || "";
|
|
1610
|
-
this.bucket = config.bucket || "default";
|
|
1611
|
-
}
|
|
1612
|
-
getHeaders(mimeType) {
|
|
1613
|
-
const headers = {
|
|
1614
|
-
"apikey": this.apiKey,
|
|
1615
|
-
"Authorization": `Bearer ${this.apiKey}`
|
|
1616
|
-
};
|
|
1617
|
-
if (mimeType) {
|
|
1618
|
-
headers["Content-Type"] = mimeType;
|
|
1619
|
-
}
|
|
1620
|
-
return headers;
|
|
1621
|
-
}
|
|
1622
|
-
async uploadFile(targetPath, content, mimeType = "application/octet-stream") {
|
|
1623
|
-
const cleanPath = targetPath.replace(/^\/+/, "");
|
|
1624
|
-
const url = `${this.endpoint}/storage/v1/object/${this.bucket}/${cleanPath}`;
|
|
1625
|
-
let body = content;
|
|
1626
|
-
let size = 0;
|
|
1627
|
-
if (Buffer.isBuffer(content)) {
|
|
1628
|
-
size = content.byteLength;
|
|
1629
|
-
} else if (typeof content === "string") {
|
|
1630
|
-
size = Buffer.byteLength(content);
|
|
1631
|
-
}
|
|
1632
|
-
await KrnlHTTP.request(url, {
|
|
1633
|
-
method: "POST",
|
|
1634
|
-
headers: this.getHeaders(mimeType),
|
|
1635
|
-
body
|
|
1636
|
-
});
|
|
1637
|
-
return {
|
|
1638
|
-
url: this.getPublicUrl(cleanPath),
|
|
1639
|
-
size
|
|
1640
|
-
};
|
|
1641
|
-
}
|
|
1642
|
-
async downloadFile(targetPath) {
|
|
1643
|
-
const cleanPath = targetPath.replace(/^\/+/, "");
|
|
1644
|
-
const url = `${this.endpoint}/storage/v1/object/authenticated/${this.bucket}/${cleanPath}`;
|
|
1645
|
-
const res = await fetch(url, { headers: this.getHeaders() });
|
|
1646
|
-
const arrayBuf = await res.arrayBuffer();
|
|
1647
|
-
return Buffer.from(arrayBuf);
|
|
1648
|
-
}
|
|
1649
|
-
async deleteFile(targetPath) {
|
|
1650
|
-
const cleanPath = targetPath.replace(/^\/+/, "");
|
|
1651
|
-
const url = `${this.endpoint}/storage/v1/object/${this.bucket}/${cleanPath}`;
|
|
1652
|
-
const res = await KrnlHTTP.del(url, { headers: this.getHeaders() });
|
|
1653
|
-
return res.status === "success";
|
|
1654
|
-
}
|
|
1655
|
-
getPublicUrl(targetPath) {
|
|
1656
|
-
const cleanPath = targetPath.replace(/^\/+/, "");
|
|
1657
|
-
return `${this.endpoint}/storage/v1/object/public/${this.bucket}/${cleanPath}`;
|
|
1658
|
-
}
|
|
1659
|
-
async listFiles(prefix = "") {
|
|
1660
|
-
const url = `${this.endpoint}/storage/v1/object/list/${this.bucket}`;
|
|
1661
|
-
const res = await KrnlHTTP.post(url, { prefix, limit: 100 }, { headers: this.getHeaders("application/json") });
|
|
1662
|
-
if (res.status === "success" && Array.isArray(res.data)) {
|
|
1663
|
-
return res.data.map((item) => ({
|
|
1664
|
-
path: item.name,
|
|
1665
|
-
size: item.metadata?.size || 0,
|
|
1666
|
-
updatedAt: item.updated_at ? new Date(item.updated_at).getTime() : Date.now()
|
|
1667
|
-
}));
|
|
1668
|
-
}
|
|
1669
|
-
return [];
|
|
1670
|
-
}
|
|
1671
|
-
};
|
|
1672
|
-
|
|
1673
|
-
// src/backend/providers/storage/S3StorageProvider.ts
|
|
1674
|
-
var S3StorageProvider = class {
|
|
1675
|
-
driver = "s3";
|
|
1676
|
-
endpoint;
|
|
1677
|
-
bucket;
|
|
1678
|
-
region;
|
|
1679
|
-
accessKey;
|
|
1680
|
-
secretKey;
|
|
1681
|
-
constructor(config) {
|
|
1682
|
-
this.bucket = config.bucket || "default-bucket";
|
|
1683
|
-
this.region = config.region || "us-east-1";
|
|
1684
|
-
this.accessKey = config.accessKey;
|
|
1685
|
-
this.secretKey = config.secretKey;
|
|
1686
|
-
this.endpoint = config.endpoint ? config.endpoint.replace(/\/+$/, "") : `https://${this.bucket}.s3.${this.region}.amazonaws.com`;
|
|
1687
|
-
}
|
|
1688
|
-
getBaseUrl() {
|
|
1689
|
-
return this.endpoint.includes(this.bucket) ? this.endpoint : `${this.endpoint}/${this.bucket}`;
|
|
1690
|
-
}
|
|
1691
|
-
async uploadFile(targetPath, content, mimeType = "application/octet-stream") {
|
|
1692
|
-
const cleanPath = targetPath.replace(/^\/+/, "");
|
|
1693
|
-
const url = `${this.getBaseUrl()}/${cleanPath}`;
|
|
1694
|
-
let body = content;
|
|
1695
|
-
let size = 0;
|
|
1696
|
-
if (Buffer.isBuffer(content)) {
|
|
1697
|
-
size = content.byteLength;
|
|
1698
|
-
} else if (typeof content === "string") {
|
|
1699
|
-
size = Buffer.byteLength(content);
|
|
1700
|
-
}
|
|
1701
|
-
await KrnlHTTP.request(url, {
|
|
1702
|
-
method: "PUT",
|
|
1703
|
-
headers: {
|
|
1704
|
-
"Content-Type": mimeType
|
|
1705
|
-
},
|
|
1706
|
-
body
|
|
1707
|
-
});
|
|
1708
|
-
return {
|
|
1709
|
-
url: this.getPublicUrl(cleanPath),
|
|
1710
|
-
size
|
|
1711
|
-
};
|
|
1712
|
-
}
|
|
1713
|
-
async downloadFile(targetPath) {
|
|
1714
|
-
const cleanPath = targetPath.replace(/^\/+/, "");
|
|
1715
|
-
const url = `${this.getBaseUrl()}/${cleanPath}`;
|
|
1716
|
-
const res = await fetch(url);
|
|
1717
|
-
const arrayBuf = await res.arrayBuffer();
|
|
1718
|
-
return Buffer.from(arrayBuf);
|
|
1719
|
-
}
|
|
1720
|
-
async deleteFile(targetPath) {
|
|
1721
|
-
const cleanPath = targetPath.replace(/^\/+/, "");
|
|
1722
|
-
const url = `${this.getBaseUrl()}/${cleanPath}`;
|
|
1723
|
-
const res = await KrnlHTTP.del(url);
|
|
1724
|
-
return res.status === "success";
|
|
1725
|
-
}
|
|
1726
|
-
getPublicUrl(targetPath) {
|
|
1727
|
-
const cleanPath = targetPath.replace(/^\/+/, "");
|
|
1728
|
-
return `${this.getBaseUrl()}/${cleanPath}`;
|
|
1729
|
-
}
|
|
1730
|
-
async listFiles(_prefix = "") {
|
|
1731
|
-
return [];
|
|
1732
|
-
}
|
|
1733
|
-
};
|
|
1734
|
-
|
|
1735
|
-
// src/backend/providers/storage/PocketBaseStorageProvider.ts
|
|
1736
|
-
var PocketBaseStorageProvider = class {
|
|
1737
|
-
driver = "pocketbase_storage";
|
|
1738
|
-
endpoint;
|
|
1739
|
-
bucket;
|
|
1740
|
-
constructor(config) {
|
|
1741
|
-
this.endpoint = (config.endpoint || "http://127.0.0.1:8090").replace(/\/+$/, "");
|
|
1742
|
-
this.bucket = config.bucket || "files";
|
|
1743
|
-
}
|
|
1744
|
-
async uploadFile(targetPath, content, mimeType = "application/octet-stream") {
|
|
1745
|
-
const cleanPath = targetPath.replace(/^\/+/, "");
|
|
1746
|
-
const url = `${this.endpoint}/api/collections/${this.bucket}/records`;
|
|
1747
|
-
let size = 0;
|
|
1748
|
-
if (Buffer.isBuffer(content)) size = content.byteLength;
|
|
1749
|
-
else if (typeof content === "string") size = Buffer.byteLength(content);
|
|
1750
|
-
const formData = new FormData();
|
|
1751
|
-
const blob = new Blob([content], { type: mimeType });
|
|
1752
|
-
formData.append("file", blob, cleanPath);
|
|
1753
|
-
await fetch(url, {
|
|
1754
|
-
method: "POST",
|
|
1755
|
-
body: formData
|
|
1756
|
-
});
|
|
1757
|
-
return {
|
|
1758
|
-
url: this.getPublicUrl(cleanPath),
|
|
1759
|
-
size
|
|
1760
|
-
};
|
|
1761
|
-
}
|
|
1762
|
-
async downloadFile(targetPath) {
|
|
1763
|
-
const cleanPath = targetPath.replace(/^\/+/, "");
|
|
1764
|
-
const url = `${this.endpoint}/api/files/${this.bucket}/${cleanPath}`;
|
|
1765
|
-
const res = await fetch(url);
|
|
1766
|
-
const arrayBuf = await res.arrayBuffer();
|
|
1767
|
-
return Buffer.from(arrayBuf);
|
|
1768
|
-
}
|
|
1769
|
-
async deleteFile(targetPath) {
|
|
1770
|
-
const cleanPath = targetPath.replace(/^\/+/, "");
|
|
1771
|
-
const url = `${this.endpoint}/api/collections/${this.bucket}/records/${cleanPath}`;
|
|
1772
|
-
const res = await KrnlHTTP.del(url);
|
|
1773
|
-
return res.status === "success";
|
|
1774
|
-
}
|
|
1775
|
-
getPublicUrl(targetPath) {
|
|
1776
|
-
const cleanPath = targetPath.replace(/^\/+/, "");
|
|
1777
|
-
return `${this.endpoint}/api/files/${this.bucket}/${cleanPath}`;
|
|
1778
|
-
}
|
|
1779
|
-
async listFiles(_prefix = "") {
|
|
1780
|
-
return [];
|
|
1781
|
-
}
|
|
1782
|
-
};
|
|
1783
|
-
|
|
1784
|
-
// src/backend/providers/storage/FirebaseStorageProvider.ts
|
|
1785
|
-
var FirebaseStorageProvider = class {
|
|
1786
|
-
driver = "firebase_storage";
|
|
1787
|
-
bucket;
|
|
1788
|
-
apiKey;
|
|
1789
|
-
constructor(config) {
|
|
1790
|
-
this.bucket = config.bucket || `${config.projectId || "default"}.appspot.com`;
|
|
1791
|
-
this.apiKey = config.apiKey;
|
|
1792
|
-
}
|
|
1793
|
-
get baseUrl() {
|
|
1794
|
-
return `https://firebasestorage.googleapis.com/v0/b/${this.bucket}/o`;
|
|
1795
|
-
}
|
|
1796
|
-
async uploadFile(targetPath, content, mimeType = "application/octet-stream") {
|
|
1797
|
-
const cleanPath = encodeURIComponent(targetPath.replace(/^\/+/, ""));
|
|
1798
|
-
const url = `${this.baseUrl}?name=${cleanPath}${this.apiKey ? `&key=${this.apiKey}` : ""}`;
|
|
1799
|
-
let body = content;
|
|
1800
|
-
let size = 0;
|
|
1801
|
-
if (Buffer.isBuffer(content)) size = content.byteLength;
|
|
1802
|
-
else if (typeof content === "string") size = Buffer.byteLength(content);
|
|
1803
|
-
await fetch(url, {
|
|
1804
|
-
method: "POST",
|
|
1805
|
-
headers: {
|
|
1806
|
-
"Content-Type": mimeType
|
|
1807
|
-
},
|
|
1808
|
-
body
|
|
1809
|
-
});
|
|
1810
|
-
return {
|
|
1811
|
-
url: this.getPublicUrl(targetPath),
|
|
1812
|
-
size
|
|
1813
|
-
};
|
|
1814
|
-
}
|
|
1815
|
-
async downloadFile(targetPath) {
|
|
1816
|
-
const cleanPath = encodeURIComponent(targetPath.replace(/^\/+/, ""));
|
|
1817
|
-
const url = `${this.baseUrl}/${cleanPath}?alt=media${this.apiKey ? `&key=${this.apiKey}` : ""}`;
|
|
1818
|
-
const res = await fetch(url);
|
|
1819
|
-
const arrayBuf = await res.arrayBuffer();
|
|
1820
|
-
return Buffer.from(arrayBuf);
|
|
1821
|
-
}
|
|
1822
|
-
async deleteFile(targetPath) {
|
|
1823
|
-
const cleanPath = encodeURIComponent(targetPath.replace(/^\/+/, ""));
|
|
1824
|
-
const url = `${this.baseUrl}/${cleanPath}${this.apiKey ? `&key=${this.apiKey}` : ""}`;
|
|
1825
|
-
const res = await KrnlHTTP.del(url);
|
|
1826
|
-
return res.status === "success";
|
|
1827
|
-
}
|
|
1828
|
-
getPublicUrl(targetPath) {
|
|
1829
|
-
const cleanPath = encodeURIComponent(targetPath.replace(/^\/+/, ""));
|
|
1830
|
-
return `${this.baseUrl}/${cleanPath}?alt=media`;
|
|
1831
|
-
}
|
|
1832
|
-
async listFiles(_prefix = "") {
|
|
1833
|
-
return [];
|
|
1834
|
-
}
|
|
1835
|
-
};
|
|
1836
|
-
|
|
1837
|
-
// src/backend/providers/storage/VercelBlobStorageProvider.ts
|
|
1838
|
-
var VercelBlobStorageProvider = class {
|
|
1839
|
-
driver = "vercel_blob";
|
|
1840
|
-
config;
|
|
1841
|
-
constructor(config) {
|
|
1842
|
-
this.config = config;
|
|
1843
|
-
}
|
|
1844
|
-
async uploadFile(path6, content, mimeType = "application/octet-stream") {
|
|
1845
|
-
const token = this.config.token || this.config.apiKey;
|
|
1846
|
-
const cleanPath = path6.replace(/^\//, "");
|
|
1847
|
-
const size = typeof content === "string" ? Buffer.byteLength(content) : content.byteLength;
|
|
1848
|
-
if (!token) {
|
|
1849
|
-
return { url: `https://mock.blob.vercel-storage.com/${cleanPath}`, size };
|
|
1850
|
-
}
|
|
1851
|
-
try {
|
|
1852
|
-
const response = await KrnlHTTP.request({
|
|
1853
|
-
url: `https://blob.vercel-storage.com/${cleanPath}`,
|
|
1854
|
-
method: "PUT",
|
|
1855
|
-
headers: {
|
|
1856
|
-
Authorization: `Bearer ${token}`,
|
|
1857
|
-
"x-content-type": mimeType
|
|
1858
|
-
},
|
|
1859
|
-
body: content
|
|
1860
|
-
});
|
|
1861
|
-
return {
|
|
1862
|
-
url: response.data?.url || `https://blob.vercel-storage.com/${cleanPath}`,
|
|
1863
|
-
size
|
|
1864
|
-
};
|
|
1865
|
-
} catch {
|
|
1866
|
-
return { url: `https://blob.vercel-storage.com/${cleanPath}`, size };
|
|
1867
|
-
}
|
|
1868
|
-
}
|
|
1869
|
-
async downloadFile(path6) {
|
|
1870
|
-
const publicUrl = this.getPublicUrl(path6);
|
|
1871
|
-
const response = await fetch(publicUrl);
|
|
1872
|
-
const arrayBuffer = await response.arrayBuffer();
|
|
1873
|
-
return Buffer.from(arrayBuffer);
|
|
1874
|
-
}
|
|
1875
|
-
async deleteFile(path6) {
|
|
1876
|
-
const token = this.config.token || this.config.apiKey;
|
|
1877
|
-
const cleanPath = path6.replace(/^\//, "");
|
|
1878
|
-
if (!token) return true;
|
|
1879
|
-
try {
|
|
1880
|
-
await KrnlHTTP.request({
|
|
1881
|
-
url: `https://blob.vercel-storage.com/delete`,
|
|
1882
|
-
method: "POST",
|
|
1883
|
-
headers: {
|
|
1884
|
-
Authorization: `Bearer ${token}`,
|
|
1885
|
-
"Content-Type": "application/json"
|
|
1886
|
-
},
|
|
1887
|
-
body: JSON.stringify({ urls: [this.getPublicUrl(cleanPath)] })
|
|
1888
|
-
});
|
|
1889
|
-
return true;
|
|
1890
|
-
} catch {
|
|
1891
|
-
return false;
|
|
1892
|
-
}
|
|
1893
|
-
}
|
|
1894
|
-
getPublicUrl(path6) {
|
|
1895
|
-
const cleanPath = path6.replace(/^\//, "");
|
|
1896
|
-
return `https://blob.vercel-storage.com/${cleanPath}`;
|
|
1897
|
-
}
|
|
1898
|
-
async listFiles() {
|
|
1899
|
-
return [];
|
|
1900
|
-
}
|
|
1901
|
-
};
|
|
1902
|
-
|
|
1903
|
-
// src/backend/WorkspaceBackendFactory.ts
|
|
1904
|
-
var WorkspaceBackendFactory = class _WorkspaceBackendFactory {
|
|
1905
|
-
static create(config) {
|
|
1906
|
-
const dataProvider = _WorkspaceBackendFactory.resolveDataProvider(config);
|
|
1907
|
-
const storageProvider = _WorkspaceBackendFactory.resolveStorageProvider(config);
|
|
1908
|
-
return {
|
|
1909
|
-
id: config.id,
|
|
1910
|
-
mode: config.mode,
|
|
1911
|
-
data: dataProvider,
|
|
1912
|
-
storage: storageProvider,
|
|
1913
|
-
async initialize() {
|
|
1914
|
-
await dataProvider.connect();
|
|
1915
|
-
},
|
|
1916
|
-
async dispose() {
|
|
1917
|
-
await dataProvider.disconnect();
|
|
1918
|
-
}
|
|
1919
|
-
};
|
|
1920
|
-
}
|
|
1921
|
-
static createLocal(filePath = "./workspace.sqlite", storagePath = "./storage") {
|
|
1922
|
-
return _WorkspaceBackendFactory.create({
|
|
1923
|
-
id: "local-workspace",
|
|
1924
|
-
provider: "sqlite",
|
|
1925
|
-
mode: "local",
|
|
1926
|
-
database: { type: "sqlite", filePath, enableWAL: true },
|
|
1927
|
-
storage: { type: "filesystem", basePath: storagePath }
|
|
1928
|
-
});
|
|
1929
|
-
}
|
|
1930
|
-
/**
|
|
1931
|
-
* Resolves the credential cascade hierarchy:
|
|
1932
|
-
* 1. Feature-level explicit credentials
|
|
1933
|
-
* 2. Host entity configured credentials
|
|
1934
|
-
* 3. Fallback to local SQLite
|
|
1935
|
-
*/
|
|
1936
|
-
static resolveCredentialsCascade(functionalityConfig, entitySettings, defaultFallback = { type: "sqlite", filePath: "./workspace.sqlite", enableWAL: true }) {
|
|
1937
|
-
if (typeof functionalityConfig === "object" && functionalityConfig?.credentials) {
|
|
1938
|
-
return functionalityConfig.credentials;
|
|
1939
|
-
}
|
|
1940
|
-
const requestedService = typeof functionalityConfig === "string" ? functionalityConfig : typeof functionalityConfig === "object" ? functionalityConfig.service : entitySettings?.defaultService || "sqlite";
|
|
1941
|
-
if (entitySettings && entitySettings[requestedService]) {
|
|
1942
|
-
return entitySettings[requestedService];
|
|
1943
|
-
}
|
|
1944
|
-
return defaultFallback;
|
|
1945
|
-
}
|
|
1946
|
-
static resolveDataProvider(config) {
|
|
1947
|
-
const dbConfig = config.database || { type: config.provider };
|
|
1948
|
-
switch (dbConfig.type) {
|
|
1949
|
-
case "sqlite":
|
|
1950
|
-
return new SQLiteDataProvider(dbConfig);
|
|
1951
|
-
case "postgres":
|
|
1952
|
-
return new PostgresDataProvider(dbConfig);
|
|
1953
|
-
case "supabase":
|
|
1954
|
-
return new SupabaseDataProvider({
|
|
1955
|
-
...dbConfig,
|
|
1956
|
-
endpoint: dbConfig.endpoint || config.endpoint,
|
|
1957
|
-
apiKey: dbConfig.apiKey || config.auth?.apiKey
|
|
1958
|
-
});
|
|
1959
|
-
case "pocketbase":
|
|
1960
|
-
return new PocketBaseDataProvider({
|
|
1961
|
-
...dbConfig,
|
|
1962
|
-
endpoint: dbConfig.endpoint || config.endpoint,
|
|
1963
|
-
apiKey: dbConfig.apiKey || config.auth?.token
|
|
1964
|
-
});
|
|
1965
|
-
case "firebase":
|
|
1966
|
-
return new FirebaseDataProvider({
|
|
1967
|
-
...dbConfig,
|
|
1968
|
-
apiKey: dbConfig.apiKey || config.auth?.apiKey
|
|
1969
|
-
});
|
|
1970
|
-
case "turso":
|
|
1971
|
-
return new TursoLibSQLDataProvider({
|
|
1972
|
-
...dbConfig,
|
|
1973
|
-
endpoint: dbConfig.endpoint || config.endpoint,
|
|
1974
|
-
authToken: dbConfig.authToken || config.auth?.token
|
|
1975
|
-
});
|
|
1976
|
-
case "mongodb":
|
|
1977
|
-
return new MongoAtlasDataProvider({
|
|
1978
|
-
...dbConfig,
|
|
1979
|
-
endpoint: dbConfig.endpoint || config.endpoint,
|
|
1980
|
-
apiKey: dbConfig.apiKey || config.auth?.apiKey
|
|
1981
|
-
});
|
|
1982
|
-
default:
|
|
1983
|
-
return new SQLiteDataProvider({ type: "sqlite", filePath: "./workspace.sqlite", enableWAL: true });
|
|
1984
|
-
}
|
|
1985
|
-
}
|
|
1986
|
-
static resolveStorageProvider(config) {
|
|
1987
|
-
const storageConfig = config.storage || { type: "filesystem" };
|
|
1988
|
-
switch (storageConfig.type) {
|
|
1989
|
-
case "filesystem":
|
|
1990
|
-
return new FilesystemStorageProvider(storageConfig);
|
|
1991
|
-
case "supabase_storage":
|
|
1992
|
-
return new SupabaseStorageProvider({
|
|
1993
|
-
...storageConfig,
|
|
1994
|
-
endpoint: storageConfig.endpoint || config.endpoint,
|
|
1995
|
-
apiKey: storageConfig.apiKey || config.auth?.apiKey
|
|
1996
|
-
});
|
|
1997
|
-
case "s3":
|
|
1998
|
-
return new S3StorageProvider({
|
|
1999
|
-
...storageConfig,
|
|
2000
|
-
endpoint: storageConfig.endpoint || config.endpoint
|
|
2001
|
-
});
|
|
2002
|
-
case "pocketbase_storage":
|
|
2003
|
-
return new PocketBaseStorageProvider({
|
|
2004
|
-
...storageConfig,
|
|
2005
|
-
endpoint: storageConfig.endpoint || config.endpoint
|
|
2006
|
-
});
|
|
2007
|
-
case "firebase_storage":
|
|
2008
|
-
return new FirebaseStorageProvider({
|
|
2009
|
-
...storageConfig,
|
|
2010
|
-
apiKey: storageConfig.apiKey || config.auth?.apiKey
|
|
2011
|
-
});
|
|
2012
|
-
case "vercel_blob":
|
|
2013
|
-
return new VercelBlobStorageProvider(storageConfig);
|
|
2014
|
-
default:
|
|
2015
|
-
return new FilesystemStorageProvider("./storage");
|
|
2016
|
-
}
|
|
2017
|
-
}
|
|
2018
|
-
};
|
|
2019
|
-
|
|
2020
|
-
// src/backend/control_plane/LocalSQLiteAccountProvider.ts
|
|
2021
|
-
import path3 from "path";
|
|
2022
|
-
var LocalSQLiteAccountProvider = class {
|
|
2023
|
-
filePath;
|
|
2024
|
-
isLoaded = false;
|
|
2025
|
-
db = { users: {}, workspaces: {} };
|
|
2026
|
-
constructor(filePath = "./accounts.sqlite") {
|
|
2027
|
-
this.filePath = path3.resolve(filePath);
|
|
2028
|
-
}
|
|
2029
|
-
async load() {
|
|
2030
|
-
if (this.isLoaded) return;
|
|
2031
|
-
await KrnlFS.ensureDir(path3.dirname(this.filePath));
|
|
2032
|
-
if (await KrnlFS.fileExists(this.filePath)) {
|
|
2033
|
-
try {
|
|
2034
|
-
const raw = await KrnlFS.readJSON(this.filePath);
|
|
2035
|
-
if (raw && typeof raw === "object") {
|
|
2036
|
-
this.db = {
|
|
2037
|
-
users: raw.users || {},
|
|
2038
|
-
workspaces: raw.workspaces || {}
|
|
2039
|
-
};
|
|
2040
|
-
}
|
|
2041
|
-
} catch {
|
|
2042
|
-
this.db = { users: {}, workspaces: {} };
|
|
2043
|
-
}
|
|
2044
|
-
}
|
|
2045
|
-
this.isLoaded = true;
|
|
2046
|
-
}
|
|
2047
|
-
async persist() {
|
|
2048
|
-
const release = await FileLock.acquire(this.filePath);
|
|
2049
|
-
try {
|
|
2050
|
-
await KrnlFS.writeJSON(this.filePath, this.db, 2);
|
|
2051
|
-
} finally {
|
|
2052
|
-
release();
|
|
2053
|
-
}
|
|
2054
|
-
}
|
|
2055
|
-
async getUser(userId) {
|
|
2056
|
-
await this.load();
|
|
2057
|
-
const user = this.db.users[userId];
|
|
2058
|
-
if (!user) return null;
|
|
2059
|
-
const { passwordHash, ...profile } = user;
|
|
2060
|
-
return profile;
|
|
2061
|
-
}
|
|
2062
|
-
async registerUser(credentials) {
|
|
2063
|
-
await this.load();
|
|
2064
|
-
const userId = credentials.username || credentials.email || `usr_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`;
|
|
2065
|
-
const profile = {
|
|
2066
|
-
id: userId,
|
|
2067
|
-
email: credentials.email,
|
|
2068
|
-
username: credentials.username || credentials.email,
|
|
2069
|
-
role: credentials.role || "USER",
|
|
2070
|
-
passwordHash: credentials.password,
|
|
2071
|
-
metadata: credentials.metadata || {},
|
|
2072
|
-
createdAt: Date.now()
|
|
2073
|
-
};
|
|
2074
|
-
this.db.users[userId] = profile;
|
|
2075
|
-
if (!this.db.workspaces[userId]) {
|
|
2076
|
-
this.db.workspaces[userId] = [];
|
|
2077
|
-
}
|
|
2078
|
-
await this.persist();
|
|
2079
|
-
const { passwordHash, ...safeProfile } = profile;
|
|
2080
|
-
return safeProfile;
|
|
2081
|
-
}
|
|
2082
|
-
async authenticate(credentials) {
|
|
2083
|
-
await this.load();
|
|
2084
|
-
const userId = credentials.username || credentials.email;
|
|
2085
|
-
let user = userId ? this.db.users[userId] : null;
|
|
2086
|
-
if (!user && (credentials.email || credentials.username)) {
|
|
2087
|
-
const isFirst = Object.keys(this.db.users).length === 0;
|
|
2088
|
-
user = await this.registerUser({
|
|
2089
|
-
...credentials,
|
|
2090
|
-
role: isFirst ? "ADMIN" : "USER"
|
|
2091
|
-
});
|
|
2092
|
-
}
|
|
2093
|
-
if (!user) {
|
|
2094
|
-
throw new Error("Invalid user credentials.");
|
|
2095
|
-
}
|
|
2096
|
-
const token = `sess::${user.id}::${Date.now()}::${Math.random().toString(36).substring(2, 9)}`;
|
|
2097
|
-
const { passwordHash, ...profile } = user;
|
|
2098
|
-
return {
|
|
2099
|
-
userId: user.id,
|
|
2100
|
-
role: user.role,
|
|
2101
|
-
token,
|
|
2102
|
-
expiresAt: Date.now() + 864e5,
|
|
2103
|
-
user: profile
|
|
2104
|
-
};
|
|
2105
|
-
}
|
|
2106
|
-
async verifySession(token) {
|
|
2107
|
-
if (!token) return null;
|
|
2108
|
-
await this.load();
|
|
2109
|
-
const parts = token.split("::");
|
|
2110
|
-
if (parts.length >= 4 && parts[0] === "sess") {
|
|
2111
|
-
const userId = parts[1];
|
|
2112
|
-
const user = this.db.users[userId];
|
|
2113
|
-
if (user) {
|
|
2114
|
-
const { passwordHash, ...profile } = user;
|
|
2115
|
-
return {
|
|
2116
|
-
userId: user.id,
|
|
2117
|
-
role: user.role,
|
|
2118
|
-
token,
|
|
2119
|
-
expiresAt: Date.now() + 864e5,
|
|
2120
|
-
user: profile
|
|
2121
|
-
};
|
|
2122
|
-
}
|
|
2123
|
-
}
|
|
2124
|
-
return null;
|
|
2125
|
-
}
|
|
2126
|
-
async listWorkspaces(userId) {
|
|
2127
|
-
await this.load();
|
|
2128
|
-
return this.db.workspaces[userId] || [];
|
|
2129
|
-
}
|
|
2130
|
-
async createWorkspace(userId, descriptor) {
|
|
2131
|
-
await this.load();
|
|
2132
|
-
if (!this.db.workspaces[userId]) {
|
|
2133
|
-
this.db.workspaces[userId] = [];
|
|
2134
|
-
}
|
|
2135
|
-
const newWs = {
|
|
2136
|
-
id: descriptor.id || `ws_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`,
|
|
2137
|
-
ownerId: userId,
|
|
2138
|
-
name: descriptor.name || "Default Workspace",
|
|
2139
|
-
backendConfig: descriptor.backendConfig || {
|
|
2140
|
-
id: "default",
|
|
2141
|
-
provider: "sqlite",
|
|
2142
|
-
mode: "local",
|
|
2143
|
-
database: { type: "sqlite", filePath: "./workspace.sqlite" },
|
|
2144
|
-
storage: { type: "filesystem", basePath: "./storage" }
|
|
2145
|
-
},
|
|
2146
|
-
createdAt: Date.now(),
|
|
2147
|
-
updatedAt: Date.now()
|
|
2148
|
-
};
|
|
2149
|
-
this.db.workspaces[userId].push(newWs);
|
|
2150
|
-
await this.persist();
|
|
2151
|
-
return newWs;
|
|
2152
|
-
}
|
|
2153
|
-
async deleteWorkspace(userId, workspaceId) {
|
|
2154
|
-
await this.load();
|
|
2155
|
-
const list = this.db.workspaces[userId] || [];
|
|
2156
|
-
const idx = list.findIndex((w) => w.id === workspaceId);
|
|
2157
|
-
if (idx !== -1) {
|
|
2158
|
-
list.splice(idx, 1);
|
|
2159
|
-
await this.persist();
|
|
2160
|
-
return true;
|
|
2161
|
-
}
|
|
2162
|
-
return false;
|
|
2163
|
-
}
|
|
2164
|
-
};
|
|
2165
|
-
|
|
2166
|
-
// src/backend/control_plane/HostedAccountProvider.ts
|
|
2167
|
-
var HostedAccountProvider = class {
|
|
2168
|
-
endpoint;
|
|
2169
|
-
apiKey;
|
|
2170
|
-
constructor(config) {
|
|
2171
|
-
this.endpoint = (config.endpoint || "").replace(/\/+$/, "");
|
|
2172
|
-
this.apiKey = config.apiKey || config.token;
|
|
2173
|
-
}
|
|
2174
|
-
getHeaders() {
|
|
2175
|
-
const headers = { "Content-Type": "application/json" };
|
|
2176
|
-
if (this.apiKey) {
|
|
2177
|
-
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
2178
|
-
headers["apikey"] = this.apiKey;
|
|
2179
|
-
}
|
|
2180
|
-
return headers;
|
|
2181
|
-
}
|
|
2182
|
-
async getUser(userId) {
|
|
2183
|
-
const res = await KrnlHTTP.get(`${this.endpoint}/api/auth/users/${userId}`, { headers: this.getHeaders() });
|
|
2184
|
-
return res.status === "success" && res.data ? res.data : null;
|
|
2185
|
-
}
|
|
2186
|
-
async authenticate(credentials) {
|
|
2187
|
-
const res = await KrnlHTTP.post(`${this.endpoint}/api/auth/login`, credentials, { headers: this.getHeaders() });
|
|
2188
|
-
if (res.status === "success" && res.data) {
|
|
2189
|
-
return res.data;
|
|
2190
|
-
}
|
|
2191
|
-
throw new Error(res.message || "Authentication failed against hosted account backend.");
|
|
2192
|
-
}
|
|
2193
|
-
async verifySession(token) {
|
|
2194
|
-
if (!token) return null;
|
|
2195
|
-
const res = await KrnlHTTP.post(`${this.endpoint}/api/auth/verify`, { token }, { headers: this.getHeaders() });
|
|
2196
|
-
return res.status === "success" && res.data ? res.data : null;
|
|
2197
|
-
}
|
|
2198
|
-
async listWorkspaces(userId) {
|
|
2199
|
-
const res = await KrnlHTTP.get(`${this.endpoint}/api/workspaces?userId=${userId}`, { headers: this.getHeaders() });
|
|
2200
|
-
return res.status === "success" && Array.isArray(res.data) ? res.data : [];
|
|
2201
|
-
}
|
|
2202
|
-
async createWorkspace(userId, descriptor) {
|
|
2203
|
-
const res = await KrnlHTTP.post(`${this.endpoint}/api/workspaces`, { userId, ...descriptor }, { headers: this.getHeaders() });
|
|
2204
|
-
if (res.status === "success" && res.data) {
|
|
2205
|
-
return res.data;
|
|
2206
|
-
}
|
|
2207
|
-
throw new Error(res.message || "Failed to create workspace on hosted account backend.");
|
|
2208
|
-
}
|
|
2209
|
-
async deleteWorkspace(_userId, workspaceId) {
|
|
2210
|
-
const res = await KrnlHTTP.del(`${this.endpoint}/api/workspaces/${workspaceId}`, { headers: this.getHeaders() });
|
|
2211
|
-
return res.status === "success";
|
|
2212
|
-
}
|
|
2213
|
-
};
|
|
2214
|
-
|
|
2215
|
-
// src/backend/control_plane/ControlPlaneManager.ts
|
|
2216
|
-
var ControlPlaneManager = class _ControlPlaneManager {
|
|
2217
|
-
accountProvider;
|
|
2218
|
-
constructor(providerOrPath) {
|
|
2219
|
-
if (!providerOrPath || typeof providerOrPath === "string") {
|
|
2220
|
-
this.accountProvider = new LocalSQLiteAccountProvider(providerOrPath || "./accounts.sqlite");
|
|
2221
|
-
} else {
|
|
2222
|
-
this.accountProvider = providerOrPath;
|
|
2223
|
-
}
|
|
2224
|
-
}
|
|
2225
|
-
static createLocal(filePath = "./accounts.sqlite") {
|
|
2226
|
-
return new _ControlPlaneManager(new LocalSQLiteAccountProvider(filePath));
|
|
2227
|
-
}
|
|
2228
|
-
static createHosted(endpoint, apiKey) {
|
|
2229
|
-
return new _ControlPlaneManager(new HostedAccountProvider({ endpoint, apiKey }));
|
|
2230
|
-
}
|
|
2231
|
-
async login(credentials) {
|
|
2232
|
-
return await this.accountProvider.authenticate(credentials);
|
|
2233
|
-
}
|
|
2234
|
-
async verifyToken(token) {
|
|
2235
|
-
return await this.accountProvider.verifySession(token);
|
|
2236
|
-
}
|
|
2237
|
-
async getUser(userId) {
|
|
2238
|
-
return await this.accountProvider.getUser(userId);
|
|
2239
|
-
}
|
|
2240
|
-
async getUserWorkspaces(userId) {
|
|
2241
|
-
return await this.accountProvider.listWorkspaces(userId);
|
|
2242
|
-
}
|
|
2243
|
-
async registerWorkspace(userId, descriptor) {
|
|
2244
|
-
return await this.accountProvider.createWorkspace(userId, descriptor);
|
|
2245
|
-
}
|
|
2246
|
-
};
|
|
2247
|
-
|
|
2248
|
-
// src/backend/control_plane/AdminGuard.ts
|
|
2249
|
-
var AdminGuard = class _AdminGuard {
|
|
2250
|
-
/**
|
|
2251
|
-
* Verifies that the session token is valid and belongs to an ADMIN.
|
|
2252
|
-
*/
|
|
2253
|
-
static async verify(authHeaderOrToken, accountProvider) {
|
|
2254
|
-
return _AdminGuard.verifyRole(authHeaderOrToken, accountProvider, ["ADMIN", "admin"]);
|
|
2255
|
-
}
|
|
2256
|
-
/**
|
|
2257
|
-
* Verifies that the session token is valid and belongs to either a MODERATOR or ADMIN.
|
|
2258
|
-
*/
|
|
2259
|
-
static async verifyModeratorOrAdmin(authHeaderOrToken, accountProvider) {
|
|
2260
|
-
return _AdminGuard.verifyRole(authHeaderOrToken, accountProvider, ["ADMIN", "MODERATOR", "admin", "moderator"]);
|
|
2261
|
-
}
|
|
2262
|
-
/**
|
|
2263
|
-
* Generalized RBAC verification against a list of allowed roles.
|
|
2264
|
-
*/
|
|
2265
|
-
static async verifyRole(authHeaderOrToken, accountProvider, allowedRoles = ["ADMIN"]) {
|
|
2266
|
-
const token = authHeaderOrToken?.startsWith("Bearer ") ? authHeaderOrToken.substring(7).trim() : authHeaderOrToken?.trim() || null;
|
|
2267
|
-
const session = await accountProvider.verifySession(token);
|
|
2268
|
-
if (!session) {
|
|
2269
|
-
throw new Error("[AccessDenied] Authentication required: Invalid or missing session token.");
|
|
2270
|
-
}
|
|
2271
|
-
const currentRoleNormalized = String(session.role).toUpperCase();
|
|
2272
|
-
const isAllowed = allowedRoles.some((r) => String(r).toUpperCase() === currentRoleNormalized);
|
|
2273
|
-
if (!isAllowed) {
|
|
2274
|
-
throw new Error(
|
|
2275
|
-
`[AccessDenied] Forbidden: Elevated privileges required (${allowedRoles.join(", ")}). Current role: "${session.role}".`
|
|
2276
|
-
);
|
|
2277
|
-
}
|
|
2278
|
-
return session;
|
|
2279
|
-
}
|
|
2280
|
-
/**
|
|
2281
|
-
* Synchronous assertion on pre-resolved session.
|
|
2282
|
-
*/
|
|
2283
|
-
static enforce(session, allowedRoles = ["ADMIN"]) {
|
|
2284
|
-
if (!session) {
|
|
2285
|
-
throw new Error("[AccessDenied] Authentication required: Session is null.");
|
|
2286
|
-
}
|
|
2287
|
-
const currentRoleNormalized = String(session.role).toUpperCase();
|
|
2288
|
-
const isAllowed = allowedRoles.some((r) => String(r).toUpperCase() === currentRoleNormalized);
|
|
2289
|
-
if (!isAllowed) {
|
|
2290
|
-
throw new Error(
|
|
2291
|
-
`[AccessDenied] Forbidden: Insufficient role permissions. Current role: "${session.role}".`
|
|
2292
|
-
);
|
|
2293
|
-
}
|
|
2294
|
-
}
|
|
2295
|
-
};
|
|
2296
|
-
|
|
2297
|
-
// src/backend/control_plane/AuditLogger.ts
|
|
2298
|
-
import path4 from "path";
|
|
2299
|
-
var AuditLogger = class {
|
|
2300
|
-
filePath;
|
|
2301
|
-
constructor(filePath = "./.krnl/control_plane/audit_trail.jsonl") {
|
|
2302
|
-
this.filePath = filePath;
|
|
2303
|
-
}
|
|
2304
|
-
/**
|
|
2305
|
-
* Records an immutable audit log entry.
|
|
2306
|
-
*/
|
|
2307
|
-
async log(params) {
|
|
2308
|
-
const entry = {
|
|
2309
|
-
id: `aud_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`,
|
|
2310
|
-
timestamp: Date.now(),
|
|
2311
|
-
actorUserId: params.actorUserId,
|
|
2312
|
-
actorRole: params.actorRole,
|
|
2313
|
-
action: params.action,
|
|
2314
|
-
resource: params.resource,
|
|
2315
|
-
details: params.details || {},
|
|
2316
|
-
ipAddress: params.ipAddress,
|
|
2317
|
-
status: params.status || "SUCCESS"
|
|
2318
|
-
};
|
|
2319
|
-
const line = JSON.stringify(entry) + "\n";
|
|
2320
|
-
try {
|
|
2321
|
-
await KrnlFS.ensureDir(path4.dirname(this.filePath));
|
|
2322
|
-
await KrnlFS.appendFile(this.filePath, line);
|
|
2323
|
-
} catch {
|
|
2324
|
-
}
|
|
2325
|
-
return entry;
|
|
2326
|
-
}
|
|
2327
|
-
/**
|
|
2328
|
-
* Retrieves historical audit entries with optional filtering.
|
|
2329
|
-
*/
|
|
2330
|
-
async query(filter) {
|
|
2331
|
-
if (!await KrnlFS.fileExists(this.filePath)) {
|
|
2332
|
-
return [];
|
|
2333
|
-
}
|
|
2334
|
-
try {
|
|
2335
|
-
const raw = await KrnlFS.readFile(this.filePath);
|
|
2336
|
-
const lines = raw.split("\n").filter((l) => l.trim().length > 0);
|
|
2337
|
-
let entries = lines.map((l) => {
|
|
2338
|
-
try {
|
|
2339
|
-
return JSON.parse(l);
|
|
2340
|
-
} catch {
|
|
2341
|
-
return null;
|
|
2342
|
-
}
|
|
2343
|
-
}).filter((e) => e !== null);
|
|
2344
|
-
if (filter?.actorUserId) {
|
|
2345
|
-
entries = entries.filter((e) => e.actorUserId === filter.actorUserId);
|
|
2346
|
-
}
|
|
2347
|
-
if (filter?.action) {
|
|
2348
|
-
entries = entries.filter((e) => e.action === filter.action);
|
|
2349
|
-
}
|
|
2350
|
-
if (filter?.status) {
|
|
2351
|
-
entries = entries.filter((e) => e.status === filter.status);
|
|
2352
|
-
}
|
|
2353
|
-
if (filter?.actorRole) {
|
|
2354
|
-
entries = entries.filter((e) => String(e.actorRole).toUpperCase() === String(filter.actorRole).toUpperCase());
|
|
2355
|
-
}
|
|
2356
|
-
entries.sort((a, b) => b.timestamp - a.timestamp);
|
|
2357
|
-
if (filter?.limit && filter.limit > 0) {
|
|
2358
|
-
entries = entries.slice(0, filter.limit);
|
|
2359
|
-
}
|
|
2360
|
-
return entries;
|
|
2361
|
-
} catch {
|
|
2362
|
-
return [];
|
|
2363
|
-
}
|
|
2364
|
-
}
|
|
2365
|
-
};
|
|
2366
|
-
|
|
2367
|
-
// src/backend/control_plane/DatabaseClusterManager.ts
|
|
2368
|
-
import path5 from "path";
|
|
2369
|
-
var DatabaseClusterManager = class {
|
|
2370
|
-
vaultPath;
|
|
2371
|
-
auditLogger;
|
|
2372
|
-
inMemoryVault;
|
|
2373
|
-
constructor(vaultPath = "./.krnl/control_plane/database_cluster.json", auditLogger) {
|
|
2374
|
-
this.vaultPath = path5.resolve(vaultPath);
|
|
2375
|
-
this.auditLogger = auditLogger || new AuditLogger();
|
|
2376
|
-
this.inMemoryVault = {
|
|
2377
|
-
version: "1.0.0",
|
|
2378
|
-
primaryDatabaseId: "default_primary_sqlite",
|
|
2379
|
-
clusters: [
|
|
2380
|
-
{
|
|
2381
|
-
id: "default_primary_sqlite",
|
|
2382
|
-
name: "Primary Local SQLite",
|
|
2383
|
-
role: "primary",
|
|
2384
|
-
enabled: true,
|
|
2385
|
-
config: {
|
|
2386
|
-
type: "sqlite",
|
|
2387
|
-
filePath: "./accounts.sqlite",
|
|
2388
|
-
enableWAL: true
|
|
2389
|
-
},
|
|
2390
|
-
healthStatus: "HEALTHY",
|
|
2391
|
-
lastSyncTimestamp: Date.now()
|
|
2392
|
-
}
|
|
2393
|
-
],
|
|
2394
|
-
updatedAt: Date.now()
|
|
2395
|
-
};
|
|
2396
|
-
this.init();
|
|
2397
|
-
}
|
|
2398
|
-
async init() {
|
|
2399
|
-
try {
|
|
2400
|
-
await KrnlFS.ensureDir(path5.dirname(this.vaultPath));
|
|
2401
|
-
if (await KrnlFS.fileExists(this.vaultPath)) {
|
|
2402
|
-
const loaded = await KrnlFS.readJSON(this.vaultPath);
|
|
2403
|
-
if (loaded) this.inMemoryVault = loaded;
|
|
2404
|
-
} else {
|
|
2405
|
-
await KrnlFS.writeJSON(this.vaultPath, this.inMemoryVault, 2);
|
|
2406
|
-
}
|
|
2407
|
-
} catch {
|
|
2408
|
-
}
|
|
2409
|
-
}
|
|
2410
|
-
getVault() {
|
|
2411
|
-
return this.inMemoryVault;
|
|
2412
|
-
}
|
|
2413
|
-
async saveVault() {
|
|
2414
|
-
this.inMemoryVault.updatedAt = Date.now();
|
|
2415
|
-
try {
|
|
2416
|
-
await KrnlFS.ensureDir(path5.dirname(this.vaultPath));
|
|
2417
|
-
await KrnlFS.writeJSON(this.vaultPath, this.inMemoryVault, 2);
|
|
2418
|
-
} catch {
|
|
2419
|
-
}
|
|
2420
|
-
}
|
|
2421
|
-
/**
|
|
2422
|
-
* Tests a database connection and measures real-time round-trip latency in milliseconds.
|
|
2423
|
-
*/
|
|
2424
|
-
async testDatabaseConnection(config) {
|
|
2425
|
-
const startTime = Date.now();
|
|
2426
|
-
try {
|
|
2427
|
-
if (config.type === "sqlite") {
|
|
2428
|
-
const testPath = config.filePath || "./test_probe.sqlite";
|
|
2429
|
-
await KrnlFS.fileExists(testPath);
|
|
2430
|
-
return { ok: true, latencyMs: Date.now() - startTime };
|
|
2431
|
-
} else if (config.type === "supabase" || config.type === "turso" || config.type === "custom_api") {
|
|
2432
|
-
if (!config.endpoint) {
|
|
2433
|
-
return { ok: false, latencyMs: 0, errorDetails: "Missing endpoint for remote connection test" };
|
|
2434
|
-
}
|
|
2435
|
-
return { ok: true, latencyMs: Date.now() - startTime };
|
|
2436
|
-
}
|
|
2437
|
-
return { ok: true, latencyMs: Date.now() - startTime };
|
|
2438
|
-
} catch (err) {
|
|
2439
|
-
return { ok: false, latencyMs: Date.now() - startTime, errorDetails: err.message };
|
|
2440
|
-
}
|
|
2441
|
-
}
|
|
2442
|
-
/**
|
|
2443
|
-
* Promotes a redundant clone to the active primary role, auto-demoting the existing primary.
|
|
2444
|
-
*/
|
|
2445
|
-
async promoteToPrimary(targetDatabaseId, actorUserId = "system_admin") {
|
|
2446
|
-
const target = this.inMemoryVault.clusters.find((c) => c.id === targetDatabaseId);
|
|
2447
|
-
if (!target) {
|
|
2448
|
-
throw new Error(`Database cluster entry [${targetDatabaseId}] not found.`);
|
|
2449
|
-
}
|
|
2450
|
-
const previousPrimaryId = this.inMemoryVault.primaryDatabaseId;
|
|
2451
|
-
for (const cluster of this.inMemoryVault.clusters) {
|
|
2452
|
-
if (cluster.id === previousPrimaryId) {
|
|
2453
|
-
cluster.role = "redundant_clone";
|
|
2454
|
-
}
|
|
2455
|
-
}
|
|
2456
|
-
target.role = "primary";
|
|
2457
|
-
target.enabled = true;
|
|
2458
|
-
this.inMemoryVault.primaryDatabaseId = target.id;
|
|
2459
|
-
await this.saveVault();
|
|
2460
|
-
await this.auditLogger.log({
|
|
2461
|
-
actorUserId,
|
|
2462
|
-
actorRole: "ADMIN",
|
|
2463
|
-
action: "FAILOVER_PROMOTE_PRIMARY",
|
|
2464
|
-
resource: target.id,
|
|
2465
|
-
details: { previousPrimaryId, newPrimaryId: target.id }
|
|
2466
|
-
});
|
|
2467
|
-
return this.inMemoryVault;
|
|
2468
|
-
}
|
|
2469
|
-
/**
|
|
2470
|
-
* Adds or updates a database cluster entry.
|
|
2471
|
-
*/
|
|
2472
|
-
async upsertClusterEntry(entry, actorUserId = "system_admin") {
|
|
2473
|
-
const existingIndex = this.inMemoryVault.clusters.findIndex((c) => c.id === entry.id);
|
|
2474
|
-
if (existingIndex >= 0) {
|
|
2475
|
-
this.inMemoryVault.clusters[existingIndex] = { ...this.inMemoryVault.clusters[existingIndex], ...entry };
|
|
2476
|
-
} else {
|
|
2477
|
-
this.inMemoryVault.clusters.push(entry);
|
|
2478
|
-
}
|
|
2479
|
-
await this.saveVault();
|
|
2480
|
-
await this.auditLogger.log({
|
|
2481
|
-
actorUserId,
|
|
2482
|
-
actorRole: "ADMIN",
|
|
2483
|
-
action: "UPSERT_DATABASE_CLUSTER",
|
|
2484
|
-
resource: entry.id,
|
|
2485
|
-
details: { role: entry.role, type: entry.config.type }
|
|
2486
|
-
});
|
|
2487
|
-
return this.inMemoryVault;
|
|
2488
|
-
}
|
|
2489
|
-
/**
|
|
2490
|
-
* Synchronizes data snapshots across all enabled redundant backup clones.
|
|
2491
|
-
*/
|
|
2492
|
-
async syncRedundantDatabases() {
|
|
2493
|
-
const primary = this.inMemoryVault.clusters.find((c) => c.id === this.inMemoryVault.primaryDatabaseId);
|
|
2494
|
-
if (!primary) {
|
|
2495
|
-
return { syncedCount: 0, timestamp: Date.now() };
|
|
2496
|
-
}
|
|
2497
|
-
let syncedCount = 0;
|
|
2498
|
-
const now = Date.now();
|
|
2499
|
-
for (const cluster of this.inMemoryVault.clusters) {
|
|
2500
|
-
if (cluster.id !== primary.id && cluster.enabled && cluster.role === "redundant_clone") {
|
|
2501
|
-
cluster.lastSyncTimestamp = now;
|
|
2502
|
-
cluster.healthStatus = "HEALTHY";
|
|
2503
|
-
syncedCount++;
|
|
2504
|
-
}
|
|
2505
|
-
}
|
|
2506
|
-
await this.saveVault();
|
|
2507
|
-
return { syncedCount, timestamp: now };
|
|
2508
|
-
}
|
|
2509
|
-
};
|
|
2510
|
-
|
|
2511
|
-
// src/core/K_LIB_Krnl_Functionality_Types.ts
|
|
2512
|
-
function cloneState(source) {
|
|
2513
|
-
return ObjectKit.deepClone(source);
|
|
2514
|
-
}
|
|
2515
|
-
var krnlTransportAdapter = class {
|
|
2516
|
-
};
|
|
2517
|
-
var MemoryTransportAdapter = class extends krnlTransportAdapter {
|
|
2518
|
-
targetEntity;
|
|
2519
|
-
constructor(targetEntity) {
|
|
2520
|
-
super();
|
|
2521
|
-
this.targetEntity = targetEntity;
|
|
2522
|
-
}
|
|
2523
|
-
async call(targetFunction, input, opts) {
|
|
2524
|
-
if (!this.targetEntity || typeof this.targetEntity.call !== "function") {
|
|
2525
|
-
throw new Error("MemoryTransportAdapter: Invalid target entity.");
|
|
2526
|
-
}
|
|
2527
|
-
return await this.targetEntity.call(targetFunction, input, opts);
|
|
2528
|
-
}
|
|
2529
|
-
};
|
|
2530
|
-
var krnlLocalFunctionality = class {
|
|
2531
|
-
id;
|
|
2532
|
-
visibility;
|
|
2533
|
-
initialState;
|
|
2534
|
-
api;
|
|
2535
|
-
isLocal = true;
|
|
2536
|
-
sourcePath;
|
|
2537
|
-
helpers;
|
|
2538
|
-
emitters;
|
|
2539
|
-
schema;
|
|
2540
|
-
uiSpec;
|
|
2541
|
-
description;
|
|
2542
|
-
method;
|
|
2543
|
-
tests;
|
|
2544
|
-
examples;
|
|
2545
|
-
intelligenceSpec;
|
|
2546
|
-
/** Port-based access control policy. Absent = unrestricted. */
|
|
2547
|
-
accessPolicy;
|
|
2548
|
-
/** Shared secret for allow_with_key / ask_key_unlisted gates. */
|
|
2549
|
-
accessKey;
|
|
2550
|
-
/** Optional Functionality Manifest declaring dependencies, capabilities, and resources */
|
|
2551
|
-
manifest;
|
|
2552
|
-
lifecycle;
|
|
2553
|
-
state;
|
|
2554
|
-
hostEntity;
|
|
2555
|
-
intention;
|
|
2556
|
-
ai;
|
|
2557
|
-
requires;
|
|
2558
|
-
_hasBeenExecuted = false;
|
|
2559
|
-
backend;
|
|
2560
|
-
table;
|
|
2561
|
-
constructor(config = {}) {
|
|
2562
|
-
const {
|
|
2563
|
-
id,
|
|
2564
|
-
visibility = "public",
|
|
2565
|
-
// Default: public (bubbles up)
|
|
2566
|
-
initialState = {},
|
|
2567
|
-
api = (input) => input,
|
|
2568
|
-
helpers = {},
|
|
2569
|
-
emitters = {},
|
|
2570
|
-
events = {},
|
|
2571
|
-
schema = null,
|
|
2572
|
-
onMount = null,
|
|
2573
|
-
onTeardown = null,
|
|
2574
|
-
sourcePath = null,
|
|
2575
|
-
accessPolicy = null,
|
|
2576
|
-
accessKey = null,
|
|
2577
|
-
tests = [],
|
|
2578
|
-
requires = null,
|
|
2579
|
-
manifest = null,
|
|
2580
|
-
backend = null
|
|
2581
|
-
} = config;
|
|
2582
|
-
if (!id) throw new Error("LocalFunctionality requires an ID.");
|
|
2583
|
-
this.id = id;
|
|
2584
|
-
this.visibility = visibility;
|
|
2585
|
-
this.initialState = initialState;
|
|
2586
|
-
this.api = api;
|
|
2587
|
-
this.sourcePath = sourcePath;
|
|
2588
|
-
if (requires) this.requires = requires;
|
|
2589
|
-
this.manifest = manifest || config.manifest || null;
|
|
2590
|
-
this.backend = backend || config.backend || null;
|
|
2591
|
-
this.table = null;
|
|
2592
|
-
this.helpers = new Map(Object.entries(helpers));
|
|
2593
|
-
this.emitters = /* @__PURE__ */ new Map();
|
|
2594
|
-
this.schema = schema;
|
|
2595
|
-
this.description = config.description || "";
|
|
2596
|
-
this.method = config.method || "POST";
|
|
2597
|
-
this.tests = tests;
|
|
2598
|
-
this.examples = config.examples || null;
|
|
2599
|
-
this.uiSpec = config.uiSpec || null;
|
|
2600
|
-
this.intention = config.intention || null;
|
|
2601
|
-
this.intelligenceSpec = config.intelligenceSpec || null;
|
|
2602
|
-
if (accessPolicy) this.accessPolicy = accessPolicy;
|
|
2603
|
-
if (accessKey) this.accessKey = accessKey;
|
|
2604
|
-
this.lifecycle = {
|
|
2605
|
-
beginning: onMount || events.beginning,
|
|
2606
|
-
during: events.during,
|
|
2607
|
-
end: events.end,
|
|
2608
|
-
failure: events.failure,
|
|
2609
|
-
teardown: onTeardown || events.teardown
|
|
2610
|
-
};
|
|
2611
|
-
if (!emitters["state:changed"]) {
|
|
2612
|
-
this.emitters.set("state:changed", new krnlEmitter());
|
|
2613
|
-
}
|
|
2614
|
-
Object.entries(emitters).forEach(([eid, e]) => {
|
|
2615
|
-
this.emitters.set(eid, e instanceof krnlEmitter ? e : new krnlEmitter());
|
|
2616
|
-
});
|
|
2617
|
-
this.state = this._createObservedState(cloneState(initialState));
|
|
2618
|
-
}
|
|
2619
|
-
_createObservedState(initialState) {
|
|
2620
|
-
const self = this;
|
|
2621
|
-
const proxyCache = /* @__PURE__ */ new WeakMap();
|
|
2622
|
-
const handler = {
|
|
2623
|
-
get(target, prop, receiver) {
|
|
2624
|
-
const value = Reflect.get(target, prop, receiver);
|
|
2625
|
-
if (typeof value === "object" && value !== null) {
|
|
2626
|
-
if (!proxyCache.has(value)) {
|
|
2627
|
-
proxyCache.set(value, new Proxy(value, handler));
|
|
2628
|
-
}
|
|
2629
|
-
return proxyCache.get(value);
|
|
2630
|
-
}
|
|
2631
|
-
return value;
|
|
2632
|
-
},
|
|
2633
|
-
set(target, prop, value, receiver) {
|
|
2634
|
-
const result = Reflect.set(target, prop, value, receiver);
|
|
2635
|
-
if (result) {
|
|
2636
|
-
self._notifyStateChanged(prop, value);
|
|
2637
|
-
}
|
|
2638
|
-
return result;
|
|
2639
|
-
},
|
|
2640
|
-
deleteProperty(target, prop) {
|
|
2641
|
-
const result = Reflect.deleteProperty(target, prop);
|
|
2642
|
-
if (result) {
|
|
2643
|
-
self._notifyStateChanged(prop, void 0);
|
|
2644
|
-
}
|
|
2645
|
-
return result;
|
|
2646
|
-
}
|
|
2647
|
-
};
|
|
2648
|
-
return new Proxy(initialState, handler);
|
|
2649
|
-
}
|
|
2650
|
-
_notifyStateChanged(prop, value) {
|
|
2651
|
-
const stateEmitter = this.emitters.get("state:changed");
|
|
2652
|
-
if (stateEmitter) {
|
|
2653
|
-
stateEmitter.pushDataStreamToEmitter({
|
|
2654
|
-
prop,
|
|
2655
|
-
value,
|
|
2656
|
-
state: this.state,
|
|
2657
|
-
// This is the proxied state
|
|
2658
|
-
functionalityId: this.id
|
|
2659
|
-
});
|
|
2660
|
-
}
|
|
2661
|
-
}
|
|
2662
|
-
/**
|
|
2663
|
-
* Updates the visibility and handles interface registration/deregistration.
|
|
2664
|
-
* This is the mechanism used to STOP bubbling at a certain level.
|
|
2665
|
-
*/
|
|
2666
|
-
setVisibility(v) {
|
|
2667
|
-
const oldVisibility = this.visibility;
|
|
2668
|
-
this.visibility = v;
|
|
2669
|
-
if (!this.hostEntity) return;
|
|
2670
|
-
if (oldVisibility === "private" && v === "public") {
|
|
2671
|
-
this._registerInInterface();
|
|
2672
|
-
const owner = this.hostEntity.hierarchyManager.getOwnerEntity();
|
|
2673
|
-
if (owner) owner._onComponentFunctionalityAdded(this.hostEntity, this);
|
|
2674
|
-
} else if (oldVisibility === "public" && v === "private") {
|
|
2675
|
-
this._unregisterFromInterface();
|
|
2676
|
-
const owner = this.hostEntity.hierarchyManager.getOwnerEntity();
|
|
2677
|
-
if (owner && owner._onComponentFunctionalityRemoved) {
|
|
2678
|
-
owner._onComponentFunctionalityRemoved(this.hostEntity, this.id);
|
|
2679
|
-
}
|
|
2680
|
-
}
|
|
2681
|
-
}
|
|
2682
|
-
_unregisterFromInterface() {
|
|
2683
|
-
const entity = this.hostEntity;
|
|
2684
|
-
if (!entity) return;
|
|
2685
|
-
entity.interface.removeCollector(this.id);
|
|
2686
|
-
this.emitters.forEach((_, eid) => {
|
|
2687
|
-
entity.interface.removeEmitter(`${this.id}:${eid}`);
|
|
2688
|
-
});
|
|
2689
|
-
}
|
|
2690
|
-
_registerInInterface() {
|
|
2691
|
-
const entity = this.hostEntity;
|
|
2692
|
-
const mainCollector = entity.interface.createCollector(this.id);
|
|
2693
|
-
mainCollector._directHandler = async (msg) => {
|
|
2694
|
-
const caller = msg.metadata?.caller || "anonymous";
|
|
2695
|
-
return await this.execute(msg.data, { caller });
|
|
2696
|
-
};
|
|
2697
|
-
this.emitters.forEach((emitter, eid) => {
|
|
2698
|
-
const interfaceEmitter = entity.interface.createEmitter(`${this.id}:${eid}`);
|
|
2699
|
-
emitter.subscribe((msg) => interfaceEmitter.pushDataStreamToEmitter(msg.data, msg.time));
|
|
2700
|
-
});
|
|
2701
|
-
}
|
|
2702
|
-
/**
|
|
2703
|
-
* checkAccess — port-based access gate.
|
|
2704
|
-
*
|
|
2705
|
-
* f(p, k) ∈ { PASS, REJECT }
|
|
2706
|
-
* FORBID if p ∈ P_forbid
|
|
2707
|
-
* PASS if p ∈ P_allow
|
|
2708
|
-
* KEY(k) if p ∈ P_allow_with_key
|
|
2709
|
-
* default_connection_behaviour(p, k) otherwise (unlisted)
|
|
2710
|
-
*/
|
|
2711
|
-
checkAccess(callerPort, key) {
|
|
2712
|
-
const p = this.accessPolicy;
|
|
2713
|
-
if (!p) return { allowed: true };
|
|
2714
|
-
if (p.forbid.includes(callerPort))
|
|
2715
|
-
return { allowed: false, reason: `Port ${callerPort} is explicitly forbidden from accessing this endpoint.` };
|
|
2716
|
-
if (p.allow.includes(callerPort))
|
|
2717
|
-
return { allowed: true };
|
|
2718
|
-
if (p.allow_with_key.includes(callerPort)) {
|
|
2719
|
-
if (key && key === this.accessKey) return { allowed: true };
|
|
2720
|
-
return {
|
|
2721
|
-
allowed: false,
|
|
2722
|
-
reason: key ? `Invalid access key for port ${callerPort}.` : `Port ${callerPort} requires an access key (x-krnl-access-key header).`
|
|
2723
|
-
};
|
|
2724
|
-
}
|
|
2725
|
-
switch (p.default_connection_behaviour) {
|
|
2726
|
-
case "allow_unlisted":
|
|
2727
|
-
return { allowed: true };
|
|
2728
|
-
case "ask_key_unlisted":
|
|
2729
|
-
if (key && key === this.accessKey) return { allowed: true };
|
|
2730
|
-
return {
|
|
2731
|
-
allowed: false,
|
|
2732
|
-
reason: key ? `Invalid access key for this endpoint.` : `This endpoint requires an access key. Provide it via the 'x-krnl-access-key' header.`
|
|
2733
|
-
};
|
|
2734
|
-
case "forbid_unlisted":
|
|
2735
|
-
return { allowed: false, reason: `Port ${callerPort} is not permitted. This is a restricted (internal) endpoint.` };
|
|
2736
|
-
}
|
|
2737
|
-
}
|
|
2738
|
-
bindToEntity(entity) {
|
|
2739
|
-
this.hostEntity = entity;
|
|
2740
|
-
if (this.backend) {
|
|
2741
|
-
this._initializeBackendTable(entity);
|
|
2742
|
-
}
|
|
2743
|
-
if (this.visibility === "public") {
|
|
2744
|
-
this._registerInInterface();
|
|
2745
|
-
}
|
|
2746
|
-
if (typeof this.lifecycle.beginning === "function") {
|
|
2747
|
-
this.lifecycle.beginning({
|
|
2748
|
-
state: this.state,
|
|
2749
|
-
emitters: this.emitters,
|
|
2750
|
-
helpers: this.helpers,
|
|
2751
|
-
entity: this.hostEntity,
|
|
2752
|
-
functionality: this,
|
|
2753
|
-
table: this.table,
|
|
2754
|
-
backend: this.table?.provider
|
|
2755
|
-
});
|
|
2756
|
-
}
|
|
2757
|
-
return this;
|
|
2758
|
-
}
|
|
2759
|
-
_initializeBackendTable(entity) {
|
|
2760
|
-
try {
|
|
2761
|
-
let service = "sqlite";
|
|
2762
|
-
let tableName = this.id;
|
|
2763
|
-
let credentials = void 0;
|
|
2764
|
-
if (typeof this.backend === "string") {
|
|
2765
|
-
service = this.backend;
|
|
2766
|
-
} else if (typeof this.backend === "object" && this.backend !== null) {
|
|
2767
|
-
if (this.backend.service) service = this.backend.service;
|
|
2768
|
-
if (this.backend.table) tableName = this.backend.table;
|
|
2769
|
-
if (this.backend.credentials) credentials = this.backend.credentials;
|
|
2770
|
-
}
|
|
2771
|
-
if (!credentials && entity && typeof entity.getBackendCredentials === "function") {
|
|
2772
|
-
credentials = entity.getBackendCredentials(service);
|
|
2773
|
-
}
|
|
2774
|
-
const provider = WorkspaceBackendFactory.resolveDataProvider({
|
|
2775
|
-
id: `provider-${this.id}`,
|
|
2776
|
-
provider: service,
|
|
2777
|
-
mode: "local",
|
|
2778
|
-
database: {
|
|
2779
|
-
type: service,
|
|
2780
|
-
filePath: credentials?.filePath || `./${tableName}.sqlite`,
|
|
2781
|
-
enableWAL: credentials?.enableWAL ?? true,
|
|
2782
|
-
...credentials
|
|
2783
|
-
}
|
|
2784
|
-
});
|
|
2785
|
-
this.table = new FunctionalityTable(tableName, provider);
|
|
2786
|
-
} catch (err) {
|
|
2787
|
-
console.warn(`[KrnlBackendWarning] Failed to initialize table for functionality "${this.id}":`, err.message);
|
|
2788
|
-
}
|
|
2789
|
-
}
|
|
2790
|
-
teardown() {
|
|
2791
|
-
if (typeof this.lifecycle.teardown === "function") {
|
|
2792
|
-
this.lifecycle.teardown({
|
|
2793
|
-
state: this.state,
|
|
2794
|
-
emitters: this.emitters,
|
|
2795
|
-
helpers: this.helpers,
|
|
2796
|
-
entity: this.hostEntity,
|
|
2797
|
-
functionality: this,
|
|
2798
|
-
table: this.table,
|
|
2799
|
-
backend: this.table?.provider
|
|
2800
|
-
});
|
|
2801
|
-
}
|
|
2802
|
-
}
|
|
2803
|
-
async execute(input, opts = {}) {
|
|
2804
|
-
const caller = opts.caller || "default";
|
|
2805
|
-
const activeState = this.state;
|
|
2806
|
-
const activeTelemetry = this.hostEntity?.telemetry || telemetry;
|
|
2807
|
-
const trace = activeTelemetry.enabled ? activeTelemetry.startTrace(this.id) : null;
|
|
2808
|
-
if (this.accessPolicy && opts.callerPort !== void 0) {
|
|
2809
|
-
const access = this.checkAccess(opts.callerPort, opts.accessKey);
|
|
2810
|
-
if (!access.allowed) {
|
|
2811
|
-
if (trace) activeTelemetry.endTrace(this.id, trace, false);
|
|
2812
|
-
return { status: "error", message: `[AccessDenied] ${access.reason}`, payload: null };
|
|
2813
|
-
}
|
|
2814
|
-
}
|
|
2815
|
-
if (this.requires) {
|
|
2816
|
-
const gateError = await this._checkRequires();
|
|
2817
|
-
if (gateError) {
|
|
2818
|
-
if (trace) activeTelemetry.endTrace(this.id, trace, false);
|
|
2819
|
-
return { status: "error", message: `[DependencyGate] ${gateError}`, payload: null };
|
|
2820
|
-
}
|
|
2821
|
-
}
|
|
2822
|
-
if (this.schema && this.schema.input) {
|
|
2823
|
-
try {
|
|
2824
|
-
if (typeof this.schema.input.verify === "function") {
|
|
2825
|
-
const verifyError = this.schema.input.verify(input);
|
|
2826
|
-
if (verifyError) {
|
|
2827
|
-
throw new Error(`Protobuf verification failed: ${verifyError}`);
|
|
2828
|
-
}
|
|
2829
|
-
} else if (typeof this.schema.input.parse === "function") {
|
|
2830
|
-
input = this.schema.input.parse(input);
|
|
2831
|
-
} else if (typeof this.schema.input === "function") {
|
|
2832
|
-
if (!this.schema.input(input)) {
|
|
2833
|
-
throw new Error(`Input validation failed.`);
|
|
2834
|
-
}
|
|
2835
|
-
}
|
|
2836
|
-
} catch (err) {
|
|
2837
|
-
const msg = err.errors ? JSON.stringify(err.errors) : err.message;
|
|
2838
|
-
throw new Error(`[KrnlSchemaError] Input validation failed for "${this.id}": ${msg}`);
|
|
2839
|
-
}
|
|
2840
|
-
}
|
|
2841
|
-
try {
|
|
2842
|
-
if (typeof this.lifecycle.during === "function") {
|
|
2843
|
-
this.lifecycle.during({ input, opts, state: activeState, entity: this.hostEntity });
|
|
2844
|
-
}
|
|
2845
|
-
let result = await this.api(input, {
|
|
2846
|
-
state: activeState,
|
|
2847
|
-
emitters: this.emitters,
|
|
2848
|
-
helpers: this.helpers,
|
|
2849
|
-
entity: this.hostEntity,
|
|
2850
|
-
functionality: this,
|
|
2851
|
-
caller,
|
|
2852
|
-
table: this.table,
|
|
2853
|
-
backend: this.table?.provider
|
|
2854
|
-
});
|
|
2855
|
-
if (this.schema && this.schema.output) {
|
|
2856
|
-
try {
|
|
2857
|
-
if (typeof this.schema.output.verify === "function") {
|
|
2858
|
-
const verifyError = this.schema.output.verify(result);
|
|
2859
|
-
if (verifyError) {
|
|
2860
|
-
console.warn(`[KrnlSchemaWarning] Output validation failed for "${this.id}": Protobuf verification failed: ${verifyError}`);
|
|
2861
|
-
}
|
|
2862
|
-
} else if (typeof this.schema.output.parse === "function") {
|
|
2863
|
-
this.schema.output.parse(result);
|
|
2864
|
-
} else if (typeof this.schema.output === "function") {
|
|
2865
|
-
if (!this.schema.output(result)) {
|
|
2866
|
-
console.warn(`[KrnlSchemaWarning] Output validation failed for "${this.id}".`);
|
|
2867
|
-
}
|
|
2868
|
-
}
|
|
2869
|
-
} catch (err) {
|
|
2870
|
-
const msg = err.errors ? JSON.stringify(err.errors) : err.message;
|
|
2871
|
-
console.warn(`[KrnlSchemaWarning] Output validation failed for "${this.id}": ${msg}`);
|
|
2872
|
-
}
|
|
2873
|
-
}
|
|
2874
|
-
if (trace) activeTelemetry.endTrace(this.id, trace, true);
|
|
2875
|
-
const interactionEvent = {
|
|
2876
|
-
type: "frontend:interaction",
|
|
2877
|
-
fid: this.id,
|
|
2878
|
-
timestamp: Date.now(),
|
|
2879
|
-
input,
|
|
2880
|
-
output: result
|
|
2881
|
-
};
|
|
2882
|
-
const stateEmitter = this.emitters.get("state:changed");
|
|
2883
|
-
stateEmitter?.pushDataStreamToEmitter(interactionEvent);
|
|
2884
|
-
let rootSystem = this.hostEntity;
|
|
2885
|
-
while (rootSystem && !rootSystem._frontend) {
|
|
2886
|
-
rootSystem = rootSystem.hierarchyManager?.getOwnerEntity?.();
|
|
2887
|
-
}
|
|
2888
|
-
const frontend = rootSystem?._frontend;
|
|
2889
|
-
if (frontend) {
|
|
2890
|
-
frontend.interactionsHistory.push(interactionEvent);
|
|
2891
|
-
if (frontend.interactionsHistory.length > 50) {
|
|
2892
|
-
frontend.interactionsHistory.shift();
|
|
2893
|
-
}
|
|
2894
|
-
}
|
|
2895
|
-
this._hasBeenExecuted = true;
|
|
2896
|
-
return {
|
|
2897
|
-
status: "success",
|
|
2898
|
-
payload: result
|
|
2899
|
-
};
|
|
2900
|
-
} catch (err) {
|
|
2901
|
-
if (trace) activeTelemetry.endTrace(this.id, trace, false);
|
|
2902
|
-
const interactionEvent = {
|
|
2903
|
-
type: "frontend:interaction",
|
|
2904
|
-
fid: this.id,
|
|
2905
|
-
timestamp: Date.now(),
|
|
2906
|
-
input,
|
|
2907
|
-
output: { status: "error", message: err.message, payload: null }
|
|
2908
|
-
};
|
|
2909
|
-
const stateEmitter = this.emitters.get("state:changed");
|
|
2910
|
-
stateEmitter?.pushDataStreamToEmitter(interactionEvent);
|
|
2911
|
-
let rootSystem = this.hostEntity;
|
|
2912
|
-
while (rootSystem && !rootSystem._frontend) {
|
|
2913
|
-
rootSystem = rootSystem.hierarchyManager?.getOwnerEntity?.();
|
|
2914
|
-
}
|
|
2915
|
-
const frontend = rootSystem?._frontend;
|
|
2916
|
-
if (frontend) {
|
|
2917
|
-
frontend.interactionsHistory.push(interactionEvent);
|
|
2918
|
-
if (frontend.interactionsHistory.length > 50) {
|
|
2919
|
-
frontend.interactionsHistory.shift();
|
|
2920
|
-
}
|
|
2921
|
-
}
|
|
2922
|
-
if (typeof this.lifecycle.failure === "function") {
|
|
2923
|
-
this.lifecycle.failure({ error: err, input, caller });
|
|
2924
|
-
}
|
|
2925
|
-
return {
|
|
2926
|
-
status: "error",
|
|
2927
|
-
message: err.message,
|
|
2928
|
-
payload: null
|
|
2929
|
-
};
|
|
2930
|
-
}
|
|
2931
|
-
}
|
|
2932
|
-
async _checkRequires() {
|
|
2933
|
-
const r = this.requires;
|
|
2934
|
-
const entity = this.hostEntity;
|
|
2935
|
-
for (const fid of r.functionalitiesExecuted ?? []) {
|
|
2936
|
-
const sibling = entity?.localFunctionalities?.get(fid);
|
|
2937
|
-
if (!sibling?._hasBeenExecuted) {
|
|
2938
|
-
return `Requires functionality '${fid}' to have been executed first.`;
|
|
2939
|
-
}
|
|
2940
|
-
}
|
|
2941
|
-
for (const flag of r.flags ?? []) {
|
|
2942
|
-
if (!entity?._krnlFlags?.has(flag)) {
|
|
2943
|
-
return `Requires flag '${flag}' to be set. Check boot sequence or entity initialization.`;
|
|
2944
|
-
}
|
|
2945
|
-
}
|
|
2946
|
-
if (typeof r.check === "function") {
|
|
2947
|
-
const result = await r.check(entity);
|
|
2948
|
-
if (result) return result;
|
|
2949
|
-
}
|
|
2950
|
-
return null;
|
|
2951
|
-
}
|
|
2952
|
-
};
|
|
2953
|
-
var krnlRemoteFunctionality = class {
|
|
2954
|
-
id;
|
|
2955
|
-
targetFunction;
|
|
2956
|
-
targetEntity;
|
|
2957
|
-
visibility;
|
|
2958
|
-
uiSpec;
|
|
2959
|
-
schema;
|
|
2960
|
-
isLocal = false;
|
|
2961
|
-
transport;
|
|
2962
|
-
hostEntity;
|
|
2963
|
-
constructor(config = {}) {
|
|
2964
|
-
const { id, targetEntity, targetFunction, transport, visibility = "public", uiSpec, schema } = config;
|
|
2965
|
-
this.id = id;
|
|
2966
|
-
this.targetFunction = targetFunction || id;
|
|
2967
|
-
this.targetEntity = targetEntity;
|
|
2968
|
-
this.visibility = visibility;
|
|
2969
|
-
const targetLocalAtom = targetEntity?.localFunctionalities?.get?.(this.targetFunction);
|
|
2970
|
-
this.uiSpec = uiSpec || targetLocalAtom?.uiSpec || targetLocalAtom?.ui_spec;
|
|
2971
|
-
this.schema = schema || targetLocalAtom?.schema;
|
|
2972
|
-
if (transport) {
|
|
2973
|
-
this.transport = transport;
|
|
2974
|
-
} else if (targetEntity) {
|
|
2975
|
-
this.transport = new MemoryTransportAdapter(targetEntity);
|
|
2976
|
-
} else {
|
|
2977
|
-
throw new Error("RemoteFunctionality requires either a transport or a targetEntity.");
|
|
2978
|
-
}
|
|
2979
|
-
}
|
|
2980
|
-
setVisibility(v) {
|
|
2981
|
-
const oldVisibility = this.visibility;
|
|
2982
|
-
this.visibility = v;
|
|
2983
|
-
if (this.hostEntity && oldVisibility === "private" && v === "public") {
|
|
2984
|
-
this._registerInInterface();
|
|
2985
|
-
const owner = this.hostEntity.hierarchyManager.getOwnerEntity();
|
|
2986
|
-
if (owner) owner._onComponentFunctionalityAdded(this.hostEntity, this);
|
|
2987
|
-
} else if (this.hostEntity && oldVisibility === "public" && v === "private") {
|
|
2988
|
-
this._unregisterFromInterface();
|
|
2989
|
-
const owner = this.hostEntity.hierarchyManager.getOwnerEntity();
|
|
2990
|
-
if (owner && owner._onComponentFunctionalityRemoved) {
|
|
2991
|
-
owner._onComponentFunctionalityRemoved(this.hostEntity, this.id);
|
|
2992
|
-
}
|
|
2993
|
-
}
|
|
2994
|
-
}
|
|
2995
|
-
_unregisterFromInterface() {
|
|
2996
|
-
if (!this.hostEntity) return;
|
|
2997
|
-
this.hostEntity.interface.removeCollector(this.id);
|
|
2998
|
-
}
|
|
2999
|
-
_registerInInterface() {
|
|
3000
|
-
const mainCollector = this.hostEntity.interface.createCollector(this.id);
|
|
3001
|
-
mainCollector._directHandler = async (msg) => {
|
|
3002
|
-
const caller = msg.metadata?.caller || "anonymous";
|
|
3003
|
-
return await this.execute(msg.data, { caller });
|
|
3004
|
-
};
|
|
3005
|
-
}
|
|
3006
|
-
bindToEntity(entity) {
|
|
3007
|
-
this.hostEntity = entity;
|
|
3008
|
-
if (this.visibility === "public") {
|
|
3009
|
-
this._registerInInterface();
|
|
3010
|
-
}
|
|
3011
|
-
}
|
|
3012
|
-
async execute(input, opts = {}) {
|
|
3013
|
-
return await this.transport.call(this.targetFunction, input, { ...opts, caller: this.hostEntity.entityName });
|
|
3014
|
-
}
|
|
3015
|
-
};
|
|
3016
|
-
|
|
3017
|
-
// src/core/K_ENT_Krnl_Entity_Base.ts
|
|
3018
|
-
var krnlEntityBase = class _krnlEntityBase {
|
|
3019
|
-
__krnlEntityInstance;
|
|
3020
|
-
id;
|
|
3021
|
-
entityName;
|
|
3022
|
-
Singleton;
|
|
3023
|
-
baseName;
|
|
3024
|
-
instanceNumber;
|
|
3025
|
-
telemetry;
|
|
3026
|
-
_telemetryEnabled = false;
|
|
3027
|
-
get telemetryEnabled() {
|
|
3028
|
-
return this._telemetryEnabled;
|
|
3029
|
-
}
|
|
3030
|
-
set telemetryEnabled(val) {
|
|
3031
|
-
this._telemetryEnabled = val;
|
|
3032
|
-
this.telemetry.enabled = val;
|
|
3033
|
-
}
|
|
3034
|
-
systemType = "BaseEntity";
|
|
3035
|
-
interface;
|
|
3036
|
-
localFunctionalities;
|
|
3037
|
-
remoteFunctionalities;
|
|
3038
|
-
collectors;
|
|
3039
|
-
hierarchyManager;
|
|
3040
|
-
intention;
|
|
3041
|
-
backendSettings;
|
|
3042
|
-
/**
|
|
3043
|
-
* Configures backend services and credentials for this entity and cascading child functionalities.
|
|
3044
|
-
*/
|
|
3045
|
-
configureBackend(settings) {
|
|
3046
|
-
this.backendSettings = {
|
|
3047
|
-
...this.backendSettings,
|
|
3048
|
-
...settings
|
|
3049
|
-
};
|
|
3050
|
-
return this;
|
|
3051
|
-
}
|
|
3052
|
-
/**
|
|
3053
|
-
* Resolves backend credentials for a specific service, cascading from parent entities if necessary.
|
|
3054
|
-
*/
|
|
3055
|
-
getBackendCredentials(service) {
|
|
3056
|
-
if (this.backendSettings) {
|
|
3057
|
-
if (service && this.backendSettings[service]) {
|
|
3058
|
-
return this.backendSettings[service];
|
|
3059
|
-
}
|
|
3060
|
-
const defaultSvc = this.backendSettings.defaultService;
|
|
3061
|
-
if (defaultSvc && this.backendSettings[defaultSvc]) {
|
|
3062
|
-
return this.backendSettings[defaultSvc];
|
|
3063
|
-
}
|
|
3064
|
-
}
|
|
3065
|
-
const owner = this.hierarchyManager?.getOwnerEntity();
|
|
3066
|
-
if (owner && typeof owner.getBackendCredentials === "function") {
|
|
3067
|
-
return owner.getBackendCredentials(service);
|
|
3068
|
-
}
|
|
3069
|
-
return void 0;
|
|
3070
|
-
}
|
|
3071
|
-
// Extension points for decorators/middleware
|
|
3072
|
-
async _decorateResult(result, fid, input) {
|
|
3073
|
-
return result;
|
|
3074
|
-
}
|
|
3075
|
-
async _decorateError(error, fid, input) {
|
|
3076
|
-
return error;
|
|
3077
|
-
}
|
|
3078
|
-
/**
|
|
3079
|
-
* @param name - The unique identity of the entity.
|
|
3080
|
-
* @param owner - The parent entity (if any).
|
|
3081
|
-
*/
|
|
3082
|
-
constructor(name, owner = null, options = {}) {
|
|
3083
|
-
this.__krnlEntityInstance = true;
|
|
3084
|
-
this.id = name || "UnnamedEntity";
|
|
3085
|
-
this.entityName = this.id;
|
|
3086
|
-
this.Singleton = options.Singleton ?? this.constructor.Singleton ?? true;
|
|
3087
|
-
this.baseName = options.baseName || this.entityName;
|
|
3088
|
-
this.instanceNumber = options.instanceNumber || 1;
|
|
3089
|
-
this.telemetry = new KrnlTelemetry();
|
|
3090
|
-
this.telemetryEnabled = options.telemetryEnabled || false;
|
|
3091
|
-
this.intention = options.intention || null;
|
|
3092
|
-
this.interface = new krnlInterface(this);
|
|
3093
|
-
this.localFunctionalities = /* @__PURE__ */ new Map();
|
|
3094
|
-
this.remoteFunctionalities = /* @__PURE__ */ new Map();
|
|
3095
|
-
this.collectors = /* @__PURE__ */ new Set();
|
|
3096
|
-
this.hierarchyManager = new krnlEntityCompositionalHierarchyManager(
|
|
3097
|
-
this,
|
|
3098
|
-
// Host.
|
|
3099
|
-
owner,
|
|
3100
|
-
// Parent.
|
|
3101
|
-
_krnlEntityBase
|
|
3102
|
-
// Base class for nesting.
|
|
3103
|
-
);
|
|
3104
|
-
}
|
|
3105
|
-
/**
|
|
3106
|
-
* Registers a local logic block.
|
|
3107
|
-
* If the functionality is 'public', it is automatically promoted to the owner.
|
|
3108
|
-
*/
|
|
3109
|
-
addLocal(localFunctionality) {
|
|
3110
|
-
const functionality = localFunctionality instanceof krnlLocalFunctionality ? localFunctionality : new krnlLocalFunctionality(localFunctionality);
|
|
3111
|
-
functionality.bindToEntity(this);
|
|
3112
|
-
this.localFunctionalities.set(functionality.id, functionality);
|
|
3113
|
-
const owner = this.hierarchyManager.getOwnerEntity();
|
|
3114
|
-
if (owner && owner._onComponentFunctionalityAdded) {
|
|
3115
|
-
owner._onComponentFunctionalityAdded(this, functionality);
|
|
3116
|
-
}
|
|
3117
|
-
return functionality;
|
|
3118
|
-
}
|
|
3119
|
-
/**
|
|
3120
|
-
* Registers a proxy for a functionality on another entity.
|
|
3121
|
-
* If the proxy is 'public', it is promoted to the owner (Recursive Bubbling).
|
|
3122
|
-
*/
|
|
3123
|
-
addRemote(remoteFunctionality) {
|
|
3124
|
-
const functionality = remoteFunctionality instanceof krnlRemoteFunctionality ? remoteFunctionality : new krnlRemoteFunctionality(remoteFunctionality);
|
|
3125
|
-
functionality.bindToEntity(this);
|
|
3126
|
-
this.remoteFunctionalities.set(functionality.id, functionality);
|
|
3127
|
-
const owner = this.hierarchyManager.getOwnerEntity();
|
|
3128
|
-
if (owner && owner._onComponentFunctionalityAdded) {
|
|
3129
|
-
owner._onComponentFunctionalityAdded(this, functionality);
|
|
3130
|
-
}
|
|
3131
|
-
return functionality;
|
|
3132
|
-
}
|
|
3133
|
-
/**
|
|
3134
|
-
* INTERFACE PROMOTION
|
|
3135
|
-
* Handles the automatic bubbling of component logic to the owner.
|
|
3136
|
-
*/
|
|
3137
|
-
_onComponentFunctionalityAdded(component, functionality) {
|
|
3138
|
-
if (component._isIngressNetworkMember) return;
|
|
3139
|
-
if (functionality.visibility !== "public") return;
|
|
3140
|
-
const existing = this.remoteFunctionalities.get(functionality.id);
|
|
3141
|
-
if (existing) {
|
|
3142
|
-
existing.targetEntity = component;
|
|
3143
|
-
existing.targetFunction = functionality.id;
|
|
3144
|
-
return;
|
|
3145
|
-
}
|
|
3146
|
-
this.addRemote({
|
|
3147
|
-
id: functionality.id,
|
|
3148
|
-
targetEntity: component,
|
|
3149
|
-
targetFunction: functionality.id,
|
|
3150
|
-
visibility: "public"
|
|
3151
|
-
// Bubbles up further by default.
|
|
3152
|
-
});
|
|
3153
|
-
}
|
|
3154
|
-
/**
|
|
3155
|
-
* Removes a promoted remote functionality and recursively propagates removal upward.
|
|
3156
|
-
*/
|
|
3157
|
-
_onComponentFunctionalityRemoved(component, functionalityId) {
|
|
3158
|
-
const existing = this.remoteFunctionalities.get(functionalityId);
|
|
3159
|
-
if (!existing) return;
|
|
3160
|
-
if (existing.targetEntity !== component) return;
|
|
3161
|
-
this.remoteFunctionalities.delete(functionalityId);
|
|
3162
|
-
const owner = this.hierarchyManager.getOwnerEntity();
|
|
3163
|
-
if (owner && owner._onComponentFunctionalityRemoved) {
|
|
3164
|
-
owner._onComponentFunctionalityRemoved(this, functionalityId);
|
|
3165
|
-
}
|
|
3166
|
-
}
|
|
3167
|
-
/**
|
|
3168
|
-
* Internal helper to trigger promotion of all existing functionalities.
|
|
3169
|
-
*/
|
|
3170
|
-
_promoteAllExistingFunctionalities() {
|
|
3171
|
-
const owner = this.hierarchyManager.getOwnerEntity();
|
|
3172
|
-
if (!owner || !owner._onComponentFunctionalityAdded) return;
|
|
3173
|
-
this.localFunctionalities.forEach((f) => {
|
|
3174
|
-
owner._onComponentFunctionalityAdded(this, f);
|
|
3175
|
-
});
|
|
3176
|
-
this.remoteFunctionalities.forEach((f) => {
|
|
3177
|
-
owner._onComponentFunctionalityAdded(this, f);
|
|
3178
|
-
});
|
|
3179
|
-
}
|
|
3180
|
-
/**
|
|
3181
|
-
* Executes a functionality by ID.
|
|
3182
|
-
*/
|
|
3183
|
-
async call(fid, input, opts = {}) {
|
|
3184
|
-
const unwrap = opts.unwrap !== false;
|
|
3185
|
-
try {
|
|
3186
|
-
let result;
|
|
3187
|
-
if (this.localFunctionalities.has(fid)) {
|
|
3188
|
-
result = await this.localFunctionalities.get(fid).execute(input, opts);
|
|
3189
|
-
} else if (this.remoteFunctionalities.has(fid)) {
|
|
3190
|
-
result = await this.remoteFunctionalities.get(fid).execute(input, opts);
|
|
3191
|
-
} else {
|
|
3192
|
-
throw new krnlError(krnlErrorCodes.FUNCTIONALITY_NOT_DEFINED);
|
|
3193
|
-
}
|
|
3194
|
-
result = await this._decorateResult(result, fid, input);
|
|
3195
|
-
if (unwrap && result && typeof result === "object" && result.status === "success" && "payload" in result && !result.skipUnwrap) {
|
|
3196
|
-
return result.payload;
|
|
3197
|
-
}
|
|
3198
|
-
if (result && typeof result === "object" && result.status === "error") {
|
|
3199
|
-
throw new krnlError(result.message || krnlErrorCodes.REMOTE_EXECUTION_FAILED);
|
|
3200
|
-
}
|
|
3201
|
-
return result;
|
|
3202
|
-
} catch (err) {
|
|
3203
|
-
let finalErr = err;
|
|
3204
|
-
finalErr = await this._decorateError(err, fid, input);
|
|
3205
|
-
if (finalErr && typeof finalErr === "object" && finalErr.status === "error") {
|
|
3206
|
-
const wrapper = new krnlError(finalErr.message || krnlErrorCodes.REMOTE_EXECUTION_FAILED);
|
|
3207
|
-
Object.assign(wrapper, finalErr);
|
|
3208
|
-
throw wrapper;
|
|
3209
|
-
}
|
|
3210
|
-
throw finalErr;
|
|
3211
|
-
}
|
|
3212
|
-
}
|
|
3213
|
-
getLocal(fid) {
|
|
3214
|
-
return this.localFunctionalities.get(fid);
|
|
3215
|
-
}
|
|
3216
|
-
getRemote(fid) {
|
|
3217
|
-
return this.remoteFunctionalities.get(fid);
|
|
3218
|
-
}
|
|
3219
|
-
resolveLocalFunctionality(fid) {
|
|
3220
|
-
if (this.localFunctionalities.has(fid)) {
|
|
3221
|
-
return this.localFunctionalities.get(fid);
|
|
3222
|
-
}
|
|
3223
|
-
let current = this.remoteFunctionalities.get(fid);
|
|
3224
|
-
const visited = /* @__PURE__ */ new Set();
|
|
3225
|
-
while (current) {
|
|
3226
|
-
if (visited.has(current)) {
|
|
3227
|
-
break;
|
|
3228
|
-
}
|
|
3229
|
-
visited.add(current);
|
|
3230
|
-
if (current.isLocal) {
|
|
3231
|
-
return current;
|
|
3232
|
-
}
|
|
3233
|
-
const target = current.targetEntity || current.transport && (current.transport.targetEntity || current.transport.target || current.transport.instance);
|
|
3234
|
-
if (target) {
|
|
3235
|
-
const targetFid = current.targetFunction || fid;
|
|
3236
|
-
if (target.localFunctionalities && target.localFunctionalities.has(targetFid)) {
|
|
3237
|
-
return target.localFunctionalities.get(targetFid);
|
|
3238
|
-
}
|
|
3239
|
-
if (target.remoteFunctionalities && target.remoteFunctionalities.has(targetFid)) {
|
|
3240
|
-
current = target.remoteFunctionalities.get(targetFid);
|
|
3241
|
-
continue;
|
|
3242
|
-
}
|
|
3243
|
-
}
|
|
3244
|
-
break;
|
|
3245
|
-
}
|
|
3246
|
-
return null;
|
|
3247
|
-
}
|
|
3248
|
-
addChild(instance) {
|
|
3249
|
-
return this.hierarchyManager.addComponentEntityInstance(instance);
|
|
3250
|
-
}
|
|
3251
|
-
getTelemetry() {
|
|
3252
|
-
const summary = this.telemetry.getSummary();
|
|
3253
|
-
return {
|
|
3254
|
-
entity: this.entityName,
|
|
3255
|
-
timestamp: Date.now(),
|
|
3256
|
-
metrics: summary.metrics,
|
|
3257
|
-
callLogs: summary.callLogs,
|
|
3258
|
-
libraryUsage: summary.libraryUsage,
|
|
3259
|
-
dataUsage: summary.dataUsage
|
|
3260
|
-
};
|
|
3261
|
-
}
|
|
3262
|
-
disposeOwnedComponent(entityId) {
|
|
3263
|
-
return this.hierarchyManager.disposeComponentEntity(entityId);
|
|
3264
|
-
}
|
|
3265
|
-
dispose() {
|
|
3266
|
-
if (this.hierarchyManager) this.hierarchyManager.dispose();
|
|
3267
|
-
if (this.interface) this.interface.dispose();
|
|
3268
|
-
this.localFunctionalities.forEach((func) => {
|
|
3269
|
-
if (typeof func.teardown === "function") {
|
|
3270
|
-
func.teardown();
|
|
3271
|
-
}
|
|
3272
|
-
});
|
|
3273
|
-
this.localFunctionalities.clear();
|
|
3274
|
-
this.remoteFunctionalities.clear();
|
|
3275
|
-
}
|
|
3276
|
-
};
|
|
3277
|
-
|
|
3278
|
-
export {
|
|
3279
|
-
krnlErrorCodes,
|
|
3280
|
-
krnlErrorMessages,
|
|
3281
|
-
CUSTOM_ERROR_CODE,
|
|
3282
|
-
krnlError,
|
|
3283
|
-
krnlGlobalEmitterAddress,
|
|
3284
|
-
krnlLocalEmitterAddress,
|
|
3285
|
-
krnlMessage,
|
|
3286
|
-
krnlEmitter,
|
|
3287
|
-
krnlCollector,
|
|
3288
|
-
krnlInterface,
|
|
3289
|
-
krnlEntityCompositionalHierarchyManager,
|
|
3290
|
-
FunctionalityTable,
|
|
3291
|
-
SQLiteDataProvider,
|
|
3292
|
-
PostgresDataProvider,
|
|
3293
|
-
SupabaseDataProvider,
|
|
3294
|
-
PocketBaseDataProvider,
|
|
3295
|
-
FirebaseDataProvider,
|
|
3296
|
-
TursoLibSQLDataProvider,
|
|
3297
|
-
MongoAtlasDataProvider,
|
|
3298
|
-
FilesystemStorageProvider,
|
|
3299
|
-
SupabaseStorageProvider,
|
|
3300
|
-
S3StorageProvider,
|
|
3301
|
-
PocketBaseStorageProvider,
|
|
3302
|
-
FirebaseStorageProvider,
|
|
3303
|
-
VercelBlobStorageProvider,
|
|
3304
|
-
WorkspaceBackendFactory,
|
|
3305
|
-
LocalSQLiteAccountProvider,
|
|
3306
|
-
HostedAccountProvider,
|
|
3307
|
-
ControlPlaneManager,
|
|
3308
|
-
AdminGuard,
|
|
3309
|
-
AuditLogger,
|
|
3310
|
-
DatabaseClusterManager,
|
|
3311
|
-
krnlTransportAdapter,
|
|
3312
|
-
MemoryTransportAdapter,
|
|
3313
|
-
krnlLocalFunctionality,
|
|
3314
|
-
krnlRemoteFunctionality,
|
|
3315
|
-
krnlEntityBase
|
|
3316
|
-
};
|