@ultrst-armada/krnl 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -65
- package/package.json +13 -9
- package/packages/cli/dist/bin/krnl.cjs +1535 -9508
- package/packages/cli/dist/bin/krnl.js +55 -1915
- package/packages/cli/dist/chunk-6SHSECXR.js +1628 -0
- package/packages/cli/dist/index.cjs +1244 -7373
- package/packages/cli/dist/index.d.cts +45 -2
- package/packages/cli/dist/index.d.ts +45 -2
- package/packages/cli/dist/index.js +5 -20
- package/packages/cli/package.json +17 -10
- package/packages/core/dist/index.cjs +3039 -141
- package/packages/core/dist/index.d.cts +633 -27
- package/packages/core/dist/index.d.ts +633 -27
- package/packages/core/dist/index.js +3028 -139
- package/packages/core/package.json +15 -7
- package/packages/create-krnl/dist/index.cjs +322 -316
- package/packages/create-krnl/dist/index.js +322 -316
- package/packages/create-krnl/package.json +5 -3
- package/packages/eslint-plugin/dist/index.cjs +17 -55
- package/packages/eslint-plugin/dist/index.d.cts +8 -43
- package/packages/eslint-plugin/dist/index.d.ts +8 -43
- package/packages/eslint-plugin/dist/index.js +17 -55
- package/packages/eslint-plugin/package.json +5 -4
- package/packages/validate/dist/index.cjs +692 -361
- package/packages/validate/dist/index.d.cts +12 -14
- package/packages/validate/dist/index.d.ts +12 -14
- package/packages/validate/dist/index.js +692 -361
- package/packages/validate/package.json +14 -7
- package/packages/cli/dist/K_ENT_Entity-PQ5MURQY-ZG6FZXXX.js +0 -13
- package/packages/cli/dist/K_LIB_Krnl_Aiko_Client-OPZAVZCW-K4CRQYA5.js +0 -8
- package/packages/cli/dist/K_LIB_Krnl_Ep_Tools-ROOE2TQF-LNPXZYOE.js +0 -110
- package/packages/cli/dist/K_LIB_Krnl_Frontend_Builder-2ZDUH6PF-3ALZPNX4.js +0 -11
- package/packages/cli/dist/K_LIB_Krnl_Frontend_Generator-UYNRNX2X-PJTICYSW.js +0 -11
- package/packages/cli/dist/K_LIB_Krnl_Registry_Client-NXV6D2QW-M5TYU55O.js +0 -8
- package/packages/cli/dist/K_LIB_Krnl_SubApp_Engine-7HR5HWW2-H6STOXRK.js +0 -20
- package/packages/cli/dist/K_LIB_Krnl_SubApp_Registry-7XYGDKJK-XPJXSF7D.js +0 -19
- package/packages/cli/dist/K_LIB_Krnl_SubApp_Worker_Host-CQVTGGG7-CUYGTWOW.js +0 -7
- package/packages/cli/dist/K_LIB_Krnl_System_App_Adapter-M6FJKLGU-VR6T4N6J.js +0 -17
- package/packages/cli/dist/K_LIB_Krnl_System_Base_Functionality_APIs-ZZUZ6ZG5-RLJ4QWDP.js +0 -58
- package/packages/cli/dist/K_LIB_Krnl_System_Magnetic_Loader-XBQO2KXF-BOQGUASC.js +0 -22
- package/packages/cli/dist/K_LIB_Krnl_System_Unified_Http_Adapter-RCEGP3OG-XJI7KIT5.js +0 -8
- package/packages/cli/dist/K_LIB_Krnl_Telemetry-77FRZ3KV-K5VCWAFW.js +0 -9
- package/packages/cli/dist/chunk-3TRM6CYL.js +0 -152
- package/packages/cli/dist/chunk-5JDK35TG.js +0 -3872
- package/packages/cli/dist/chunk-63PTK33I.js +0 -997
- package/packages/cli/dist/chunk-6NIKAMUZ.js +0 -82
- package/packages/cli/dist/chunk-BOXWWXQG.js +0 -400
- package/packages/cli/dist/chunk-EEKLK7QJ.js +0 -565
- package/packages/cli/dist/chunk-EG7T2IAD.js +0 -107
- package/packages/cli/dist/chunk-F5EHXE2O.js +0 -1036
- package/packages/cli/dist/chunk-F65ANSQG.js +0 -269
- package/packages/cli/dist/chunk-FDNCADFE.js +0 -71
- package/packages/cli/dist/chunk-GDLRMDAN.js +0 -127
- package/packages/cli/dist/chunk-HWMTCMOS.js +0 -656
- package/packages/cli/dist/chunk-I74QIQOD.js +0 -5536
- package/packages/cli/dist/chunk-JTLSTO25.js +0 -71
- package/packages/cli/dist/chunk-JULBBJ27.js +0 -1033
- package/packages/cli/dist/chunk-OHVMVRBP.js +0 -117
- package/packages/cli/dist/chunk-P2PCLEKX.js +0 -666
- package/packages/cli/dist/chunk-PJQIUOX2.js +0 -7767
- package/packages/cli/dist/chunk-PPOF3DJW.js +0 -471
- package/packages/cli/dist/chunk-Q45257P6.js +0 -173
- package/packages/cli/dist/chunk-QNROWGBI.js +0 -1036
- package/packages/cli/dist/chunk-RJNYUXZD.js +0 -977
- package/packages/cli/dist/chunk-SRTOPJ5W.js +0 -104
- package/packages/cli/dist/chunk-TCEX3MPS.js +0 -702
- package/packages/cli/dist/chunk-TNOAJO2J.js +0 -675
- package/packages/cli/dist/chunk-UR2SUCSF.js +0 -701
- package/packages/cli/dist/chunk-X5LOIFM7.js +0 -68
- package/packages/cli/dist/chunk-XIRFOO3H.js +0 -589
- package/packages/cli/dist/chunk-ZAAV3JXQ.js +0 -809
- package/packages/core/dist/K_ENT_Entity-4MB7CU4A.js +0 -12
- package/packages/core/dist/K_ENT_Entity-DEJGIDGH.js +0 -12
- package/packages/core/dist/K_ENT_Entity-FXZO5UZ2.js +0 -12
- package/packages/core/dist/K_ENT_Entity-HDSCXC5F.js +0 -12
- package/packages/core/dist/K_ENT_Entity-JILABHLW.js +0 -12
- package/packages/core/dist/K_ENT_Entity-PQ5MURQY.js +0 -13
- package/packages/core/dist/K_ENT_Entity-ROAKDIHW.js +0 -12
- package/packages/core/dist/K_ENT_Entity-TBVFSKDV.js +0 -12
- package/packages/core/dist/K_ENT_Entity-ZFLKG3JO.js +0 -12
- package/packages/core/dist/K_LIB_Krnl_Aiko_Client-OPZAVZCW.js +0 -8
- package/packages/core/dist/K_LIB_Krnl_Ep_Tools-ROOE2TQF.js +0 -110
- package/packages/core/dist/K_LIB_Krnl_Frontend_Builder-2ZDUH6PF.js +0 -9
- package/packages/core/dist/K_LIB_Krnl_Frontend_Generator-UYNRNX2X.js +0 -9
- package/packages/core/dist/K_LIB_Krnl_Registry_Client-NXV6D2QW.js +0 -8
- package/packages/core/dist/K_LIB_Krnl_SubApp_Engine-7HR5HWW2.js +0 -20
- package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-4C6GBQAG.js +0 -18
- package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-6V23A6F6.js +0 -18
- package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-7XYGDKJK.js +0 -19
- package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-DYLLC5G2.js +0 -18
- package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-F4FOFAUP.js +0 -18
- package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-FBOXXMI3.js +0 -18
- package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-Q73BWLNP.js +0 -18
- package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-QM2C4MVA.js +0 -18
- package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-QUGI6SMP.js +0 -18
- package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-RGMTBHNP.js +0 -18
- package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-RWR7UNDW.js +0 -18
- package/packages/core/dist/K_LIB_Krnl_SubApp_Worker_Host-CQVTGGG7.js +0 -7
- package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-32MFJJ5Z.js +0 -17
- package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-4J4CEWRY.js +0 -17
- package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-A366YHF2.js +0 -17
- package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-LK4LYJHI.js +0 -17
- package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-M6FJKLGU.js +0 -17
- package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-NW3K63II.js +0 -17
- package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-QDHTI5ZV.js +0 -17
- package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-VHCT6VIW.js +0 -17
- package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-WBRKF24R.js +0 -17
- package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-XKYQYT6Q.js +0 -17
- package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-Z2PWZYDZ.js +0 -17
- package/packages/core/dist/K_LIB_Krnl_System_Base_Functionality_APIs-ZZUZ6ZG5.js +0 -58
- package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-2EZU7QF4.js +0 -21
- package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-6G3AQNAV.js +0 -21
- package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-B2NZMQUG.js +0 -21
- package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-C2RABMZP.js +0 -21
- package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-HHE3NZ4F.js +0 -21
- package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-LHFGR6WK.js +0 -21
- package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-M74BLM3N.js +0 -21
- package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-PT6ZA6EA.js +0 -21
- package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-SP52V3CX.js +0 -21
- package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-XBQO2KXF.js +0 -22
- package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-YNSEUHJJ.js +0 -21
- package/packages/core/dist/K_LIB_Krnl_System_Unified_Http_Adapter-M6SBHIVA.js +0 -8
- package/packages/core/dist/K_LIB_Krnl_System_Unified_Http_Adapter-RCEGP3OG.js +0 -8
- package/packages/core/dist/K_LIB_Krnl_Telemetry-77FRZ3KV.js +0 -9
- package/packages/core/dist/chunk-33L2J7V7.js +0 -82
- package/packages/core/dist/chunk-3ES4K2ZP.js +0 -817
- package/packages/core/dist/chunk-3SMHVVLL.js +0 -82
- package/packages/core/dist/chunk-3UEZ7JQA.js +0 -4877
- package/packages/core/dist/chunk-3XIE6KBD.js +0 -4877
- package/packages/core/dist/chunk-46ZLCML6.js +0 -107
- package/packages/core/dist/chunk-4UEL3DWJ.js +0 -107
- package/packages/core/dist/chunk-54GWZ5PC.js +0 -82
- package/packages/core/dist/chunk-6QCYRS5L.js +0 -127
- package/packages/core/dist/chunk-6ULMHNXR.js +0 -104
- package/packages/core/dist/chunk-7WUETJEV.js +0 -117
- package/packages/core/dist/chunk-A5KRAYDB.js +0 -107
- package/packages/core/dist/chunk-ANEAZ3PO.js +0 -107
- package/packages/core/dist/chunk-BE76RL6F.js +0 -82
- package/packages/core/dist/chunk-CIARVYDO.js +0 -107
- package/packages/core/dist/chunk-CR2ANE62.js +0 -107
- package/packages/core/dist/chunk-DI27MOR7.js +0 -173
- package/packages/core/dist/chunk-E6RLFHM5.js +0 -596
- package/packages/core/dist/chunk-EELAPMNV.js +0 -4877
- package/packages/core/dist/chunk-ENZ2RQVJ.js +0 -107
- package/packages/core/dist/chunk-F2U6CX4F.js +0 -4877
- package/packages/core/dist/chunk-FKG64OHL.js +0 -82
- package/packages/core/dist/chunk-FYAZJYM5.js +0 -772
- package/packages/core/dist/chunk-G4NP4RDQ.js +0 -82
- package/packages/core/dist/chunk-GADIRLRS.js +0 -473
- package/packages/core/dist/chunk-GR6CSLQJ.js +0 -772
- package/packages/core/dist/chunk-HDS7AT6O.js +0 -772
- package/packages/core/dist/chunk-HIMEZUU2.js +0 -71
- package/packages/core/dist/chunk-IB4UHG5I.js +0 -596
- package/packages/core/dist/chunk-J7ZYNVRS.js +0 -772
- package/packages/core/dist/chunk-JVLZU4K7.js +0 -604
- package/packages/core/dist/chunk-JYXXI7Q3.js +0 -82
- package/packages/core/dist/chunk-K54K2KZW.js +0 -192
- package/packages/core/dist/chunk-K6VHLBNH.js +0 -3316
- package/packages/core/dist/chunk-KCBP7ZYA.js +0 -596
- package/packages/core/dist/chunk-KFQGP6VL.js +0 -33
- package/packages/core/dist/chunk-KIUEUI7K.js +0 -82
- package/packages/core/dist/chunk-LB67NIZP.js +0 -949
- package/packages/core/dist/chunk-LDTYFUNW.js +0 -596
- package/packages/core/dist/chunk-MLVWARD4.js +0 -596
- package/packages/core/dist/chunk-MPYVXHKO.js +0 -2777
- package/packages/core/dist/chunk-MRQ3MITU.js +0 -772
- package/packages/core/dist/chunk-MVUN7I7O.js +0 -107
- package/packages/core/dist/chunk-NDDOIRW7.js +0 -596
- package/packages/core/dist/chunk-O3KDB5MH.js +0 -3330
- package/packages/core/dist/chunk-OEINUDSE.js +0 -400
- package/packages/core/dist/chunk-OPDENPT4.js +0 -4877
- package/packages/core/dist/chunk-P6XEM2LA.js +0 -429
- package/packages/core/dist/chunk-QKMRSQV4.js +0 -82
- package/packages/core/dist/chunk-R6CCWALL.js +0 -4877
- package/packages/core/dist/chunk-RAMMOO73.js +0 -774
- package/packages/core/dist/chunk-S3FTZFG6.js +0 -772
- package/packages/core/dist/chunk-T2ZVN434.js +0 -4877
- package/packages/core/dist/chunk-TFHLDMIA.js +0 -107
- package/packages/core/dist/chunk-TI3QUCIJ.js +0 -107
- package/packages/core/dist/chunk-TR2BAIGI.js +0 -107
- package/packages/core/dist/chunk-UGVVJKC6.js +0 -4877
- package/packages/core/dist/chunk-V2DX7YIU.js +0 -984
- package/packages/core/dist/chunk-W4IHYUYD.js +0 -772
- package/packages/core/dist/chunk-W6WBG5OY.js +0 -4877
- package/packages/core/dist/chunk-WN3M7O7H.js +0 -3312
- package/packages/core/dist/chunk-WR3NCU7U.js +0 -4877
- package/packages/core/dist/chunk-XC6B6L27.js +0 -82
- package/packages/core/dist/chunk-XNSE64ZY.js +0 -3316
- package/packages/core/dist/chunk-XROES2WN.js +0 -634
- package/packages/core/dist/chunk-XSCKNRCZ.js +0 -5020
- package/packages/core/dist/chunk-XVABPAUZ.js +0 -82
- package/packages/core/dist/chunk-YOBCB5C5.js +0 -596
- package/packages/core/dist/chunk-YUK6Q563.js +0 -666
- package/packages/core/dist/chunk-YXGL3ZEU.js +0 -1030
- package/packages/core/dist/chunk-ZINJ5OUX.js +0 -596
- package/packages/core/dist/chunk-ZPFD4BIQ.js +0 -604
- package/packages/core/dist/chunk-ZQZBFG6F.js +0 -4826
- package/packages/core/dist/chunk-ZZFRJGXM.js +0 -4877
- package/packages/create-krnl/dist/K_ENT_Entity-PQ5MURQY-R2OH4VXE.js +0 -14
- package/packages/create-krnl/dist/K_LIB_Krnl_Aiko_Client-OPZAVZCW-AZM5HF4M.js +0 -9
- package/packages/create-krnl/dist/K_LIB_Krnl_Ep_Tools-ROOE2TQF-X5N6SV2S.js +0 -111
- package/packages/create-krnl/dist/K_LIB_Krnl_Frontend_Builder-2ZDUH6PF-5PFHF4JU.js +0 -12
- package/packages/create-krnl/dist/K_LIB_Krnl_Frontend_Generator-UYNRNX2X-ED33OQPH.js +0 -12
- package/packages/create-krnl/dist/K_LIB_Krnl_Registry_Client-NXV6D2QW-YGQLVR4P.js +0 -9
- package/packages/create-krnl/dist/K_LIB_Krnl_SubApp_Engine-7HR5HWW2-VM7CTU2L.js +0 -21
- package/packages/create-krnl/dist/K_LIB_Krnl_SubApp_Registry-7XYGDKJK-Y2GQFJ7I.js +0 -21
- package/packages/create-krnl/dist/K_LIB_Krnl_SubApp_Worker_Host-CQVTGGG7-L3JQWO54.js +0 -8
- package/packages/create-krnl/dist/K_LIB_Krnl_System_App_Adapter-M6FJKLGU-KQARASPR.js +0 -19
- package/packages/create-krnl/dist/K_LIB_Krnl_System_Base_Functionality_APIs-ZZUZ6ZG5-OF3IGLVL.js +0 -59
- package/packages/create-krnl/dist/K_LIB_Krnl_System_Magnetic_Loader-XBQO2KXF-JOLHKOEX.js +0 -24
- package/packages/create-krnl/dist/K_LIB_Krnl_System_Unified_Http_Adapter-RCEGP3OG-L46LSHKQ.js +0 -10
- package/packages/create-krnl/dist/K_LIB_Krnl_Telemetry-77FRZ3KV-4JUPRR3X.js +0 -10
- package/packages/create-krnl/dist/chunk-3TRM6CYL.js +0 -152
- package/packages/create-krnl/dist/chunk-3URCB3QS.js +0 -127
- package/packages/create-krnl/dist/chunk-5JDK35TG.js +0 -3872
- package/packages/create-krnl/dist/chunk-A2XAXDOW.js +0 -812
- package/packages/create-krnl/dist/chunk-AN6TMQHG.js +0 -1033
- package/packages/create-krnl/dist/chunk-BOXWWXQG.js +0 -400
- package/packages/create-krnl/dist/chunk-BPTBREBG.js +0 -38
- package/packages/create-krnl/dist/chunk-FDNCADFE.js +0 -71
- package/packages/create-krnl/dist/chunk-JTLSTO25.js +0 -71
- package/packages/create-krnl/dist/chunk-M3NCUIPL.js +0 -107
- package/packages/create-krnl/dist/chunk-N2EONSOC.js +0 -7769
- package/packages/create-krnl/dist/chunk-OHVMVRBP.js +0 -117
- package/packages/create-krnl/dist/chunk-Q45257P6.js +0 -173
- package/packages/create-krnl/dist/chunk-QGM4M3NI.js +0 -37
- package/packages/create-krnl/dist/chunk-RJNYUXZD.js +0 -977
- package/packages/create-krnl/dist/chunk-SRTOPJ5W.js +0 -104
- package/packages/create-krnl/dist/chunk-UPBIGEC3.js +0 -666
- package/packages/create-krnl/dist/chunk-V2DB7UD7.js +0 -82
- package/packages/create-krnl/dist/chunk-WMHLIUI6.js +0 -3644
- package/packages/create-krnl/dist/chunk-XIRFOO3H.js +0 -589
- package/packages/create-krnl/dist/chunk-Y2CEEVZP.js +0 -5536
- package/packages/create-krnl/dist/main-DXUMPBND.js +0 -2217
- package/packages/create-krnl/dist/wrapper-SDF2VKX3.js +0 -31
|
@@ -35,361 +35,386 @@ __export(index_exports, {
|
|
|
35
35
|
module.exports = __toCommonJS(index_exports);
|
|
36
36
|
|
|
37
37
|
// src/KrnlValidatorEngine.ts
|
|
38
|
-
var
|
|
39
|
-
var
|
|
40
|
-
var
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
} else {
|
|
57
|
-
this.project = new import_ts_morph.Project({ compilerOptions: {
|
|
58
|
-
target: 99
|
|
59
|
-
/* ESNext */
|
|
60
|
-
} });
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Scans and validates all source files in the project.
|
|
65
|
-
*/
|
|
66
|
-
validateAll() {
|
|
67
|
-
const files = this.collectSourceFiles(this.projectRoot);
|
|
68
|
-
const errors = [];
|
|
69
|
-
const warnings = [];
|
|
70
|
-
this.validateWorkspaceLayout(errors);
|
|
71
|
-
for (const f of files) {
|
|
72
|
-
const sf = this.project.addSourceFileAtPath(f);
|
|
73
|
-
const fileReport = this.validateSourceFile(sf);
|
|
74
|
-
errors.push(...fileReport.errors);
|
|
75
|
-
warnings.push(...fileReport.warnings);
|
|
76
|
-
}
|
|
77
|
-
return {
|
|
78
|
-
valid: errors.length === 0,
|
|
79
|
-
totalFiles: files.length,
|
|
80
|
-
errors,
|
|
81
|
-
warnings
|
|
82
|
-
};
|
|
38
|
+
var fs2 = __toESM(require("fs"), 1);
|
|
39
|
+
var path3 = __toESM(require("path"), 1);
|
|
40
|
+
var import_ts_morph5 = require("ts-morph");
|
|
41
|
+
|
|
42
|
+
// src/rules/fileLimits.ts
|
|
43
|
+
function checkFileLimits(sf, options) {
|
|
44
|
+
const errors = [];
|
|
45
|
+
const warnings = [];
|
|
46
|
+
const filePath = sf.getFilePath();
|
|
47
|
+
const sourceText = sf.getFullText();
|
|
48
|
+
const lines = sourceText.split("\n");
|
|
49
|
+
if (lines.length > options.maxFileLineLimit) {
|
|
50
|
+
errors.push({
|
|
51
|
+
ruleId: "krnl/file-line-limit",
|
|
52
|
+
filePath,
|
|
53
|
+
message: `File exceeds maximum length of ${options.maxFileLineLimit} lines (currently ${lines.length} lines). Decompose into sub-components, custom hooks, quad-units, or concept libraries.`,
|
|
54
|
+
severity: "error"
|
|
55
|
+
});
|
|
83
56
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
validateSourceFile(sf) {
|
|
88
|
-
const errors = [];
|
|
89
|
-
const warnings = [];
|
|
90
|
-
const filePath = sf.getFilePath();
|
|
91
|
-
const sourceText = sf.getText();
|
|
92
|
-
const baseName = path.basename(filePath);
|
|
93
|
-
if (baseName.includes("_pb.") || baseName.endsWith(".d.ts")) {
|
|
94
|
-
return { errors, warnings };
|
|
95
|
-
}
|
|
96
|
-
const lines = sourceText.split("\n");
|
|
97
|
-
if (lines.length > this.maxFileLineLimit) {
|
|
57
|
+
for (const decl of sf.getImportDeclarations()) {
|
|
58
|
+
const moduleSpec = decl.getModuleSpecifierValue();
|
|
59
|
+
if (moduleSpec.startsWith(".") && !moduleSpec.endsWith(".js") && !moduleSpec.endsWith(".json") && !moduleSpec.endsWith(".css") && !moduleSpec.endsWith(".svg") && !moduleSpec.endsWith(".png")) {
|
|
98
60
|
errors.push({
|
|
99
|
-
ruleId: "krnl/
|
|
61
|
+
ruleId: "krnl/import-suffix",
|
|
100
62
|
filePath,
|
|
101
|
-
|
|
63
|
+
line: decl.getStartLineNumber(),
|
|
64
|
+
message: `Relative import '${moduleSpec}' must end with '.js' for NodeNext module resolution.`,
|
|
102
65
|
severity: "error"
|
|
103
66
|
});
|
|
104
67
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
68
|
+
}
|
|
69
|
+
const isHelper = filePath.includes("helper_functions") || filePath.includes("/helpers/") || filePath.includes("\\helpers\\");
|
|
70
|
+
if (isHelper) {
|
|
71
|
+
for (const fn of sf.getFunctions()) {
|
|
72
|
+
const fnLines = fn.getText().split("\n").length;
|
|
73
|
+
if (fnLines > options.maxHelperLineLimit && !sourceText.includes("Concepts.")) {
|
|
109
74
|
errors.push({
|
|
110
|
-
ruleId: "krnl/
|
|
75
|
+
ruleId: "krnl/helper-line-limit",
|
|
111
76
|
filePath,
|
|
112
|
-
line,
|
|
113
|
-
message: `
|
|
77
|
+
line: fn.getStartLineNumber(),
|
|
78
|
+
message: `Helper function '${fn.getName() || "anonymous"}' exceeds ${options.maxHelperLineLimit} lines (${fnLines} lines). Must delegate to a root libraries/{Domain}Concepts.ts library.`,
|
|
114
79
|
severity: "error"
|
|
115
80
|
});
|
|
116
81
|
}
|
|
117
82
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
severity: "error"
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
const calls2 = sf.getDescendantsOfKind(import_ts_morph.SyntaxKind.CallExpression);
|
|
137
|
-
for (const call of calls2) {
|
|
138
|
-
const exprText = call.getExpression().getText();
|
|
139
|
-
if (["useState", "useEffect", "useRef", "useMemo", "useCallback", "useReducer", "useContext", "useLayoutEffect"].includes(exprText) || /^use[A-Z]/.test(exprText) && exprText !== "use") {
|
|
140
|
-
errors.push({
|
|
141
|
-
ruleId: "krnl/top-level-purity",
|
|
142
|
-
filePath,
|
|
143
|
-
line: call.getStartLineNumber(),
|
|
144
|
-
message: `Top-level file '${baseName}' violates Top-Level Purity Law. React hook '${exprText}' is forbidden in top-level files. Encapsulate state and effects inside dedicated component files.`,
|
|
145
|
-
severity: "error"
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
}
|
|
83
|
+
}
|
|
84
|
+
const isMainLogic = filePath.endsWith("logic.ts") || filePath.endsWith("logic.js");
|
|
85
|
+
if (isMainLogic) {
|
|
86
|
+
const substantiveLines = lines.filter((l) => {
|
|
87
|
+
const trimmed = l.trim();
|
|
88
|
+
return trimmed.length > 0 && !trimmed.startsWith("//") && !trimmed.startsWith("/*") && !trimmed.startsWith("*");
|
|
89
|
+
});
|
|
90
|
+
if (substantiveLines.length > options.maxMainLogicLimit && !sourceText.includes("helpers") && !sourceText.includes("Concepts") && !sourceText.includes("state")) {
|
|
91
|
+
errors.push({
|
|
92
|
+
ruleId: "krnl/main-delegation",
|
|
93
|
+
filePath,
|
|
94
|
+
message: `Main logic function has ${substantiveLines.length} substantive lines without delegating to Layer 1 helper functions or Layer 0 libraries.`,
|
|
95
|
+
severity: "error"
|
|
96
|
+
});
|
|
149
97
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
98
|
+
}
|
|
99
|
+
return { errors, warnings };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// src/rules/componentRules.ts
|
|
103
|
+
var path = __toESM(require("path"), 1);
|
|
104
|
+
var import_ts_morph = require("ts-morph");
|
|
105
|
+
function checkComponentRules(sf) {
|
|
106
|
+
const errors = [];
|
|
107
|
+
const warnings = [];
|
|
108
|
+
const filePath = sf.getFilePath();
|
|
109
|
+
const baseName = path.basename(filePath);
|
|
110
|
+
if (filePath.endsWith(".tsx") || filePath.endsWith(".jsx")) {
|
|
111
|
+
const componentNames = [];
|
|
112
|
+
for (const fn of sf.getFunctions()) {
|
|
113
|
+
const name = fn.getName();
|
|
114
|
+
if (name && /^[A-Z]/.test(name)) {
|
|
115
|
+
const hasJsx = fn.getDescendantsOfKind(import_ts_morph.SyntaxKind.JsxElement).length > 0 || fn.getDescendantsOfKind(import_ts_morph.SyntaxKind.JsxSelfClosingElement).length > 0 || fn.getDescendantsOfKind(import_ts_morph.SyntaxKind.JsxFragment).length > 0;
|
|
116
|
+
if (hasJsx) componentNames.push(name);
|
|
163
117
|
}
|
|
164
118
|
}
|
|
165
|
-
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
for (const stmt of sf.getVariableStatements()) {
|
|
177
|
-
for (const decl of stmt.getDeclarations()) {
|
|
178
|
-
const name = decl.getName();
|
|
179
|
-
if (/^[A-Z]/.test(name)) {
|
|
180
|
-
const typeText = decl.getTypeNode()?.getText() || "";
|
|
181
|
-
const isFcType = typeText.includes("FC") || typeText.includes("FunctionComponent") || typeText.includes("ComponentType");
|
|
182
|
-
const init = decl.getInitializer();
|
|
183
|
-
if (init) {
|
|
184
|
-
const hasJsx = init.getDescendantsOfKind(import_ts_morph.SyntaxKind.JsxElement).length > 0 || init.getDescendantsOfKind(import_ts_morph.SyntaxKind.JsxSelfClosingElement).length > 0 || init.getDescendantsOfKind(import_ts_morph.SyntaxKind.JsxFragment).length > 0;
|
|
185
|
-
if (isFcType || hasJsx) {
|
|
186
|
-
if (!componentNames.includes(name)) {
|
|
187
|
-
componentNames.push(name);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
119
|
+
for (const stmt of sf.getVariableStatements()) {
|
|
120
|
+
for (const decl of stmt.getDeclarations()) {
|
|
121
|
+
const name = decl.getName();
|
|
122
|
+
if (/^[A-Z]/.test(name)) {
|
|
123
|
+
const typeText = decl.getTypeNode()?.getText() || "";
|
|
124
|
+
const isFcType = typeText.includes("FC") || typeText.includes("FunctionComponent") || typeText.includes("ComponentType");
|
|
125
|
+
const init = decl.getInitializer();
|
|
126
|
+
if (init) {
|
|
127
|
+
const hasJsx = init.getDescendantsOfKind(import_ts_morph.SyntaxKind.JsxElement).length > 0 || init.getDescendantsOfKind(import_ts_morph.SyntaxKind.JsxSelfClosingElement).length > 0 || init.getDescendantsOfKind(import_ts_morph.SyntaxKind.JsxFragment).length > 0;
|
|
128
|
+
if (isFcType || hasJsx) {
|
|
129
|
+
if (!componentNames.includes(name)) componentNames.push(name);
|
|
190
130
|
}
|
|
191
131
|
}
|
|
192
132
|
}
|
|
193
133
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}
|
|
201
|
-
}
|
|
134
|
+
}
|
|
135
|
+
for (const cls of sf.getClasses()) {
|
|
136
|
+
const name = cls.getName();
|
|
137
|
+
const ext = cls.getExtends()?.getText() || "";
|
|
138
|
+
if (name && (ext.includes("Component") || ext.includes("PureComponent"))) {
|
|
139
|
+
if (!componentNames.includes(name)) componentNames.push(name);
|
|
202
140
|
}
|
|
203
|
-
|
|
141
|
+
}
|
|
142
|
+
if (componentNames.length > 1) {
|
|
143
|
+
errors.push({
|
|
144
|
+
ruleId: "krnl/one-component-per-file",
|
|
145
|
+
filePath,
|
|
146
|
+
message: `File '${baseName}' defines multiple React components (${componentNames.join(
|
|
147
|
+
", "
|
|
148
|
+
)}). Krnl strictly enforces ONE component per file. Decompose into sub-component files.`,
|
|
149
|
+
severity: "error"
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
const isHelper = filePath.includes("helper_functions") || filePath.includes("/helpers/") || filePath.includes("\\helpers\\");
|
|
154
|
+
if (isHelper) {
|
|
155
|
+
for (const decl of sf.getImportDeclarations()) {
|
|
156
|
+
const spec = decl.getModuleSpecifierValue();
|
|
157
|
+
if (["fs", "node:fs", "http", "node:http", "ws"].includes(spec)) {
|
|
204
158
|
errors.push({
|
|
205
|
-
ruleId: "krnl/
|
|
159
|
+
ruleId: "krnl/helper-purity",
|
|
206
160
|
filePath,
|
|
207
|
-
|
|
161
|
+
line: decl.getStartLineNumber(),
|
|
162
|
+
message: `Helper functions (Layer 1) cannot directly import I/O modules ('${spec}'). Delegate to Layer 0 libraries or ServiceFacade context.`,
|
|
208
163
|
severity: "error"
|
|
209
164
|
});
|
|
210
165
|
}
|
|
211
166
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
filePath,
|
|
234
|
-
line: fn.getStartLineNumber(),
|
|
235
|
-
message: `Helper function '${fn.getName() || "anonymous"}' exceeds ${this.maxHelperLineLimit} lines (${fnLines} lines). Must delegate to a root libraries/{Domain}Concepts.ts library.`,
|
|
236
|
-
severity: "error"
|
|
237
|
-
});
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
if (isMainLogic) {
|
|
242
|
-
const substantiveLines = lines.filter((l) => {
|
|
243
|
-
const trimmed = l.trim();
|
|
244
|
-
return trimmed.length > 0 && !trimmed.startsWith("//") && !trimmed.startsWith("/*") && !trimmed.startsWith("*");
|
|
167
|
+
}
|
|
168
|
+
return { errors, warnings };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// src/rules/orchestrationRules.ts
|
|
172
|
+
var import_ts_morph2 = require("ts-morph");
|
|
173
|
+
function checkOrchestrationRules(sf) {
|
|
174
|
+
const errors = [];
|
|
175
|
+
const warnings = [];
|
|
176
|
+
const filePath = sf.getFilePath();
|
|
177
|
+
const sourceText = sf.getText();
|
|
178
|
+
const calls = sf.getDescendantsOfKind(import_ts_morph2.SyntaxKind.CallExpression);
|
|
179
|
+
for (const call of calls) {
|
|
180
|
+
const exprText = call.getExpression().getText();
|
|
181
|
+
if (exprText === "setInterval" || exprText === "window.setInterval") {
|
|
182
|
+
errors.push({
|
|
183
|
+
ruleId: "krnl/no-polling",
|
|
184
|
+
filePath,
|
|
185
|
+
line: call.getStartLineNumber(),
|
|
186
|
+
message: `Prohibited use of 'setInterval'. Krnl applications must use reactive event streams or triggers.`,
|
|
187
|
+
severity: "error"
|
|
245
188
|
});
|
|
246
|
-
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (sourceText.includes("main = async") || sourceText.includes("export const main = async")) {
|
|
192
|
+
const match = sourceText.match(/main\s*=\s*async\s*\([^,]+,\s*\{([^}]*)\}\s*\)/);
|
|
193
|
+
if (match) {
|
|
194
|
+
const destructured = match[1];
|
|
195
|
+
if (/\bsystem\b/.test(destructured)) {
|
|
247
196
|
errors.push({
|
|
248
|
-
ruleId: "krnl/
|
|
197
|
+
ruleId: "krnl/context-destructuring",
|
|
249
198
|
filePath,
|
|
250
|
-
message: `
|
|
199
|
+
message: `Functionality context parameter destructures 'system'. Functionality passes { state, orchestration } or service context.`,
|
|
251
200
|
severity: "error"
|
|
252
201
|
});
|
|
253
202
|
}
|
|
254
203
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
severity: "error"
|
|
265
|
-
});
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
const calls = sf.getDescendantsOfKind(import_ts_morph.SyntaxKind.CallExpression);
|
|
270
|
-
for (const call of calls) {
|
|
271
|
-
const exprText = call.getExpression().getText();
|
|
272
|
-
if (exprText === "setInterval" || exprText === "window.setInterval") {
|
|
204
|
+
}
|
|
205
|
+
const isOrchestrationClass = sf.getClasses().some((c) => {
|
|
206
|
+
const ext = c.getExtends();
|
|
207
|
+
return ext && (ext.getText().includes("FunctionalityOrchestration") || ext.getText().includes("Orchestration") || ext.getText().includes("krnlOrchestrationBase"));
|
|
208
|
+
});
|
|
209
|
+
if (isOrchestrationClass) {
|
|
210
|
+
for (const decl of sf.getImportDeclarations()) {
|
|
211
|
+
const spec = decl.getModuleSpecifierValue();
|
|
212
|
+
if (spec.includes("libraries/") || spec.includes("Concepts")) {
|
|
273
213
|
errors.push({
|
|
274
|
-
ruleId: "krnl/no-
|
|
214
|
+
ruleId: "krnl/orchestration-no-libraries",
|
|
275
215
|
filePath,
|
|
276
|
-
line:
|
|
277
|
-
message: `
|
|
216
|
+
line: decl.getStartLineNumber(),
|
|
217
|
+
message: `FunctionalityOrchestration class violates Library Calling Invariant. FunctionalityOrchestrations must not call or import libraries directly ('${spec}'). Only functionalities are permitted to call shared libraries.`,
|
|
278
218
|
severity: "error"
|
|
279
219
|
});
|
|
280
220
|
}
|
|
281
221
|
}
|
|
282
|
-
const
|
|
283
|
-
const ext =
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
222
|
+
for (const cls of sf.getClasses()) {
|
|
223
|
+
const ext = cls.getExtends();
|
|
224
|
+
if (ext && (ext.getText().includes("FunctionalityOrchestration") || ext.getText().includes("Orchestration") || ext.getText().includes("krnlOrchestrationBase"))) {
|
|
225
|
+
const loops = [
|
|
226
|
+
...cls.getDescendantsOfKind(import_ts_morph2.SyntaxKind.ForStatement),
|
|
227
|
+
...cls.getDescendantsOfKind(import_ts_morph2.SyntaxKind.ForOfStatement),
|
|
228
|
+
...cls.getDescendantsOfKind(import_ts_morph2.SyntaxKind.ForInStatement),
|
|
229
|
+
...cls.getDescendantsOfKind(import_ts_morph2.SyntaxKind.WhileStatement),
|
|
230
|
+
...cls.getDescendantsOfKind(import_ts_morph2.SyntaxKind.DoStatement)
|
|
231
|
+
];
|
|
232
|
+
for (const loop of loops) {
|
|
290
233
|
errors.push({
|
|
291
|
-
ruleId: "krnl/
|
|
234
|
+
ruleId: "krnl/orchestration-purity",
|
|
292
235
|
filePath,
|
|
293
|
-
line:
|
|
294
|
-
message: `
|
|
236
|
+
line: loop.getStartLineNumber(),
|
|
237
|
+
message: `FunctionalityOrchestration '${cls.getName() || "Anonymous"}' violates Orchestration Purity Law. Loops and procedural algorithms are prohibited inside orchestrations. Move logic to a functionality or a global library function.`,
|
|
295
238
|
severity: "error"
|
|
296
239
|
});
|
|
297
240
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
}
|
|
315
|
-
if (loadAllLine !== -1) {
|
|
316
|
-
for (const al of addLocalLines) {
|
|
317
|
-
if (al.hasUiSpec && al.line > loadAllLine) {
|
|
241
|
+
const allowedMethods = /* @__PURE__ */ new Set([
|
|
242
|
+
"flow",
|
|
243
|
+
"getFlow",
|
|
244
|
+
"listFlows",
|
|
245
|
+
"clearFlows",
|
|
246
|
+
"thenFunctionality",
|
|
247
|
+
"parallel",
|
|
248
|
+
"branch",
|
|
249
|
+
"fanOut",
|
|
250
|
+
"thenFunctionalityOrchestration",
|
|
251
|
+
"getSteps",
|
|
252
|
+
"assertServiceBoundary"
|
|
253
|
+
]);
|
|
254
|
+
for (const method of cls.getMethods()) {
|
|
255
|
+
const methodName = method.getName();
|
|
256
|
+
if (!allowedMethods.has(methodName)) {
|
|
318
257
|
errors.push({
|
|
319
|
-
ruleId: "krnl/
|
|
258
|
+
ruleId: "krnl/orchestration-purity",
|
|
320
259
|
filePath,
|
|
321
|
-
line:
|
|
322
|
-
message: `
|
|
260
|
+
line: method.getStartLineNumber(),
|
|
261
|
+
message: `FunctionalityOrchestration '${cls.getName() || "Anonymous"}' defines custom method '${methodName}'. FunctionalityOrchestrations are purely declarative compositors. Define capabilities as functionalities or global library functions.`,
|
|
323
262
|
severity: "error"
|
|
324
263
|
});
|
|
325
264
|
}
|
|
326
265
|
}
|
|
327
266
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
267
|
+
}
|
|
268
|
+
for (const call of calls) {
|
|
269
|
+
const callText = call.getText();
|
|
270
|
+
if (callText.startsWith("this.flow(") || callText.startsWith("orchestration.flow(")) {
|
|
271
|
+
let currentExpr = call;
|
|
272
|
+
const chainCalls = [];
|
|
273
|
+
while (currentExpr && import_ts_morph2.Node.isCallExpression(currentExpr)) {
|
|
274
|
+
const propAccess = currentExpr.getExpression();
|
|
275
|
+
if (import_ts_morph2.Node.isPropertyAccessExpression(propAccess)) {
|
|
276
|
+
const methodName = propAccess.getName();
|
|
277
|
+
const args = currentExpr.getArguments();
|
|
278
|
+
const argText = args.length > 0 ? args[0].getText() : void 0;
|
|
279
|
+
chainCalls.unshift({ name: methodName, node: currentExpr, argText });
|
|
280
|
+
currentExpr = propAccess.getExpression();
|
|
281
|
+
} else {
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
let chainId = "orchestration";
|
|
286
|
+
for (const step of chainCalls) {
|
|
287
|
+
if (step.name === "flow") {
|
|
288
|
+
chainId = step.argText ? step.argText.replace(/['"]/g, "") : "orchestration";
|
|
289
|
+
} else if (step.name === "thenFunction") {
|
|
339
290
|
errors.push({
|
|
340
|
-
ruleId: "krnl/
|
|
291
|
+
ruleId: "krnl/orchestration-no-direct-library-calls",
|
|
341
292
|
filePath,
|
|
342
|
-
line:
|
|
343
|
-
message: `
|
|
293
|
+
line: step.node.getStartLineNumber(),
|
|
294
|
+
message: `FunctionalityOrchestration flow '${chainId}' violates Orchestration Purity Law. Orchestrations cannot chain direct library functions via thenFunction(). Orchestrations must only sequence functionalities (thenFunctionality) and peer functionality orchestrations (thenFunctionalityOrchestration).`,
|
|
295
|
+
severity: "error"
|
|
296
|
+
});
|
|
297
|
+
} else if (step.name === "thenService") {
|
|
298
|
+
errors.push({
|
|
299
|
+
ruleId: "krnl/orchestration-no-external-services",
|
|
300
|
+
filePath,
|
|
301
|
+
line: step.node.getStartLineNumber(),
|
|
302
|
+
message: `FunctionalityOrchestration flow '${chainId}' violates Plane Isolation Law L1. A service-level FunctionalityOrchestration cannot compose external services. Use Skipo ServiceOrchestration at the application layer instead.`,
|
|
344
303
|
severity: "error"
|
|
345
304
|
});
|
|
346
305
|
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return { errors, warnings };
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// src/rules/workspaceRules.ts
|
|
314
|
+
var fs = __toESM(require("fs"), 1);
|
|
315
|
+
var path2 = __toESM(require("path"), 1);
|
|
316
|
+
var import_ts_morph3 = require("ts-morph");
|
|
317
|
+
function checkTopLevelPurity(sf) {
|
|
318
|
+
const errors = [];
|
|
319
|
+
const warnings = [];
|
|
320
|
+
const filePath = sf.getFilePath();
|
|
321
|
+
const baseName = path2.basename(filePath);
|
|
322
|
+
const isUiTopLevel = /^(App|main|index|_app|root)\.(tsx|jsx)$/i.test(baseName);
|
|
323
|
+
const isBackendTopLevel = /^(server|app|index)\.(ts|js)$/i.test(baseName) && !filePath.includes("/components/") && !filePath.includes("/orchestrations/") && !filePath.includes("/services/") && !filePath.includes("/libraries/");
|
|
324
|
+
if (isUiTopLevel) {
|
|
325
|
+
const jsxElements = sf.getDescendantsOfKind(import_ts_morph3.SyntaxKind.JsxOpeningElement);
|
|
326
|
+
const jsxSelfClosing = sf.getDescendantsOfKind(import_ts_morph3.SyntaxKind.JsxSelfClosingElement);
|
|
327
|
+
const allJsxTags = [...jsxElements, ...jsxSelfClosing];
|
|
328
|
+
for (const jsx of allJsxTags) {
|
|
329
|
+
const tagName = jsx.getTagNameNode().getText();
|
|
330
|
+
if (/^[a-z]/.test(tagName)) {
|
|
331
|
+
errors.push({
|
|
332
|
+
ruleId: "krnl/top-level-purity",
|
|
333
|
+
filePath,
|
|
334
|
+
line: jsx.getStartLineNumber(),
|
|
335
|
+
message: `Top-level file '${baseName}' violates Top-Level Purity Law. Raw JSX/HTML element '<${tagName}>' is strictly forbidden. Top-level files must ONLY import and compose React components.`,
|
|
336
|
+
severity: "error"
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
const calls = sf.getDescendantsOfKind(import_ts_morph3.SyntaxKind.CallExpression);
|
|
341
|
+
for (const call of calls) {
|
|
342
|
+
const exprText = call.getExpression().getText();
|
|
343
|
+
if ([
|
|
344
|
+
"useState",
|
|
345
|
+
"useEffect",
|
|
346
|
+
"useRef",
|
|
347
|
+
"useMemo",
|
|
348
|
+
"useCallback",
|
|
349
|
+
"useReducer",
|
|
350
|
+
"useContext",
|
|
351
|
+
"useLayoutEffect"
|
|
352
|
+
].includes(exprText) || /^use[A-Z]/.test(exprText) && exprText !== "use") {
|
|
353
|
+
errors.push({
|
|
354
|
+
ruleId: "krnl/top-level-purity",
|
|
355
|
+
filePath,
|
|
356
|
+
line: call.getStartLineNumber(),
|
|
357
|
+
message: `Top-level file '${baseName}' violates Top-Level Purity Law. React hook '${exprText}' is forbidden in top-level files. Encapsulate state and effects inside dedicated component files.`,
|
|
358
|
+
severity: "error"
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
if (isBackendTopLevel) {
|
|
364
|
+
const calls = sf.getDescendantsOfKind(import_ts_morph3.SyntaxKind.CallExpression);
|
|
365
|
+
for (const call of calls) {
|
|
366
|
+
const exprText = call.getExpression().getText();
|
|
367
|
+
if (/^(app|router|server)\.(get|post|put|delete|patch|options|head)$/.test(exprText)) {
|
|
368
|
+
errors.push({
|
|
369
|
+
ruleId: "krnl/top-level-purity",
|
|
370
|
+
filePath,
|
|
371
|
+
line: call.getStartLineNumber(),
|
|
372
|
+
message: `Backend entry file '${baseName}' violates Entry Purity Law. Inline route definition '${exprText}' is forbidden. Delegate to dedicated route/controller modules.`,
|
|
373
|
+
severity: "error"
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
return { errors, warnings };
|
|
379
|
+
}
|
|
380
|
+
function checkWorkspaceLayout(projectRoot, errors) {
|
|
381
|
+
const krnlDir = path2.join(projectRoot, "krnl");
|
|
382
|
+
const srcDir = path2.join(projectRoot, "src");
|
|
383
|
+
const targetDir = fs.existsSync(krnlDir) ? krnlDir : fs.existsSync(srcDir) ? srcDir : null;
|
|
384
|
+
if (!targetDir) return;
|
|
385
|
+
const backendDir = path2.join(targetDir, "backend");
|
|
386
|
+
const frontendDir = path2.join(targetDir, "frontend");
|
|
387
|
+
if (fs.existsSync(backendDir)) {
|
|
388
|
+
const bServices = path2.join(backendDir, "services");
|
|
389
|
+
if (fs.existsSync(bServices)) {
|
|
390
|
+
for (const sEntry of fs.readdirSync(bServices, { withFileTypes: true })) {
|
|
391
|
+
if (!sEntry.isDirectory()) continue;
|
|
392
|
+
const sDir = path2.join(bServices, sEntry.name);
|
|
393
|
+
const sLibs = path2.join(sDir, "libraries");
|
|
394
|
+
if (fs.existsSync(sLibs)) {
|
|
395
|
+
for (const entry of fs.readdirSync(sLibs, { withFileTypes: true })) {
|
|
396
|
+
if (!entry.isDirectory()) continue;
|
|
397
|
+
const libPath = path2.join(sLibs, entry.name);
|
|
398
|
+
if (!fs.existsSync(path2.join(libPath, "manifest.json"))) {
|
|
350
399
|
errors.push({
|
|
351
|
-
ruleId: "krnl/
|
|
352
|
-
filePath,
|
|
353
|
-
|
|
354
|
-
message: `Entity '${cls.getName() || "Anonymous"}' defines custom method '${methodName}'. Entities are purely declarative compositors/orchestrators. Define capabilities as functionalities or global library functions.`,
|
|
400
|
+
ruleId: "krnl/concept-manifest-required",
|
|
401
|
+
filePath: libPath,
|
|
402
|
+
message: `Service-owned concept library "${sEntry.name}/${entry.name}" is missing a manifest.json file.`,
|
|
355
403
|
severity: "error"
|
|
356
404
|
});
|
|
357
405
|
}
|
|
358
406
|
}
|
|
359
407
|
}
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
while (currentExpr && import_ts_morph.Node.isCallExpression(currentExpr)) {
|
|
367
|
-
const propAccess = currentExpr.getExpression();
|
|
368
|
-
if (import_ts_morph.Node.isPropertyAccessExpression(propAccess)) {
|
|
369
|
-
const methodName = propAccess.getName();
|
|
370
|
-
const args = currentExpr.getArguments();
|
|
371
|
-
const argText = args.length > 0 ? args[0].getText() : void 0;
|
|
372
|
-
chainCalls.unshift({ name: methodName, node: currentExpr, argText });
|
|
373
|
-
currentExpr = propAccess.getExpression();
|
|
374
|
-
} else {
|
|
375
|
-
break;
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
let consecutiveFunctions = 0;
|
|
379
|
-
let chainId = "orchestration";
|
|
380
|
-
for (const step of chainCalls) {
|
|
381
|
-
if (step.name === "orchestration" || step.name === "flow") {
|
|
382
|
-
chainId = step.argText ? step.argText.replace(/['"]/g, "") : "orchestration";
|
|
383
|
-
} else if (step.name === "thenFunctionality" || step.name === "then") {
|
|
384
|
-
consecutiveFunctions = 0;
|
|
385
|
-
} else if (step.name === "thenEntityOrchestration" || step.name === "thenEntity") {
|
|
386
|
-
consecutiveFunctions = 0;
|
|
387
|
-
} else if (step.name === "thenFunction") {
|
|
408
|
+
const sFuncs = path2.join(sDir, "functionalities");
|
|
409
|
+
if (fs.existsSync(sFuncs)) {
|
|
410
|
+
for (const entry of fs.readdirSync(sFuncs, { withFileTypes: true })) {
|
|
411
|
+
if (!entry.isDirectory()) continue;
|
|
412
|
+
const funcPath = path2.join(sFuncs, entry.name);
|
|
413
|
+
if (!fs.existsSync(path2.join(funcPath, "manifest.json"))) {
|
|
388
414
|
errors.push({
|
|
389
|
-
ruleId: "krnl/
|
|
390
|
-
filePath,
|
|
391
|
-
|
|
392
|
-
message: `Orchestration chain '${chainId}' violates Entity Purity Law. Entities cannot chain direct library functions via thenFunction(). Entities must only orchestrate functionalities (thenFunctionality) and orchestrations from other entities (thenEntityOrchestration).`,
|
|
415
|
+
ruleId: "krnl/functionality-manifest-required",
|
|
416
|
+
filePath: funcPath,
|
|
417
|
+
message: `Service-owned functionality "${sEntry.name}/${entry.name}" is missing a manifest.json file.`,
|
|
393
418
|
severity: "error"
|
|
394
419
|
});
|
|
395
420
|
}
|
|
@@ -397,93 +422,399 @@ var KrnlValidatorEngine = class {
|
|
|
397
422
|
}
|
|
398
423
|
}
|
|
399
424
|
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
if (fs.existsSync(backendDir)) {
|
|
413
|
-
const bLibs = path.join(backendDir, "libraries");
|
|
414
|
-
if (fs.existsSync(bLibs)) {
|
|
415
|
-
for (const entry of fs.readdirSync(bLibs, { withFileTypes: true })) {
|
|
416
|
-
if (!entry.isDirectory()) continue;
|
|
417
|
-
const libPath = path.join(bLibs, entry.name);
|
|
418
|
-
const manifestPath = path.join(libPath, "manifest.json");
|
|
419
|
-
if (!fs.existsSync(manifestPath)) {
|
|
420
|
-
errors.push({
|
|
421
|
-
ruleId: "krnl/concept-manifest-required",
|
|
422
|
-
filePath: libPath,
|
|
423
|
-
message: `Backend concept library "${entry.name}" is missing a manifest.json file.`,
|
|
424
|
-
severity: "error"
|
|
425
|
-
});
|
|
426
|
-
}
|
|
425
|
+
const bLibs = path2.join(backendDir, "libraries");
|
|
426
|
+
if (fs.existsSync(bLibs)) {
|
|
427
|
+
for (const entry of fs.readdirSync(bLibs, { withFileTypes: true })) {
|
|
428
|
+
if (!entry.isDirectory()) continue;
|
|
429
|
+
const libPath = path2.join(bLibs, entry.name);
|
|
430
|
+
if (!fs.existsSync(path2.join(libPath, "manifest.json"))) {
|
|
431
|
+
errors.push({
|
|
432
|
+
ruleId: "krnl/concept-manifest-required",
|
|
433
|
+
filePath: libPath,
|
|
434
|
+
message: `Backend concept library "${entry.name}" is missing a manifest.json file.`,
|
|
435
|
+
severity: "error"
|
|
436
|
+
});
|
|
427
437
|
}
|
|
428
438
|
}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
}
|
|
439
|
+
}
|
|
440
|
+
const bFuncs = path2.join(backendDir, "functionalities");
|
|
441
|
+
if (fs.existsSync(bFuncs)) {
|
|
442
|
+
for (const entry of fs.readdirSync(bFuncs, { withFileTypes: true })) {
|
|
443
|
+
if (!entry.isDirectory()) continue;
|
|
444
|
+
const funcPath = path2.join(bFuncs, entry.name);
|
|
445
|
+
if (!fs.existsSync(path2.join(funcPath, "manifest.json"))) {
|
|
446
|
+
errors.push({
|
|
447
|
+
ruleId: "krnl/functionality-manifest-required",
|
|
448
|
+
filePath: funcPath,
|
|
449
|
+
message: `Backend functionality "${entry.name}" is missing a manifest.json file.`,
|
|
450
|
+
severity: "error"
|
|
451
|
+
});
|
|
443
452
|
}
|
|
444
453
|
}
|
|
445
454
|
}
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
455
|
+
}
|
|
456
|
+
if (fs.existsSync(frontendDir)) {
|
|
457
|
+
const fLibs = path2.join(frontendDir, "libraries");
|
|
458
|
+
if (fs.existsSync(fLibs)) {
|
|
459
|
+
for (const entry of fs.readdirSync(fLibs, { withFileTypes: true })) {
|
|
460
|
+
if (!entry.isDirectory()) continue;
|
|
461
|
+
const libPath = path2.join(fLibs, entry.name);
|
|
462
|
+
if (!fs.existsSync(path2.join(libPath, "manifest.json"))) {
|
|
463
|
+
errors.push({
|
|
464
|
+
ruleId: "krnl/frontend-concept-manifest-required",
|
|
465
|
+
filePath: libPath,
|
|
466
|
+
message: `Frontend concept library "${entry.name}" is missing a manifest.json file.`,
|
|
467
|
+
severity: "error"
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
const fFuncs = path2.join(frontendDir, "functionalities");
|
|
473
|
+
if (fs.existsSync(fFuncs)) {
|
|
474
|
+
for (const entry of fs.readdirSync(fFuncs, { withFileTypes: true })) {
|
|
475
|
+
if (!entry.isDirectory()) continue;
|
|
476
|
+
const funcPath = path2.join(fFuncs, entry.name);
|
|
477
|
+
if (!fs.existsSync(path2.join(funcPath, "manifest.json"))) {
|
|
478
|
+
errors.push({
|
|
479
|
+
ruleId: "krnl/frontend-functionality-manifest-required",
|
|
480
|
+
filePath: funcPath,
|
|
481
|
+
message: `Frontend functionality "${entry.name}" is missing a manifest.json file.`,
|
|
482
|
+
severity: "error"
|
|
483
|
+
});
|
|
461
484
|
}
|
|
462
485
|
}
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// src/rules/serviceBoundaryRules.ts
|
|
491
|
+
function checkServiceBoundaries(sf) {
|
|
492
|
+
const errors = [];
|
|
493
|
+
const warnings = [];
|
|
494
|
+
const filePath = sf.getFilePath().replace(/\\/g, "/");
|
|
495
|
+
const serviceMatch = filePath.match(/\/services\/([^/]+)\//);
|
|
496
|
+
if (!serviceMatch) {
|
|
497
|
+
return { errors, warnings };
|
|
498
|
+
}
|
|
499
|
+
const currentService = serviceMatch[1];
|
|
500
|
+
const imports = sf.getImportDeclarations();
|
|
501
|
+
for (const imp of imports) {
|
|
502
|
+
const spec = imp.getModuleSpecifierValue();
|
|
503
|
+
const crossServiceLibMatch = spec.match(/(?:^|\/|\.\.\/)services\/([^/]+)\/libraries\//) || spec.match(/\.\.\/(?:(?:\.\.\/)+)?([^/]+)\/libraries\//);
|
|
504
|
+
if (crossServiceLibMatch) {
|
|
505
|
+
const targetService = crossServiceLibMatch[1];
|
|
506
|
+
if (targetService !== currentService && targetService !== "." && targetService !== "..") {
|
|
507
|
+
errors.push({
|
|
508
|
+
ruleId: "krnl/no-cross-service-library-imports",
|
|
509
|
+
filePath,
|
|
510
|
+
line: imp.getStartLineNumber(),
|
|
511
|
+
message: `Cross-service library import forbidden: '${spec}'. Service '${currentService}' cannot import '${targetService}' libraries directly via filesystem. Access libraries through the '${targetService}' ServiceFacade (e.g. ${targetService}Facade.libraries).`,
|
|
512
|
+
severity: "error"
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
return { errors, warnings };
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// src/rules/scopedLibraryRules.ts
|
|
521
|
+
var import_ts_morph4 = require("ts-morph");
|
|
522
|
+
var PRIMITIVE_AND_BUILTIN_TYPES = /* @__PURE__ */ new Set([
|
|
523
|
+
"string",
|
|
524
|
+
"number",
|
|
525
|
+
"boolean",
|
|
526
|
+
"symbol",
|
|
527
|
+
"bigint",
|
|
528
|
+
"void",
|
|
529
|
+
"undefined",
|
|
530
|
+
"null",
|
|
531
|
+
"never",
|
|
532
|
+
"unknown",
|
|
533
|
+
"any",
|
|
534
|
+
"object",
|
|
535
|
+
"Record",
|
|
536
|
+
"Array",
|
|
537
|
+
"ReadonlyArray",
|
|
538
|
+
"Map",
|
|
539
|
+
"Set",
|
|
540
|
+
"WeakMap",
|
|
541
|
+
"WeakSet",
|
|
542
|
+
"Promise",
|
|
543
|
+
"Date",
|
|
544
|
+
"RegExp",
|
|
545
|
+
"Error",
|
|
546
|
+
"Uint8Array",
|
|
547
|
+
"Int8Array",
|
|
548
|
+
"Uint16Array",
|
|
549
|
+
"Int16Array",
|
|
550
|
+
"Uint32Array",
|
|
551
|
+
"Int32Array",
|
|
552
|
+
"Float32Array",
|
|
553
|
+
"Float64Array",
|
|
554
|
+
"ArrayBuffer",
|
|
555
|
+
"Buffer",
|
|
556
|
+
"Iterable",
|
|
557
|
+
"AsyncIterable",
|
|
558
|
+
"Iterator",
|
|
559
|
+
"AsyncIterator",
|
|
560
|
+
"Partial",
|
|
561
|
+
"Required",
|
|
562
|
+
"Readonly",
|
|
563
|
+
"Pick",
|
|
564
|
+
"Omit",
|
|
565
|
+
"Exclude",
|
|
566
|
+
"Extract",
|
|
567
|
+
"NonNullable",
|
|
568
|
+
"Parameters",
|
|
569
|
+
"ConstructorParameters",
|
|
570
|
+
"ReturnType",
|
|
571
|
+
"InstanceType"
|
|
572
|
+
]);
|
|
573
|
+
function detectServiceContext(filePath) {
|
|
574
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
575
|
+
const match = normalized.match(/(?:^|\/)services\/([^/]+)\/libraries\/([^/]+)/);
|
|
576
|
+
if (match) {
|
|
577
|
+
return { serviceName: match[1], libraryName: match[2] };
|
|
578
|
+
}
|
|
579
|
+
return null;
|
|
580
|
+
}
|
|
581
|
+
function containsIdentifier(name, target) {
|
|
582
|
+
return name.toLowerCase().includes(target.toLowerCase());
|
|
583
|
+
}
|
|
584
|
+
function isCustomContextualType(typeName, serviceName, localTypes) {
|
|
585
|
+
if (PRIMITIVE_AND_BUILTIN_TYPES.has(typeName)) return false;
|
|
586
|
+
if (localTypes.has(typeName)) return true;
|
|
587
|
+
if (containsIdentifier(typeName, serviceName)) return true;
|
|
588
|
+
if (/^[A-Z][a-zA-Z0-9]+$/.test(typeName)) return true;
|
|
589
|
+
return false;
|
|
590
|
+
}
|
|
591
|
+
function hasContextualTypeReference(node, serviceName, localTypes) {
|
|
592
|
+
const typeRefs = node.getDescendantsOfKind(import_ts_morph4.SyntaxKind.TypeReference);
|
|
593
|
+
for (const ref of typeRefs) {
|
|
594
|
+
const text = ref.getTypeName().getText().trim();
|
|
595
|
+
if (isCustomContextualType(text, serviceName, localTypes)) {
|
|
596
|
+
return true;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
return false;
|
|
600
|
+
}
|
|
601
|
+
function checkScopedLibraryRules(sf) {
|
|
602
|
+
const errors = [];
|
|
603
|
+
const warnings = [];
|
|
604
|
+
const promotableAssets = [];
|
|
605
|
+
const filePath = sf.getFilePath().replace(/\\/g, "/");
|
|
606
|
+
const context = detectServiceContext(filePath);
|
|
607
|
+
if (!context) {
|
|
608
|
+
return { errors, warnings, promotableAssets };
|
|
609
|
+
}
|
|
610
|
+
const { serviceName, libraryName } = context;
|
|
611
|
+
const localTypes = /* @__PURE__ */ new Set();
|
|
612
|
+
sf.getInterfaces().forEach((i) => localTypes.add(i.getName()));
|
|
613
|
+
sf.getTypeAliases().forEach((t) => localTypes.add(t.getName()));
|
|
614
|
+
sf.getClasses().forEach((c) => {
|
|
615
|
+
const n = c.getName();
|
|
616
|
+
if (n) localTypes.add(n);
|
|
617
|
+
});
|
|
618
|
+
sf.getEnums().forEach((e) => localTypes.add(e.getName()));
|
|
619
|
+
const functions = sf.getFunctions();
|
|
620
|
+
for (const fn of functions) {
|
|
621
|
+
const fnName = fn.getName();
|
|
622
|
+
if (!fnName) continue;
|
|
623
|
+
const isContextual = hasContextualTypeReference(fn, serviceName, localTypes);
|
|
624
|
+
const hasServiceName = containsIdentifier(fnName, serviceName);
|
|
625
|
+
if (isContextual) {
|
|
626
|
+
if (!hasServiceName) {
|
|
627
|
+
errors.push({
|
|
628
|
+
ruleId: "krnl/service-library-asset-naming",
|
|
629
|
+
filePath,
|
|
630
|
+
line: fn.getStartLineNumber(),
|
|
631
|
+
message: `Service library asset '${fnName}' in service '${serviceName}' violates scoped library naming convention. Custom library asset identifiers must contain the owning service name ('${serviceName}'). Expected name containing '${serviceName}' (e.g. '${serviceName.toLowerCase()}${fnName[0].toUpperCase()}${fnName.slice(1)}'), or eliminate contextual custom type dependencies to make it a generic promotable utility.`,
|
|
632
|
+
severity: "error"
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
} else {
|
|
636
|
+
promotableAssets.push({
|
|
637
|
+
name: fnName,
|
|
638
|
+
kind: "function",
|
|
639
|
+
filePath,
|
|
640
|
+
scope: "service",
|
|
641
|
+
scopeName: serviceName,
|
|
642
|
+
target: "GutTS"
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
for (const stmt of sf.getVariableStatements()) {
|
|
647
|
+
for (const decl of stmt.getDeclarations()) {
|
|
648
|
+
const varName = decl.getName();
|
|
649
|
+
const init = decl.getInitializer();
|
|
650
|
+
const isFunction = init && (init.getKind() === import_ts_morph4.SyntaxKind.ArrowFunction || init.getKind() === import_ts_morph4.SyntaxKind.FunctionExpression);
|
|
651
|
+
if (isFunction) {
|
|
652
|
+
const isContextual = hasContextualTypeReference(decl, serviceName, localTypes);
|
|
653
|
+
const hasServiceName = containsIdentifier(varName, serviceName);
|
|
654
|
+
if (isContextual) {
|
|
655
|
+
if (!hasServiceName) {
|
|
470
656
|
errors.push({
|
|
471
|
-
ruleId: "krnl/
|
|
472
|
-
filePath
|
|
473
|
-
|
|
657
|
+
ruleId: "krnl/service-library-asset-naming",
|
|
658
|
+
filePath,
|
|
659
|
+
line: decl.getStartLineNumber(),
|
|
660
|
+
message: `Service library asset '${varName}' in service '${serviceName}' violates scoped library naming convention. Custom library asset identifiers must contain the owning service name ('${serviceName}'). Expected name containing '${serviceName}', or eliminate contextual custom type dependencies to make it a generic promotable utility.`,
|
|
474
661
|
severity: "error"
|
|
475
662
|
});
|
|
476
663
|
}
|
|
664
|
+
} else {
|
|
665
|
+
promotableAssets.push({
|
|
666
|
+
name: varName,
|
|
667
|
+
kind: "function",
|
|
668
|
+
filePath,
|
|
669
|
+
scope: "service",
|
|
670
|
+
scopeName: serviceName,
|
|
671
|
+
target: "GutTS"
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
} else {
|
|
675
|
+
const isContextual = hasContextualTypeReference(decl, serviceName, localTypes);
|
|
676
|
+
const hasServiceName = containsIdentifier(varName, serviceName);
|
|
677
|
+
if (isContextual && !hasServiceName) {
|
|
678
|
+
errors.push({
|
|
679
|
+
ruleId: "krnl/service-library-asset-naming",
|
|
680
|
+
filePath,
|
|
681
|
+
line: decl.getStartLineNumber(),
|
|
682
|
+
message: `Service library asset '${varName}' in service '${serviceName}' violates scoped library naming convention. Custom library asset identifiers must contain the owning service name ('${serviceName}').`,
|
|
683
|
+
severity: "error"
|
|
684
|
+
});
|
|
477
685
|
}
|
|
478
686
|
}
|
|
479
687
|
}
|
|
480
688
|
}
|
|
689
|
+
for (const iface of sf.getInterfaces()) {
|
|
690
|
+
const name = iface.getName();
|
|
691
|
+
const hasServiceName = containsIdentifier(name, serviceName);
|
|
692
|
+
const hasProps = iface.getProperties().length > 0;
|
|
693
|
+
if (hasProps && !hasServiceName) {
|
|
694
|
+
errors.push({
|
|
695
|
+
ruleId: "krnl/service-library-asset-naming",
|
|
696
|
+
filePath,
|
|
697
|
+
line: iface.getStartLineNumber(),
|
|
698
|
+
message: `Service library interface '${name}' in service '${serviceName}' violates scoped library naming convention. Custom library asset identifiers must contain the owning service name ('${serviceName}'). Expected name like '${serviceName}${name}'.`,
|
|
699
|
+
severity: "error"
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
for (const t of sf.getTypeAliases()) {
|
|
704
|
+
const name = t.getName();
|
|
705
|
+
const hasServiceName = containsIdentifier(name, serviceName);
|
|
706
|
+
const isContextual = hasContextualTypeReference(t, serviceName, localTypes);
|
|
707
|
+
if (isContextual && !hasServiceName) {
|
|
708
|
+
errors.push({
|
|
709
|
+
ruleId: "krnl/service-library-asset-naming",
|
|
710
|
+
filePath,
|
|
711
|
+
line: t.getStartLineNumber(),
|
|
712
|
+
message: `Service library type alias '${name}' in service '${serviceName}' violates scoped library naming convention. Custom library asset identifiers must contain the owning service name ('${serviceName}'). Expected name like '${serviceName}${name}'.`,
|
|
713
|
+
severity: "error"
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
for (const cls of sf.getClasses()) {
|
|
718
|
+
const name = cls.getName();
|
|
719
|
+
if (!name) continue;
|
|
720
|
+
const hasServiceName = containsIdentifier(name, serviceName);
|
|
721
|
+
if (!hasServiceName) {
|
|
722
|
+
errors.push({
|
|
723
|
+
ruleId: "krnl/service-library-asset-naming",
|
|
724
|
+
filePath,
|
|
725
|
+
line: cls.getStartLineNumber(),
|
|
726
|
+
message: `Service library class '${name}' in service '${serviceName}' violates scoped library naming convention. Custom library asset identifiers must contain the owning service name ('${serviceName}'). Expected name like '${serviceName}${name}'.`,
|
|
727
|
+
severity: "error"
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
return { errors, warnings, promotableAssets };
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
// src/KrnlValidatorEngine.ts
|
|
735
|
+
var KrnlValidatorEngine = class {
|
|
736
|
+
project;
|
|
737
|
+
projectRoot;
|
|
738
|
+
limitOptions;
|
|
739
|
+
constructor(optionsOrRoot) {
|
|
740
|
+
const options = typeof optionsOrRoot === "string" ? { projectRoot: optionsOrRoot } : optionsOrRoot;
|
|
741
|
+
this.projectRoot = path3.resolve(options.projectRoot || process.cwd());
|
|
742
|
+
this.limitOptions = {
|
|
743
|
+
maxFileLineLimit: options.maxFileLineLimit ?? 200,
|
|
744
|
+
maxMainLogicLimit: options.maxMainLogicLimit ?? 20,
|
|
745
|
+
maxHelperLineLimit: options.maxHelperLineLimit ?? 30
|
|
746
|
+
};
|
|
747
|
+
const tsConfigPath = path3.join(this.projectRoot, "tsconfig.json");
|
|
748
|
+
if (fs2.existsSync(tsConfigPath)) {
|
|
749
|
+
this.project = new import_ts_morph5.Project({ tsConfigFilePath: tsConfigPath, skipAddingFilesFromTsConfig: true });
|
|
750
|
+
} else {
|
|
751
|
+
this.project = new import_ts_morph5.Project({ compilerOptions: {
|
|
752
|
+
target: 99
|
|
753
|
+
/* ESNext */
|
|
754
|
+
} });
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
validateAll() {
|
|
758
|
+
const files = this.collectSourceFiles(this.projectRoot);
|
|
759
|
+
const errors = [];
|
|
760
|
+
const warnings = [];
|
|
761
|
+
const promotableAssets = [];
|
|
762
|
+
checkWorkspaceLayout(this.projectRoot, errors);
|
|
763
|
+
for (const f of files) {
|
|
764
|
+
const sf = this.project.addSourceFileAtPath(f);
|
|
765
|
+
const fileReport = this.validateSourceFile(sf);
|
|
766
|
+
errors.push(...fileReport.errors);
|
|
767
|
+
warnings.push(...fileReport.warnings);
|
|
768
|
+
if (fileReport.promotableAssets) {
|
|
769
|
+
promotableAssets.push(...fileReport.promotableAssets);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
return {
|
|
773
|
+
valid: errors.length === 0,
|
|
774
|
+
totalFiles: files.length,
|
|
775
|
+
errors,
|
|
776
|
+
warnings,
|
|
777
|
+
promotableAssets
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
validateSourceFile(sf) {
|
|
781
|
+
const filePath = sf.getFilePath();
|
|
782
|
+
const baseName = path3.basename(filePath);
|
|
783
|
+
if (baseName.includes("_pb.") || baseName.endsWith(".d.ts")) {
|
|
784
|
+
return { errors: [], warnings: [], promotableAssets: [] };
|
|
785
|
+
}
|
|
786
|
+
const limits = checkFileLimits(sf, this.limitOptions);
|
|
787
|
+
const purity = checkTopLevelPurity(sf);
|
|
788
|
+
const components = checkComponentRules(sf);
|
|
789
|
+
const orchestration = checkOrchestrationRules(sf);
|
|
790
|
+
const boundaries = checkServiceBoundaries(sf);
|
|
791
|
+
const scopedLibraries = checkScopedLibraryRules(sf);
|
|
792
|
+
return {
|
|
793
|
+
errors: [
|
|
794
|
+
...limits.errors,
|
|
795
|
+
...purity.errors,
|
|
796
|
+
...components.errors,
|
|
797
|
+
...orchestration.errors,
|
|
798
|
+
...boundaries.errors,
|
|
799
|
+
...scopedLibraries.errors
|
|
800
|
+
],
|
|
801
|
+
warnings: [
|
|
802
|
+
...limits.warnings,
|
|
803
|
+
...purity.warnings,
|
|
804
|
+
...components.warnings,
|
|
805
|
+
...orchestration.warnings,
|
|
806
|
+
...boundaries.warnings,
|
|
807
|
+
...scopedLibraries.warnings
|
|
808
|
+
],
|
|
809
|
+
promotableAssets: scopedLibraries.promotableAssets
|
|
810
|
+
};
|
|
811
|
+
}
|
|
481
812
|
collectSourceFiles(dir, collected = []) {
|
|
482
|
-
if (!
|
|
483
|
-
const entries =
|
|
813
|
+
if (!fs2.existsSync(dir)) return collected;
|
|
814
|
+
const entries = fs2.readdirSync(dir, { withFileTypes: true });
|
|
484
815
|
for (const entry of entries) {
|
|
485
816
|
if (entry.name === "node_modules" || entry.name === "dist" || entry.name === ".git") continue;
|
|
486
|
-
const full =
|
|
817
|
+
const full = path3.join(dir, entry.name);
|
|
487
818
|
if (entry.isDirectory()) {
|
|
488
819
|
this.collectSourceFiles(full, collected);
|
|
489
820
|
} else if (entry.isFile() && (entry.name.endsWith(".ts") || entry.name.endsWith(".js") || entry.name.endsWith(".tsx") || entry.name.endsWith(".jsx"))) {
|