heicat-core 0.1.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/dist/contract-loader.d.ts +4 -0
- package/dist/contract-loader.d.ts.map +1 -0
- package/dist/contract-loader.js +45 -0
- package/dist/contract-loader.js.map +1 -0
- package/dist/engine.d.ts +22 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +75 -0
- package/dist/engine.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware.d.ts +13 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +78 -0
- package/dist/middleware.js.map +1 -0
- package/dist/types/contract.d.ts +26 -0
- package/dist/types/contract.d.ts.map +1 -0
- package/dist/types/contract.js +3 -0
- package/dist/types/contract.js.map +1 -0
- package/dist/types/options.d.ts +7 -0
- package/dist/types/options.d.ts.map +1 -0
- package/dist/types/options.js +3 -0
- package/dist/types/options.js.map +1 -0
- package/dist/types/violation.d.ts +19 -0
- package/dist/types/violation.d.ts.map +1 -0
- package/dist/types/violation.js +3 -0
- package/dist/types/violation.js.map +1 -0
- package/dist/validation-engine.d.ts +10 -0
- package/dist/validation-engine.d.ts.map +1 -0
- package/dist/validation-engine.js +138 -0
- package/dist/validation-engine.js.map +1 -0
- package/dist/violation-store.d.ts +16 -0
- package/dist/violation-store.d.ts.map +1 -0
- package/dist/violation-store.js +41 -0
- package/dist/violation-store.js.map +1 -0
- package/jest.config.js +7 -0
- package/package.json +45 -0
- package/src/__tests__/contract-loader.test.ts +112 -0
- package/src/__tests__/validation-engine.test.ts +213 -0
- package/src/contract-loader.ts +55 -0
- package/src/engine.ts +95 -0
- package/src/index.ts +9 -0
- package/src/middleware.ts +97 -0
- package/src/types/contract.ts +28 -0
- package/src/types/options.ts +7 -0
- package/src/types/violation.ts +19 -0
- package/src/validation-engine.ts +157 -0
- package/src/violation-store.ts +46 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Contract } from './types/contract';
|
|
2
|
+
export declare function loadContracts(contractsPath: string): Promise<Contract[]>;
|
|
3
|
+
export declare function findContract(contracts: Contract[], method: string, path: string): Contract | undefined;
|
|
4
|
+
//# sourceMappingURL=contract-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract-loader.d.ts","sourceRoot":"","sources":["../src/contract-loader.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,wBAAsB,aAAa,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAG9E;AAqCD,wBAAgB,YAAY,CAC1B,SAAS,EAAE,QAAQ,EAAE,EACrB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,GACX,QAAQ,GAAG,SAAS,CAKtB"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadContracts = loadContracts;
|
|
4
|
+
exports.findContract = findContract;
|
|
5
|
+
const fs_1 = require("fs");
|
|
6
|
+
const path_1 = require("path");
|
|
7
|
+
async function loadContracts(contractsPath) {
|
|
8
|
+
// Use manual directory traversal (more reliable than glob)
|
|
9
|
+
return loadContractsManually(contractsPath);
|
|
10
|
+
}
|
|
11
|
+
function loadContractsManually(contractsPath) {
|
|
12
|
+
const contracts = [];
|
|
13
|
+
const fullPath = (0, path_1.resolve)(contractsPath);
|
|
14
|
+
try {
|
|
15
|
+
const items = (0, fs_1.readdirSync)(fullPath);
|
|
16
|
+
for (const item of items) {
|
|
17
|
+
const itemPath = (0, path_1.join)(fullPath, item);
|
|
18
|
+
const stat = (0, fs_1.statSync)(itemPath);
|
|
19
|
+
if (stat.isFile() && item.endsWith('.contract.json')) {
|
|
20
|
+
try {
|
|
21
|
+
const content = (0, fs_1.readFileSync)(itemPath, 'utf-8');
|
|
22
|
+
const contract = JSON.parse(content);
|
|
23
|
+
// Basic validation
|
|
24
|
+
if (!contract.method || !contract.path) {
|
|
25
|
+
console.warn(`Invalid contract in ${itemPath}: missing method or path`);
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
contracts.push(contract);
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
console.warn(`Failed to load contract from ${itemPath}:`, error);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
console.warn(`Failed to read contracts directory ${fullPath}:`, error);
|
|
38
|
+
}
|
|
39
|
+
return contracts;
|
|
40
|
+
}
|
|
41
|
+
function findContract(contracts, method, path) {
|
|
42
|
+
return contracts.find(contract => contract.method.toUpperCase() === method.toUpperCase() &&
|
|
43
|
+
contract.path === path);
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=contract-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract-loader.js","sourceRoot":"","sources":["../src/contract-loader.ts"],"names":[],"mappings":";;AAKA,sCAGC;AAqCD,oCASC;AArDD,2BAAyD;AACzD,+BAA8C;AAGvC,KAAK,UAAU,aAAa,CAAC,aAAqB;IACvD,2DAA2D;IAC3D,OAAO,qBAAqB,CAAC,aAAa,CAAC,CAAC;AAC9C,CAAC;AAED,SAAS,qBAAqB,CAAC,aAAqB;IAClD,MAAM,SAAS,GAAe,EAAE,CAAC;IACjC,MAAM,QAAQ,GAAG,IAAA,cAAO,EAAC,aAAa,CAAC,CAAC;IAExC,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,IAAA,gBAAW,EAAC,QAAQ,CAAC,CAAC;QAEpC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,QAAQ,GAAG,IAAA,WAAI,EAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;YACtC,MAAM,IAAI,GAAG,IAAA,aAAQ,EAAC,QAAQ,CAAC,CAAC;YAEhC,IAAI,IAAI,CAAC,MAAM,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC;gBACrD,IAAI,CAAC;oBACH,MAAM,OAAO,GAAG,IAAA,iBAAY,EAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;oBAChD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAa,CAAC;oBAEjD,mBAAmB;oBACnB,IAAI,CAAC,QAAQ,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;wBACvC,OAAO,CAAC,IAAI,CAAC,uBAAuB,QAAQ,0BAA0B,CAAC,CAAC;wBACxE,SAAS;oBACX,CAAC;oBAED,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBAC3B,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,OAAO,CAAC,IAAI,CAAC,gCAAgC,QAAQ,GAAG,EAAE,KAAK,CAAC,CAAC;gBACnE,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,sCAAsC,QAAQ,GAAG,EAAE,KAAK,CAAC,CAAC;IACzE,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAgB,YAAY,CAC1B,SAAqB,EACrB,MAAc,EACd,IAAY;IAEZ,OAAO,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAC/B,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC,WAAW,EAAE;QACtD,QAAQ,CAAC,IAAI,KAAK,IAAI,CACvB,CAAC;AACJ,CAAC"}
|
package/dist/engine.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Violation } from './types/violation';
|
|
2
|
+
import { ValidationMode } from './types/options';
|
|
3
|
+
export declare class ContractEngine {
|
|
4
|
+
private contracts;
|
|
5
|
+
private validationEngine;
|
|
6
|
+
private violationStore;
|
|
7
|
+
private mode;
|
|
8
|
+
constructor(mode: ValidationMode);
|
|
9
|
+
loadContracts(contractsPath: string): Promise<void>;
|
|
10
|
+
validateRequest(method: string, path: string, req: any): Violation[];
|
|
11
|
+
validateResponse(method: string, path: string, statusCode: number, body: any): Violation[];
|
|
12
|
+
validateError(method: string, path: string, statusCode: number, body: any): Violation[];
|
|
13
|
+
private logViolation;
|
|
14
|
+
getViolations(): Violation[];
|
|
15
|
+
getStats(): {
|
|
16
|
+
total: number;
|
|
17
|
+
byType: Record<string, number>;
|
|
18
|
+
bySeverity: Record<string, number>;
|
|
19
|
+
};
|
|
20
|
+
clearViolations(): void;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAI9C,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD,qBAAa,cAAc;IACzB,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,IAAI,CAAiB;gBAEjB,IAAI,EAAE,cAAc;IAM1B,aAAa,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKzD,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,GAAG,SAAS,EAAE;IAkBpE,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,SAAS,EAAE;IAkB1F,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,SAAS,EAAE;IAkBvF,OAAO,CAAC,YAAY;IAKpB,aAAa;IAIb,QAAQ;;;;;IAIR,eAAe,IAAI,IAAI;CAGxB"}
|
package/dist/engine.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ContractEngine = void 0;
|
|
4
|
+
const validation_engine_1 = require("./validation-engine");
|
|
5
|
+
const violation_store_1 = require("./violation-store");
|
|
6
|
+
const contract_loader_1 = require("./contract-loader");
|
|
7
|
+
class ContractEngine {
|
|
8
|
+
constructor(mode) {
|
|
9
|
+
this.contracts = [];
|
|
10
|
+
this.validationEngine = new validation_engine_1.ValidationEngine();
|
|
11
|
+
this.violationStore = new violation_store_1.ViolationStore();
|
|
12
|
+
this.mode = mode;
|
|
13
|
+
}
|
|
14
|
+
async loadContracts(contractsPath) {
|
|
15
|
+
this.contracts = await (0, contract_loader_1.loadContracts)(contractsPath);
|
|
16
|
+
console.log(`Loaded ${this.contracts.length} contracts`);
|
|
17
|
+
}
|
|
18
|
+
validateRequest(method, path, req) {
|
|
19
|
+
const contract = (0, contract_loader_1.findContract)(this.contracts, method, path);
|
|
20
|
+
if (!contract) {
|
|
21
|
+
return []; // No contract means no validation
|
|
22
|
+
}
|
|
23
|
+
const result = this.validationEngine.validateRequest(contract, req);
|
|
24
|
+
if (!result.isValid) {
|
|
25
|
+
result.violations.forEach(violation => {
|
|
26
|
+
this.violationStore.add(violation);
|
|
27
|
+
this.logViolation(violation);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
return result.violations;
|
|
31
|
+
}
|
|
32
|
+
validateResponse(method, path, statusCode, body) {
|
|
33
|
+
const contract = (0, contract_loader_1.findContract)(this.contracts, method, path);
|
|
34
|
+
if (!contract) {
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
const result = this.validationEngine.validateResponse(contract, statusCode, body);
|
|
38
|
+
if (!result.isValid) {
|
|
39
|
+
result.violations.forEach(violation => {
|
|
40
|
+
this.violationStore.add(violation);
|
|
41
|
+
this.logViolation(violation);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return result.violations;
|
|
45
|
+
}
|
|
46
|
+
validateError(method, path, statusCode, body) {
|
|
47
|
+
const contract = (0, contract_loader_1.findContract)(this.contracts, method, path);
|
|
48
|
+
if (!contract) {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
const result = this.validationEngine.validateError(contract, statusCode, body);
|
|
52
|
+
if (!result.isValid) {
|
|
53
|
+
result.violations.forEach(violation => {
|
|
54
|
+
this.violationStore.add(violation);
|
|
55
|
+
this.logViolation(violation);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
return result.violations;
|
|
59
|
+
}
|
|
60
|
+
logViolation(violation) {
|
|
61
|
+
const prefix = violation.severity === 'error' ? '❌' : '⚠️';
|
|
62
|
+
console.log(`${prefix} ${violation.endpoint.method} ${violation.endpoint.path}: ${violation.message}`);
|
|
63
|
+
}
|
|
64
|
+
getViolations() {
|
|
65
|
+
return this.violationStore.getAll();
|
|
66
|
+
}
|
|
67
|
+
getStats() {
|
|
68
|
+
return this.violationStore.getStats();
|
|
69
|
+
}
|
|
70
|
+
clearViolations() {
|
|
71
|
+
this.violationStore.clear();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
exports.ContractEngine = ContractEngine;
|
|
75
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { contractMiddleware } from './middleware';
|
|
2
|
+
export { ContractEngine } from './engine';
|
|
3
|
+
export { ViolationStore } from './violation-store';
|
|
4
|
+
export { loadContracts } from './contract-loader';
|
|
5
|
+
export type { Contract } from './types/contract';
|
|
6
|
+
export type { Violation } from './types/violation';
|
|
7
|
+
export type { MiddlewareOptions } from './types/options';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGlD,YAAY,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACjD,YAAY,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,YAAY,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadContracts = exports.ViolationStore = exports.ContractEngine = exports.contractMiddleware = void 0;
|
|
4
|
+
var middleware_1 = require("./middleware");
|
|
5
|
+
Object.defineProperty(exports, "contractMiddleware", { enumerable: true, get: function () { return middleware_1.contractMiddleware; } });
|
|
6
|
+
var engine_1 = require("./engine");
|
|
7
|
+
Object.defineProperty(exports, "ContractEngine", { enumerable: true, get: function () { return engine_1.ContractEngine; } });
|
|
8
|
+
var violation_store_1 = require("./violation-store");
|
|
9
|
+
Object.defineProperty(exports, "ViolationStore", { enumerable: true, get: function () { return violation_store_1.ViolationStore; } });
|
|
10
|
+
var contract_loader_1 = require("./contract-loader");
|
|
11
|
+
Object.defineProperty(exports, "loadContracts", { enumerable: true, get: function () { return contract_loader_1.loadContracts; } });
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,2CAAkD;AAAzC,gHAAA,kBAAkB,OAAA;AAC3B,mCAA0C;AAAjC,wGAAA,cAAc,OAAA;AACvB,qDAAmD;AAA1C,iHAAA,cAAc,OAAA;AACvB,qDAAkD;AAAzC,gHAAA,aAAa,OAAA"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from 'express';
|
|
2
|
+
import { MiddlewareOptions } from './types/options';
|
|
3
|
+
import { ContractEngine } from './engine';
|
|
4
|
+
declare global {
|
|
5
|
+
namespace Express {
|
|
6
|
+
interface Request {
|
|
7
|
+
_contractEngine?: ContractEngine;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export declare function contractMiddleware(options: MiddlewareOptions): (req: Request, res: Response, next: NextFunction) => Promise<Response<any, Record<string, any>> | undefined>;
|
|
12
|
+
export declare function getContractEngine(req: Request): ContractEngine | undefined;
|
|
13
|
+
//# sourceMappingURL=middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE1C,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,OAAO,CAAC;QAChB,UAAU,OAAO;YACf,eAAe,CAAC,EAAE,cAAc,CAAC;SAClC;KACF;CACF;AAKD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,iBAAiB,IAW7C,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,6DAkE9D;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,cAAc,GAAG,SAAS,CAE1E"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.contractMiddleware = contractMiddleware;
|
|
4
|
+
exports.getContractEngine = getContractEngine;
|
|
5
|
+
const engine_1 = require("./engine");
|
|
6
|
+
// Shared engine instance across all requests
|
|
7
|
+
let sharedEngine = null;
|
|
8
|
+
function contractMiddleware(options) {
|
|
9
|
+
// Create shared engine instance only once
|
|
10
|
+
if (!sharedEngine) {
|
|
11
|
+
sharedEngine = new engine_1.ContractEngine(options.mode);
|
|
12
|
+
// Load contracts asynchronously
|
|
13
|
+
sharedEngine.loadContracts(options.contractsPath).catch(error => {
|
|
14
|
+
console.error('Failed to load contracts:', error);
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return async (req, res, next) => {
|
|
18
|
+
req._contractEngine = sharedEngine;
|
|
19
|
+
// Validate request
|
|
20
|
+
const requestViolations = sharedEngine.validateRequest(req.method, req.path, req);
|
|
21
|
+
if (requestViolations.some((v) => v.severity === 'error')) {
|
|
22
|
+
// Block invalid requests in all modes
|
|
23
|
+
const violation = requestViolations.find((v) => v.severity === 'error');
|
|
24
|
+
return res.status(400).json({
|
|
25
|
+
error: 'Request validation failed',
|
|
26
|
+
message: violation.message,
|
|
27
|
+
contract: violation.contractPath
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
// Store original send method
|
|
31
|
+
const originalSend = res.send;
|
|
32
|
+
const originalJson = res.json;
|
|
33
|
+
const originalStatus = res.status;
|
|
34
|
+
let responseSent = false;
|
|
35
|
+
let responseStatus = 200;
|
|
36
|
+
let responseBody = null;
|
|
37
|
+
// Override status to capture status code
|
|
38
|
+
res.status = function (code) {
|
|
39
|
+
responseStatus = code;
|
|
40
|
+
return originalStatus.call(this, code);
|
|
41
|
+
};
|
|
42
|
+
// Override json to capture response body
|
|
43
|
+
res.json = function (body) {
|
|
44
|
+
if (!responseSent) {
|
|
45
|
+
responseBody = body;
|
|
46
|
+
responseSent = true;
|
|
47
|
+
// Validate response
|
|
48
|
+
sharedEngine.validateResponse(req.method, req.path, responseStatus, body);
|
|
49
|
+
}
|
|
50
|
+
return originalJson.call(this, body);
|
|
51
|
+
};
|
|
52
|
+
// Override send to capture response body
|
|
53
|
+
res.send = function (body) {
|
|
54
|
+
if (!responseSent) {
|
|
55
|
+
responseBody = body;
|
|
56
|
+
responseSent = true;
|
|
57
|
+
// Try to parse JSON, otherwise treat as raw response
|
|
58
|
+
let parsedBody = body;
|
|
59
|
+
if (typeof body === 'string') {
|
|
60
|
+
try {
|
|
61
|
+
parsedBody = JSON.parse(body);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// Keep as string
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// Validate response
|
|
68
|
+
sharedEngine.validateResponse(req.method, req.path, responseStatus, parsedBody);
|
|
69
|
+
}
|
|
70
|
+
return originalSend.call(this, body);
|
|
71
|
+
};
|
|
72
|
+
next();
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function getContractEngine(req) {
|
|
76
|
+
return req._contractEngine;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=middleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface Contract {
|
|
2
|
+
method: string;
|
|
3
|
+
path: string;
|
|
4
|
+
auth?: string;
|
|
5
|
+
request?: {
|
|
6
|
+
body?: Record<string, any>;
|
|
7
|
+
query?: Record<string, any>;
|
|
8
|
+
params?: Record<string, any>;
|
|
9
|
+
headers?: Record<string, any>;
|
|
10
|
+
};
|
|
11
|
+
response?: Record<string, any>;
|
|
12
|
+
errors?: Record<string, any>;
|
|
13
|
+
}
|
|
14
|
+
export interface RequestContract {
|
|
15
|
+
body?: Record<string, any>;
|
|
16
|
+
query?: Record<string, any>;
|
|
17
|
+
params?: Record<string, any>;
|
|
18
|
+
headers?: Record<string, any>;
|
|
19
|
+
}
|
|
20
|
+
export interface ResponseContract {
|
|
21
|
+
[statusCode: string]: Record<string, any>;
|
|
22
|
+
}
|
|
23
|
+
export interface ErrorContract {
|
|
24
|
+
[statusCode: string]: Record<string, any>;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=contract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../src/types/contract.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE;QACR,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC3B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC5B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC7B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;KAC/B,CAAC;IACF,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,gBAAgB;IAC/B,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC3C;AAED,MAAM,WAAW,aAAa;IAC5B,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC3C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../../src/types/contract.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../src/types/options.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,KAAK,GAAG,QAAQ,GAAG,IAAI,CAAC;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,QAAQ,GAAG,IAAI,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options.js","sourceRoot":"","sources":["../../src/types/options.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface Violation {
|
|
2
|
+
id: string;
|
|
3
|
+
timestamp: Date;
|
|
4
|
+
type: 'request' | 'response' | 'error';
|
|
5
|
+
contractPath: string;
|
|
6
|
+
endpoint: {
|
|
7
|
+
method: string;
|
|
8
|
+
path: string;
|
|
9
|
+
};
|
|
10
|
+
expected: any;
|
|
11
|
+
actual: any;
|
|
12
|
+
message: string;
|
|
13
|
+
severity: 'error' | 'warning';
|
|
14
|
+
}
|
|
15
|
+
export interface ValidationResult {
|
|
16
|
+
isValid: boolean;
|
|
17
|
+
violations: Violation[];
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=violation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"violation.d.ts","sourceRoot":"","sources":["../../src/types/violation.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,IAAI,CAAC;IAChB,IAAI,EAAE,SAAS,GAAG,UAAU,GAAG,OAAO,CAAC;IACvC,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE;QACR,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,QAAQ,EAAE,GAAG,CAAC;IACd,MAAM,EAAE,GAAG,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;CAC/B;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,SAAS,EAAE,CAAC;CACzB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"violation.js","sourceRoot":"","sources":["../../src/types/violation.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Contract } from './types/contract';
|
|
2
|
+
import { ValidationResult } from './types/violation';
|
|
3
|
+
export declare class ValidationEngine {
|
|
4
|
+
private createSchema;
|
|
5
|
+
private createTypeSchema;
|
|
6
|
+
validateRequest(contract: Contract, req: any): ValidationResult;
|
|
7
|
+
validateResponse(contract: Contract, statusCode: number, body: any): ValidationResult;
|
|
8
|
+
validateError(contract: Contract, statusCode: number, body: any): ValidationResult;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=validation-engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation-engine.d.ts","sourceRoot":"","sources":["../src/validation-engine.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAqC,MAAM,kBAAkB,CAAC;AAC/E,OAAO,EAAa,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAEhE,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,YAAY;IA2BpB,OAAO,CAAC,gBAAgB;IA+BxB,eAAe,CACb,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,GAAG,GACP,gBAAgB;IA6BnB,gBAAgB,CACd,QAAQ,EAAE,QAAQ,EAClB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,GAAG,GACR,gBAAgB;IA2BnB,aAAa,CACX,QAAQ,EAAE,QAAQ,EAClB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,GAAG,GACR,gBAAgB;CA0BpB"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ValidationEngine = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
class ValidationEngine {
|
|
6
|
+
createSchema(obj) {
|
|
7
|
+
if (!obj || typeof obj !== 'object') {
|
|
8
|
+
return zod_1.z.any();
|
|
9
|
+
}
|
|
10
|
+
const shape = {};
|
|
11
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
12
|
+
if (typeof value === 'object' && value !== null) {
|
|
13
|
+
if (value.type) {
|
|
14
|
+
// Handle type-based schema
|
|
15
|
+
shape[key] = this.createTypeSchema(value);
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
// Handle nested object
|
|
19
|
+
shape[key] = this.createSchema(value);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
else if (typeof value === 'string') {
|
|
23
|
+
// Simple string type
|
|
24
|
+
shape[key] = zod_1.z.string();
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
shape[key] = zod_1.z.any();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return zod_1.z.object(shape);
|
|
31
|
+
}
|
|
32
|
+
createTypeSchema(config) {
|
|
33
|
+
let schema;
|
|
34
|
+
switch (config.type) {
|
|
35
|
+
case 'string':
|
|
36
|
+
schema = zod_1.z.string();
|
|
37
|
+
if (config.minLength)
|
|
38
|
+
schema = schema.min(config.minLength);
|
|
39
|
+
if (config.maxLength)
|
|
40
|
+
schema = schema.max(config.maxLength);
|
|
41
|
+
break;
|
|
42
|
+
case 'number':
|
|
43
|
+
schema = zod_1.z.number();
|
|
44
|
+
if (config.minimum)
|
|
45
|
+
schema = schema.min(config.minimum);
|
|
46
|
+
if (config.maximum)
|
|
47
|
+
schema = schema.max(config.maximum);
|
|
48
|
+
break;
|
|
49
|
+
case 'boolean':
|
|
50
|
+
schema = zod_1.z.boolean();
|
|
51
|
+
break;
|
|
52
|
+
case 'array':
|
|
53
|
+
schema = zod_1.z.array(this.createTypeSchema(config.items || {}));
|
|
54
|
+
break;
|
|
55
|
+
default:
|
|
56
|
+
schema = zod_1.z.any();
|
|
57
|
+
}
|
|
58
|
+
if (config.required === false) {
|
|
59
|
+
schema = schema.optional();
|
|
60
|
+
}
|
|
61
|
+
return schema;
|
|
62
|
+
}
|
|
63
|
+
validateRequest(contract, req) {
|
|
64
|
+
const violations = [];
|
|
65
|
+
if (contract.request?.body) {
|
|
66
|
+
const bodySchema = this.createSchema(contract.request.body);
|
|
67
|
+
const result = bodySchema.safeParse(req.body);
|
|
68
|
+
if (!result.success) {
|
|
69
|
+
violations.push({
|
|
70
|
+
id: `req-${Date.now()}-${Math.random()}`,
|
|
71
|
+
timestamp: new Date(),
|
|
72
|
+
type: 'request',
|
|
73
|
+
contractPath: contract.path,
|
|
74
|
+
endpoint: { method: contract.method, path: contract.path },
|
|
75
|
+
expected: contract.request.body,
|
|
76
|
+
actual: req.body,
|
|
77
|
+
message: `Request body validation failed: ${result.error.message}`,
|
|
78
|
+
severity: 'error'
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// TODO: Add query, params, headers validation
|
|
83
|
+
return {
|
|
84
|
+
isValid: violations.length === 0,
|
|
85
|
+
violations
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
validateResponse(contract, statusCode, body) {
|
|
89
|
+
const violations = [];
|
|
90
|
+
if (contract.response && contract.response[statusCode]) {
|
|
91
|
+
const responseSchema = this.createSchema(contract.response[statusCode]);
|
|
92
|
+
const result = responseSchema.safeParse(body);
|
|
93
|
+
if (!result.success) {
|
|
94
|
+
violations.push({
|
|
95
|
+
id: `res-${Date.now()}-${Math.random()}`,
|
|
96
|
+
timestamp: new Date(),
|
|
97
|
+
type: 'response',
|
|
98
|
+
contractPath: contract.path,
|
|
99
|
+
endpoint: { method: contract.method, path: contract.path },
|
|
100
|
+
expected: contract.response[statusCode],
|
|
101
|
+
actual: body,
|
|
102
|
+
message: `Response validation failed for status ${statusCode}: ${result.error.message}`,
|
|
103
|
+
severity: 'warning' // In dev mode, responses are warnings
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
isValid: violations.length === 0,
|
|
109
|
+
violations
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
validateError(contract, statusCode, body) {
|
|
113
|
+
const violations = [];
|
|
114
|
+
if (contract.errors && contract.errors[statusCode]) {
|
|
115
|
+
const errorSchema = this.createSchema(contract.errors[statusCode]);
|
|
116
|
+
const result = errorSchema.safeParse(body);
|
|
117
|
+
if (!result.success) {
|
|
118
|
+
violations.push({
|
|
119
|
+
id: `err-${Date.now()}-${Math.random()}`,
|
|
120
|
+
timestamp: new Date(),
|
|
121
|
+
type: 'error',
|
|
122
|
+
contractPath: contract.path,
|
|
123
|
+
endpoint: { method: contract.method, path: contract.path },
|
|
124
|
+
expected: contract.errors[statusCode],
|
|
125
|
+
actual: body,
|
|
126
|
+
message: `Error validation failed for status ${statusCode}: ${result.error.message}`,
|
|
127
|
+
severity: 'warning'
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
isValid: violations.length === 0,
|
|
133
|
+
violations
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
exports.ValidationEngine = ValidationEngine;
|
|
138
|
+
//# sourceMappingURL=validation-engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation-engine.js","sourceRoot":"","sources":["../src/validation-engine.ts"],"names":[],"mappings":";;;AAAA,6BAAwB;AAIxB,MAAa,gBAAgB;IACnB,YAAY,CAAC,GAAwB;QAC3C,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;YACpC,OAAO,OAAC,CAAC,GAAG,EAAE,CAAC;QACjB,CAAC;QAED,MAAM,KAAK,GAAiC,EAAE,CAAC;QAE/C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YAC/C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;gBAChD,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;oBACf,2BAA2B;oBAC3B,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;gBAC5C,CAAC;qBAAM,CAAC;oBACN,uBAAuB;oBACvB,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;gBACxC,CAAC;YACH,CAAC;iBAAM,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;gBACrC,qBAAqB;gBACrB,KAAK,CAAC,GAAG,CAAC,GAAG,OAAC,CAAC,MAAM,EAAE,CAAC;YAC1B,CAAC;iBAAM,CAAC;gBACN,KAAK,CAAC,GAAG,CAAC,GAAG,OAAC,CAAC,GAAG,EAAE,CAAC;YACvB,CAAC;QACH,CAAC;QAED,OAAO,OAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC;IAEO,gBAAgB,CAAC,MAAW;QAClC,IAAI,MAAoB,CAAC;QAEzB,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;YACpB,KAAK,QAAQ;gBACX,MAAM,GAAG,OAAC,CAAC,MAAM,EAAE,CAAC;gBACpB,IAAI,MAAM,CAAC,SAAS;oBAAE,MAAM,GAAI,MAAsB,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;gBAC7E,IAAI,MAAM,CAAC,SAAS;oBAAE,MAAM,GAAI,MAAsB,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;gBAC7E,MAAM;YACR,KAAK,QAAQ;gBACX,MAAM,GAAG,OAAC,CAAC,MAAM,EAAE,CAAC;gBACpB,IAAI,MAAM,CAAC,OAAO;oBAAE,MAAM,GAAI,MAAsB,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;gBACzE,IAAI,MAAM,CAAC,OAAO;oBAAE,MAAM,GAAI,MAAsB,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;gBACzE,MAAM;YACR,KAAK,SAAS;gBACZ,MAAM,GAAG,OAAC,CAAC,OAAO,EAAE,CAAC;gBACrB,MAAM;YACR,KAAK,OAAO;gBACV,MAAM,GAAG,OAAC,CAAC,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC;gBAC5D,MAAM;YACR;gBACE,MAAM,GAAG,OAAC,CAAC,GAAG,EAAE,CAAC;QACrB,CAAC;QAED,IAAI,MAAM,CAAC,QAAQ,KAAK,KAAK,EAAE,CAAC;YAC9B,MAAM,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC;QAC7B,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,eAAe,CACb,QAAkB,EAClB,GAAQ;QAER,MAAM,UAAU,GAAgB,EAAE,CAAC;QAEnC,IAAI,QAAQ,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC;YAC3B,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAC5D,MAAM,MAAM,GAAG,UAAU,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC9C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACpB,UAAU,CAAC,IAAI,CAAC;oBACd,EAAE,EAAE,OAAO,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE;oBACxC,SAAS,EAAE,IAAI,IAAI,EAAE;oBACrB,IAAI,EAAE,SAAS;oBACf,YAAY,EAAE,QAAQ,CAAC,IAAI;oBAC3B,QAAQ,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE;oBAC1D,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,IAAI;oBAC/B,MAAM,EAAE,GAAG,CAAC,IAAI;oBAChB,OAAO,EAAE,mCAAmC,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE;oBAClE,QAAQ,EAAE,OAAO;iBAClB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,8CAA8C;QAE9C,OAAO;YACL,OAAO,EAAE,UAAU,CAAC,MAAM,KAAK,CAAC;YAChC,UAAU;SACX,CAAC;IACJ,CAAC;IAED,gBAAgB,CACd,QAAkB,EAClB,UAAkB,EAClB,IAAS;QAET,MAAM,UAAU,GAAgB,EAAE,CAAC;QAEnC,IAAI,QAAQ,CAAC,QAAQ,IAAI,QAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;YACvD,MAAM,cAAc,GAAG,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;YACxE,MAAM,MAAM,GAAG,cAAc,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YAC9C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACpB,UAAU,CAAC,IAAI,CAAC;oBACd,EAAE,EAAE,OAAO,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE;oBACxC,SAAS,EAAE,IAAI,IAAI,EAAE;oBACrB,IAAI,EAAE,UAAU;oBAChB,YAAY,EAAE,QAAQ,CAAC,IAAI;oBAC3B,QAAQ,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE;oBAC1D,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC;oBACvC,MAAM,EAAE,IAAI;oBACZ,OAAO,EAAE,yCAAyC,UAAU,KAAK,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE;oBACvF,QAAQ,EAAE,SAAS,CAAC,sCAAsC;iBAC3D,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO;YACL,OAAO,EAAE,UAAU,CAAC,MAAM,KAAK,CAAC;YAChC,UAAU;SACX,CAAC;IACJ,CAAC;IAED,aAAa,CACX,QAAkB,EAClB,UAAkB,EAClB,IAAS;QAET,MAAM,UAAU,GAAgB,EAAE,CAAC;QAEnC,IAAI,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;YACnD,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;YACnE,MAAM,MAAM,GAAG,WAAW,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YAC3C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACpB,UAAU,CAAC,IAAI,CAAC;oBACd,EAAE,EAAE,OAAO,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE;oBACxC,SAAS,EAAE,IAAI,IAAI,EAAE;oBACrB,IAAI,EAAE,OAAO;oBACb,YAAY,EAAE,QAAQ,CAAC,IAAI;oBAC3B,QAAQ,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE;oBAC1D,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC;oBACrC,MAAM,EAAE,IAAI;oBACZ,OAAO,EAAE,sCAAsC,UAAU,KAAK,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE;oBACpF,QAAQ,EAAE,SAAS;iBACpB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO;YACL,OAAO,EAAE,UAAU,CAAC,MAAM,KAAK,CAAC;YAChC,UAAU;SACX,CAAC;IACJ,CAAC;CACF;AAxJD,4CAwJC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Violation } from './types/violation';
|
|
2
|
+
export declare class ViolationStore {
|
|
3
|
+
private violations;
|
|
4
|
+
private maxViolations;
|
|
5
|
+
add(violation: Violation): void;
|
|
6
|
+
getAll(): Violation[];
|
|
7
|
+
getByEndpoint(method: string, path: string): Violation[];
|
|
8
|
+
getByType(type: Violation['type']): Violation[];
|
|
9
|
+
clear(): void;
|
|
10
|
+
getStats(): {
|
|
11
|
+
total: number;
|
|
12
|
+
byType: Record<string, number>;
|
|
13
|
+
bySeverity: Record<string, number>;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=violation-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"violation-store.d.ts","sourceRoot":"","sources":["../src/violation-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAE9C,qBAAa,cAAc;IACzB,OAAO,CAAC,UAAU,CAAmB;IACrC,OAAO,CAAC,aAAa,CAAQ;IAE7B,GAAG,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI;IAO/B,MAAM,IAAI,SAAS,EAAE;IAIrB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE;IAMxD,SAAS,CAAC,IAAI,EAAE,SAAS,CAAC,MAAM,CAAC,GAAG,SAAS,EAAE;IAI/C,KAAK,IAAI,IAAI;IAIb,QAAQ;;;;;CAcT"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ViolationStore = void 0;
|
|
4
|
+
class ViolationStore {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.violations = [];
|
|
7
|
+
this.maxViolations = 1000;
|
|
8
|
+
}
|
|
9
|
+
add(violation) {
|
|
10
|
+
this.violations.unshift(violation);
|
|
11
|
+
if (this.violations.length > this.maxViolations) {
|
|
12
|
+
this.violations = this.violations.slice(0, this.maxViolations);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
getAll() {
|
|
16
|
+
return [...this.violations];
|
|
17
|
+
}
|
|
18
|
+
getByEndpoint(method, path) {
|
|
19
|
+
return this.violations.filter(v => v.endpoint.method === method && v.endpoint.path === path);
|
|
20
|
+
}
|
|
21
|
+
getByType(type) {
|
|
22
|
+
return this.violations.filter(v => v.type === type);
|
|
23
|
+
}
|
|
24
|
+
clear() {
|
|
25
|
+
this.violations = [];
|
|
26
|
+
}
|
|
27
|
+
getStats() {
|
|
28
|
+
const total = this.violations.length;
|
|
29
|
+
const byType = this.violations.reduce((acc, v) => {
|
|
30
|
+
acc[v.type] = (acc[v.type] || 0) + 1;
|
|
31
|
+
return acc;
|
|
32
|
+
}, {});
|
|
33
|
+
const bySeverity = this.violations.reduce((acc, v) => {
|
|
34
|
+
acc[v.severity] = (acc[v.severity] || 0) + 1;
|
|
35
|
+
return acc;
|
|
36
|
+
}, {});
|
|
37
|
+
return { total, byType, bySeverity };
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
exports.ViolationStore = ViolationStore;
|
|
41
|
+
//# sourceMappingURL=violation-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"violation-store.js","sourceRoot":"","sources":["../src/violation-store.ts"],"names":[],"mappings":";;;AAEA,MAAa,cAAc;IAA3B;QACU,eAAU,GAAgB,EAAE,CAAC;QAC7B,kBAAa,GAAG,IAAI,CAAC;IAyC/B,CAAC;IAvCC,GAAG,CAAC,SAAoB;QACtB,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACnC,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;YAChD,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;QACjE,CAAC;IACH,CAAC;IAED,MAAM;QACJ,OAAO,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC;IAC9B,CAAC;IAED,aAAa,CAAC,MAAc,EAAE,IAAY;QACxC,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAChC,CAAC,CAAC,QAAQ,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,CAAC,QAAQ,CAAC,IAAI,KAAK,IAAI,CACzD,CAAC;IACJ,CAAC;IAED,SAAS,CAAC,IAAuB;QAC/B,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IACtD,CAAC;IAED,KAAK;QACH,IAAI,CAAC,UAAU,GAAG,EAAE,CAAC;IACvB,CAAC;IAED,QAAQ;QACN,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;QACrC,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE;YAC/C,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;YACrC,OAAO,GAAG,CAAC;QACb,CAAC,EAAE,EAA4B,CAAC,CAAC;QAEjC,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE;YACnD,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;YAC7C,OAAO,GAAG,CAAC;QACb,CAAC,EAAE,EAA4B,CAAC,CAAC;QAEjC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IACvC,CAAC;CACF;AA3CD,wCA2CC"}
|