@kici-dev/engine 0.0.0 → 0.1.1
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/LICENSE +661 -0
- package/README.md +1 -6
- package/dist/audit/access-log-policy.d.ts +97 -0
- package/dist/audit/access-log-policy.js +195 -0
- package/dist/audit/activity.d.ts +156 -0
- package/dist/audit/activity.js +175 -0
- package/dist/audit/retention-policy.d.ts +157 -0
- package/dist/audit/retention-policy.js +364 -0
- package/dist/bundler/index.d.ts +3 -0
- package/dist/bundler/index.js +3 -0
- package/dist/bundler/rolldown-config.d.ts +41 -0
- package/dist/bundler/rolldown-config.js +40 -0
- package/dist/chunk-gOLHoazu.js +4 -0
- package/dist/env/environment-allowlist.d.ts +45 -0
- package/dist/env/environment-allowlist.js +69 -0
- package/dist/environment/index.d.ts +3 -0
- package/dist/environment/index.js +3 -0
- package/dist/environment/scope-resolver.d.ts +35 -0
- package/dist/environment/scope-resolver.js +77 -0
- package/dist/environment/types.d.ts +98 -0
- package/dist/environment/types.js +2 -0
- package/dist/index.d.ts +54 -0
- package/dist/index.js +45 -0
- package/dist/labels.d.ts +106 -0
- package/dist/labels.js +162 -0
- package/dist/metrics/metric-catalog.generated.d.ts +400 -0
- package/dist/metrics/metric-catalog.generated.js +510 -0
- package/dist/protocol/dashboard-global-workflows.d.ts +9 -0
- package/dist/protocol/dashboard-global-workflows.js +3 -0
- package/dist/protocol/event-log-payload.d.ts +16 -0
- package/dist/protocol/event-log-payload.js +21 -0
- package/dist/protocol/messages/access-log.d.ts +545 -0
- package/dist/protocol/messages/access-log.js +175 -0
- package/dist/protocol/messages/actor.d.ts +100 -0
- package/dist/protocol/messages/actor.js +139 -0
- package/dist/protocol/messages/auth.d.ts +25 -0
- package/dist/protocol/messages/auth.js +36 -0
- package/dist/protocol/messages/browser.d.ts +369 -0
- package/dist/protocol/messages/browser.js +225 -0
- package/dist/protocol/messages/capabilities.d.ts +46 -0
- package/dist/protocol/messages/capabilities.js +42 -0
- package/dist/protocol/messages/common.d.ts +53 -0
- package/dist/protocol/messages/common.js +55 -0
- package/dist/protocol/messages/dashboard-global-workflows.d.ts +166 -0
- package/dist/protocol/messages/dashboard-global-workflows.js +80 -0
- package/dist/protocol/messages/dashboard.d.ts +4068 -0
- package/dist/protocol/messages/dashboard.js +1237 -0
- package/dist/protocol/messages/event-log.d.ts +49 -0
- package/dist/protocol/messages/event-log.js +45 -0
- package/dist/protocol/messages/execution-status.d.ts +164 -0
- package/dist/protocol/messages/execution-status.js +166 -0
- package/dist/protocol/messages/join.d.ts +18 -0
- package/dist/protocol/messages/join.js +28 -0
- package/dist/protocol/messages/log-pull.d.ts +27 -0
- package/dist/protocol/messages/log-pull.js +36 -0
- package/dist/protocol/messages/observe.d.ts +75 -0
- package/dist/protocol/messages/observe.js +70 -0
- package/dist/protocol/messages/orchestrator-agent.d.ts +606 -0
- package/dist/protocol/messages/orchestrator-agent.js +481 -0
- package/dist/protocol/messages/peer.d.ts +795 -0
- package/dist/protocol/messages/peer.js +372 -0
- package/dist/protocol/messages/platform-orchestrator.d.ts +1888 -0
- package/dist/protocol/messages/platform-orchestrator.js +379 -0
- package/dist/protocol/messages/run-events.d.ts +155 -0
- package/dist/protocol/messages/run-events.js +115 -0
- package/dist/protocol/messages/saas-orchestrator.d.ts +2179 -0
- package/dist/protocol/messages/source-registration.d.ts +103 -0
- package/dist/protocol/messages/source-registration.js +109 -0
- package/dist/protocol/messages/test-run.d.ts +59 -0
- package/dist/protocol/messages/test-run.js +52 -0
- package/dist/protocol/version.d.ts +12 -0
- package/dist/protocol/version.js +17 -0
- package/dist/provider/changed-files-fetcher.d.ts +24 -0
- package/dist/provider/changed-files-fetcher.js +2 -0
- package/dist/provider/check-run-conclusion.d.ts +15 -0
- package/dist/provider/check-run-conclusion.js +19 -0
- package/dist/provider/check-status-poster.d.ts +15 -0
- package/dist/provider/check-status-poster.js +2 -0
- package/dist/provider/clone-token-provider.d.ts +58 -0
- package/dist/provider/clone-token-provider.js +2 -0
- package/dist/provider/contributor-resolver.d.ts +22 -0
- package/dist/provider/contributor-resolver.js +2 -0
- package/dist/provider/index.d.ts +23 -0
- package/dist/provider/index.js +3 -0
- package/dist/provider/lock-file-fetcher.d.ts +24 -0
- package/dist/provider/lock-file-fetcher.js +2 -0
- package/dist/provider/repo-url-builder.d.ts +35 -0
- package/dist/provider/repo-url-builder.js +2 -0
- package/dist/provider/types.d.ts +9 -0
- package/dist/provider/types.js +2 -0
- package/dist/provider/webhook-normalizer.d.ts +163 -0
- package/dist/provider/webhook-normalizer.js +2 -0
- package/dist/registration/registerable-trigger-type.d.ts +52 -0
- package/dist/registration/registerable-trigger-type.js +56 -0
- package/dist/scaler/resource-types.d.ts +90 -0
- package/dist/scaler/resource-types.js +99 -0
- package/dist/scaler/scaler-backend-type.d.ts +16 -0
- package/dist/scaler/scaler-backend-type.js +20 -0
- package/dist/secrets/index.d.ts +7 -0
- package/dist/secrets/index.js +2 -0
- package/dist/secrets/types.d.ts +138 -0
- package/dist/secrets/types.js +2 -0
- package/dist/state-machine/index.d.ts +2 -0
- package/dist/state-machine/index.js +3 -0
- package/dist/state-machine/machine.d.ts +34 -0
- package/dist/state-machine/machine.js +103 -0
- package/dist/state-machine/types.d.ts +34 -0
- package/dist/state-machine/types.js +15 -0
- package/dist/trigger/decision-trace.d.ts +41 -0
- package/dist/trigger/decision-trace.js +30 -0
- package/dist/trigger/jsonpath-matcher.d.ts +27 -0
- package/dist/trigger/jsonpath-matcher.js +92 -0
- package/dist/trigger/matcher.d.ts +29 -0
- package/dist/trigger/matcher.js +704 -0
- package/dist/trigger/trigger-event-type.d.ts +14 -0
- package/dist/trigger/trigger-event-type.js +66 -0
- package/dist/trigger/types.d.ts +524 -0
- package/dist/trigger/types.js +48 -0
- package/dist/webhook/signature.d.ts +15 -0
- package/dist/webhook/signature.js +32 -0
- package/dist/ws/close-codes.d.ts +28 -0
- package/dist/ws/close-codes.js +33 -0
- package/dist/ws/rate-limiter.d.ts +86 -0
- package/dist/ws/rate-limiter.js +136 -0
- package/dist/ws/ws-like.d.ts +12 -0
- package/dist/ws/ws-like.js +2 -0
- package/package.json +54 -6
- package/sbom.spdx.json +202 -0
- package/index.js +0 -3
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import "../chunk-gOLHoazu.js";
|
|
2
|
+
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
3
|
+
//#region src/webhook/signature.ts
|
|
4
|
+
/**
|
|
5
|
+
* Verify GitHub webhook signature using HMAC-SHA256.
|
|
6
|
+
*
|
|
7
|
+
* @param body - Raw webhook payload body (as string)
|
|
8
|
+
* @param signature - Signature from X-Hub-Signature-256 header
|
|
9
|
+
* @param secret - Webhook secret configured on GitHub
|
|
10
|
+
* @returns true if signature is valid, false otherwise
|
|
11
|
+
*
|
|
12
|
+
* Security:
|
|
13
|
+
* - Uses crypto.timingSafeEqual to prevent timing attacks
|
|
14
|
+
* - Validates signature format before comparison
|
|
15
|
+
* - Handles length mismatches before timingSafeEqual (which throws on length mismatch)
|
|
16
|
+
*/
|
|
17
|
+
function verifySignature(body, signature, secret) {
|
|
18
|
+
if (!signature.startsWith("sha256=")) return false;
|
|
19
|
+
const providedHex = signature.slice(7);
|
|
20
|
+
const hmac = createHmac("sha256", secret);
|
|
21
|
+
hmac.update(body, "utf8");
|
|
22
|
+
const expectedHex = hmac.digest("hex");
|
|
23
|
+
if (providedHex.length !== expectedHex.length) return false;
|
|
24
|
+
const providedBuffer = Buffer.from(providedHex, "hex");
|
|
25
|
+
const expectedBuffer = Buffer.from(expectedHex, "hex");
|
|
26
|
+
if (providedBuffer.length !== expectedBuffer.length) return false;
|
|
27
|
+
return timingSafeEqual(providedBuffer, expectedBuffer);
|
|
28
|
+
}
|
|
29
|
+
//#endregion
|
|
30
|
+
export { verifySignature };
|
|
31
|
+
|
|
32
|
+
//# sourceMappingURL=signature.js.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified WebSocket close codes for KiCI connections.
|
|
3
|
+
* Single source of truth — replaces duplicate definitions in other KiCI packages.
|
|
4
|
+
*
|
|
5
|
+
* Standard codes (1000-1015) are defined by RFC 6455.
|
|
6
|
+
* Custom codes (4000-4999) are reserved for application use.
|
|
7
|
+
*/
|
|
8
|
+
/** The endpoint is going away (e.g., server shutdown or browser navigating away). */
|
|
9
|
+
export declare const WS_CLOSE_GOING_AWAY = 1001;
|
|
10
|
+
/** The connection was closed because the client failed authentication. */
|
|
11
|
+
export declare const WS_CLOSE_UNAUTHORIZED = 4001;
|
|
12
|
+
/** The connection was closed because the auth timeout expired. */
|
|
13
|
+
export declare const WS_CLOSE_AUTH_TIMEOUT = 4002;
|
|
14
|
+
/** The connection was closed due to an invalid or unparseable message. */
|
|
15
|
+
export declare const WS_CLOSE_INVALID_MESSAGE = 4003;
|
|
16
|
+
/** The connection was closed because the heartbeat timed out. */
|
|
17
|
+
export declare const WS_CLOSE_HEARTBEAT_TIMEOUT = 4004;
|
|
18
|
+
/** The connection was closed due to a protocol-level error. */
|
|
19
|
+
export declare const WS_CLOSE_PROTOCOL_ERROR = 4005;
|
|
20
|
+
/** The connection was closed due to an unexpected internal server error. */
|
|
21
|
+
export declare const WS_CLOSE_INTERNAL_ERROR = 4006;
|
|
22
|
+
/** The connection was closed because the agent token authentication failed. */
|
|
23
|
+
export declare const WS_CLOSE_AGENT_AUTH_FAILED = 4010;
|
|
24
|
+
/** The connection was closed because the organization has reached its plan limit. */
|
|
25
|
+
export declare const WS_CLOSE_PLAN_LIMIT = 4020;
|
|
26
|
+
/** The connection was closed because the requested run was not found. */
|
|
27
|
+
export declare const WS_CLOSE_RUN_NOT_FOUND = 4030;
|
|
28
|
+
//# sourceMappingURL=close-codes.d.ts.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import "../chunk-gOLHoazu.js";
|
|
2
|
+
//#region src/ws/close-codes.ts
|
|
3
|
+
/**
|
|
4
|
+
* Unified WebSocket close codes for KiCI connections.
|
|
5
|
+
* Single source of truth — replaces duplicate definitions in other KiCI packages.
|
|
6
|
+
*
|
|
7
|
+
* Standard codes (1000-1015) are defined by RFC 6455.
|
|
8
|
+
* Custom codes (4000-4999) are reserved for application use.
|
|
9
|
+
*/
|
|
10
|
+
/** The endpoint is going away (e.g., server shutdown or browser navigating away). */
|
|
11
|
+
const WS_CLOSE_GOING_AWAY = 1001;
|
|
12
|
+
/** The connection was closed because the client failed authentication. */
|
|
13
|
+
const WS_CLOSE_UNAUTHORIZED = 4001;
|
|
14
|
+
/** The connection was closed because the auth timeout expired. */
|
|
15
|
+
const WS_CLOSE_AUTH_TIMEOUT = 4002;
|
|
16
|
+
/** The connection was closed due to an invalid or unparseable message. */
|
|
17
|
+
const WS_CLOSE_INVALID_MESSAGE = 4003;
|
|
18
|
+
/** The connection was closed because the heartbeat timed out. */
|
|
19
|
+
const WS_CLOSE_HEARTBEAT_TIMEOUT = 4004;
|
|
20
|
+
/** The connection was closed due to a protocol-level error. */
|
|
21
|
+
const WS_CLOSE_PROTOCOL_ERROR = 4005;
|
|
22
|
+
/** The connection was closed due to an unexpected internal server error. */
|
|
23
|
+
const WS_CLOSE_INTERNAL_ERROR = 4006;
|
|
24
|
+
/** The connection was closed because the agent token authentication failed. */
|
|
25
|
+
const WS_CLOSE_AGENT_AUTH_FAILED = 4010;
|
|
26
|
+
/** The connection was closed because the organization has reached its plan limit. */
|
|
27
|
+
const WS_CLOSE_PLAN_LIMIT = 4020;
|
|
28
|
+
/** The connection was closed because the requested run was not found. */
|
|
29
|
+
const WS_CLOSE_RUN_NOT_FOUND = 4030;
|
|
30
|
+
//#endregion
|
|
31
|
+
export { WS_CLOSE_AGENT_AUTH_FAILED, WS_CLOSE_AUTH_TIMEOUT, WS_CLOSE_GOING_AWAY, WS_CLOSE_HEARTBEAT_TIMEOUT, WS_CLOSE_INTERNAL_ERROR, WS_CLOSE_INVALID_MESSAGE, WS_CLOSE_PLAN_LIMIT, WS_CLOSE_PROTOCOL_ERROR, WS_CLOSE_RUN_NOT_FOUND, WS_CLOSE_UNAUTHORIZED };
|
|
32
|
+
|
|
33
|
+
//# sourceMappingURL=close-codes.js.map
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebSocket rate limiter with two-tier token bucket (messages + bytes).
|
|
3
|
+
*
|
|
4
|
+
* Provides defense-in-depth against DoS by limiting:
|
|
5
|
+
* - Message count per second (burst capacity + refill)
|
|
6
|
+
* - Byte throughput per second (burst capacity + refill)
|
|
7
|
+
* - Hard message size limit (4MB default)
|
|
8
|
+
*
|
|
9
|
+
* Heartbeats are exempt from rate limiting.
|
|
10
|
+
* Sustained violations (>5s default) result in disconnect.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Token bucket algorithm for rate limiting.
|
|
14
|
+
*
|
|
15
|
+
* Supports burst up to `capacity` tokens, refilling at `refillRate` tokens/second.
|
|
16
|
+
* When tokens are exhausted, returns a retryAfterMs estimate.
|
|
17
|
+
*/
|
|
18
|
+
export declare class TokenBucket {
|
|
19
|
+
private readonly capacity;
|
|
20
|
+
private readonly refillRate;
|
|
21
|
+
private tokens;
|
|
22
|
+
private lastRefill;
|
|
23
|
+
constructor(capacity: number, refillRate: number);
|
|
24
|
+
/**
|
|
25
|
+
* Try to consume `amount` tokens.
|
|
26
|
+
* @returns `{ allowed: true }` if tokens available, or `{ allowed: false, retryAfterMs }`.
|
|
27
|
+
*/
|
|
28
|
+
consume(amount?: number): {
|
|
29
|
+
allowed: boolean;
|
|
30
|
+
retryAfterMs?: number;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Check if `amount` tokens are available without consuming them.
|
|
34
|
+
* @returns `{ allowed: true }` if tokens available, or `{ allowed: false, retryAfterMs }`.
|
|
35
|
+
*/
|
|
36
|
+
peek(amount?: number): {
|
|
37
|
+
allowed: boolean;
|
|
38
|
+
retryAfterMs?: number;
|
|
39
|
+
};
|
|
40
|
+
private refill;
|
|
41
|
+
}
|
|
42
|
+
export interface RateLimiterConfig {
|
|
43
|
+
/** Maximum burst of messages. Default: 200 */
|
|
44
|
+
messageCapacity?: number;
|
|
45
|
+
/** Message refill rate per second. Default: 100 */
|
|
46
|
+
messageRefillRate?: number;
|
|
47
|
+
/** Maximum burst of bytes. Default: 2MB */
|
|
48
|
+
bytesCapacity?: number;
|
|
49
|
+
/** Bytes refill rate per second. Default: 500KB */
|
|
50
|
+
bytesRefillRate?: number;
|
|
51
|
+
/** Hard message size limit in bytes. Default: 4MB */
|
|
52
|
+
maxMessageSize?: number;
|
|
53
|
+
/** Time in ms of sustained violation before disconnect. Default: 5000 */
|
|
54
|
+
disconnectAfterMs?: number;
|
|
55
|
+
}
|
|
56
|
+
export interface RateLimitResult {
|
|
57
|
+
allowed: boolean;
|
|
58
|
+
action: 'allow' | 'warn' | 'disconnect';
|
|
59
|
+
retryAfterMs?: number;
|
|
60
|
+
reason?: string;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Two-tier WebSocket rate limiter.
|
|
64
|
+
*
|
|
65
|
+
* Checks both message count and byte throughput independently.
|
|
66
|
+
* Heartbeats are exempt. Oversized messages trigger immediate disconnect.
|
|
67
|
+
* Sustained violations (exceeding `disconnectAfterMs`) trigger disconnect.
|
|
68
|
+
*/
|
|
69
|
+
export declare class WsRateLimiter {
|
|
70
|
+
private readonly messages;
|
|
71
|
+
private readonly bytes;
|
|
72
|
+
private violationStart;
|
|
73
|
+
private readonly maxMessageSize;
|
|
74
|
+
private readonly bytesCapacity;
|
|
75
|
+
private readonly disconnectAfterMs;
|
|
76
|
+
constructor(config?: RateLimiterConfig);
|
|
77
|
+
/**
|
|
78
|
+
* Check if a message is allowed through.
|
|
79
|
+
*
|
|
80
|
+
* @param messageSize - Size of the message in bytes
|
|
81
|
+
* @param isHeartbeat - Whether this is a heartbeat message (exempt from limiting)
|
|
82
|
+
* @returns Rate limit decision with action and optional retryAfterMs
|
|
83
|
+
*/
|
|
84
|
+
check(messageSize: number, isHeartbeat: boolean): RateLimitResult;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=rate-limiter.d.ts.map
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import "../chunk-gOLHoazu.js";
|
|
2
|
+
//#region src/ws/rate-limiter.ts
|
|
3
|
+
/**
|
|
4
|
+
* WebSocket rate limiter with two-tier token bucket (messages + bytes).
|
|
5
|
+
*
|
|
6
|
+
* Provides defense-in-depth against DoS by limiting:
|
|
7
|
+
* - Message count per second (burst capacity + refill)
|
|
8
|
+
* - Byte throughput per second (burst capacity + refill)
|
|
9
|
+
* - Hard message size limit (4MB default)
|
|
10
|
+
*
|
|
11
|
+
* Heartbeats are exempt from rate limiting.
|
|
12
|
+
* Sustained violations (>5s default) result in disconnect.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Token bucket algorithm for rate limiting.
|
|
16
|
+
*
|
|
17
|
+
* Supports burst up to `capacity` tokens, refilling at `refillRate` tokens/second.
|
|
18
|
+
* When tokens are exhausted, returns a retryAfterMs estimate.
|
|
19
|
+
*/
|
|
20
|
+
var TokenBucket = class {
|
|
21
|
+
tokens;
|
|
22
|
+
lastRefill;
|
|
23
|
+
constructor(capacity, refillRate) {
|
|
24
|
+
this.capacity = capacity;
|
|
25
|
+
this.refillRate = refillRate;
|
|
26
|
+
this.tokens = capacity;
|
|
27
|
+
this.lastRefill = Date.now();
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Try to consume `amount` tokens.
|
|
31
|
+
* @returns `{ allowed: true }` if tokens available, or `{ allowed: false, retryAfterMs }`.
|
|
32
|
+
*/
|
|
33
|
+
consume(amount = 1) {
|
|
34
|
+
this.refill();
|
|
35
|
+
if (this.tokens >= amount) {
|
|
36
|
+
this.tokens -= amount;
|
|
37
|
+
return { allowed: true };
|
|
38
|
+
}
|
|
39
|
+
const deficit = amount - this.tokens;
|
|
40
|
+
return {
|
|
41
|
+
allowed: false,
|
|
42
|
+
retryAfterMs: Math.ceil(deficit / this.refillRate * 1e3)
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Check if `amount` tokens are available without consuming them.
|
|
47
|
+
* @returns `{ allowed: true }` if tokens available, or `{ allowed: false, retryAfterMs }`.
|
|
48
|
+
*/
|
|
49
|
+
peek(amount = 1) {
|
|
50
|
+
this.refill();
|
|
51
|
+
if (this.tokens >= amount) return { allowed: true };
|
|
52
|
+
const deficit = amount - this.tokens;
|
|
53
|
+
return {
|
|
54
|
+
allowed: false,
|
|
55
|
+
retryAfterMs: Math.ceil(deficit / this.refillRate * 1e3)
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
refill() {
|
|
59
|
+
const now = Date.now();
|
|
60
|
+
const elapsed = (now - this.lastRefill) / 1e3;
|
|
61
|
+
this.tokens = Math.min(this.capacity, this.tokens + elapsed * this.refillRate);
|
|
62
|
+
this.lastRefill = now;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Two-tier WebSocket rate limiter.
|
|
67
|
+
*
|
|
68
|
+
* Checks both message count and byte throughput independently.
|
|
69
|
+
* Heartbeats are exempt. Oversized messages trigger immediate disconnect.
|
|
70
|
+
* Sustained violations (exceeding `disconnectAfterMs`) trigger disconnect.
|
|
71
|
+
*/
|
|
72
|
+
var WsRateLimiter = class {
|
|
73
|
+
messages;
|
|
74
|
+
bytes;
|
|
75
|
+
violationStart = null;
|
|
76
|
+
maxMessageSize;
|
|
77
|
+
bytesCapacity;
|
|
78
|
+
disconnectAfterMs;
|
|
79
|
+
constructor(config) {
|
|
80
|
+
const bytesCapacity = config?.bytesCapacity ?? 2 * 1024 * 1024;
|
|
81
|
+
this.messages = new TokenBucket(config?.messageCapacity ?? 200, config?.messageRefillRate ?? 100);
|
|
82
|
+
this.bytes = new TokenBucket(bytesCapacity, config?.bytesRefillRate ?? 500 * 1024);
|
|
83
|
+
this.maxMessageSize = config?.maxMessageSize ?? 4 * 1024 * 1024;
|
|
84
|
+
this.bytesCapacity = bytesCapacity;
|
|
85
|
+
this.disconnectAfterMs = config?.disconnectAfterMs ?? 5e3;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Check if a message is allowed through.
|
|
89
|
+
*
|
|
90
|
+
* @param messageSize - Size of the message in bytes
|
|
91
|
+
* @param isHeartbeat - Whether this is a heartbeat message (exempt from limiting)
|
|
92
|
+
* @returns Rate limit decision with action and optional retryAfterMs
|
|
93
|
+
*/
|
|
94
|
+
check(messageSize, isHeartbeat) {
|
|
95
|
+
if (isHeartbeat) return {
|
|
96
|
+
allowed: true,
|
|
97
|
+
action: "allow"
|
|
98
|
+
};
|
|
99
|
+
if (messageSize > this.maxMessageSize) return {
|
|
100
|
+
allowed: false,
|
|
101
|
+
action: "disconnect",
|
|
102
|
+
reason: "Message too large"
|
|
103
|
+
};
|
|
104
|
+
if (messageSize > this.bytesCapacity) return {
|
|
105
|
+
allowed: false,
|
|
106
|
+
action: "disconnect",
|
|
107
|
+
reason: "Message exceeds byte burst capacity"
|
|
108
|
+
};
|
|
109
|
+
const msgResult = this.messages.peek(1);
|
|
110
|
+
const byteResult = this.bytes.peek(messageSize);
|
|
111
|
+
if (msgResult.allowed && byteResult.allowed) {
|
|
112
|
+
this.messages.consume(1);
|
|
113
|
+
this.bytes.consume(messageSize);
|
|
114
|
+
this.violationStart = null;
|
|
115
|
+
return {
|
|
116
|
+
allowed: true,
|
|
117
|
+
action: "allow"
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
if (this.violationStart === null) this.violationStart = Date.now();
|
|
121
|
+
if (Date.now() - this.violationStart > this.disconnectAfterMs) return {
|
|
122
|
+
allowed: false,
|
|
123
|
+
action: "disconnect",
|
|
124
|
+
reason: "Sustained rate limit violation"
|
|
125
|
+
};
|
|
126
|
+
return {
|
|
127
|
+
allowed: false,
|
|
128
|
+
action: "warn",
|
|
129
|
+
retryAfterMs: Math.max(msgResult.retryAfterMs ?? 0, byteResult.retryAfterMs ?? 0)
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
//#endregion
|
|
134
|
+
export { TokenBucket, WsRateLimiter };
|
|
135
|
+
|
|
136
|
+
//# sourceMappingURL=rate-limiter.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal WebSocket interface for testability.
|
|
3
|
+
*
|
|
4
|
+
* Avoids importing framework-specific types so registries and handlers
|
|
5
|
+
* can be unit-tested with plain mock objects.
|
|
6
|
+
*/
|
|
7
|
+
export interface WsLike {
|
|
8
|
+
send(data: string | ArrayBuffer): void;
|
|
9
|
+
close(code?: number, reason?: string): void;
|
|
10
|
+
readyState: number;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=ws-like.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kici-dev/engine",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Shared business logic for the KiCI CI/CD stack: protocol, triggers, state machine, and provider interfaces used by the Platform relay, orchestrator, and compiler.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"kici",
|
|
@@ -9,17 +9,65 @@
|
|
|
9
9
|
"ci-cd",
|
|
10
10
|
"typescript",
|
|
11
11
|
"workflows",
|
|
12
|
-
"devops"
|
|
12
|
+
"devops",
|
|
13
|
+
"engine",
|
|
14
|
+
"protocol"
|
|
13
15
|
],
|
|
14
16
|
"homepage": "https://kici.dev",
|
|
15
17
|
"author": {
|
|
16
18
|
"name": "KiCI",
|
|
17
19
|
"email": "hello@kici.dev"
|
|
18
20
|
},
|
|
19
|
-
"license": "
|
|
20
|
-
"
|
|
21
|
+
"license": "AGPL-3.0-only",
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=24"
|
|
24
|
+
},
|
|
25
|
+
"type": "module",
|
|
26
|
+
"files": [
|
|
27
|
+
"dist",
|
|
28
|
+
"!dist/**/*.map",
|
|
29
|
+
"sbom.spdx.json"
|
|
30
|
+
],
|
|
21
31
|
"publishConfig": {
|
|
22
|
-
"
|
|
23
|
-
"
|
|
32
|
+
"registry": "https://registry.npmjs.org/",
|
|
33
|
+
"access": "public"
|
|
34
|
+
},
|
|
35
|
+
"main": "dist/index.js",
|
|
36
|
+
"types": "dist/index.d.ts",
|
|
37
|
+
"exports": {
|
|
38
|
+
".": {
|
|
39
|
+
"import": "./dist/index.js",
|
|
40
|
+
"types": "./dist/index.d.ts"
|
|
41
|
+
},
|
|
42
|
+
"./trigger/trigger-event-type": {
|
|
43
|
+
"import": "./dist/trigger/trigger-event-type.js",
|
|
44
|
+
"types": "./dist/trigger/trigger-event-type.d.ts"
|
|
45
|
+
},
|
|
46
|
+
"./webhook/signature": {
|
|
47
|
+
"import": "./dist/webhook/signature.js",
|
|
48
|
+
"types": "./dist/webhook/signature.d.ts"
|
|
49
|
+
},
|
|
50
|
+
"./protocol/dashboard-global-workflows": {
|
|
51
|
+
"import": "./dist/protocol/dashboard-global-workflows.js",
|
|
52
|
+
"types": "./dist/protocol/dashboard-global-workflows.d.ts"
|
|
53
|
+
},
|
|
54
|
+
"./metrics/catalog": {
|
|
55
|
+
"import": "./dist/metrics/metric-catalog.generated.js",
|
|
56
|
+
"types": "./dist/metrics/metric-catalog.generated.d.ts"
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"scripts": {
|
|
60
|
+
"build": "node ../../scripts/build-ts.mjs && tsc --emitDeclarationOnly",
|
|
61
|
+
"typecheck": "tsc --noEmit",
|
|
62
|
+
"test": "vitest run",
|
|
63
|
+
"prepublishOnly": "npx tsx ../../hack/generate-sbom.ts --package packages/engine --name @kici-dev/engine"
|
|
64
|
+
},
|
|
65
|
+
"dependencies": {
|
|
66
|
+
"jsonpath-plus": "^10.4.0",
|
|
67
|
+
"picomatch": "^4.0.4",
|
|
68
|
+
"zod": "^4.3.6"
|
|
69
|
+
},
|
|
70
|
+
"devDependencies": {
|
|
71
|
+
"@types/node": "^24.12.0"
|
|
24
72
|
}
|
|
25
73
|
}
|
package/sbom.spdx.json
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
{
|
|
2
|
+
"spdxVersion": "SPDX-2.3",
|
|
3
|
+
"dataLicense": "CC0-1.0",
|
|
4
|
+
"SPDXID": "SPDXRef-DOCUMENT",
|
|
5
|
+
"name": "@kici-dev/engine@0.1.1",
|
|
6
|
+
"documentNamespace": "https://kici.dev/sbom/%40kici-dev%2Fengine/0.1.1/65f9a13a-ac0d-4133-8799-a7d4789d528d",
|
|
7
|
+
"creationInfo": {
|
|
8
|
+
"created": "2026-05-15T10:53:18Z",
|
|
9
|
+
"creators": [
|
|
10
|
+
"Tool: kici-sbom-generator"
|
|
11
|
+
]
|
|
12
|
+
},
|
|
13
|
+
"packages": [
|
|
14
|
+
{
|
|
15
|
+
"SPDXID": "SPDXRef-Package--jsep-plugin-assignment-1.3.0",
|
|
16
|
+
"name": "@jsep-plugin/assignment",
|
|
17
|
+
"versionInfo": "1.3.0",
|
|
18
|
+
"downloadLocation": "https://registry.npmjs.org/@jsep-plugin/assignment/-/assignment-1.3.0.tgz",
|
|
19
|
+
"filesAnalyzed": false,
|
|
20
|
+
"licenseConcluded": "NOASSERTION",
|
|
21
|
+
"licenseDeclared": "MIT",
|
|
22
|
+
"copyrightText": "NOASSERTION",
|
|
23
|
+
"supplier": "NOASSERTION",
|
|
24
|
+
"externalRefs": [
|
|
25
|
+
{
|
|
26
|
+
"referenceCategory": "PACKAGE-MANAGER",
|
|
27
|
+
"referenceType": "purl",
|
|
28
|
+
"referenceLocator": "pkg:npm/%40jsep-plugin/assignment@1.3.0"
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
"description": "Adds assignment expression support",
|
|
32
|
+
"homepage": "https://ericsmekens.github.io/jsep/tree/master/packages/assignment#readme"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"SPDXID": "SPDXRef-Package--jsep-plugin-regex-1.0.4",
|
|
36
|
+
"name": "@jsep-plugin/regex",
|
|
37
|
+
"versionInfo": "1.0.4",
|
|
38
|
+
"downloadLocation": "https://registry.npmjs.org/@jsep-plugin/regex/-/regex-1.0.4.tgz",
|
|
39
|
+
"filesAnalyzed": false,
|
|
40
|
+
"licenseConcluded": "NOASSERTION",
|
|
41
|
+
"licenseDeclared": "MIT",
|
|
42
|
+
"copyrightText": "NOASSERTION",
|
|
43
|
+
"supplier": "NOASSERTION",
|
|
44
|
+
"externalRefs": [
|
|
45
|
+
{
|
|
46
|
+
"referenceCategory": "PACKAGE-MANAGER",
|
|
47
|
+
"referenceType": "purl",
|
|
48
|
+
"referenceLocator": "pkg:npm/%40jsep-plugin/regex@1.0.4"
|
|
49
|
+
}
|
|
50
|
+
],
|
|
51
|
+
"description": "Adds regex expression support",
|
|
52
|
+
"homepage": "https://ericsmekens.github.io/jsep/tree/master/packages/regex#readme"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"SPDXID": "SPDXRef-RootPackage",
|
|
56
|
+
"name": "@kici-dev/engine",
|
|
57
|
+
"versionInfo": "0.1.1",
|
|
58
|
+
"downloadLocation": "NOASSERTION",
|
|
59
|
+
"filesAnalyzed": false,
|
|
60
|
+
"licenseConcluded": "NOASSERTION",
|
|
61
|
+
"licenseDeclared": "AGPL-3.0-only",
|
|
62
|
+
"copyrightText": "NOASSERTION",
|
|
63
|
+
"supplier": "NOASSERTION",
|
|
64
|
+
"externalRefs": [
|
|
65
|
+
{
|
|
66
|
+
"referenceCategory": "PACKAGE-MANAGER",
|
|
67
|
+
"referenceType": "purl",
|
|
68
|
+
"referenceLocator": "pkg:npm/%40kici-dev/engine@0.1.1"
|
|
69
|
+
}
|
|
70
|
+
],
|
|
71
|
+
"description": "Shared business logic for the KiCI CI/CD stack: protocol, triggers, state machine, and provider interfaces used by the Platform relay, orchestrator, and compiler.",
|
|
72
|
+
"homepage": "https://kici.dev"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"SPDXID": "SPDXRef-Package-jsep-1.4.0",
|
|
76
|
+
"name": "jsep",
|
|
77
|
+
"versionInfo": "1.4.0",
|
|
78
|
+
"downloadLocation": "https://registry.npmjs.org/jsep/-/jsep-1.4.0.tgz",
|
|
79
|
+
"filesAnalyzed": false,
|
|
80
|
+
"licenseConcluded": "NOASSERTION",
|
|
81
|
+
"licenseDeclared": "MIT",
|
|
82
|
+
"copyrightText": "NOASSERTION",
|
|
83
|
+
"supplier": "NOASSERTION",
|
|
84
|
+
"externalRefs": [
|
|
85
|
+
{
|
|
86
|
+
"referenceCategory": "PACKAGE-MANAGER",
|
|
87
|
+
"referenceType": "purl",
|
|
88
|
+
"referenceLocator": "pkg:npm/jsep@1.4.0"
|
|
89
|
+
}
|
|
90
|
+
],
|
|
91
|
+
"description": "a tiny JavaScript expression parser",
|
|
92
|
+
"homepage": "https://ericsmekens.github.io/jsep/"
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"SPDXID": "SPDXRef-Package-jsonpath-plus-10.4.0",
|
|
96
|
+
"name": "jsonpath-plus",
|
|
97
|
+
"versionInfo": "10.4.0",
|
|
98
|
+
"downloadLocation": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-10.4.0.tgz",
|
|
99
|
+
"filesAnalyzed": false,
|
|
100
|
+
"licenseConcluded": "NOASSERTION",
|
|
101
|
+
"licenseDeclared": "MIT",
|
|
102
|
+
"copyrightText": "NOASSERTION",
|
|
103
|
+
"supplier": "NOASSERTION",
|
|
104
|
+
"externalRefs": [
|
|
105
|
+
{
|
|
106
|
+
"referenceCategory": "PACKAGE-MANAGER",
|
|
107
|
+
"referenceType": "purl",
|
|
108
|
+
"referenceLocator": "pkg:npm/jsonpath-plus@10.4.0"
|
|
109
|
+
}
|
|
110
|
+
],
|
|
111
|
+
"description": "A JS implementation of JSONPath with some additional operators",
|
|
112
|
+
"homepage": "https://github.com/s3u/JSONPath"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"SPDXID": "SPDXRef-Package-picomatch-4.0.4",
|
|
116
|
+
"name": "picomatch",
|
|
117
|
+
"versionInfo": "4.0.4",
|
|
118
|
+
"downloadLocation": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
|
119
|
+
"filesAnalyzed": false,
|
|
120
|
+
"licenseConcluded": "NOASSERTION",
|
|
121
|
+
"licenseDeclared": "MIT",
|
|
122
|
+
"copyrightText": "NOASSERTION",
|
|
123
|
+
"supplier": "NOASSERTION",
|
|
124
|
+
"externalRefs": [
|
|
125
|
+
{
|
|
126
|
+
"referenceCategory": "PACKAGE-MANAGER",
|
|
127
|
+
"referenceType": "purl",
|
|
128
|
+
"referenceLocator": "pkg:npm/picomatch@4.0.4"
|
|
129
|
+
}
|
|
130
|
+
],
|
|
131
|
+
"description": "Blazing fast and accurate glob matcher written in JavaScript, with no dependencies and full support for standard and extended Bash glob features, including braces, extglobs, POSIX brackets, and regular expressions.",
|
|
132
|
+
"homepage": "https://github.com/micromatch/picomatch"
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"SPDXID": "SPDXRef-Package-zod-4.3.6",
|
|
136
|
+
"name": "zod",
|
|
137
|
+
"versionInfo": "4.3.6",
|
|
138
|
+
"downloadLocation": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
|
|
139
|
+
"filesAnalyzed": false,
|
|
140
|
+
"licenseConcluded": "NOASSERTION",
|
|
141
|
+
"licenseDeclared": "MIT",
|
|
142
|
+
"copyrightText": "NOASSERTION",
|
|
143
|
+
"supplier": "NOASSERTION",
|
|
144
|
+
"externalRefs": [
|
|
145
|
+
{
|
|
146
|
+
"referenceCategory": "PACKAGE-MANAGER",
|
|
147
|
+
"referenceType": "purl",
|
|
148
|
+
"referenceLocator": "pkg:npm/zod@4.3.6"
|
|
149
|
+
}
|
|
150
|
+
],
|
|
151
|
+
"description": "TypeScript-first schema declaration and validation library with static type inference",
|
|
152
|
+
"homepage": "https://zod.dev"
|
|
153
|
+
}
|
|
154
|
+
],
|
|
155
|
+
"relationships": [
|
|
156
|
+
{
|
|
157
|
+
"spdxElementId": "SPDXRef-DOCUMENT",
|
|
158
|
+
"relatedSpdxElement": "SPDXRef-RootPackage",
|
|
159
|
+
"relationshipType": "DESCRIBES"
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
"spdxElementId": "SPDXRef-Package--jsep-plugin-assignment-1.3.0",
|
|
163
|
+
"relatedSpdxElement": "SPDXRef-Package-jsep-1.4.0",
|
|
164
|
+
"relationshipType": "DEPENDS_ON"
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
"spdxElementId": "SPDXRef-Package--jsep-plugin-regex-1.0.4",
|
|
168
|
+
"relatedSpdxElement": "SPDXRef-Package-jsep-1.4.0",
|
|
169
|
+
"relationshipType": "DEPENDS_ON"
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
"spdxElementId": "SPDXRef-RootPackage",
|
|
173
|
+
"relatedSpdxElement": "SPDXRef-Package-jsonpath-plus-10.4.0",
|
|
174
|
+
"relationshipType": "DEPENDS_ON"
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
"spdxElementId": "SPDXRef-RootPackage",
|
|
178
|
+
"relatedSpdxElement": "SPDXRef-Package-picomatch-4.0.4",
|
|
179
|
+
"relationshipType": "DEPENDS_ON"
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
"spdxElementId": "SPDXRef-RootPackage",
|
|
183
|
+
"relatedSpdxElement": "SPDXRef-Package-zod-4.3.6",
|
|
184
|
+
"relationshipType": "DEPENDS_ON"
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
"spdxElementId": "SPDXRef-Package-jsonpath-plus-10.4.0",
|
|
188
|
+
"relatedSpdxElement": "SPDXRef-Package--jsep-plugin-assignment-1.3.0",
|
|
189
|
+
"relationshipType": "DEPENDS_ON"
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
"spdxElementId": "SPDXRef-Package-jsonpath-plus-10.4.0",
|
|
193
|
+
"relatedSpdxElement": "SPDXRef-Package--jsep-plugin-regex-1.0.4",
|
|
194
|
+
"relationshipType": "DEPENDS_ON"
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
"spdxElementId": "SPDXRef-Package-jsonpath-plus-10.4.0",
|
|
198
|
+
"relatedSpdxElement": "SPDXRef-Package-jsep-1.4.0",
|
|
199
|
+
"relationshipType": "DEPENDS_ON"
|
|
200
|
+
}
|
|
201
|
+
]
|
|
202
|
+
}
|
package/index.js
DELETED