@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
|
@@ -1,359 +1,384 @@
|
|
|
1
1
|
// src/KrnlValidatorEngine.ts
|
|
2
|
-
import * as
|
|
3
|
-
import * as
|
|
4
|
-
import { Project
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
} else {
|
|
21
|
-
this.project = new Project({ compilerOptions: {
|
|
22
|
-
target: 99
|
|
23
|
-
/* ESNext */
|
|
24
|
-
} });
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Scans and validates all source files in the project.
|
|
29
|
-
*/
|
|
30
|
-
validateAll() {
|
|
31
|
-
const files = this.collectSourceFiles(this.projectRoot);
|
|
32
|
-
const errors = [];
|
|
33
|
-
const warnings = [];
|
|
34
|
-
this.validateWorkspaceLayout(errors);
|
|
35
|
-
for (const f of files) {
|
|
36
|
-
const sf = this.project.addSourceFileAtPath(f);
|
|
37
|
-
const fileReport = this.validateSourceFile(sf);
|
|
38
|
-
errors.push(...fileReport.errors);
|
|
39
|
-
warnings.push(...fileReport.warnings);
|
|
40
|
-
}
|
|
41
|
-
return {
|
|
42
|
-
valid: errors.length === 0,
|
|
43
|
-
totalFiles: files.length,
|
|
44
|
-
errors,
|
|
45
|
-
warnings
|
|
46
|
-
};
|
|
2
|
+
import * as fs2 from "fs";
|
|
3
|
+
import * as path3 from "path";
|
|
4
|
+
import { Project } from "ts-morph";
|
|
5
|
+
|
|
6
|
+
// src/rules/fileLimits.ts
|
|
7
|
+
function checkFileLimits(sf, options) {
|
|
8
|
+
const errors = [];
|
|
9
|
+
const warnings = [];
|
|
10
|
+
const filePath = sf.getFilePath();
|
|
11
|
+
const sourceText = sf.getFullText();
|
|
12
|
+
const lines = sourceText.split("\n");
|
|
13
|
+
if (lines.length > options.maxFileLineLimit) {
|
|
14
|
+
errors.push({
|
|
15
|
+
ruleId: "krnl/file-line-limit",
|
|
16
|
+
filePath,
|
|
17
|
+
message: `File exceeds maximum length of ${options.maxFileLineLimit} lines (currently ${lines.length} lines). Decompose into sub-components, custom hooks, quad-units, or concept libraries.`,
|
|
18
|
+
severity: "error"
|
|
19
|
+
});
|
|
47
20
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
validateSourceFile(sf) {
|
|
52
|
-
const errors = [];
|
|
53
|
-
const warnings = [];
|
|
54
|
-
const filePath = sf.getFilePath();
|
|
55
|
-
const sourceText = sf.getText();
|
|
56
|
-
const baseName = path.basename(filePath);
|
|
57
|
-
if (baseName.includes("_pb.") || baseName.endsWith(".d.ts")) {
|
|
58
|
-
return { errors, warnings };
|
|
59
|
-
}
|
|
60
|
-
const lines = sourceText.split("\n");
|
|
61
|
-
if (lines.length > this.maxFileLineLimit) {
|
|
21
|
+
for (const decl of sf.getImportDeclarations()) {
|
|
22
|
+
const moduleSpec = decl.getModuleSpecifierValue();
|
|
23
|
+
if (moduleSpec.startsWith(".") && !moduleSpec.endsWith(".js") && !moduleSpec.endsWith(".json") && !moduleSpec.endsWith(".css") && !moduleSpec.endsWith(".svg") && !moduleSpec.endsWith(".png")) {
|
|
62
24
|
errors.push({
|
|
63
|
-
ruleId: "krnl/
|
|
25
|
+
ruleId: "krnl/import-suffix",
|
|
64
26
|
filePath,
|
|
65
|
-
|
|
27
|
+
line: decl.getStartLineNumber(),
|
|
28
|
+
message: `Relative import '${moduleSpec}' must end with '.js' for NodeNext module resolution.`,
|
|
66
29
|
severity: "error"
|
|
67
30
|
});
|
|
68
31
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
32
|
+
}
|
|
33
|
+
const isHelper = filePath.includes("helper_functions") || filePath.includes("/helpers/") || filePath.includes("\\helpers\\");
|
|
34
|
+
if (isHelper) {
|
|
35
|
+
for (const fn of sf.getFunctions()) {
|
|
36
|
+
const fnLines = fn.getText().split("\n").length;
|
|
37
|
+
if (fnLines > options.maxHelperLineLimit && !sourceText.includes("Concepts.")) {
|
|
73
38
|
errors.push({
|
|
74
|
-
ruleId: "krnl/
|
|
39
|
+
ruleId: "krnl/helper-line-limit",
|
|
75
40
|
filePath,
|
|
76
|
-
line,
|
|
77
|
-
message: `
|
|
41
|
+
line: fn.getStartLineNumber(),
|
|
42
|
+
message: `Helper function '${fn.getName() || "anonymous"}' exceeds ${options.maxHelperLineLimit} lines (${fnLines} lines). Must delegate to a root libraries/{Domain}Concepts.ts library.`,
|
|
78
43
|
severity: "error"
|
|
79
44
|
});
|
|
80
45
|
}
|
|
81
46
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
severity: "error"
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
const calls2 = sf.getDescendantsOfKind(SyntaxKind.CallExpression);
|
|
101
|
-
for (const call of calls2) {
|
|
102
|
-
const exprText = call.getExpression().getText();
|
|
103
|
-
if (["useState", "useEffect", "useRef", "useMemo", "useCallback", "useReducer", "useContext", "useLayoutEffect"].includes(exprText) || /^use[A-Z]/.test(exprText) && exprText !== "use") {
|
|
104
|
-
errors.push({
|
|
105
|
-
ruleId: "krnl/top-level-purity",
|
|
106
|
-
filePath,
|
|
107
|
-
line: call.getStartLineNumber(),
|
|
108
|
-
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.`,
|
|
109
|
-
severity: "error"
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
}
|
|
47
|
+
}
|
|
48
|
+
const isMainLogic = filePath.endsWith("logic.ts") || filePath.endsWith("logic.js");
|
|
49
|
+
if (isMainLogic) {
|
|
50
|
+
const substantiveLines = lines.filter((l) => {
|
|
51
|
+
const trimmed = l.trim();
|
|
52
|
+
return trimmed.length > 0 && !trimmed.startsWith("//") && !trimmed.startsWith("/*") && !trimmed.startsWith("*");
|
|
53
|
+
});
|
|
54
|
+
if (substantiveLines.length > options.maxMainLogicLimit && !sourceText.includes("helpers") && !sourceText.includes("Concepts") && !sourceText.includes("state")) {
|
|
55
|
+
errors.push({
|
|
56
|
+
ruleId: "krnl/main-delegation",
|
|
57
|
+
filePath,
|
|
58
|
+
message: `Main logic function has ${substantiveLines.length} substantive lines without delegating to Layer 1 helper functions or Layer 0 libraries.`,
|
|
59
|
+
severity: "error"
|
|
60
|
+
});
|
|
113
61
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
62
|
+
}
|
|
63
|
+
return { errors, warnings };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// src/rules/componentRules.ts
|
|
67
|
+
import * as path from "path";
|
|
68
|
+
import { SyntaxKind } from "ts-morph";
|
|
69
|
+
function checkComponentRules(sf) {
|
|
70
|
+
const errors = [];
|
|
71
|
+
const warnings = [];
|
|
72
|
+
const filePath = sf.getFilePath();
|
|
73
|
+
const baseName = path.basename(filePath);
|
|
74
|
+
if (filePath.endsWith(".tsx") || filePath.endsWith(".jsx")) {
|
|
75
|
+
const componentNames = [];
|
|
76
|
+
for (const fn of sf.getFunctions()) {
|
|
77
|
+
const name = fn.getName();
|
|
78
|
+
if (name && /^[A-Z]/.test(name)) {
|
|
79
|
+
const hasJsx = fn.getDescendantsOfKind(SyntaxKind.JsxElement).length > 0 || fn.getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement).length > 0 || fn.getDescendantsOfKind(SyntaxKind.JsxFragment).length > 0;
|
|
80
|
+
if (hasJsx) componentNames.push(name);
|
|
127
81
|
}
|
|
128
82
|
}
|
|
129
|
-
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
for (const stmt of sf.getVariableStatements()) {
|
|
141
|
-
for (const decl of stmt.getDeclarations()) {
|
|
142
|
-
const name = decl.getName();
|
|
143
|
-
if (/^[A-Z]/.test(name)) {
|
|
144
|
-
const typeText = decl.getTypeNode()?.getText() || "";
|
|
145
|
-
const isFcType = typeText.includes("FC") || typeText.includes("FunctionComponent") || typeText.includes("ComponentType");
|
|
146
|
-
const init = decl.getInitializer();
|
|
147
|
-
if (init) {
|
|
148
|
-
const hasJsx = init.getDescendantsOfKind(SyntaxKind.JsxElement).length > 0 || init.getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement).length > 0 || init.getDescendantsOfKind(SyntaxKind.JsxFragment).length > 0;
|
|
149
|
-
if (isFcType || hasJsx) {
|
|
150
|
-
if (!componentNames.includes(name)) {
|
|
151
|
-
componentNames.push(name);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
83
|
+
for (const stmt of sf.getVariableStatements()) {
|
|
84
|
+
for (const decl of stmt.getDeclarations()) {
|
|
85
|
+
const name = decl.getName();
|
|
86
|
+
if (/^[A-Z]/.test(name)) {
|
|
87
|
+
const typeText = decl.getTypeNode()?.getText() || "";
|
|
88
|
+
const isFcType = typeText.includes("FC") || typeText.includes("FunctionComponent") || typeText.includes("ComponentType");
|
|
89
|
+
const init = decl.getInitializer();
|
|
90
|
+
if (init) {
|
|
91
|
+
const hasJsx = init.getDescendantsOfKind(SyntaxKind.JsxElement).length > 0 || init.getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement).length > 0 || init.getDescendantsOfKind(SyntaxKind.JsxFragment).length > 0;
|
|
92
|
+
if (isFcType || hasJsx) {
|
|
93
|
+
if (!componentNames.includes(name)) componentNames.push(name);
|
|
154
94
|
}
|
|
155
95
|
}
|
|
156
96
|
}
|
|
157
97
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
165
|
-
}
|
|
98
|
+
}
|
|
99
|
+
for (const cls of sf.getClasses()) {
|
|
100
|
+
const name = cls.getName();
|
|
101
|
+
const ext = cls.getExtends()?.getText() || "";
|
|
102
|
+
if (name && (ext.includes("Component") || ext.includes("PureComponent"))) {
|
|
103
|
+
if (!componentNames.includes(name)) componentNames.push(name);
|
|
166
104
|
}
|
|
167
|
-
|
|
105
|
+
}
|
|
106
|
+
if (componentNames.length > 1) {
|
|
107
|
+
errors.push({
|
|
108
|
+
ruleId: "krnl/one-component-per-file",
|
|
109
|
+
filePath,
|
|
110
|
+
message: `File '${baseName}' defines multiple React components (${componentNames.join(
|
|
111
|
+
", "
|
|
112
|
+
)}). Krnl strictly enforces ONE component per file. Decompose into sub-component files.`,
|
|
113
|
+
severity: "error"
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const isHelper = filePath.includes("helper_functions") || filePath.includes("/helpers/") || filePath.includes("\\helpers\\");
|
|
118
|
+
if (isHelper) {
|
|
119
|
+
for (const decl of sf.getImportDeclarations()) {
|
|
120
|
+
const spec = decl.getModuleSpecifierValue();
|
|
121
|
+
if (["fs", "node:fs", "http", "node:http", "ws"].includes(spec)) {
|
|
168
122
|
errors.push({
|
|
169
|
-
ruleId: "krnl/
|
|
123
|
+
ruleId: "krnl/helper-purity",
|
|
170
124
|
filePath,
|
|
171
|
-
|
|
125
|
+
line: decl.getStartLineNumber(),
|
|
126
|
+
message: `Helper functions (Layer 1) cannot directly import I/O modules ('${spec}'). Delegate to Layer 0 libraries or ServiceFacade context.`,
|
|
172
127
|
severity: "error"
|
|
173
128
|
});
|
|
174
129
|
}
|
|
175
130
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
filePath,
|
|
198
|
-
line: fn.getStartLineNumber(),
|
|
199
|
-
message: `Helper function '${fn.getName() || "anonymous"}' exceeds ${this.maxHelperLineLimit} lines (${fnLines} lines). Must delegate to a root libraries/{Domain}Concepts.ts library.`,
|
|
200
|
-
severity: "error"
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
if (isMainLogic) {
|
|
206
|
-
const substantiveLines = lines.filter((l) => {
|
|
207
|
-
const trimmed = l.trim();
|
|
208
|
-
return trimmed.length > 0 && !trimmed.startsWith("//") && !trimmed.startsWith("/*") && !trimmed.startsWith("*");
|
|
131
|
+
}
|
|
132
|
+
return { errors, warnings };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// src/rules/orchestrationRules.ts
|
|
136
|
+
import { SyntaxKind as SyntaxKind2, Node } from "ts-morph";
|
|
137
|
+
function checkOrchestrationRules(sf) {
|
|
138
|
+
const errors = [];
|
|
139
|
+
const warnings = [];
|
|
140
|
+
const filePath = sf.getFilePath();
|
|
141
|
+
const sourceText = sf.getText();
|
|
142
|
+
const calls = sf.getDescendantsOfKind(SyntaxKind2.CallExpression);
|
|
143
|
+
for (const call of calls) {
|
|
144
|
+
const exprText = call.getExpression().getText();
|
|
145
|
+
if (exprText === "setInterval" || exprText === "window.setInterval") {
|
|
146
|
+
errors.push({
|
|
147
|
+
ruleId: "krnl/no-polling",
|
|
148
|
+
filePath,
|
|
149
|
+
line: call.getStartLineNumber(),
|
|
150
|
+
message: `Prohibited use of 'setInterval'. Krnl applications must use reactive event streams or triggers.`,
|
|
151
|
+
severity: "error"
|
|
209
152
|
});
|
|
210
|
-
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (sourceText.includes("main = async") || sourceText.includes("export const main = async")) {
|
|
156
|
+
const match = sourceText.match(/main\s*=\s*async\s*\([^,]+,\s*\{([^}]*)\}\s*\)/);
|
|
157
|
+
if (match) {
|
|
158
|
+
const destructured = match[1];
|
|
159
|
+
if (/\bsystem\b/.test(destructured)) {
|
|
211
160
|
errors.push({
|
|
212
|
-
ruleId: "krnl/
|
|
161
|
+
ruleId: "krnl/context-destructuring",
|
|
213
162
|
filePath,
|
|
214
|
-
message: `
|
|
163
|
+
message: `Functionality context parameter destructures 'system'. Functionality passes { state, orchestration } or service context.`,
|
|
215
164
|
severity: "error"
|
|
216
165
|
});
|
|
217
166
|
}
|
|
218
167
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
severity: "error"
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
const calls = sf.getDescendantsOfKind(SyntaxKind.CallExpression);
|
|
234
|
-
for (const call of calls) {
|
|
235
|
-
const exprText = call.getExpression().getText();
|
|
236
|
-
if (exprText === "setInterval" || exprText === "window.setInterval") {
|
|
168
|
+
}
|
|
169
|
+
const isOrchestrationClass = sf.getClasses().some((c) => {
|
|
170
|
+
const ext = c.getExtends();
|
|
171
|
+
return ext && (ext.getText().includes("FunctionalityOrchestration") || ext.getText().includes("Orchestration") || ext.getText().includes("krnlOrchestrationBase"));
|
|
172
|
+
});
|
|
173
|
+
if (isOrchestrationClass) {
|
|
174
|
+
for (const decl of sf.getImportDeclarations()) {
|
|
175
|
+
const spec = decl.getModuleSpecifierValue();
|
|
176
|
+
if (spec.includes("libraries/") || spec.includes("Concepts")) {
|
|
237
177
|
errors.push({
|
|
238
|
-
ruleId: "krnl/no-
|
|
178
|
+
ruleId: "krnl/orchestration-no-libraries",
|
|
239
179
|
filePath,
|
|
240
|
-
line:
|
|
241
|
-
message: `
|
|
180
|
+
line: decl.getStartLineNumber(),
|
|
181
|
+
message: `FunctionalityOrchestration class violates Library Calling Invariant. FunctionalityOrchestrations must not call or import libraries directly ('${spec}'). Only functionalities are permitted to call shared libraries.`,
|
|
242
182
|
severity: "error"
|
|
243
183
|
});
|
|
244
184
|
}
|
|
245
185
|
}
|
|
246
|
-
const
|
|
247
|
-
const ext =
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
186
|
+
for (const cls of sf.getClasses()) {
|
|
187
|
+
const ext = cls.getExtends();
|
|
188
|
+
if (ext && (ext.getText().includes("FunctionalityOrchestration") || ext.getText().includes("Orchestration") || ext.getText().includes("krnlOrchestrationBase"))) {
|
|
189
|
+
const loops = [
|
|
190
|
+
...cls.getDescendantsOfKind(SyntaxKind2.ForStatement),
|
|
191
|
+
...cls.getDescendantsOfKind(SyntaxKind2.ForOfStatement),
|
|
192
|
+
...cls.getDescendantsOfKind(SyntaxKind2.ForInStatement),
|
|
193
|
+
...cls.getDescendantsOfKind(SyntaxKind2.WhileStatement),
|
|
194
|
+
...cls.getDescendantsOfKind(SyntaxKind2.DoStatement)
|
|
195
|
+
];
|
|
196
|
+
for (const loop of loops) {
|
|
254
197
|
errors.push({
|
|
255
|
-
ruleId: "krnl/
|
|
198
|
+
ruleId: "krnl/orchestration-purity",
|
|
256
199
|
filePath,
|
|
257
|
-
line:
|
|
258
|
-
message: `
|
|
200
|
+
line: loop.getStartLineNumber(),
|
|
201
|
+
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.`,
|
|
259
202
|
severity: "error"
|
|
260
203
|
});
|
|
261
204
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
}
|
|
279
|
-
if (loadAllLine !== -1) {
|
|
280
|
-
for (const al of addLocalLines) {
|
|
281
|
-
if (al.hasUiSpec && al.line > loadAllLine) {
|
|
205
|
+
const allowedMethods = /* @__PURE__ */ new Set([
|
|
206
|
+
"flow",
|
|
207
|
+
"getFlow",
|
|
208
|
+
"listFlows",
|
|
209
|
+
"clearFlows",
|
|
210
|
+
"thenFunctionality",
|
|
211
|
+
"parallel",
|
|
212
|
+
"branch",
|
|
213
|
+
"fanOut",
|
|
214
|
+
"thenFunctionalityOrchestration",
|
|
215
|
+
"getSteps",
|
|
216
|
+
"assertServiceBoundary"
|
|
217
|
+
]);
|
|
218
|
+
for (const method of cls.getMethods()) {
|
|
219
|
+
const methodName = method.getName();
|
|
220
|
+
if (!allowedMethods.has(methodName)) {
|
|
282
221
|
errors.push({
|
|
283
|
-
ruleId: "krnl/
|
|
222
|
+
ruleId: "krnl/orchestration-purity",
|
|
284
223
|
filePath,
|
|
285
|
-
line:
|
|
286
|
-
message: `
|
|
224
|
+
line: method.getStartLineNumber(),
|
|
225
|
+
message: `FunctionalityOrchestration '${cls.getName() || "Anonymous"}' defines custom method '${methodName}'. FunctionalityOrchestrations are purely declarative compositors. Define capabilities as functionalities or global library functions.`,
|
|
287
226
|
severity: "error"
|
|
288
227
|
});
|
|
289
228
|
}
|
|
290
229
|
}
|
|
291
230
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
231
|
+
}
|
|
232
|
+
for (const call of calls) {
|
|
233
|
+
const callText = call.getText();
|
|
234
|
+
if (callText.startsWith("this.flow(") || callText.startsWith("orchestration.flow(")) {
|
|
235
|
+
let currentExpr = call;
|
|
236
|
+
const chainCalls = [];
|
|
237
|
+
while (currentExpr && Node.isCallExpression(currentExpr)) {
|
|
238
|
+
const propAccess = currentExpr.getExpression();
|
|
239
|
+
if (Node.isPropertyAccessExpression(propAccess)) {
|
|
240
|
+
const methodName = propAccess.getName();
|
|
241
|
+
const args = currentExpr.getArguments();
|
|
242
|
+
const argText = args.length > 0 ? args[0].getText() : void 0;
|
|
243
|
+
chainCalls.unshift({ name: methodName, node: currentExpr, argText });
|
|
244
|
+
currentExpr = propAccess.getExpression();
|
|
245
|
+
} else {
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
let chainId = "orchestration";
|
|
250
|
+
for (const step of chainCalls) {
|
|
251
|
+
if (step.name === "flow") {
|
|
252
|
+
chainId = step.argText ? step.argText.replace(/['"]/g, "") : "orchestration";
|
|
253
|
+
} else if (step.name === "thenFunction") {
|
|
303
254
|
errors.push({
|
|
304
|
-
ruleId: "krnl/
|
|
255
|
+
ruleId: "krnl/orchestration-no-direct-library-calls",
|
|
305
256
|
filePath,
|
|
306
|
-
line:
|
|
307
|
-
message: `
|
|
257
|
+
line: step.node.getStartLineNumber(),
|
|
258
|
+
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).`,
|
|
259
|
+
severity: "error"
|
|
260
|
+
});
|
|
261
|
+
} else if (step.name === "thenService") {
|
|
262
|
+
errors.push({
|
|
263
|
+
ruleId: "krnl/orchestration-no-external-services",
|
|
264
|
+
filePath,
|
|
265
|
+
line: step.node.getStartLineNumber(),
|
|
266
|
+
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.`,
|
|
308
267
|
severity: "error"
|
|
309
268
|
});
|
|
310
269
|
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return { errors, warnings };
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// src/rules/workspaceRules.ts
|
|
278
|
+
import * as fs from "fs";
|
|
279
|
+
import * as path2 from "path";
|
|
280
|
+
import { SyntaxKind as SyntaxKind3 } from "ts-morph";
|
|
281
|
+
function checkTopLevelPurity(sf) {
|
|
282
|
+
const errors = [];
|
|
283
|
+
const warnings = [];
|
|
284
|
+
const filePath = sf.getFilePath();
|
|
285
|
+
const baseName = path2.basename(filePath);
|
|
286
|
+
const isUiTopLevel = /^(App|main|index|_app|root)\.(tsx|jsx)$/i.test(baseName);
|
|
287
|
+
const isBackendTopLevel = /^(server|app|index)\.(ts|js)$/i.test(baseName) && !filePath.includes("/components/") && !filePath.includes("/orchestrations/") && !filePath.includes("/services/") && !filePath.includes("/libraries/");
|
|
288
|
+
if (isUiTopLevel) {
|
|
289
|
+
const jsxElements = sf.getDescendantsOfKind(SyntaxKind3.JsxOpeningElement);
|
|
290
|
+
const jsxSelfClosing = sf.getDescendantsOfKind(SyntaxKind3.JsxSelfClosingElement);
|
|
291
|
+
const allJsxTags = [...jsxElements, ...jsxSelfClosing];
|
|
292
|
+
for (const jsx of allJsxTags) {
|
|
293
|
+
const tagName = jsx.getTagNameNode().getText();
|
|
294
|
+
if (/^[a-z]/.test(tagName)) {
|
|
295
|
+
errors.push({
|
|
296
|
+
ruleId: "krnl/top-level-purity",
|
|
297
|
+
filePath,
|
|
298
|
+
line: jsx.getStartLineNumber(),
|
|
299
|
+
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.`,
|
|
300
|
+
severity: "error"
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
const calls = sf.getDescendantsOfKind(SyntaxKind3.CallExpression);
|
|
305
|
+
for (const call of calls) {
|
|
306
|
+
const exprText = call.getExpression().getText();
|
|
307
|
+
if ([
|
|
308
|
+
"useState",
|
|
309
|
+
"useEffect",
|
|
310
|
+
"useRef",
|
|
311
|
+
"useMemo",
|
|
312
|
+
"useCallback",
|
|
313
|
+
"useReducer",
|
|
314
|
+
"useContext",
|
|
315
|
+
"useLayoutEffect"
|
|
316
|
+
].includes(exprText) || /^use[A-Z]/.test(exprText) && exprText !== "use") {
|
|
317
|
+
errors.push({
|
|
318
|
+
ruleId: "krnl/top-level-purity",
|
|
319
|
+
filePath,
|
|
320
|
+
line: call.getStartLineNumber(),
|
|
321
|
+
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.`,
|
|
322
|
+
severity: "error"
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
if (isBackendTopLevel) {
|
|
328
|
+
const calls = sf.getDescendantsOfKind(SyntaxKind3.CallExpression);
|
|
329
|
+
for (const call of calls) {
|
|
330
|
+
const exprText = call.getExpression().getText();
|
|
331
|
+
if (/^(app|router|server)\.(get|post|put|delete|patch|options|head)$/.test(exprText)) {
|
|
332
|
+
errors.push({
|
|
333
|
+
ruleId: "krnl/top-level-purity",
|
|
334
|
+
filePath,
|
|
335
|
+
line: call.getStartLineNumber(),
|
|
336
|
+
message: `Backend entry file '${baseName}' violates Entry Purity Law. Inline route definition '${exprText}' is forbidden. Delegate to dedicated route/controller modules.`,
|
|
337
|
+
severity: "error"
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return { errors, warnings };
|
|
343
|
+
}
|
|
344
|
+
function checkWorkspaceLayout(projectRoot, errors) {
|
|
345
|
+
const krnlDir = path2.join(projectRoot, "krnl");
|
|
346
|
+
const srcDir = path2.join(projectRoot, "src");
|
|
347
|
+
const targetDir = fs.existsSync(krnlDir) ? krnlDir : fs.existsSync(srcDir) ? srcDir : null;
|
|
348
|
+
if (!targetDir) return;
|
|
349
|
+
const backendDir = path2.join(targetDir, "backend");
|
|
350
|
+
const frontendDir = path2.join(targetDir, "frontend");
|
|
351
|
+
if (fs.existsSync(backendDir)) {
|
|
352
|
+
const bServices = path2.join(backendDir, "services");
|
|
353
|
+
if (fs.existsSync(bServices)) {
|
|
354
|
+
for (const sEntry of fs.readdirSync(bServices, { withFileTypes: true })) {
|
|
355
|
+
if (!sEntry.isDirectory()) continue;
|
|
356
|
+
const sDir = path2.join(bServices, sEntry.name);
|
|
357
|
+
const sLibs = path2.join(sDir, "libraries");
|
|
358
|
+
if (fs.existsSync(sLibs)) {
|
|
359
|
+
for (const entry of fs.readdirSync(sLibs, { withFileTypes: true })) {
|
|
360
|
+
if (!entry.isDirectory()) continue;
|
|
361
|
+
const libPath = path2.join(sLibs, entry.name);
|
|
362
|
+
if (!fs.existsSync(path2.join(libPath, "manifest.json"))) {
|
|
314
363
|
errors.push({
|
|
315
|
-
ruleId: "krnl/
|
|
316
|
-
filePath,
|
|
317
|
-
|
|
318
|
-
message: `Entity '${cls.getName() || "Anonymous"}' defines custom method '${methodName}'. Entities are purely declarative compositors/orchestrators. Define capabilities as functionalities or global library functions.`,
|
|
364
|
+
ruleId: "krnl/concept-manifest-required",
|
|
365
|
+
filePath: libPath,
|
|
366
|
+
message: `Service-owned concept library "${sEntry.name}/${entry.name}" is missing a manifest.json file.`,
|
|
319
367
|
severity: "error"
|
|
320
368
|
});
|
|
321
369
|
}
|
|
322
370
|
}
|
|
323
371
|
}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
while (currentExpr && Node.isCallExpression(currentExpr)) {
|
|
331
|
-
const propAccess = currentExpr.getExpression();
|
|
332
|
-
if (Node.isPropertyAccessExpression(propAccess)) {
|
|
333
|
-
const methodName = propAccess.getName();
|
|
334
|
-
const args = currentExpr.getArguments();
|
|
335
|
-
const argText = args.length > 0 ? args[0].getText() : void 0;
|
|
336
|
-
chainCalls.unshift({ name: methodName, node: currentExpr, argText });
|
|
337
|
-
currentExpr = propAccess.getExpression();
|
|
338
|
-
} else {
|
|
339
|
-
break;
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
let consecutiveFunctions = 0;
|
|
343
|
-
let chainId = "orchestration";
|
|
344
|
-
for (const step of chainCalls) {
|
|
345
|
-
if (step.name === "orchestration" || step.name === "flow") {
|
|
346
|
-
chainId = step.argText ? step.argText.replace(/['"]/g, "") : "orchestration";
|
|
347
|
-
} else if (step.name === "thenFunctionality" || step.name === "then") {
|
|
348
|
-
consecutiveFunctions = 0;
|
|
349
|
-
} else if (step.name === "thenEntityOrchestration" || step.name === "thenEntity") {
|
|
350
|
-
consecutiveFunctions = 0;
|
|
351
|
-
} else if (step.name === "thenFunction") {
|
|
372
|
+
const sFuncs = path2.join(sDir, "functionalities");
|
|
373
|
+
if (fs.existsSync(sFuncs)) {
|
|
374
|
+
for (const entry of fs.readdirSync(sFuncs, { withFileTypes: true })) {
|
|
375
|
+
if (!entry.isDirectory()) continue;
|
|
376
|
+
const funcPath = path2.join(sFuncs, entry.name);
|
|
377
|
+
if (!fs.existsSync(path2.join(funcPath, "manifest.json"))) {
|
|
352
378
|
errors.push({
|
|
353
|
-
ruleId: "krnl/
|
|
354
|
-
filePath,
|
|
355
|
-
|
|
356
|
-
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).`,
|
|
379
|
+
ruleId: "krnl/functionality-manifest-required",
|
|
380
|
+
filePath: funcPath,
|
|
381
|
+
message: `Service-owned functionality "${sEntry.name}/${entry.name}" is missing a manifest.json file.`,
|
|
357
382
|
severity: "error"
|
|
358
383
|
});
|
|
359
384
|
}
|
|
@@ -361,93 +386,399 @@ var KrnlValidatorEngine = class {
|
|
|
361
386
|
}
|
|
362
387
|
}
|
|
363
388
|
}
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
if (fs.existsSync(backendDir)) {
|
|
377
|
-
const bLibs = path.join(backendDir, "libraries");
|
|
378
|
-
if (fs.existsSync(bLibs)) {
|
|
379
|
-
for (const entry of fs.readdirSync(bLibs, { withFileTypes: true })) {
|
|
380
|
-
if (!entry.isDirectory()) continue;
|
|
381
|
-
const libPath = path.join(bLibs, entry.name);
|
|
382
|
-
const manifestPath = path.join(libPath, "manifest.json");
|
|
383
|
-
if (!fs.existsSync(manifestPath)) {
|
|
384
|
-
errors.push({
|
|
385
|
-
ruleId: "krnl/concept-manifest-required",
|
|
386
|
-
filePath: libPath,
|
|
387
|
-
message: `Backend concept library "${entry.name}" is missing a manifest.json file.`,
|
|
388
|
-
severity: "error"
|
|
389
|
-
});
|
|
390
|
-
}
|
|
389
|
+
const bLibs = path2.join(backendDir, "libraries");
|
|
390
|
+
if (fs.existsSync(bLibs)) {
|
|
391
|
+
for (const entry of fs.readdirSync(bLibs, { withFileTypes: true })) {
|
|
392
|
+
if (!entry.isDirectory()) continue;
|
|
393
|
+
const libPath = path2.join(bLibs, entry.name);
|
|
394
|
+
if (!fs.existsSync(path2.join(libPath, "manifest.json"))) {
|
|
395
|
+
errors.push({
|
|
396
|
+
ruleId: "krnl/concept-manifest-required",
|
|
397
|
+
filePath: libPath,
|
|
398
|
+
message: `Backend concept library "${entry.name}" is missing a manifest.json file.`,
|
|
399
|
+
severity: "error"
|
|
400
|
+
});
|
|
391
401
|
}
|
|
392
402
|
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
}
|
|
403
|
+
}
|
|
404
|
+
const bFuncs = path2.join(backendDir, "functionalities");
|
|
405
|
+
if (fs.existsSync(bFuncs)) {
|
|
406
|
+
for (const entry of fs.readdirSync(bFuncs, { withFileTypes: true })) {
|
|
407
|
+
if (!entry.isDirectory()) continue;
|
|
408
|
+
const funcPath = path2.join(bFuncs, entry.name);
|
|
409
|
+
if (!fs.existsSync(path2.join(funcPath, "manifest.json"))) {
|
|
410
|
+
errors.push({
|
|
411
|
+
ruleId: "krnl/functionality-manifest-required",
|
|
412
|
+
filePath: funcPath,
|
|
413
|
+
message: `Backend functionality "${entry.name}" is missing a manifest.json file.`,
|
|
414
|
+
severity: "error"
|
|
415
|
+
});
|
|
407
416
|
}
|
|
408
417
|
}
|
|
409
418
|
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
419
|
+
}
|
|
420
|
+
if (fs.existsSync(frontendDir)) {
|
|
421
|
+
const fLibs = path2.join(frontendDir, "libraries");
|
|
422
|
+
if (fs.existsSync(fLibs)) {
|
|
423
|
+
for (const entry of fs.readdirSync(fLibs, { withFileTypes: true })) {
|
|
424
|
+
if (!entry.isDirectory()) continue;
|
|
425
|
+
const libPath = path2.join(fLibs, entry.name);
|
|
426
|
+
if (!fs.existsSync(path2.join(libPath, "manifest.json"))) {
|
|
427
|
+
errors.push({
|
|
428
|
+
ruleId: "krnl/frontend-concept-manifest-required",
|
|
429
|
+
filePath: libPath,
|
|
430
|
+
message: `Frontend concept library "${entry.name}" is missing a manifest.json file.`,
|
|
431
|
+
severity: "error"
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
const fFuncs = path2.join(frontendDir, "functionalities");
|
|
437
|
+
if (fs.existsSync(fFuncs)) {
|
|
438
|
+
for (const entry of fs.readdirSync(fFuncs, { withFileTypes: true })) {
|
|
439
|
+
if (!entry.isDirectory()) continue;
|
|
440
|
+
const funcPath = path2.join(fFuncs, entry.name);
|
|
441
|
+
if (!fs.existsSync(path2.join(funcPath, "manifest.json"))) {
|
|
442
|
+
errors.push({
|
|
443
|
+
ruleId: "krnl/frontend-functionality-manifest-required",
|
|
444
|
+
filePath: funcPath,
|
|
445
|
+
message: `Frontend functionality "${entry.name}" is missing a manifest.json file.`,
|
|
446
|
+
severity: "error"
|
|
447
|
+
});
|
|
425
448
|
}
|
|
426
449
|
}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// src/rules/serviceBoundaryRules.ts
|
|
455
|
+
function checkServiceBoundaries(sf) {
|
|
456
|
+
const errors = [];
|
|
457
|
+
const warnings = [];
|
|
458
|
+
const filePath = sf.getFilePath().replace(/\\/g, "/");
|
|
459
|
+
const serviceMatch = filePath.match(/\/services\/([^/]+)\//);
|
|
460
|
+
if (!serviceMatch) {
|
|
461
|
+
return { errors, warnings };
|
|
462
|
+
}
|
|
463
|
+
const currentService = serviceMatch[1];
|
|
464
|
+
const imports = sf.getImportDeclarations();
|
|
465
|
+
for (const imp of imports) {
|
|
466
|
+
const spec = imp.getModuleSpecifierValue();
|
|
467
|
+
const crossServiceLibMatch = spec.match(/(?:^|\/|\.\.\/)services\/([^/]+)\/libraries\//) || spec.match(/\.\.\/(?:(?:\.\.\/)+)?([^/]+)\/libraries\//);
|
|
468
|
+
if (crossServiceLibMatch) {
|
|
469
|
+
const targetService = crossServiceLibMatch[1];
|
|
470
|
+
if (targetService !== currentService && targetService !== "." && targetService !== "..") {
|
|
471
|
+
errors.push({
|
|
472
|
+
ruleId: "krnl/no-cross-service-library-imports",
|
|
473
|
+
filePath,
|
|
474
|
+
line: imp.getStartLineNumber(),
|
|
475
|
+
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).`,
|
|
476
|
+
severity: "error"
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
return { errors, warnings };
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// src/rules/scopedLibraryRules.ts
|
|
485
|
+
import { SyntaxKind as SyntaxKind4 } from "ts-morph";
|
|
486
|
+
var PRIMITIVE_AND_BUILTIN_TYPES = /* @__PURE__ */ new Set([
|
|
487
|
+
"string",
|
|
488
|
+
"number",
|
|
489
|
+
"boolean",
|
|
490
|
+
"symbol",
|
|
491
|
+
"bigint",
|
|
492
|
+
"void",
|
|
493
|
+
"undefined",
|
|
494
|
+
"null",
|
|
495
|
+
"never",
|
|
496
|
+
"unknown",
|
|
497
|
+
"any",
|
|
498
|
+
"object",
|
|
499
|
+
"Record",
|
|
500
|
+
"Array",
|
|
501
|
+
"ReadonlyArray",
|
|
502
|
+
"Map",
|
|
503
|
+
"Set",
|
|
504
|
+
"WeakMap",
|
|
505
|
+
"WeakSet",
|
|
506
|
+
"Promise",
|
|
507
|
+
"Date",
|
|
508
|
+
"RegExp",
|
|
509
|
+
"Error",
|
|
510
|
+
"Uint8Array",
|
|
511
|
+
"Int8Array",
|
|
512
|
+
"Uint16Array",
|
|
513
|
+
"Int16Array",
|
|
514
|
+
"Uint32Array",
|
|
515
|
+
"Int32Array",
|
|
516
|
+
"Float32Array",
|
|
517
|
+
"Float64Array",
|
|
518
|
+
"ArrayBuffer",
|
|
519
|
+
"Buffer",
|
|
520
|
+
"Iterable",
|
|
521
|
+
"AsyncIterable",
|
|
522
|
+
"Iterator",
|
|
523
|
+
"AsyncIterator",
|
|
524
|
+
"Partial",
|
|
525
|
+
"Required",
|
|
526
|
+
"Readonly",
|
|
527
|
+
"Pick",
|
|
528
|
+
"Omit",
|
|
529
|
+
"Exclude",
|
|
530
|
+
"Extract",
|
|
531
|
+
"NonNullable",
|
|
532
|
+
"Parameters",
|
|
533
|
+
"ConstructorParameters",
|
|
534
|
+
"ReturnType",
|
|
535
|
+
"InstanceType"
|
|
536
|
+
]);
|
|
537
|
+
function detectServiceContext(filePath) {
|
|
538
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
539
|
+
const match = normalized.match(/(?:^|\/)services\/([^/]+)\/libraries\/([^/]+)/);
|
|
540
|
+
if (match) {
|
|
541
|
+
return { serviceName: match[1], libraryName: match[2] };
|
|
542
|
+
}
|
|
543
|
+
return null;
|
|
544
|
+
}
|
|
545
|
+
function containsIdentifier(name, target) {
|
|
546
|
+
return name.toLowerCase().includes(target.toLowerCase());
|
|
547
|
+
}
|
|
548
|
+
function isCustomContextualType(typeName, serviceName, localTypes) {
|
|
549
|
+
if (PRIMITIVE_AND_BUILTIN_TYPES.has(typeName)) return false;
|
|
550
|
+
if (localTypes.has(typeName)) return true;
|
|
551
|
+
if (containsIdentifier(typeName, serviceName)) return true;
|
|
552
|
+
if (/^[A-Z][a-zA-Z0-9]+$/.test(typeName)) return true;
|
|
553
|
+
return false;
|
|
554
|
+
}
|
|
555
|
+
function hasContextualTypeReference(node, serviceName, localTypes) {
|
|
556
|
+
const typeRefs = node.getDescendantsOfKind(SyntaxKind4.TypeReference);
|
|
557
|
+
for (const ref of typeRefs) {
|
|
558
|
+
const text = ref.getTypeName().getText().trim();
|
|
559
|
+
if (isCustomContextualType(text, serviceName, localTypes)) {
|
|
560
|
+
return true;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
return false;
|
|
564
|
+
}
|
|
565
|
+
function checkScopedLibraryRules(sf) {
|
|
566
|
+
const errors = [];
|
|
567
|
+
const warnings = [];
|
|
568
|
+
const promotableAssets = [];
|
|
569
|
+
const filePath = sf.getFilePath().replace(/\\/g, "/");
|
|
570
|
+
const context = detectServiceContext(filePath);
|
|
571
|
+
if (!context) {
|
|
572
|
+
return { errors, warnings, promotableAssets };
|
|
573
|
+
}
|
|
574
|
+
const { serviceName, libraryName } = context;
|
|
575
|
+
const localTypes = /* @__PURE__ */ new Set();
|
|
576
|
+
sf.getInterfaces().forEach((i) => localTypes.add(i.getName()));
|
|
577
|
+
sf.getTypeAliases().forEach((t) => localTypes.add(t.getName()));
|
|
578
|
+
sf.getClasses().forEach((c) => {
|
|
579
|
+
const n = c.getName();
|
|
580
|
+
if (n) localTypes.add(n);
|
|
581
|
+
});
|
|
582
|
+
sf.getEnums().forEach((e) => localTypes.add(e.getName()));
|
|
583
|
+
const functions = sf.getFunctions();
|
|
584
|
+
for (const fn of functions) {
|
|
585
|
+
const fnName = fn.getName();
|
|
586
|
+
if (!fnName) continue;
|
|
587
|
+
const isContextual = hasContextualTypeReference(fn, serviceName, localTypes);
|
|
588
|
+
const hasServiceName = containsIdentifier(fnName, serviceName);
|
|
589
|
+
if (isContextual) {
|
|
590
|
+
if (!hasServiceName) {
|
|
591
|
+
errors.push({
|
|
592
|
+
ruleId: "krnl/service-library-asset-naming",
|
|
593
|
+
filePath,
|
|
594
|
+
line: fn.getStartLineNumber(),
|
|
595
|
+
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.`,
|
|
596
|
+
severity: "error"
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
} else {
|
|
600
|
+
promotableAssets.push({
|
|
601
|
+
name: fnName,
|
|
602
|
+
kind: "function",
|
|
603
|
+
filePath,
|
|
604
|
+
scope: "service",
|
|
605
|
+
scopeName: serviceName,
|
|
606
|
+
target: "GutTS"
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
for (const stmt of sf.getVariableStatements()) {
|
|
611
|
+
for (const decl of stmt.getDeclarations()) {
|
|
612
|
+
const varName = decl.getName();
|
|
613
|
+
const init = decl.getInitializer();
|
|
614
|
+
const isFunction = init && (init.getKind() === SyntaxKind4.ArrowFunction || init.getKind() === SyntaxKind4.FunctionExpression);
|
|
615
|
+
if (isFunction) {
|
|
616
|
+
const isContextual = hasContextualTypeReference(decl, serviceName, localTypes);
|
|
617
|
+
const hasServiceName = containsIdentifier(varName, serviceName);
|
|
618
|
+
if (isContextual) {
|
|
619
|
+
if (!hasServiceName) {
|
|
434
620
|
errors.push({
|
|
435
|
-
ruleId: "krnl/
|
|
436
|
-
filePath
|
|
437
|
-
|
|
621
|
+
ruleId: "krnl/service-library-asset-naming",
|
|
622
|
+
filePath,
|
|
623
|
+
line: decl.getStartLineNumber(),
|
|
624
|
+
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.`,
|
|
438
625
|
severity: "error"
|
|
439
626
|
});
|
|
440
627
|
}
|
|
628
|
+
} else {
|
|
629
|
+
promotableAssets.push({
|
|
630
|
+
name: varName,
|
|
631
|
+
kind: "function",
|
|
632
|
+
filePath,
|
|
633
|
+
scope: "service",
|
|
634
|
+
scopeName: serviceName,
|
|
635
|
+
target: "GutTS"
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
} else {
|
|
639
|
+
const isContextual = hasContextualTypeReference(decl, serviceName, localTypes);
|
|
640
|
+
const hasServiceName = containsIdentifier(varName, serviceName);
|
|
641
|
+
if (isContextual && !hasServiceName) {
|
|
642
|
+
errors.push({
|
|
643
|
+
ruleId: "krnl/service-library-asset-naming",
|
|
644
|
+
filePath,
|
|
645
|
+
line: decl.getStartLineNumber(),
|
|
646
|
+
message: `Service library asset '${varName}' in service '${serviceName}' violates scoped library naming convention. Custom library asset identifiers must contain the owning service name ('${serviceName}').`,
|
|
647
|
+
severity: "error"
|
|
648
|
+
});
|
|
441
649
|
}
|
|
442
650
|
}
|
|
443
651
|
}
|
|
444
652
|
}
|
|
653
|
+
for (const iface of sf.getInterfaces()) {
|
|
654
|
+
const name = iface.getName();
|
|
655
|
+
const hasServiceName = containsIdentifier(name, serviceName);
|
|
656
|
+
const hasProps = iface.getProperties().length > 0;
|
|
657
|
+
if (hasProps && !hasServiceName) {
|
|
658
|
+
errors.push({
|
|
659
|
+
ruleId: "krnl/service-library-asset-naming",
|
|
660
|
+
filePath,
|
|
661
|
+
line: iface.getStartLineNumber(),
|
|
662
|
+
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}'.`,
|
|
663
|
+
severity: "error"
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
for (const t of sf.getTypeAliases()) {
|
|
668
|
+
const name = t.getName();
|
|
669
|
+
const hasServiceName = containsIdentifier(name, serviceName);
|
|
670
|
+
const isContextual = hasContextualTypeReference(t, serviceName, localTypes);
|
|
671
|
+
if (isContextual && !hasServiceName) {
|
|
672
|
+
errors.push({
|
|
673
|
+
ruleId: "krnl/service-library-asset-naming",
|
|
674
|
+
filePath,
|
|
675
|
+
line: t.getStartLineNumber(),
|
|
676
|
+
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}'.`,
|
|
677
|
+
severity: "error"
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
for (const cls of sf.getClasses()) {
|
|
682
|
+
const name = cls.getName();
|
|
683
|
+
if (!name) continue;
|
|
684
|
+
const hasServiceName = containsIdentifier(name, serviceName);
|
|
685
|
+
if (!hasServiceName) {
|
|
686
|
+
errors.push({
|
|
687
|
+
ruleId: "krnl/service-library-asset-naming",
|
|
688
|
+
filePath,
|
|
689
|
+
line: cls.getStartLineNumber(),
|
|
690
|
+
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}'.`,
|
|
691
|
+
severity: "error"
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
return { errors, warnings, promotableAssets };
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
// src/KrnlValidatorEngine.ts
|
|
699
|
+
var KrnlValidatorEngine = class {
|
|
700
|
+
project;
|
|
701
|
+
projectRoot;
|
|
702
|
+
limitOptions;
|
|
703
|
+
constructor(optionsOrRoot) {
|
|
704
|
+
const options = typeof optionsOrRoot === "string" ? { projectRoot: optionsOrRoot } : optionsOrRoot;
|
|
705
|
+
this.projectRoot = path3.resolve(options.projectRoot || process.cwd());
|
|
706
|
+
this.limitOptions = {
|
|
707
|
+
maxFileLineLimit: options.maxFileLineLimit ?? 200,
|
|
708
|
+
maxMainLogicLimit: options.maxMainLogicLimit ?? 20,
|
|
709
|
+
maxHelperLineLimit: options.maxHelperLineLimit ?? 30
|
|
710
|
+
};
|
|
711
|
+
const tsConfigPath = path3.join(this.projectRoot, "tsconfig.json");
|
|
712
|
+
if (fs2.existsSync(tsConfigPath)) {
|
|
713
|
+
this.project = new Project({ tsConfigFilePath: tsConfigPath, skipAddingFilesFromTsConfig: true });
|
|
714
|
+
} else {
|
|
715
|
+
this.project = new Project({ compilerOptions: {
|
|
716
|
+
target: 99
|
|
717
|
+
/* ESNext */
|
|
718
|
+
} });
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
validateAll() {
|
|
722
|
+
const files = this.collectSourceFiles(this.projectRoot);
|
|
723
|
+
const errors = [];
|
|
724
|
+
const warnings = [];
|
|
725
|
+
const promotableAssets = [];
|
|
726
|
+
checkWorkspaceLayout(this.projectRoot, errors);
|
|
727
|
+
for (const f of files) {
|
|
728
|
+
const sf = this.project.addSourceFileAtPath(f);
|
|
729
|
+
const fileReport = this.validateSourceFile(sf);
|
|
730
|
+
errors.push(...fileReport.errors);
|
|
731
|
+
warnings.push(...fileReport.warnings);
|
|
732
|
+
if (fileReport.promotableAssets) {
|
|
733
|
+
promotableAssets.push(...fileReport.promotableAssets);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
return {
|
|
737
|
+
valid: errors.length === 0,
|
|
738
|
+
totalFiles: files.length,
|
|
739
|
+
errors,
|
|
740
|
+
warnings,
|
|
741
|
+
promotableAssets
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
validateSourceFile(sf) {
|
|
745
|
+
const filePath = sf.getFilePath();
|
|
746
|
+
const baseName = path3.basename(filePath);
|
|
747
|
+
if (baseName.includes("_pb.") || baseName.endsWith(".d.ts")) {
|
|
748
|
+
return { errors: [], warnings: [], promotableAssets: [] };
|
|
749
|
+
}
|
|
750
|
+
const limits = checkFileLimits(sf, this.limitOptions);
|
|
751
|
+
const purity = checkTopLevelPurity(sf);
|
|
752
|
+
const components = checkComponentRules(sf);
|
|
753
|
+
const orchestration = checkOrchestrationRules(sf);
|
|
754
|
+
const boundaries = checkServiceBoundaries(sf);
|
|
755
|
+
const scopedLibraries = checkScopedLibraryRules(sf);
|
|
756
|
+
return {
|
|
757
|
+
errors: [
|
|
758
|
+
...limits.errors,
|
|
759
|
+
...purity.errors,
|
|
760
|
+
...components.errors,
|
|
761
|
+
...orchestration.errors,
|
|
762
|
+
...boundaries.errors,
|
|
763
|
+
...scopedLibraries.errors
|
|
764
|
+
],
|
|
765
|
+
warnings: [
|
|
766
|
+
...limits.warnings,
|
|
767
|
+
...purity.warnings,
|
|
768
|
+
...components.warnings,
|
|
769
|
+
...orchestration.warnings,
|
|
770
|
+
...boundaries.warnings,
|
|
771
|
+
...scopedLibraries.warnings
|
|
772
|
+
],
|
|
773
|
+
promotableAssets: scopedLibraries.promotableAssets
|
|
774
|
+
};
|
|
775
|
+
}
|
|
445
776
|
collectSourceFiles(dir, collected = []) {
|
|
446
|
-
if (!
|
|
447
|
-
const entries =
|
|
777
|
+
if (!fs2.existsSync(dir)) return collected;
|
|
778
|
+
const entries = fs2.readdirSync(dir, { withFileTypes: true });
|
|
448
779
|
for (const entry of entries) {
|
|
449
780
|
if (entry.name === "node_modules" || entry.name === "dist" || entry.name === ".git") continue;
|
|
450
|
-
const full =
|
|
781
|
+
const full = path3.join(dir, entry.name);
|
|
451
782
|
if (entry.isDirectory()) {
|
|
452
783
|
this.collectSourceFiles(full, collected);
|
|
453
784
|
} else if (entry.isFile() && (entry.name.endsWith(".ts") || entry.name.endsWith(".js") || entry.name.endsWith(".tsx") || entry.name.endsWith(".jsx"))) {
|