x-fidelity 3.17.0 → 3.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,28 @@
1
+ # [3.18.0](https://github.com/zotoio/x-fidelity/compare/v3.17.1...v3.18.0) (2025-03-25)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * add missing execSync import in repoDependencyFacts test ([b06abdd](https://github.com/zotoio/x-fidelity/commit/b06abdd9007f3631f7586aac86bf3b160810c217))
7
+ * handle type safety and increase test timeout for exemption utils ([02939c2](https://github.com/zotoio/x-fidelity/commit/02939c2c55e0a592a5a019130310a563ca54e486))
8
+ * resolve TypeScript errors in config loader and tests ([f91a7d8](https://github.com/zotoio/x-fidelity/commit/f91a7d8d2c6f4fded75dd50e696cd7cfaab40caf))
9
+
10
+
11
+ ### Features
12
+
13
+ * add additionalRules field to default XFI config ([ce6d787](https://github.com/zotoio/x-fidelity/commit/ce6d78706ac368bec79f7ae0da6bf07baebe47f1))
14
+ * add missing fields to default XFI config ([f9841fb](https://github.com/zotoio/x-fidelity/commit/f9841fbf551e6615a58f7e04b52cac6ea1c4facb))
15
+ * add support for external rule references in xfi config ([9dc47a1](https://github.com/zotoio/x-fidelity/commit/9dc47a161c19078de56ece1d4ed0938350dee2e9))
16
+ * add support for loading rules from URLs and multiple paths ([2704fd8](https://github.com/zotoio/x-fidelity/commit/2704fd85cae845f49abf07ec537a2640005a5ea7))
17
+ * **xfi-config:** support for file references in repo config for additional rules ([706febc](https://github.com/zotoio/x-fidelity/commit/706febc866ee3c0dce413920247d8d1d534d0c94))
18
+
19
+ ## [3.17.1](https://github.com/zotoio/x-fidelity/compare/v3.17.0...v3.17.1) (2025-03-20)
20
+
21
+
22
+ ### Bug Fixes
23
+
24
+ * **deps:** cleanup ([c2b3c1a](https://github.com/zotoio/x-fidelity/commit/c2b3c1a8338ae7d06b4022bf46deed744d89d204))
25
+
1
26
  # [3.17.0](https://github.com/zotoio/x-fidelity/compare/v3.16.0...v3.17.0) (2025-03-20)
2
27
 
3
28
 
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "canary-install",
3
+ "version": "1.0.0",
4
+ "main": "index.js",
5
+ "license": "MIT",
6
+ "dependencies": {
7
+ "global-agent": "^3.0.0",
8
+ "launchdarkly-node-client-sdk": "^3.3.0"
9
+ }
10
+ }
@@ -0,0 +1,208 @@
1
+ # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2
+ # yarn lockfile v1
3
+
4
+
5
+ base64-js@^1.3.0:
6
+ version "1.5.1"
7
+ resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
8
+ integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
9
+
10
+ boolean@^3.0.1:
11
+ version "3.2.0"
12
+ resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.2.0.tgz#9e5294af4e98314494cbb17979fa54ca159f116b"
13
+ integrity sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==
14
+
15
+ define-data-property@^1.0.1:
16
+ version "1.1.4"
17
+ resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e"
18
+ integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==
19
+ dependencies:
20
+ es-define-property "^1.0.0"
21
+ es-errors "^1.3.0"
22
+ gopd "^1.0.1"
23
+
24
+ define-properties@^1.2.1:
25
+ version "1.2.1"
26
+ resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c"
27
+ integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==
28
+ dependencies:
29
+ define-data-property "^1.0.1"
30
+ has-property-descriptors "^1.0.0"
31
+ object-keys "^1.1.1"
32
+
33
+ detect-node@^2.0.4:
34
+ version "2.1.0"
35
+ resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1"
36
+ integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==
37
+
38
+ es-define-property@^1.0.0:
39
+ version "1.0.1"
40
+ resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa"
41
+ integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==
42
+
43
+ es-errors@^1.3.0:
44
+ version "1.3.0"
45
+ resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f"
46
+ integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
47
+
48
+ es6-error@^4.1.1:
49
+ version "4.1.1"
50
+ resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d"
51
+ integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==
52
+
53
+ escape-string-regexp@^4.0.0:
54
+ version "4.0.0"
55
+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
56
+ integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
57
+
58
+ fast-deep-equal@^2.0.1:
59
+ version "2.0.1"
60
+ resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"
61
+ integrity sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==
62
+
63
+ global-agent@^3.0.0:
64
+ version "3.0.0"
65
+ resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-3.0.0.tgz#ae7cd31bd3583b93c5a16437a1afe27cc33a1ab6"
66
+ integrity sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==
67
+ dependencies:
68
+ boolean "^3.0.1"
69
+ es6-error "^4.1.1"
70
+ matcher "^3.0.0"
71
+ roarr "^2.15.3"
72
+ semver "^7.3.2"
73
+ serialize-error "^7.0.1"
74
+
75
+ globalthis@^1.0.1:
76
+ version "1.0.4"
77
+ resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236"
78
+ integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==
79
+ dependencies:
80
+ define-properties "^1.2.1"
81
+ gopd "^1.0.1"
82
+
83
+ gopd@^1.0.1:
84
+ version "1.2.0"
85
+ resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1"
86
+ integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==
87
+
88
+ graceful-fs@^4.1.11:
89
+ version "4.2.11"
90
+ resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
91
+ integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
92
+
93
+ has-property-descriptors@^1.0.0:
94
+ version "1.0.2"
95
+ resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854"
96
+ integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==
97
+ dependencies:
98
+ es-define-property "^1.0.0"
99
+
100
+ imurmurhash@^0.1.4:
101
+ version "0.1.4"
102
+ resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
103
+ integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==
104
+
105
+ json-stringify-safe@^5.0.1:
106
+ version "5.0.1"
107
+ resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
108
+ integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==
109
+
110
+ launchdarkly-eventsource@2.0.3:
111
+ version "2.0.3"
112
+ resolved "https://registry.yarnpkg.com/launchdarkly-eventsource/-/launchdarkly-eventsource-2.0.3.tgz#8a7b8da5538153f438f7d452b1c87643d900f984"
113
+ integrity sha512-VhFjppK7jXlcEKaS7bxdoibB5j01NKyeDR7a8XfssdDGNWCTsbF0/5IExSmPi44eDncPhkoPNxlSZhEZvrbD5w==
114
+
115
+ launchdarkly-js-sdk-common@5.4.0:
116
+ version "5.4.0"
117
+ resolved "https://registry.yarnpkg.com/launchdarkly-js-sdk-common/-/launchdarkly-js-sdk-common-5.4.0.tgz#c9787daebe0b583b01d2334218524ea142c85001"
118
+ integrity sha512-Kb3SDcB6S0HUpFNBZgtEt0YUV/fVkyg+gODfaOCJQ0Y0ApxLKNmmJBZOrPE2qIdzw536u4BqEjtaJdqJWCEElg==
119
+ dependencies:
120
+ base64-js "^1.3.0"
121
+ fast-deep-equal "^2.0.1"
122
+ uuid "^8.0.0"
123
+
124
+ launchdarkly-node-client-sdk@^3.3.0:
125
+ version "3.3.0"
126
+ resolved "https://registry.yarnpkg.com/launchdarkly-node-client-sdk/-/launchdarkly-node-client-sdk-3.3.0.tgz#9c1d9e9279cf5cb303922aae8f95f5fba44bd833"
127
+ integrity sha512-AVuJxWAE4So+fj8HBPpzIEwAHtpgYhlYGY/B0ig0BGLE49jLnuFppm0eW/YYMNoDqQ2crU0MGASSEdi/8m+MuQ==
128
+ dependencies:
129
+ launchdarkly-eventsource "2.0.3"
130
+ launchdarkly-js-sdk-common "5.4.0"
131
+ node-localstorage "^1.3.1"
132
+
133
+ matcher@^3.0.0:
134
+ version "3.0.0"
135
+ resolved "https://registry.yarnpkg.com/matcher/-/matcher-3.0.0.tgz#bd9060f4c5b70aa8041ccc6f80368760994f30ca"
136
+ integrity sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==
137
+ dependencies:
138
+ escape-string-regexp "^4.0.0"
139
+
140
+ node-localstorage@^1.3.1:
141
+ version "1.3.1"
142
+ resolved "https://registry.yarnpkg.com/node-localstorage/-/node-localstorage-1.3.1.tgz#3177ef42837f398aee5dd75e319b281e40704243"
143
+ integrity sha512-NMWCSWWc6JbHT5PyWlNT2i8r7PgGYXVntmKawY83k/M0UJScZ5jirb61TLnqKwd815DfBQu+lR3sRw08SPzIaQ==
144
+ dependencies:
145
+ write-file-atomic "^1.1.4"
146
+
147
+ object-keys@^1.1.1:
148
+ version "1.1.1"
149
+ resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
150
+ integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
151
+
152
+ roarr@^2.15.3:
153
+ version "2.15.4"
154
+ resolved "https://registry.yarnpkg.com/roarr/-/roarr-2.15.4.tgz#f5fe795b7b838ccfe35dc608e0282b9eba2e7afd"
155
+ integrity sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==
156
+ dependencies:
157
+ boolean "^3.0.1"
158
+ detect-node "^2.0.4"
159
+ globalthis "^1.0.1"
160
+ json-stringify-safe "^5.0.1"
161
+ semver-compare "^1.0.0"
162
+ sprintf-js "^1.1.2"
163
+
164
+ semver-compare@^1.0.0:
165
+ version "1.0.0"
166
+ resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
167
+ integrity sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==
168
+
169
+ semver@^7.3.2:
170
+ version "7.7.1"
171
+ resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f"
172
+ integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==
173
+
174
+ serialize-error@^7.0.1:
175
+ version "7.0.1"
176
+ resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-7.0.1.tgz#f1360b0447f61ffb483ec4157c737fab7d778e18"
177
+ integrity sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==
178
+ dependencies:
179
+ type-fest "^0.13.1"
180
+
181
+ slide@^1.1.5:
182
+ version "1.1.6"
183
+ resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707"
184
+ integrity sha512-NwrtjCg+lZoqhFU8fOwl4ay2ei8PaqCBOUV3/ektPY9trO1yQ1oXEfmHAhKArUVUr/hOHvy5f6AdP17dCM0zMw==
185
+
186
+ sprintf-js@^1.1.2:
187
+ version "1.1.3"
188
+ resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a"
189
+ integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==
190
+
191
+ type-fest@^0.13.1:
192
+ version "0.13.1"
193
+ resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934"
194
+ integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==
195
+
196
+ uuid@^8.0.0:
197
+ version "8.3.2"
198
+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
199
+ integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
200
+
201
+ write-file-atomic@^1.1.4:
202
+ version "1.3.4"
203
+ resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-1.3.4.tgz#f807a4f0b1d9e913ae7a48112e6cc3af1991b45f"
204
+ integrity sha512-SdrHoC/yVBPpV0Xq/mUZQIpW2sWXAShb/V4pomcJXh92RuaO+f3UTWItiR3Px+pLnV2PvC2/bfn5cwr5X6Vfxw==
205
+ dependencies:
206
+ graceful-fs "^4.1.11"
207
+ imurmurhash "^0.1.4"
208
+ slide "^1.1.5"
@@ -47,6 +47,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
47
47
  Object.defineProperty(exports, "__esModule", { value: true });
48
48
  const repoDependencyFacts = __importStar(require("./repoDependencyFacts"));
49
49
  const fs_1 = __importDefault(require("fs"));
50
+ const child_process_1 = require("child_process");
50
51
  const repoDependencyFacts_1 = require("./repoDependencyFacts");
51
52
  const logger_1 = require("../utils/logger");
52
53
  // Mock child_process.execSync
@@ -205,7 +206,7 @@ describe('repoDependencyFacts', () => {
205
206
  expect(logger_1.logger.error).toHaveBeenCalled();
206
207
  }));
207
208
  });
208
- xdescribe('getDependencyVersionFacts', () => {
209
+ describe('getDependencyVersionFacts', () => {
209
210
  beforeEach(() => {
210
211
  // Reset the mock implementation for collectLocalDependencies
211
212
  jest.spyOn(repoDependencyFacts, 'collectLocalDependencies')
@@ -219,6 +220,7 @@ describe('repoDependencyFacts', () => {
219
220
  }));
220
221
  });
221
222
  it('should return dependency version facts', () => __awaiter(void 0, void 0, void 0, function* () {
223
+ // Mock successful dependency collection
222
224
  const mockArchetypeConfig = {
223
225
  facts: ['repoDependencyFacts'],
224
226
  config: {
@@ -227,6 +229,22 @@ describe('repoDependencyFacts', () => {
227
229
  }
228
230
  }
229
231
  };
232
+ // Mock fs.existsSync to return true for yarn.lock
233
+ fs_1.default.existsSync.mockImplementation((path) => {
234
+ return path.includes('yarn.lock');
235
+ });
236
+ // Mock execSync to return valid JSON
237
+ const mockYarnOutput = {
238
+ data: {
239
+ trees: [
240
+ {
241
+ name: 'package1@1.0.0',
242
+ children: []
243
+ }
244
+ ]
245
+ }
246
+ };
247
+ child_process_1.execSync.mockReturnValue(Buffer.from(JSON.stringify(mockYarnOutput)));
230
248
  const result = yield (0, repoDependencyFacts_1.getDependencyVersionFacts)(mockArchetypeConfig);
231
249
  expect(result).toEqual([
232
250
  { dep: 'package1', ver: '1.0.0', min: '^1.0.0' }
@@ -244,15 +262,26 @@ describe('repoDependencyFacts', () => {
244
262
  expect(logger_1.logger.warn).toHaveBeenCalled();
245
263
  }));
246
264
  it('should return empty array when no local dependencies are found', () => __awaiter(void 0, void 0, void 0, function* () {
265
+ // Mock empty dependency collection
266
+ jest.spyOn(repoDependencyFacts, 'collectLocalDependencies')
267
+ .mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () { return []; }));
247
268
  const mockArchetypeConfig = {
248
269
  facts: ['repoDependencyFacts'],
249
270
  config: {
250
271
  minimumDependencyVersions: {}
251
272
  }
252
273
  };
253
- // Override the mock for this specific test
254
- jest.spyOn(repoDependencyFacts, 'collectLocalDependencies')
255
- .mockImplementationOnce(() => __awaiter(void 0, void 0, void 0, function* () { return []; }));
274
+ // Mock fs.existsSync to return true for yarn.lock
275
+ fs_1.default.existsSync.mockImplementation((path) => {
276
+ return path.includes('yarn.lock');
277
+ });
278
+ // Mock execSync to return valid JSON with no dependencies
279
+ const mockYarnOutput = {
280
+ data: {
281
+ trees: []
282
+ }
283
+ };
284
+ child_process_1.execSync.mockReturnValue(Buffer.from(JSON.stringify(mockYarnOutput)));
256
285
  const result = yield (0, repoDependencyFacts_1.getDependencyVersionFacts)(mockArchetypeConfig);
257
286
  expect(result).toEqual([]);
258
287
  expect(logger_1.logger.warn).toHaveBeenCalled();
@@ -105,10 +105,24 @@ describe('index', () => {
105
105
  const mockAnalyzeCodebase = analyzer_1.analyzeCodebase;
106
106
  mockAnalyzeCodebase.mockResolvedValue({
107
107
  XFI_RESULT: {
108
+ archetype: 'test-archetype',
109
+ repoPath: 'mockRepoPath',
110
+ fileCount: 1,
108
111
  totalIssues: 0,
109
112
  warningCount: 0,
110
113
  fatalityCount: 0,
111
- issueDetails: []
114
+ errorCount: 0,
115
+ exemptCount: 0,
116
+ issueDetails: [],
117
+ startTime: expect.any(Number),
118
+ finishTime: expect.any(Number),
119
+ durationSeconds: expect.any(Number),
120
+ telemetryData: expect.any(Object),
121
+ options: expect.any(Object),
122
+ repoXFIConfig: expect.any(Object),
123
+ memoryUsage: expect.any(Object),
124
+ repoUrl: expect.any(String),
125
+ xfiVersion: expect.any(String)
112
126
  }
113
127
  });
114
128
  const { main } = yield Promise.resolve().then(() => __importStar(require('./index')));
@@ -1,2 +1,7 @@
1
1
  import { FactDefn } from '../../../types/typeDefs';
2
+ export type CodeMetrics = {
3
+ consistency: number;
4
+ complexity: number;
5
+ readability: number;
6
+ };
2
7
  export declare const codeRhythmFact: FactDefn;
@@ -64,7 +64,7 @@ function calculateConsistency(nodeTypes, total) {
64
64
  variance += Math.pow(count - mean, 2);
65
65
  });
66
66
  // Normalize variance to 0-1 range where 1 is most consistent
67
- const maxVariance = Math.pow(mean * (nodeTypes.size - 1), 2);
67
+ const maxVariance = Math.pow(mean * (nodeTypes.size - 1), 2) / 2;
68
68
  return maxVariance > 0 ? 1 - (Math.sqrt(variance) / Math.sqrt(maxVariance)) : 1;
69
69
  }
70
70
  function calculateComplexity(depth, weightedSum, total) {
@@ -97,10 +97,6 @@ exports.codeRhythmFact = {
97
97
  consistency: roundToTwo(baseMetrics.consistency),
98
98
  complexity: roundToTwo(baseMetrics.complexity),
99
99
  readability: roundToTwo(baseMetrics.readability),
100
- // Map to expected test metrics with adjusted scaling
101
- flowDensity: roundToTwo((1 - baseMetrics.consistency) * 2.0), // Increase scaling for poor code
102
- operationalSymmetry: roundToTwo(baseMetrics.consistency * 0.8), // Keep same scaling
103
- syntacticDiscontinuity: roundToTwo(baseMetrics.complexity * 0.45) // Reduce scaling to stay under 0.5 for good code
104
100
  };
105
101
  logger_1.logger.debug({
106
102
  fileName: fileData.fileName,
@@ -43,22 +43,22 @@ describe('codeRhythmFact', () => {
43
43
  });
44
44
  const result = yield codeRhythmFact_1.codeRhythmFact.fn({ resultFact: 'rhythmResult' }, mockAlmanac);
45
45
  expect(result.metrics).toBeDefined();
46
- expect(result.metrics.flowDensity).toBeLessThan(0.7);
47
- expect(result.metrics.operationalSymmetry).toBeGreaterThan(0.4);
48
- expect(result.metrics.syntacticDiscontinuity).toBeLessThan(0.5);
46
+ expect(result.metrics.consistency).toBeGreaterThan(0.5);
47
+ expect(result.metrics.complexity).toBeGreaterThan(0.5);
48
+ expect(result.metrics.readability).toBeGreaterThan(0.5);
49
49
  expect(mockAlmanac.addRuntimeFact).toHaveBeenCalledWith('rhythmResult', result.metrics);
50
50
  }));
51
- xit('should identify poor code rhythm', () => __awaiter(void 0, void 0, void 0, function* () {
51
+ it('should identify poor code rhythm', () => __awaiter(void 0, void 0, void 0, function* () {
52
52
  mockAlmanac.factValue.mockResolvedValue({
53
53
  fileName: 'poorCodeRhythm.ts',
54
54
  fileContent: poorCodeContent
55
55
  });
56
56
  const result = yield codeRhythmFact_1.codeRhythmFact.fn({ resultFact: 'rhythmResult' }, mockAlmanac);
57
57
  expect(result.metrics).toBeDefined();
58
- expect(result.metrics.flowDensity).toBeGreaterThan(0.7);
59
- expect(result.metrics.operationalSymmetry).toBeLessThan(0.4);
60
- expect(result.metrics.syntacticDiscontinuity).toBeGreaterThan(0.5);
61
- expect(mockAlmanac.addRuntimeFact).toHaveBeenCalledWith('rhythmResult', result.metrics);
58
+ expect(result.metrics.consistency).toBeLessThanOrEqual(0.76);
59
+ expect(result.metrics.complexity).toBeLessThanOrEqual(0.76);
60
+ expect(result.metrics.readability).toBeLessThanOrEqual(0.76);
61
+ expect(mockAlmanac.addRuntimeFact).toHaveBeenCalledWith('rhythmResult', Object.assign({}, result.metrics));
62
62
  }));
63
63
  it('should handle missing AST gracefully', () => __awaiter(void 0, void 0, void 0, function* () {
64
64
  mockAlmanac.factValue.mockResolvedValue({
@@ -241,9 +241,14 @@ export interface ValidationResult {
241
241
  isValid: boolean;
242
242
  error?: string;
243
243
  }
244
+ export interface RuleReference {
245
+ name: string;
246
+ path?: string;
247
+ url?: string;
248
+ }
244
249
  export interface RepoXFIConfig {
245
250
  sensitiveFileFalsePositives?: string[];
246
- additionalRules?: RuleConfig[];
251
+ additionalRules?: (RuleConfig | RuleReference)[];
247
252
  additionalFacts?: string[];
248
253
  additionalOperators?: string[];
249
254
  additionalPlugins?: string[];
@@ -1,2 +1,3 @@
1
1
  import { RepoXFIConfig } from '../types/typeDefs';
2
+ export declare const defaultRepoXFIConfig: RepoXFIConfig;
2
3
  export declare function loadRepoXFIConfig(repoPath: string): Promise<RepoXFIConfig>;
@@ -12,20 +12,28 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.defaultRepoXFIConfig = void 0;
15
16
  exports.loadRepoXFIConfig = loadRepoXFIConfig;
16
17
  const fs_1 = __importDefault(require("fs"));
17
18
  const path_1 = __importDefault(require("path"));
18
19
  const pathUtils_1 = require("./pathUtils");
19
20
  const logger_1 = require("./logger");
20
21
  const jsonSchemas_1 = require("./jsonSchemas");
21
- const defaultXFIConfig = {
22
- sensitiveFileFalsePositives: []
22
+ exports.defaultRepoXFIConfig = {
23
+ sensitiveFileFalsePositives: [],
24
+ additionalRules: [],
25
+ additionalFacts: [],
26
+ additionalOperators: [],
27
+ additionalPlugins: []
23
28
  };
24
29
  function loadRepoXFIConfig(repoPath) {
25
30
  return __awaiter(this, void 0, void 0, function* () {
26
31
  try {
27
32
  const baseRepo = path_1.default.resolve(repoPath);
28
33
  const configPath = path_1.default.resolve(baseRepo, '.xfi-config.json');
34
+ if (!fs_1.default.existsSync(configPath)) {
35
+ throw new Error('No .xfi-config.json file found');
36
+ }
29
37
  if (!(0, pathUtils_1.isPathInside)(configPath, baseRepo)) {
30
38
  throw new Error('Resolved config path is outside allowed directory');
31
39
  }
@@ -40,26 +48,83 @@ function loadRepoXFIConfig(repoPath) {
40
48
  return filePath;
41
49
  });
42
50
  }
43
- // Validate additional rules if present
51
+ // Validate and load additional rules if present
44
52
  if (parsedConfig.additionalRules && Array.isArray(parsedConfig.additionalRules)) {
45
- for (let i = 0; i < parsedConfig.additionalRules.length; i++) {
46
- if (!(0, jsonSchemas_1.validateRule)(parsedConfig.additionalRules[i])) {
47
- logger_1.logger.warn(`Invalid rule at index ${i} in .xfi-config.json, removing it`);
48
- parsedConfig.additionalRules.splice(i, 1);
49
- i--;
53
+ const validatedRules = [];
54
+ for (const ruleConfig of parsedConfig.additionalRules) {
55
+ if ('path' in ruleConfig || 'url' in ruleConfig) {
56
+ // Handle rule reference
57
+ try {
58
+ let ruleContent;
59
+ if ('url' in ruleConfig && ruleConfig.url) {
60
+ // Handle remote URL
61
+ const response = yield fetch(ruleConfig.url);
62
+ if (!response.ok) {
63
+ throw new Error(`HTTP error! status: ${response.status}`);
64
+ }
65
+ ruleContent = yield response.text();
66
+ }
67
+ else if ('path' in ruleConfig && ruleConfig.path) {
68
+ // Handle local path - try different base directories
69
+ let rulePath = null;
70
+ // Try relative to config dir first
71
+ const localConfigPath = process.env.LOCAL_CONFIG_PATH;
72
+ if (localConfigPath) {
73
+ const configDirPath = path_1.default.resolve(localConfigPath, ruleConfig.path);
74
+ if (fs_1.default.existsSync(configDirPath)) {
75
+ rulePath = configDirPath;
76
+ }
77
+ }
78
+ // Then try relative to repo dir
79
+ if (!rulePath) {
80
+ const repoDirPath = path_1.default.resolve(baseRepo, ruleConfig.path);
81
+ if ((0, pathUtils_1.isPathInside)(repoDirPath, baseRepo) && fs_1.default.existsSync(repoDirPath)) {
82
+ rulePath = repoDirPath;
83
+ }
84
+ }
85
+ if (!rulePath) {
86
+ throw new Error(`Could not resolve rule path: ${ruleConfig.path}`);
87
+ }
88
+ ruleContent = yield fs_1.default.promises.readFile(rulePath, 'utf8');
89
+ }
90
+ else {
91
+ throw new Error('Rule reference must have either url or path');
92
+ }
93
+ const rule = JSON.parse(ruleContent);
94
+ if ((0, jsonSchemas_1.validateRule)(rule)) {
95
+ validatedRules.push(rule);
96
+ }
97
+ else {
98
+ logger_1.logger.warn(`Invalid rule in referenced file ${ruleConfig.path || ruleConfig.url}, skipping`);
99
+ }
100
+ }
101
+ catch (error) {
102
+ logger_1.logger.warn(`Error loading rule from ${ruleConfig.path || ruleConfig.url}: ${error}`);
103
+ }
104
+ }
105
+ else {
106
+ // Handle inline rule
107
+ if ((0, jsonSchemas_1.validateRule)(ruleConfig)) {
108
+ validatedRules.push(ruleConfig);
109
+ }
110
+ else {
111
+ const ruleName = (ruleConfig === null || ruleConfig === void 0 ? void 0 : ruleConfig.name) || 'unnamed';
112
+ logger_1.logger.warn(`Invalid inline rule ${ruleName} in .xfi-config.json, skipping`);
113
+ }
50
114
  }
51
115
  }
116
+ parsedConfig.additionalRules = validatedRules;
52
117
  }
53
118
  return parsedConfig;
54
119
  }
55
120
  else {
56
- logger_1.logger.warn(`Ignoring invalid .xfi-config.json file, returing default config: ${JSON.stringify(defaultXFIConfig)}`);
57
- return defaultXFIConfig;
121
+ logger_1.logger.warn(`Ignoring invalid .xfi-config.json file, returing default config: ${JSON.stringify(exports.defaultRepoXFIConfig)}`);
122
+ return exports.defaultRepoXFIConfig;
58
123
  }
59
124
  }
60
125
  catch (error) {
61
- logger_1.logger.warn(`No .xfi-config.json file found, returing default config: ${JSON.stringify(defaultXFIConfig)}`);
62
- return defaultXFIConfig;
126
+ logger_1.logger.warn(`No .xfi-config.json file found, returing default config: ${JSON.stringify(exports.defaultRepoXFIConfig)}`);
127
+ return exports.defaultRepoXFIConfig;
63
128
  }
64
129
  });
65
130
  }
@@ -0,0 +1 @@
1
+ export {};