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