@kici-dev/engine 0.4.0 → 0.6.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/audit/access-log-policy.js +1 -0
- package/dist/audit/retention-policy.js +2 -0
- package/dist/context/held-run-job-id.d.ts +26 -10
- package/dist/context/held-run-job-id.js +30 -11
- package/dist/context/host-match.js +2 -1
- package/dist/context/index.d.ts +1 -1
- package/dist/context/index.js +3 -3
- package/dist/context/types.d.ts +10 -1
- package/dist/context/types.js +10 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.js +17 -12
- package/dist/labels/compile.d.ts +2 -7
- package/dist/labels/compile.js +1 -10
- package/dist/labels.d.ts +74 -12
- package/dist/labels.js +82 -17
- package/dist/mcp/held-run-resolve.d.ts +40 -0
- package/dist/mcp/held-run-resolve.js +68 -15
- package/dist/mcp/tool-schemas.d.ts +4 -0
- package/dist/mcp/tool-schemas.js +13 -1
- package/dist/metrics/catalog-policy.d.ts +17 -3
- package/dist/metrics/catalog-policy.js +44 -13
- package/dist/metrics/metric-catalog.generated.d.ts +140 -0
- package/dist/metrics/metric-catalog.generated.js +168 -0
- package/dist/protocol/dashboard-global-workflows.js +2 -2
- package/dist/protocol/event-log-payload.js +1 -1
- package/dist/protocol/messages/access-log.d.ts +5 -0
- package/dist/protocol/messages/access-log.js +1 -0
- package/dist/protocol/messages/actor.d.ts +13 -2
- package/dist/protocol/messages/actor.js +16 -5
- package/dist/protocol/messages/common.js +1 -1
- package/dist/protocol/messages/dashboard-global-workflows.d.ts +29 -3
- package/dist/protocol/messages/dashboard-global-workflows.js +48 -4
- package/dist/protocol/messages/dashboard.d.ts +48 -7
- package/dist/protocol/messages/dashboard.js +62 -6
- package/dist/protocol/messages/execution-status.d.ts +44 -0
- package/dist/protocol/messages/execution-status.js +69 -2
- package/dist/protocol/messages/git-credential-relay.d.ts +78 -0
- package/dist/protocol/messages/git-credential-relay.js +86 -0
- package/dist/protocol/messages/orchestrator-agent.d.ts +138 -0
- package/dist/protocol/messages/orchestrator-agent.js +131 -3
- package/dist/protocol/messages/peer.d.ts +7 -0
- package/dist/protocol/messages/peer.js +18 -1
- package/dist/protocol/messages/platform-orchestrator.d.ts +155 -2
- package/dist/protocol/messages/platform-orchestrator.js +178 -19
- package/dist/protocol/version.d.ts +19 -2
- package/dist/protocol/version.js +20 -3
- package/dist/provenance/verify.js +11 -10
- package/dist/provider/check-status-poster.d.ts +37 -2
- package/dist/provider/contributor-resolver.d.ts +11 -3
- package/dist/provider/file-contents-fetcher.d.ts +39 -0
- package/dist/provider/file-contents-fetcher.js +2 -0
- package/dist/provider/git-credential.d.ts +77 -0
- package/dist/provider/git-credential.js +10 -0
- package/dist/provider/index.d.ts +4 -0
- package/dist/provider/index.js +2 -1
- package/dist/provider/webhook-normalizer.d.ts +12 -12
- package/dist/repo/pattern-negation.d.ts +73 -0
- package/dist/repo/pattern-negation.js +86 -0
- package/dist/safe-regex.d.ts +16 -0
- package/dist/safe-regex.js +24 -0
- package/dist/scaler/registry-auth.d.ts +18 -0
- package/dist/scaler/registry-auth.js +28 -0
- package/dist/scaler/scaler-backend-type.d.ts +35 -0
- package/dist/scaler/scaler-backend-type.js +39 -2
- package/dist/scaler/scaler-events.d.ts +79 -0
- package/dist/scaler/scaler-events.js +87 -0
- package/dist/trigger/compiled-matchers.d.ts +21 -1
- package/dist/trigger/compiled-matchers.js +30 -3
- package/dist/trigger/content-requirements.d.ts +31 -0
- package/dist/trigger/content-requirements.js +125 -0
- package/dist/trigger/decision-trace.d.ts +168 -0
- package/dist/trigger/decision-trace.js +207 -4
- package/dist/trigger/jsonpath-matcher.js +5 -1
- package/dist/trigger/matcher.js +61 -10
- package/dist/trigger/text-match.d.ts +27 -0
- package/dist/trigger/text-match.js +86 -0
- package/dist/trigger/types.d.ts +265 -23
- package/dist/trigger/types.js +37 -5
- package/dist/ws/rate-limiter.js +3 -3
- package/package.json +20 -3
- package/sbom.spdx.json +40 -15
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one classifier for "picomatch would read this repo pattern as a
|
|
3
|
+
* negation".
|
|
4
|
+
*
|
|
5
|
+
* Repo patterns are written on lists whose direction is already fixed by the
|
|
6
|
+
* list itself — a role's allowed repositories, a global-workflow allow list, a
|
|
7
|
+
* global-workflow deny list. A pattern picomatch reads as a negation inverts
|
|
8
|
+
* that direction inside a single entry, so an entry that reads as a restriction
|
|
9
|
+
* matches as its complement. On an allow list that grants almost everything; on
|
|
10
|
+
* a deny list it admits the one repository the entry named.
|
|
11
|
+
*
|
|
12
|
+
* This lives in the engine, and not beside either consumer, because two
|
|
13
|
+
* hand-maintained ban lists for one pattern language cannot be kept in step.
|
|
14
|
+
* The two ways they drift apart are both live hazards: a list that misses the
|
|
15
|
+
* regular-expression assertions accepts a real inversion, and a list that
|
|
16
|
+
* refuses `[!…]` turns away a genuine restriction. Every surface that stores a
|
|
17
|
+
* repo pattern reads its verdict from here, so neither can happen on one
|
|
18
|
+
* surface alone.
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* The regular-expression negations picomatch passes through into the compiled
|
|
22
|
+
* matcher: the negative lookahead `(?!…)` and the negative lookbehind `(?<!…)`.
|
|
23
|
+
* The optional `<` is what makes one pattern cover both; the positive `(?=…)`,
|
|
24
|
+
* `(?<=…)`, `(?:…)` and a plain capture group are deliberately not matched.
|
|
25
|
+
*/
|
|
26
|
+
export declare const REGEX_NEGATIVE_ASSERTION: RegExp;
|
|
27
|
+
/**
|
|
28
|
+
* Why picomatch would read `pattern` as a negation, or null when it would not.
|
|
29
|
+
*
|
|
30
|
+
* Four arms, because picomatch reads four negation forms, each of which turns a
|
|
31
|
+
* pattern that reads as a restriction into a grant.
|
|
32
|
+
*
|
|
33
|
+
* A leading `!` negates the whole pattern. The extglob complement `!(…)`
|
|
34
|
+
* negates wherever it appears, so `org/!(secret)` covers every repository under
|
|
35
|
+
* `org/` except that one — the same defect in a prefix-scoped shape. The
|
|
36
|
+
* negated character class `[^…]` does it one character at a time: `org/[^s]*`
|
|
37
|
+
* covers every repository under `org/` whose name does not begin with `s`. The
|
|
38
|
+
* negative assertions are the widest of the four, because they can spell a
|
|
39
|
+
* whole repository identifier rather than one character: picomatch compiles a
|
|
40
|
+
* pattern to a regular expression and passes a group it does not recognise
|
|
41
|
+
* through verbatim, so `(?!org/secret)**` reaches the matcher as a real
|
|
42
|
+
* lookahead and matches every repository in every organization except the one
|
|
43
|
+
* it names.
|
|
44
|
+
*
|
|
45
|
+
* The extglob arm matches the two-character sequence `!(` and nothing wider:
|
|
46
|
+
* `*(`, `+(`, `@(` and `?(` are the non-complementing extglob heads and do not
|
|
47
|
+
* invert, so rejecting a bare `(` would refuse four harmless forms for no gain.
|
|
48
|
+
* A `(` cannot appear in a repository identifier, so no legitimate pattern
|
|
49
|
+
* contains `!(`. The assertion arm is narrow for the same reason: `(?=…)`,
|
|
50
|
+
* `(?<=…)`, `(?:…)` and a plain capture group match strictly what they name, so
|
|
51
|
+
* only the two negative forms are refused.
|
|
52
|
+
*
|
|
53
|
+
* The character-class arm matches `[^` and nothing wider: a `[` cannot appear
|
|
54
|
+
* in a repository identifier either, and a bracket that is not a negation —
|
|
55
|
+
* `org/[abc]*` — is a legitimate restriction. It matches `[^` and NOT `[!`:
|
|
56
|
+
* picomatch does not read `[!…]` as the POSIX negation. It reads it as a
|
|
57
|
+
* literal class containing `!` and the listed characters, so `org/[!s]*`
|
|
58
|
+
* matches exactly the repositories whose name begins with `!` or `s` — the
|
|
59
|
+
* exact inverse of `org/[^s]*`, and a genuine restriction. Rejecting it would
|
|
60
|
+
* refuse a pattern that grants strictly less than it names while leaving the
|
|
61
|
+
* real inversion open.
|
|
62
|
+
*
|
|
63
|
+
* A bare `!` is different — it *can* appear in a repository name — so the first
|
|
64
|
+
* arm stays anchored: `org/we!rd` is a legitimate literal, and an
|
|
65
|
+
* `includes('!')` check would silently reject a valid repository name.
|
|
66
|
+
*/
|
|
67
|
+
export declare function negatedPatternReason(pattern: string): string | null;
|
|
68
|
+
/**
|
|
69
|
+
* True when `pattern` is a picomatch negation — whole-pattern `!…`, extglob
|
|
70
|
+
* `!(…)`, negated character class `[^…]`, or a negative lookahead / lookbehind.
|
|
71
|
+
*/
|
|
72
|
+
export declare function isNegatedPattern(pattern: string): boolean;
|
|
73
|
+
//# sourceMappingURL=pattern-negation.d.ts.map
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
|
+
//#region src/repo/pattern-negation.ts
|
|
3
|
+
/**
|
|
4
|
+
* The one classifier for "picomatch would read this repo pattern as a
|
|
5
|
+
* negation".
|
|
6
|
+
*
|
|
7
|
+
* Repo patterns are written on lists whose direction is already fixed by the
|
|
8
|
+
* list itself — a role's allowed repositories, a global-workflow allow list, a
|
|
9
|
+
* global-workflow deny list. A pattern picomatch reads as a negation inverts
|
|
10
|
+
* that direction inside a single entry, so an entry that reads as a restriction
|
|
11
|
+
* matches as its complement. On an allow list that grants almost everything; on
|
|
12
|
+
* a deny list it admits the one repository the entry named.
|
|
13
|
+
*
|
|
14
|
+
* This lives in the engine, and not beside either consumer, because two
|
|
15
|
+
* hand-maintained ban lists for one pattern language cannot be kept in step.
|
|
16
|
+
* The two ways they drift apart are both live hazards: a list that misses the
|
|
17
|
+
* regular-expression assertions accepts a real inversion, and a list that
|
|
18
|
+
* refuses `[!…]` turns away a genuine restriction. Every surface that stores a
|
|
19
|
+
* repo pattern reads its verdict from here, so neither can happen on one
|
|
20
|
+
* surface alone.
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* The regular-expression negations picomatch passes through into the compiled
|
|
24
|
+
* matcher: the negative lookahead `(?!…)` and the negative lookbehind `(?<!…)`.
|
|
25
|
+
* The optional `<` is what makes one pattern cover both; the positive `(?=…)`,
|
|
26
|
+
* `(?<=…)`, `(?:…)` and a plain capture group are deliberately not matched.
|
|
27
|
+
*/
|
|
28
|
+
const REGEX_NEGATIVE_ASSERTION = /\(\?<?!/;
|
|
29
|
+
/**
|
|
30
|
+
* Why picomatch would read `pattern` as a negation, or null when it would not.
|
|
31
|
+
*
|
|
32
|
+
* Four arms, because picomatch reads four negation forms, each of which turns a
|
|
33
|
+
* pattern that reads as a restriction into a grant.
|
|
34
|
+
*
|
|
35
|
+
* A leading `!` negates the whole pattern. The extglob complement `!(…)`
|
|
36
|
+
* negates wherever it appears, so `org/!(secret)` covers every repository under
|
|
37
|
+
* `org/` except that one — the same defect in a prefix-scoped shape. The
|
|
38
|
+
* negated character class `[^…]` does it one character at a time: `org/[^s]*`
|
|
39
|
+
* covers every repository under `org/` whose name does not begin with `s`. The
|
|
40
|
+
* negative assertions are the widest of the four, because they can spell a
|
|
41
|
+
* whole repository identifier rather than one character: picomatch compiles a
|
|
42
|
+
* pattern to a regular expression and passes a group it does not recognise
|
|
43
|
+
* through verbatim, so `(?!org/secret)**` reaches the matcher as a real
|
|
44
|
+
* lookahead and matches every repository in every organization except the one
|
|
45
|
+
* it names.
|
|
46
|
+
*
|
|
47
|
+
* The extglob arm matches the two-character sequence `!(` and nothing wider:
|
|
48
|
+
* `*(`, `+(`, `@(` and `?(` are the non-complementing extglob heads and do not
|
|
49
|
+
* invert, so rejecting a bare `(` would refuse four harmless forms for no gain.
|
|
50
|
+
* A `(` cannot appear in a repository identifier, so no legitimate pattern
|
|
51
|
+
* contains `!(`. The assertion arm is narrow for the same reason: `(?=…)`,
|
|
52
|
+
* `(?<=…)`, `(?:…)` and a plain capture group match strictly what they name, so
|
|
53
|
+
* only the two negative forms are refused.
|
|
54
|
+
*
|
|
55
|
+
* The character-class arm matches `[^` and nothing wider: a `[` cannot appear
|
|
56
|
+
* in a repository identifier either, and a bracket that is not a negation —
|
|
57
|
+
* `org/[abc]*` — is a legitimate restriction. It matches `[^` and NOT `[!`:
|
|
58
|
+
* picomatch does not read `[!…]` as the POSIX negation. It reads it as a
|
|
59
|
+
* literal class containing `!` and the listed characters, so `org/[!s]*`
|
|
60
|
+
* matches exactly the repositories whose name begins with `!` or `s` — the
|
|
61
|
+
* exact inverse of `org/[^s]*`, and a genuine restriction. Rejecting it would
|
|
62
|
+
* refuse a pattern that grants strictly less than it names while leaving the
|
|
63
|
+
* real inversion open.
|
|
64
|
+
*
|
|
65
|
+
* A bare `!` is different — it *can* appear in a repository name — so the first
|
|
66
|
+
* arm stays anchored: `org/we!rd` is a legitimate literal, and an
|
|
67
|
+
* `includes('!')` check would silently reject a valid repository name.
|
|
68
|
+
*/
|
|
69
|
+
function negatedPatternReason(pattern) {
|
|
70
|
+
if (pattern.startsWith("!")) return "negation ('!' prefix)";
|
|
71
|
+
if (pattern.includes("!(")) return "extglob negation ('!(…)')";
|
|
72
|
+
if (pattern.includes("[^")) return "character-class negation ('[^…]')";
|
|
73
|
+
if (REGEX_NEGATIVE_ASSERTION.test(pattern)) return "negative assertion ('(?!…)' / '(?<!…)')";
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* True when `pattern` is a picomatch negation — whole-pattern `!…`, extglob
|
|
78
|
+
* `!(…)`, negated character class `[^…]`, or a negative lookahead / lookbehind.
|
|
79
|
+
*/
|
|
80
|
+
function isNegatedPattern(pattern) {
|
|
81
|
+
return negatedPatternReason(pattern) !== null;
|
|
82
|
+
}
|
|
83
|
+
//#endregion
|
|
84
|
+
export { REGEX_NEGATIVE_ASSERTION, isNegatedPattern, negatedPatternReason };
|
|
85
|
+
|
|
86
|
+
//# sourceMappingURL=pattern-negation.js.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reject a ReDoS-prone author-supplied regex before it is compiled.
|
|
3
|
+
*
|
|
4
|
+
* `safe-regex` is a star-height heuristic. The orchestrator is single-tenant, so
|
|
5
|
+
* a slipped-through pattern only stalls the author's own orchestrator — but every
|
|
6
|
+
* author regex the orchestrator compiles from lock-file data on the webhook path
|
|
7
|
+
* (label selectors, branch/tag/repo patterns, embedded JSONPath `/re/flags`,
|
|
8
|
+
* comment `bodyMatch`) must be checked so a catastrophic pattern is rejected at
|
|
9
|
+
* compile/registration time rather than silently compiled. Globs are linear by
|
|
10
|
+
* construction but pass through here for uniformity.
|
|
11
|
+
*
|
|
12
|
+
* `ctx` is a human label (e.g. "branch pattern", "job 'web' runsOn") woven into
|
|
13
|
+
* the thrown error so an operator can identify the offending pattern.
|
|
14
|
+
*/
|
|
15
|
+
export declare function assertSafeRegex(source: string, flags: string, ctx: string): void;
|
|
16
|
+
//# sourceMappingURL=safe-regex.d.ts.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import "./rolldown-runtime-ClRpJifh.js";
|
|
2
|
+
import safeRegex from "safe-regex";
|
|
3
|
+
//#region src/safe-regex.ts
|
|
4
|
+
/**
|
|
5
|
+
* Reject a ReDoS-prone author-supplied regex before it is compiled.
|
|
6
|
+
*
|
|
7
|
+
* `safe-regex` is a star-height heuristic. The orchestrator is single-tenant, so
|
|
8
|
+
* a slipped-through pattern only stalls the author's own orchestrator — but every
|
|
9
|
+
* author regex the orchestrator compiles from lock-file data on the webhook path
|
|
10
|
+
* (label selectors, branch/tag/repo patterns, embedded JSONPath `/re/flags`,
|
|
11
|
+
* comment `bodyMatch`) must be checked so a catastrophic pattern is rejected at
|
|
12
|
+
* compile/registration time rather than silently compiled. Globs are linear by
|
|
13
|
+
* construction but pass through here for uniformity.
|
|
14
|
+
*
|
|
15
|
+
* `ctx` is a human label (e.g. "branch pattern", "job 'web' runsOn") woven into
|
|
16
|
+
* the thrown error so an operator can identify the offending pattern.
|
|
17
|
+
*/
|
|
18
|
+
function assertSafeRegex(source, flags, ctx) {
|
|
19
|
+
if (!safeRegex(new RegExp(source, flags))) throw new Error(`${ctx}: regex /${source}/${flags} is ReDoS-prone — rejected`);
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
export { assertSafeRegex };
|
|
23
|
+
|
|
24
|
+
//# sourceMappingURL=safe-regex.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registry-host extraction for container image references.
|
|
3
|
+
*
|
|
4
|
+
* Lives in the engine because three sites need the same answer: the agent's
|
|
5
|
+
* container sandbox, the container scaler backend, and the bare-metal backend
|
|
6
|
+
* in container mode. A private pull authenticates against the registry HOST,
|
|
7
|
+
* which is not a field of the image ref — it has to be derived from it.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Registry host (a container runtime's `authconfig.serveraddress`) for an image ref.
|
|
11
|
+
*
|
|
12
|
+
* Docker's own rule: the first path segment is the registry only when it looks
|
|
13
|
+
* like a host — it contains a dot or a colon, or it is exactly `localhost`.
|
|
14
|
+
* Otherwise the ref is a Docker Hub short name (`nginx`, `acme/ci`) and the
|
|
15
|
+
* registry is `docker.io`.
|
|
16
|
+
*/
|
|
17
|
+
export declare function registryHostFromImageRef(image: string): string;
|
|
18
|
+
//# sourceMappingURL=registry-auth.d.ts.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
|
+
//#region src/scaler/registry-auth.ts
|
|
3
|
+
/**
|
|
4
|
+
* Registry-host extraction for container image references.
|
|
5
|
+
*
|
|
6
|
+
* Lives in the engine because three sites need the same answer: the agent's
|
|
7
|
+
* container sandbox, the container scaler backend, and the bare-metal backend
|
|
8
|
+
* in container mode. A private pull authenticates against the registry HOST,
|
|
9
|
+
* which is not a field of the image ref — it has to be derived from it.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Registry host (a container runtime's `authconfig.serveraddress`) for an image ref.
|
|
13
|
+
*
|
|
14
|
+
* Docker's own rule: the first path segment is the registry only when it looks
|
|
15
|
+
* like a host — it contains a dot or a colon, or it is exactly `localhost`.
|
|
16
|
+
* Otherwise the ref is a Docker Hub short name (`nginx`, `acme/ci`) and the
|
|
17
|
+
* registry is `docker.io`.
|
|
18
|
+
*/
|
|
19
|
+
function registryHostFromImageRef(image) {
|
|
20
|
+
const firstSlash = image.indexOf("/");
|
|
21
|
+
if (firstSlash === -1) return "docker.io";
|
|
22
|
+
const candidate = image.slice(0, firstSlash);
|
|
23
|
+
return candidate.includes(".") || candidate.includes(":") || candidate === "localhost" ? candidate : "docker.io";
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
export { registryHostFromImageRef };
|
|
27
|
+
|
|
28
|
+
//# sourceMappingURL=registry-auth.js.map
|
|
@@ -9,8 +9,43 @@ import { z } from 'zod';
|
|
|
9
9
|
export declare const ScalerBackendType: z.ZodEnum<{
|
|
10
10
|
"bare-metal": "bare-metal";
|
|
11
11
|
container: "container";
|
|
12
|
+
event: "event";
|
|
12
13
|
firecracker: "firecracker";
|
|
13
14
|
kubernetes: "kubernetes";
|
|
14
15
|
}>;
|
|
15
16
|
export type ScalerBackendType = z.infer<typeof ScalerBackendType>;
|
|
17
|
+
/**
|
|
18
|
+
* Reserved event-name prefix for KiCI-internal system events (today: the event
|
|
19
|
+
* scaler's scale-up / scale-down events). Custom events emitted from user
|
|
20
|
+
* workflow steps (`ctx.emit(...)`) MUST NOT use this prefix — both the SDK
|
|
21
|
+
* (client-side) and the orchestrator (authoritative) reject a name that starts
|
|
22
|
+
* with it, so a user step cannot forge a system event. The rate limiter also
|
|
23
|
+
* exempts it. Defined in `@kici-dev/engine` so the SDK and the orchestrator
|
|
24
|
+
* share one source of truth without the SDK importing the orchestrator.
|
|
25
|
+
*/
|
|
26
|
+
export declare const KICI_EVENT_NAME_PREFIX = "kici.";
|
|
27
|
+
/**
|
|
28
|
+
* Reserved event-name prefix for the events the ORCHESTRATOR mints for itself
|
|
29
|
+
* (`__schedule_fire`, `__workflow_complete`, `__job_complete`,
|
|
30
|
+
* `__workflows_failed_batch`).
|
|
31
|
+
*
|
|
32
|
+
* Reserved for the same reason as {@link KICI_EVENT_NAME_PREFIX}, and more
|
|
33
|
+
* sharply: every name under this prefix is exempt from the event-storm rate
|
|
34
|
+
* limiter, and `__schedule_fire` is additionally classified as a TRUSTED ref —
|
|
35
|
+
* no run causes it, so nothing external shaped it. The other three ARE caused
|
|
36
|
+
* by runs and inherit the tier of the run (or, for the failure batch, the most
|
|
37
|
+
* restrictive tier across the runs) behind them, so they forge no privilege on
|
|
38
|
+
* their own. A user step that could emit any of them would forge the
|
|
39
|
+
* rate-limiter exemption, and `__schedule_fire` the trusted classification on
|
|
40
|
+
* top of it — so the same two-sided reservation applies to the whole prefix,
|
|
41
|
+
* SDK first and orchestrator authoritatively.
|
|
42
|
+
*/
|
|
43
|
+
export declare const INTERNAL_EVENT_NAME_PREFIX = "__";
|
|
44
|
+
/**
|
|
45
|
+
* The reserved prefix `eventName` uses, or `undefined` when a user step may
|
|
46
|
+
* emit it. One definition of "reserved", so the SDK-side check and the
|
|
47
|
+
* orchestrator's authoritative backstop can never disagree about which names a
|
|
48
|
+
* workflow may emit.
|
|
49
|
+
*/
|
|
50
|
+
export declare function reservedEventNamePrefix(eventName: string): string | undefined;
|
|
16
51
|
//# sourceMappingURL=scaler-backend-type.d.ts.map
|
|
@@ -12,9 +12,46 @@ const ScalerBackendType = z.enum([
|
|
|
12
12
|
"container",
|
|
13
13
|
"bare-metal",
|
|
14
14
|
"firecracker",
|
|
15
|
-
"kubernetes"
|
|
15
|
+
"kubernetes",
|
|
16
|
+
"event"
|
|
16
17
|
]);
|
|
18
|
+
/**
|
|
19
|
+
* Reserved event-name prefix for KiCI-internal system events (today: the event
|
|
20
|
+
* scaler's scale-up / scale-down events). Custom events emitted from user
|
|
21
|
+
* workflow steps (`ctx.emit(...)`) MUST NOT use this prefix — both the SDK
|
|
22
|
+
* (client-side) and the orchestrator (authoritative) reject a name that starts
|
|
23
|
+
* with it, so a user step cannot forge a system event. The rate limiter also
|
|
24
|
+
* exempts it. Defined in `@kici-dev/engine` so the SDK and the orchestrator
|
|
25
|
+
* share one source of truth without the SDK importing the orchestrator.
|
|
26
|
+
*/
|
|
27
|
+
const KICI_EVENT_NAME_PREFIX = "kici.";
|
|
28
|
+
/**
|
|
29
|
+
* Reserved event-name prefix for the events the ORCHESTRATOR mints for itself
|
|
30
|
+
* (`__schedule_fire`, `__workflow_complete`, `__job_complete`,
|
|
31
|
+
* `__workflows_failed_batch`).
|
|
32
|
+
*
|
|
33
|
+
* Reserved for the same reason as {@link KICI_EVENT_NAME_PREFIX}, and more
|
|
34
|
+
* sharply: every name under this prefix is exempt from the event-storm rate
|
|
35
|
+
* limiter, and `__schedule_fire` is additionally classified as a TRUSTED ref —
|
|
36
|
+
* no run causes it, so nothing external shaped it. The other three ARE caused
|
|
37
|
+
* by runs and inherit the tier of the run (or, for the failure batch, the most
|
|
38
|
+
* restrictive tier across the runs) behind them, so they forge no privilege on
|
|
39
|
+
* their own. A user step that could emit any of them would forge the
|
|
40
|
+
* rate-limiter exemption, and `__schedule_fire` the trusted classification on
|
|
41
|
+
* top of it — so the same two-sided reservation applies to the whole prefix,
|
|
42
|
+
* SDK first and orchestrator authoritatively.
|
|
43
|
+
*/
|
|
44
|
+
const INTERNAL_EVENT_NAME_PREFIX = "__";
|
|
45
|
+
/**
|
|
46
|
+
* The reserved prefix `eventName` uses, or `undefined` when a user step may
|
|
47
|
+
* emit it. One definition of "reserved", so the SDK-side check and the
|
|
48
|
+
* orchestrator's authoritative backstop can never disagree about which names a
|
|
49
|
+
* workflow may emit.
|
|
50
|
+
*/
|
|
51
|
+
function reservedEventNamePrefix(eventName) {
|
|
52
|
+
for (const prefix of [KICI_EVENT_NAME_PREFIX, "__"]) if (eventName.startsWith(prefix)) return prefix;
|
|
53
|
+
}
|
|
17
54
|
//#endregion
|
|
18
|
-
export { ScalerBackendType };
|
|
55
|
+
export { INTERNAL_EVENT_NAME_PREFIX, KICI_EVENT_NAME_PREFIX, ScalerBackendType, reservedEventNamePrefix };
|
|
19
56
|
|
|
20
57
|
//# sourceMappingURL=scaler-backend-type.js.map
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared schemas and constants for the event scaler backend.
|
|
3
|
+
*
|
|
4
|
+
* The event scaler performs no local compute: its `spawn()` / `destroy()` emit
|
|
5
|
+
* reserved `kici.`-prefixed custom events (`kici.scaler.scale-up` /
|
|
6
|
+
* `kici.scaler.scale-down`) that a customer-authored provisioning / teardown
|
|
7
|
+
* workflow consumes via the `kiciEvent()` trigger. These schemas define the
|
|
8
|
+
* event payloads and the single source of truth for the reserved event names.
|
|
9
|
+
*
|
|
10
|
+
* They live here, not in the orchestrator, because both sides of the contract
|
|
11
|
+
* need them: the orchestrator emits, and a workflow file — which may import
|
|
12
|
+
* `@kici-dev/sdk` and nothing else — consumes. Engine is the only package both
|
|
13
|
+
* the SDK and the orchestrator depend on, so it is the shared floor. The SDK
|
|
14
|
+
* re-exports all four symbols; the orchestrator's `scaler/scaler-events.ts`
|
|
15
|
+
* re-exports them for its own call sites.
|
|
16
|
+
*/
|
|
17
|
+
import { z } from 'zod';
|
|
18
|
+
/**
|
|
19
|
+
* Reserved event names the event scaler emits. Both start with
|
|
20
|
+
* `KICI_EVENT_NAME_PREFIX` (`./scaler-backend-type.ts`), so the rate limiter
|
|
21
|
+
* exempts them and user steps cannot forge them — `scaler-events.test.ts`
|
|
22
|
+
* asserts that relationship holds for both names.
|
|
23
|
+
*/
|
|
24
|
+
export declare const SCALER_EVENT_NAMES: {
|
|
25
|
+
readonly scaleUp: 'kici.scaler.scale-up';
|
|
26
|
+
readonly scaleDown: 'kici.scaler.scale-down';
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Why the scaler asked for an agent to be torn down. Carried on the
|
|
30
|
+
* `kici.scaler.scale-down` event so a teardown workflow (and the timeline) can
|
|
31
|
+
* distinguish an idle reap from a job-complete teardown or a spawn timeout.
|
|
32
|
+
*/
|
|
33
|
+
export declare const ScaleDownReason: z.ZodEnum<{
|
|
34
|
+
drain: "drain";
|
|
35
|
+
"heartbeat-timeout": "heartbeat-timeout";
|
|
36
|
+
idle: "idle";
|
|
37
|
+
"job-complete": "job-complete";
|
|
38
|
+
shutdown: "shutdown";
|
|
39
|
+
"spawn-timeout": "spawn-timeout";
|
|
40
|
+
}>;
|
|
41
|
+
export type ScaleDownReason = z.infer<typeof ScaleDownReason>;
|
|
42
|
+
/**
|
|
43
|
+
* Payload of a `kici.scaler.scale-up` event. Everything a provisioning workflow
|
|
44
|
+
* needs to boot an instance whose agent registers back with `agentId` and claim
|
|
45
|
+
* its ephemeral credentials with `claimCode`. The ephemeral token itself is
|
|
46
|
+
* NEVER in this payload — it is delivered only via the
|
|
47
|
+
* `scaler.claim-credentials` RPC response.
|
|
48
|
+
*/
|
|
49
|
+
export declare const ScalerScaleUpPayload: z.ZodObject<{
|
|
50
|
+
scalerName: z.ZodString;
|
|
51
|
+
agentId: z.ZodString;
|
|
52
|
+
labels: z.ZodArray<z.ZodString>;
|
|
53
|
+
mandatoryLabels: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
54
|
+
resources: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
55
|
+
orchestratorUrl: z.ZodString;
|
|
56
|
+
claimCode: z.ZodString;
|
|
57
|
+
jobId: z.ZodOptional<z.ZodString>;
|
|
58
|
+
requestId: z.ZodString;
|
|
59
|
+
}, z.core.$strip>;
|
|
60
|
+
export type ScalerScaleUpPayload = z.infer<typeof ScalerScaleUpPayload>;
|
|
61
|
+
/**
|
|
62
|
+
* Payload of a `kici.scaler.scale-down` event. A teardown workflow deletes the
|
|
63
|
+
* instance registered under `agentId`.
|
|
64
|
+
*/
|
|
65
|
+
export declare const ScalerScaleDownPayload: z.ZodObject<{
|
|
66
|
+
scalerName: z.ZodString;
|
|
67
|
+
agentId: z.ZodString;
|
|
68
|
+
reason: z.ZodEnum<{
|
|
69
|
+
drain: "drain";
|
|
70
|
+
"heartbeat-timeout": "heartbeat-timeout";
|
|
71
|
+
idle: "idle";
|
|
72
|
+
"job-complete": "job-complete";
|
|
73
|
+
shutdown: "shutdown";
|
|
74
|
+
"spawn-timeout": "spawn-timeout";
|
|
75
|
+
}>;
|
|
76
|
+
requestId: z.ZodString;
|
|
77
|
+
}, z.core.$strip>;
|
|
78
|
+
export type ScalerScaleDownPayload = z.infer<typeof ScalerScaleDownPayload>;
|
|
79
|
+
//# sourceMappingURL=scaler-events.d.ts.map
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
//#region src/scaler/scaler-events.ts
|
|
4
|
+
/**
|
|
5
|
+
* Shared schemas and constants for the event scaler backend.
|
|
6
|
+
*
|
|
7
|
+
* The event scaler performs no local compute: its `spawn()` / `destroy()` emit
|
|
8
|
+
* reserved `kici.`-prefixed custom events (`kici.scaler.scale-up` /
|
|
9
|
+
* `kici.scaler.scale-down`) that a customer-authored provisioning / teardown
|
|
10
|
+
* workflow consumes via the `kiciEvent()` trigger. These schemas define the
|
|
11
|
+
* event payloads and the single source of truth for the reserved event names.
|
|
12
|
+
*
|
|
13
|
+
* They live here, not in the orchestrator, because both sides of the contract
|
|
14
|
+
* need them: the orchestrator emits, and a workflow file — which may import
|
|
15
|
+
* `@kici-dev/sdk` and nothing else — consumes. Engine is the only package both
|
|
16
|
+
* the SDK and the orchestrator depend on, so it is the shared floor. The SDK
|
|
17
|
+
* re-exports all four symbols; the orchestrator's `scaler/scaler-events.ts`
|
|
18
|
+
* re-exports them for its own call sites.
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Reserved event names the event scaler emits. Both start with
|
|
22
|
+
* `KICI_EVENT_NAME_PREFIX` (`./scaler-backend-type.ts`), so the rate limiter
|
|
23
|
+
* exempts them and user steps cannot forge them — `scaler-events.test.ts`
|
|
24
|
+
* asserts that relationship holds for both names.
|
|
25
|
+
*/
|
|
26
|
+
const SCALER_EVENT_NAMES = {
|
|
27
|
+
scaleUp: "kici.scaler.scale-up",
|
|
28
|
+
scaleDown: "kici.scaler.scale-down"
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Why the scaler asked for an agent to be torn down. Carried on the
|
|
32
|
+
* `kici.scaler.scale-down` event so a teardown workflow (and the timeline) can
|
|
33
|
+
* distinguish an idle reap from a job-complete teardown or a spawn timeout.
|
|
34
|
+
*/
|
|
35
|
+
const ScaleDownReason = z.enum([
|
|
36
|
+
"idle",
|
|
37
|
+
"job-complete",
|
|
38
|
+
"heartbeat-timeout",
|
|
39
|
+
"spawn-timeout",
|
|
40
|
+
"drain",
|
|
41
|
+
"shutdown"
|
|
42
|
+
]);
|
|
43
|
+
/**
|
|
44
|
+
* Payload of a `kici.scaler.scale-up` event. Everything a provisioning workflow
|
|
45
|
+
* needs to boot an instance whose agent registers back with `agentId` and claim
|
|
46
|
+
* its ephemeral credentials with `claimCode`. The ephemeral token itself is
|
|
47
|
+
* NEVER in this payload — it is delivered only via the
|
|
48
|
+
* `scaler.claim-credentials` RPC response.
|
|
49
|
+
*/
|
|
50
|
+
const ScalerScaleUpPayload = z.object({
|
|
51
|
+
/** Name of the scaler entry that emitted the event. */
|
|
52
|
+
scalerName: z.string(),
|
|
53
|
+
/** Agent id the provisioned instance must register with (correlates the spawn). */
|
|
54
|
+
agentId: z.string(),
|
|
55
|
+
/** Exact label set the pending job needs. */
|
|
56
|
+
labels: z.array(z.string()),
|
|
57
|
+
/** Mandatory (taint) labels the pool gates on, if any. */
|
|
58
|
+
mandatoryLabels: z.array(z.string()).default([]),
|
|
59
|
+
/** Resolved resource hints for the provision (e.g. cpus / memBytes). */
|
|
60
|
+
resources: z.record(z.string(), z.unknown()).default({}),
|
|
61
|
+
/** Orchestrator WS URL the provisioned agent connects back to. */
|
|
62
|
+
orchestratorUrl: z.string(),
|
|
63
|
+
/** Single-use code the workflow exchanges for ephemeral agent credentials. */
|
|
64
|
+
claimCode: z.string(),
|
|
65
|
+
/** Execution job id the spawn is bound to (absent for unbound / warm spawns). */
|
|
66
|
+
jobId: z.string().optional(),
|
|
67
|
+
/** Correlation id for this scale-up request. */
|
|
68
|
+
requestId: z.string()
|
|
69
|
+
});
|
|
70
|
+
/**
|
|
71
|
+
* Payload of a `kici.scaler.scale-down` event. A teardown workflow deletes the
|
|
72
|
+
* instance registered under `agentId`.
|
|
73
|
+
*/
|
|
74
|
+
const ScalerScaleDownPayload = z.object({
|
|
75
|
+
/** Name of the scaler entry that emitted the event. */
|
|
76
|
+
scalerName: z.string(),
|
|
77
|
+
/** Agent id whose instance should be torn down. */
|
|
78
|
+
agentId: z.string(),
|
|
79
|
+
/** Why the teardown was requested. */
|
|
80
|
+
reason: ScaleDownReason,
|
|
81
|
+
/** Correlation id for this scale-down request. */
|
|
82
|
+
requestId: z.string()
|
|
83
|
+
});
|
|
84
|
+
//#endregion
|
|
85
|
+
export { SCALER_EVENT_NAMES, ScaleDownReason, ScalerScaleDownPayload, ScalerScaleUpPayload };
|
|
86
|
+
|
|
87
|
+
//# sourceMappingURL=scaler-events.js.map
|
|
@@ -3,11 +3,31 @@
|
|
|
3
3
|
* flags first with a space separator; regex flags are restricted to
|
|
4
4
|
* `[gimsuydv]`, so the separator can never collide with a pattern that starts
|
|
5
5
|
* with a space.
|
|
6
|
+
*
|
|
7
|
+
* Every pattern reaching here is author-supplied (branch/tag/repo patterns and
|
|
8
|
+
* comment `bodyMatch`), so it is ReDoS-checked before compiling. The guard runs
|
|
9
|
+
* inside the memo factory, so a benign pattern pays the check once (on cache
|
|
10
|
+
* miss) and a catastrophic one throws — and, being unsafe, is never cached, so
|
|
11
|
+
* it is rejected on every attempt. `ctx` names the dialect in the thrown error.
|
|
6
12
|
*/
|
|
7
|
-
export declare function getCompiledRegex(pattern: string, flags?: string): RegExp;
|
|
13
|
+
export declare function getCompiledRegex(pattern: string, flags?: string, ctx?: string): RegExp;
|
|
8
14
|
/**
|
|
9
15
|
* Return a memoized picomatch matcher function for the given glob pattern.
|
|
10
16
|
* `getGlobMatcher(pattern)(str)` is equivalent to `picomatch.isMatch(str, pattern)`.
|
|
11
17
|
*/
|
|
12
18
|
export declare function getGlobMatcher(pattern: string): (s: string) => boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Repo-identifier matcher. Identical to {@link getGlobMatcher} except it sets
|
|
21
|
+
* picomatch's `dot: true`, so a dot-prefixed identifier (`.hidden/repo`) is
|
|
22
|
+
* matched by `**` — `repos: ['**']` is documented as "every repo in the org",
|
|
23
|
+
* and silently excluding a whole class of identifier contradicts that.
|
|
24
|
+
*
|
|
25
|
+
* Deliberately NOT the shared matcher: `dot: true` on path globs would make
|
|
26
|
+
* `paths: ['**']` start matching `.github/**` for every existing workflow, a
|
|
27
|
+
* semantics change this does not intend.
|
|
28
|
+
*
|
|
29
|
+
* Note a bare `.` still does not match, in either mode — picomatch will not
|
|
30
|
+
* match a lone `.`. That is upstream behavior, not something this option fixes.
|
|
31
|
+
*/
|
|
32
|
+
export declare function getRepoGlobMatcher(pattern: string): (s: string) => boolean;
|
|
13
33
|
//# sourceMappingURL=compiled-matchers.d.ts.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
|
+
import { assertSafeRegex } from "../safe-regex.js";
|
|
2
3
|
import picomatch from "picomatch";
|
|
3
4
|
//#region src/trigger/compiled-matchers.ts
|
|
4
5
|
/**
|
|
@@ -22,14 +23,24 @@ function boundedGet(cache, key, make) {
|
|
|
22
23
|
}
|
|
23
24
|
const regexCache = /* @__PURE__ */ new Map();
|
|
24
25
|
const globCache = /* @__PURE__ */ new Map();
|
|
26
|
+
const repoGlobCache = /* @__PURE__ */ new Map();
|
|
25
27
|
/**
|
|
26
28
|
* Return a memoized RegExp for the given pattern + flags. The key encodes the
|
|
27
29
|
* flags first with a space separator; regex flags are restricted to
|
|
28
30
|
* `[gimsuydv]`, so the separator can never collide with a pattern that starts
|
|
29
31
|
* with a space.
|
|
32
|
+
*
|
|
33
|
+
* Every pattern reaching here is author-supplied (branch/tag/repo patterns and
|
|
34
|
+
* comment `bodyMatch`), so it is ReDoS-checked before compiling. The guard runs
|
|
35
|
+
* inside the memo factory, so a benign pattern pays the check once (on cache
|
|
36
|
+
* miss) and a catastrophic one throws — and, being unsafe, is never cached, so
|
|
37
|
+
* it is rejected on every attempt. `ctx` names the dialect in the thrown error.
|
|
30
38
|
*/
|
|
31
|
-
function getCompiledRegex(pattern, flags) {
|
|
32
|
-
return boundedGet(regexCache, `${flags ?? ""} ${pattern}`, () =>
|
|
39
|
+
function getCompiledRegex(pattern, flags, ctx = "trigger pattern") {
|
|
40
|
+
return boundedGet(regexCache, `${flags ?? ""} ${pattern}`, () => {
|
|
41
|
+
assertSafeRegex(pattern, flags ?? "", ctx);
|
|
42
|
+
return new RegExp(pattern, flags);
|
|
43
|
+
});
|
|
33
44
|
}
|
|
34
45
|
/**
|
|
35
46
|
* Return a memoized picomatch matcher function for the given glob pattern.
|
|
@@ -38,7 +49,23 @@ function getCompiledRegex(pattern, flags) {
|
|
|
38
49
|
function getGlobMatcher(pattern) {
|
|
39
50
|
return boundedGet(globCache, pattern, () => picomatch(pattern));
|
|
40
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Repo-identifier matcher. Identical to {@link getGlobMatcher} except it sets
|
|
54
|
+
* picomatch's `dot: true`, so a dot-prefixed identifier (`.hidden/repo`) is
|
|
55
|
+
* matched by `**` — `repos: ['**']` is documented as "every repo in the org",
|
|
56
|
+
* and silently excluding a whole class of identifier contradicts that.
|
|
57
|
+
*
|
|
58
|
+
* Deliberately NOT the shared matcher: `dot: true` on path globs would make
|
|
59
|
+
* `paths: ['**']` start matching `.github/**` for every existing workflow, a
|
|
60
|
+
* semantics change this does not intend.
|
|
61
|
+
*
|
|
62
|
+
* Note a bare `.` still does not match, in either mode — picomatch will not
|
|
63
|
+
* match a lone `.`. That is upstream behavior, not something this option fixes.
|
|
64
|
+
*/
|
|
65
|
+
function getRepoGlobMatcher(pattern) {
|
|
66
|
+
return boundedGet(repoGlobCache, pattern, () => picomatch(pattern, { dot: true }));
|
|
67
|
+
}
|
|
41
68
|
//#endregion
|
|
42
|
-
export { getCompiledRegex, getGlobMatcher };
|
|
69
|
+
export { getCompiledRegex, getGlobMatcher, getRepoGlobMatcher };
|
|
43
70
|
|
|
44
71
|
//# sourceMappingURL=compiled-matchers.js.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { LockContentRequirement } from './types.js';
|
|
2
|
+
export type { ContentFormat, ContentRequirement, LockContentRequirement } from './types.js';
|
|
3
|
+
/** Result of evaluating a `requires` list: a definite verdict, or fail-visible indeterminate. */
|
|
4
|
+
export interface ContentRequirementResult {
|
|
5
|
+
readonly pass: boolean;
|
|
6
|
+
/** Set (with `pass:false`) when a file could not be evaluated — never a silent pass. */
|
|
7
|
+
readonly indeterminate?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Parse `bytes` for the given concrete format. `text` returns the raw string;
|
|
11
|
+
* `json` uses `JSON.parse`; `yaml` uses a hardened `yaml.parse` with an explicit
|
|
12
|
+
* anchor/alias cap. Throws on a malformed document (the caller treats a throw as
|
|
13
|
+
* indeterminate).
|
|
14
|
+
*/
|
|
15
|
+
export declare function parseForFormat(bytes: string, format: 'json' | 'yaml' | 'text'): unknown;
|
|
16
|
+
/**
|
|
17
|
+
* Evaluate an AND-ed list of content requirements against a resolved file map.
|
|
18
|
+
*
|
|
19
|
+
* Every entry must pass for the overall result to pass; an empty list passes.
|
|
20
|
+
* The first indeterminate entry short-circuits and is surfaced (fail-visible):
|
|
21
|
+
* an unevaluable file NEVER passes silently.
|
|
22
|
+
*
|
|
23
|
+
* @param reqs The lock `requires` list (each entry AND-ed).
|
|
24
|
+
* @param files Resolved file contents keyed by repo-relative path. A missing key
|
|
25
|
+
* or `{ present: false }` means the file does not exist at the ref.
|
|
26
|
+
*/
|
|
27
|
+
export declare function evaluateContentRequirements(reqs: readonly LockContentRequirement[], files: Map<string, {
|
|
28
|
+
present: boolean;
|
|
29
|
+
bytes?: string;
|
|
30
|
+
}>): ContentRequirementResult;
|
|
31
|
+
//# sourceMappingURL=content-requirements.d.ts.map
|