@compr/opscontext-mcp 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +313 -0
- package/LICENSE +83 -0
- package/README.md +470 -0
- package/defaults/learnings.json +146 -0
- package/dist/activation.d.ts +48 -0
- package/dist/activation.js +377 -0
- package/dist/adapters.d.ts +101 -0
- package/dist/adapters.js +171 -0
- package/dist/agents.d.ts +137 -0
- package/dist/agents.js +1638 -0
- package/dist/audit.d.ts +23 -0
- package/dist/audit.js +163 -0
- package/dist/cache.d.ts +15 -0
- package/dist/cache.js +117 -0
- package/dist/claude-integration.d.ts +95 -0
- package/dist/claude-integration.js +247 -0
- package/dist/cli.d.ts +18 -0
- package/dist/cli.js +1823 -0
- package/dist/code-chunker.d.ts +12 -0
- package/dist/code-chunker.js +270 -0
- package/dist/collectors.d.ts +63 -0
- package/dist/collectors.js +617 -0
- package/dist/config.d.ts +73 -0
- package/dist/config.js +239 -0
- package/dist/embeddings.d.ts +36 -0
- package/dist/embeddings.js +124 -0
- package/dist/firewall.d.ts +133 -0
- package/dist/firewall.js +631 -0
- package/dist/hooks.d.ts +76 -0
- package/dist/hooks.js +313 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1081 -0
- package/dist/ingest.d.ts +32 -0
- package/dist/ingest.js +162 -0
- package/dist/learnings.d.ts +108 -0
- package/dist/learnings.js +714 -0
- package/dist/license-sig.d.ts +47 -0
- package/dist/license-sig.js +104 -0
- package/dist/policy.d.ts +131 -0
- package/dist/policy.js +182 -0
- package/dist/search.d.ts +11 -0
- package/dist/search.js +99 -0
- package/dist/sessions.d.ts +46 -0
- package/dist/sessions.js +153 -0
- package/examples/adapters/notion-adapter.js +108 -0
- package/examples/adapters/rss-adapter.js +76 -0
- package/package.json +87 -0
- package/skills/opscontext/SKILL.md +260 -0
package/dist/hooks.d.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { Policy } from "./policy.js";
|
|
2
|
+
/**
|
|
3
|
+
* Convert a glob with `*`, `**`, `?` into an anchored RegExp.
|
|
4
|
+
* Supports the subset of globs that policy.json paths use in practice:
|
|
5
|
+
* - `**\/` recursively matches directories
|
|
6
|
+
* - `*` matches any char except `/`
|
|
7
|
+
* - `?` matches a single char except `/`
|
|
8
|
+
* - Literal path separators and dots
|
|
9
|
+
*/
|
|
10
|
+
export declare function globToRegExp(glob: string): RegExp;
|
|
11
|
+
export declare function matchesAnyGlob(path: string, globs: string[]): boolean;
|
|
12
|
+
export interface StagedFile {
|
|
13
|
+
path: string;
|
|
14
|
+
/** Added lines only (the `+` lines from --unified=0, with the leading `+` stripped) */
|
|
15
|
+
addedLines: Array<{
|
|
16
|
+
lineNumber: number;
|
|
17
|
+
content: string;
|
|
18
|
+
}>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Read the staged diff as a structured list. Uses git CLI directly — no
|
|
22
|
+
* surprises about index state, no library to keep in sync with git.
|
|
23
|
+
*
|
|
24
|
+
* Errors from git (not a repo, no staged changes, etc.) are RE-THROWN, not
|
|
25
|
+
* swallowed. The caller decides whether "no staged changes" is fatal.
|
|
26
|
+
*/
|
|
27
|
+
export declare function getStagedFiles(repoRoot: string): StagedFile[];
|
|
28
|
+
export interface SecretViolation {
|
|
29
|
+
patternId: string;
|
|
30
|
+
severity: "block" | "warn";
|
|
31
|
+
file: string;
|
|
32
|
+
lineNumber: number;
|
|
33
|
+
/** Pattern source for audit attribution. Never the matched value. */
|
|
34
|
+
patternSource: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Apply policy.secret_patterns to a list of staged files. Returns one
|
|
38
|
+
* violation per matched line. Honors the `paths` glob scoping per pattern.
|
|
39
|
+
*
|
|
40
|
+
* IMPORTANT: We never return the matched secret value. Only the location
|
|
41
|
+
* + pattern id. This is the redaction contract.
|
|
42
|
+
*/
|
|
43
|
+
export declare function runSecretScan(policy: Policy, files: StagedFile[]): SecretViolation[];
|
|
44
|
+
export interface DocCoverageViolation {
|
|
45
|
+
severity: "block" | "warn";
|
|
46
|
+
sourcePaths: string[];
|
|
47
|
+
matchedFiles: string[];
|
|
48
|
+
requiresSection: string;
|
|
49
|
+
reason: "doc-section-not-found" | "doc-not-staged-and-section-unchanged";
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* For each doc_coverage rule, check:
|
|
53
|
+
* - did this commit touch any of the rule's source paths?
|
|
54
|
+
* - if yes, is the required doc-section file either (a) also in the staged
|
|
55
|
+
* diff, or (b) present at the expected path?
|
|
56
|
+
*
|
|
57
|
+
* "Section unchanged" detection requires reading the doc; we treat presence
|
|
58
|
+
* of the file as the floor (a real next-iteration improvement: hash the
|
|
59
|
+
* section under the anchor and require staged change to the anchor section
|
|
60
|
+
* when triggered. For now, presence + warn=non-staged is the contract).
|
|
61
|
+
*/
|
|
62
|
+
export declare function runDocCoverage(policy: Policy, files: StagedFile[], repoRoot: string): DocCoverageViolation[];
|
|
63
|
+
/**
|
|
64
|
+
* Compute a stable SHA-256 of the section content under a markdown anchor.
|
|
65
|
+
* "Section" = lines from `## Anchor` (or `### Anchor` etc.) up to the next
|
|
66
|
+
* heading at the same or higher level. Used by the next-iteration v2 check.
|
|
67
|
+
*
|
|
68
|
+
* Exposed now so the CLI can surface a stable hash for compliance evidence
|
|
69
|
+
* (e.g., "as of this commit, the firewall section is hash X").
|
|
70
|
+
*/
|
|
71
|
+
export declare function hashDocSection(filePath: string, anchor: string): string | null;
|
|
72
|
+
export declare function formatSecretViolations(violations: SecretViolation[]): string;
|
|
73
|
+
export declare function formatDocCoverageViolations(violations: DocCoverageViolation[]): string;
|
|
74
|
+
export declare function formatSecretViolationsJson(violations: SecretViolation[]): string;
|
|
75
|
+
export declare function formatDocCoverageViolationsJson(violations: DocCoverageViolation[]): string;
|
|
76
|
+
//# sourceMappingURL=hooks.d.ts.map
|
package/dist/hooks.js
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
// 🔒 LOCKED [HOOK-CHECKERS] — 2026-06-10
|
|
2
|
+
// ⛔ NEVER print the matched secret value in violation output. Print pattern
|
|
3
|
+
// id + file + line + redaction only. Leaking secrets via "helpful" error
|
|
4
|
+
// messages was a classic regression in v1.x of similar tools.
|
|
5
|
+
// ⛔ NEVER swallow git errors silently — if git isn't available, surface the
|
|
6
|
+
// failure so the user knows the gate is not actually running.
|
|
7
|
+
// WHY: This is the production enforcement path. A check that silently
|
|
8
|
+
// passes when broken is worse than no check — it ships false
|
|
9
|
+
// compliance evidence. Loud failure ≫ silent skip.
|
|
10
|
+
// FIX: To add a new gate, add a runXxx() function here, expose via the
|
|
11
|
+
// `hook` CLI subcommand, never widen the secret-redaction contract.
|
|
12
|
+
//
|
|
13
|
+
// Hook checkers — TypeScript implementations of the policy gates that the
|
|
14
|
+
// pre-commit hook (or any other PreToolUse / CI surface) invokes.
|
|
15
|
+
//
|
|
16
|
+
// Inputs are fed by helpers that read the actual git working state.
|
|
17
|
+
// Each runner returns a list of violations + a summary; the CLI maps that
|
|
18
|
+
// onto exit codes + audit events + human/machine output.
|
|
19
|
+
import { execSync } from "child_process";
|
|
20
|
+
import { existsSync, readFileSync } from "fs";
|
|
21
|
+
import { createHash } from "crypto";
|
|
22
|
+
import { join } from "path";
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Glob matching (tiny, no dep)
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
/**
|
|
27
|
+
* Convert a glob with `*`, `**`, `?` into an anchored RegExp.
|
|
28
|
+
* Supports the subset of globs that policy.json paths use in practice:
|
|
29
|
+
* - `**\/` recursively matches directories
|
|
30
|
+
* - `*` matches any char except `/`
|
|
31
|
+
* - `?` matches a single char except `/`
|
|
32
|
+
* - Literal path separators and dots
|
|
33
|
+
*/
|
|
34
|
+
export function globToRegExp(glob) {
|
|
35
|
+
let re = "^";
|
|
36
|
+
let i = 0;
|
|
37
|
+
while (i < glob.length) {
|
|
38
|
+
const c = glob[i];
|
|
39
|
+
if (c === "*") {
|
|
40
|
+
if (glob[i + 1] === "*") {
|
|
41
|
+
// `**` — match across directory separators (including zero dirs)
|
|
42
|
+
// `**/` consumes the trailing slash if present
|
|
43
|
+
if (glob[i + 2] === "/") {
|
|
44
|
+
re += "(?:.*/)?";
|
|
45
|
+
i += 3;
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
re += ".*";
|
|
49
|
+
i += 2;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
re += "[^/]*";
|
|
54
|
+
i++;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else if (c === "?") {
|
|
58
|
+
re += "[^/]";
|
|
59
|
+
i++;
|
|
60
|
+
}
|
|
61
|
+
else if (".+^$()|{}[]\\".includes(c)) {
|
|
62
|
+
re += "\\" + c;
|
|
63
|
+
i++;
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
re += c;
|
|
67
|
+
i++;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
re += "$";
|
|
71
|
+
return new RegExp(re);
|
|
72
|
+
}
|
|
73
|
+
export function matchesAnyGlob(path, globs) {
|
|
74
|
+
return globs.some((g) => globToRegExp(g).test(path));
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Read the staged diff as a structured list. Uses git CLI directly — no
|
|
78
|
+
* surprises about index state, no library to keep in sync with git.
|
|
79
|
+
*
|
|
80
|
+
* Errors from git (not a repo, no staged changes, etc.) are RE-THROWN, not
|
|
81
|
+
* swallowed. The caller decides whether "no staged changes" is fatal.
|
|
82
|
+
*/
|
|
83
|
+
export function getStagedFiles(repoRoot) {
|
|
84
|
+
const nameOutput = execSync("git diff --cached --name-only --diff-filter=ACMR", {
|
|
85
|
+
cwd: repoRoot,
|
|
86
|
+
encoding: "utf-8",
|
|
87
|
+
}).trim();
|
|
88
|
+
if (!nameOutput)
|
|
89
|
+
return [];
|
|
90
|
+
const fileNames = nameOutput.split("\n");
|
|
91
|
+
const result = [];
|
|
92
|
+
for (const file of fileNames) {
|
|
93
|
+
let diff;
|
|
94
|
+
try {
|
|
95
|
+
diff = execSync(`git diff --cached --unified=0 -- "${file.replace(/"/g, '\\"')}"`, {
|
|
96
|
+
cwd: repoRoot,
|
|
97
|
+
encoding: "utf-8",
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
// Binary file, deleted, or path that doesn't roundtrip — skip safely.
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
const addedLines = [];
|
|
105
|
+
let currentLine = 0;
|
|
106
|
+
for (const line of diff.split("\n")) {
|
|
107
|
+
// Hunk header: @@ -a,b +c,d @@
|
|
108
|
+
const hunk = line.match(/^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/);
|
|
109
|
+
if (hunk) {
|
|
110
|
+
currentLine = parseInt(hunk[1], 10);
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (line.startsWith("+++"))
|
|
114
|
+
continue;
|
|
115
|
+
if (line.startsWith("+")) {
|
|
116
|
+
addedLines.push({ lineNumber: currentLine, content: line.slice(1) });
|
|
117
|
+
currentLine++;
|
|
118
|
+
}
|
|
119
|
+
else if (line.startsWith(" ") || line.startsWith("---")) {
|
|
120
|
+
// Context or header — should not appear with --unified=0 but be safe
|
|
121
|
+
}
|
|
122
|
+
else if (line.startsWith("-")) {
|
|
123
|
+
// Removed lines don't advance the new-file line counter
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
currentLine++;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
result.push({ path: file, addedLines });
|
|
130
|
+
}
|
|
131
|
+
return result;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Apply policy.secret_patterns to a list of staged files. Returns one
|
|
135
|
+
* violation per matched line. Honors the `paths` glob scoping per pattern.
|
|
136
|
+
*
|
|
137
|
+
* IMPORTANT: We never return the matched secret value. Only the location
|
|
138
|
+
* + pattern id. This is the redaction contract.
|
|
139
|
+
*/
|
|
140
|
+
export function runSecretScan(policy, files) {
|
|
141
|
+
const violations = [];
|
|
142
|
+
for (const pattern of policy.secret_patterns) {
|
|
143
|
+
const re = new RegExp(pattern.pattern);
|
|
144
|
+
for (const file of files) {
|
|
145
|
+
if (pattern.paths?.length && !matchesAnyGlob(file.path, pattern.paths))
|
|
146
|
+
continue;
|
|
147
|
+
for (const line of file.addedLines) {
|
|
148
|
+
if (re.test(line.content)) {
|
|
149
|
+
violations.push({
|
|
150
|
+
patternId: pattern.id,
|
|
151
|
+
severity: pattern.severity,
|
|
152
|
+
file: file.path,
|
|
153
|
+
lineNumber: line.lineNumber,
|
|
154
|
+
patternSource: pattern.pattern,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return violations;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* For each doc_coverage rule, check:
|
|
164
|
+
* - did this commit touch any of the rule's source paths?
|
|
165
|
+
* - if yes, is the required doc-section file either (a) also in the staged
|
|
166
|
+
* diff, or (b) present at the expected path?
|
|
167
|
+
*
|
|
168
|
+
* "Section unchanged" detection requires reading the doc; we treat presence
|
|
169
|
+
* of the file as the floor (a real next-iteration improvement: hash the
|
|
170
|
+
* section under the anchor and require staged change to the anchor section
|
|
171
|
+
* when triggered. For now, presence + warn=non-staged is the contract).
|
|
172
|
+
*/
|
|
173
|
+
export function runDocCoverage(policy, files, repoRoot) {
|
|
174
|
+
const stagedSet = new Set(files.map((f) => f.path));
|
|
175
|
+
const violations = [];
|
|
176
|
+
for (const rule of policy.doc_coverage) {
|
|
177
|
+
const matchedFiles = files
|
|
178
|
+
.map((f) => f.path)
|
|
179
|
+
.filter((p) => matchesAnyGlob(p, rule.paths));
|
|
180
|
+
if (matchedFiles.length === 0)
|
|
181
|
+
continue; // rule did not fire
|
|
182
|
+
const [docPath /*, anchor*/] = rule.requires_section.split("#");
|
|
183
|
+
const absoluteDocPath = join(repoRoot, docPath);
|
|
184
|
+
if (!existsSync(absoluteDocPath)) {
|
|
185
|
+
violations.push({
|
|
186
|
+
severity: rule.severity,
|
|
187
|
+
sourcePaths: rule.paths,
|
|
188
|
+
matchedFiles,
|
|
189
|
+
requiresSection: rule.requires_section,
|
|
190
|
+
reason: "doc-section-not-found",
|
|
191
|
+
});
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
// If the doc file IS staged, the commit author is updating it — pass.
|
|
195
|
+
if (stagedSet.has(docPath))
|
|
196
|
+
continue;
|
|
197
|
+
// Doc exists but is not in this commit. v1 contract: warn-or-block based
|
|
198
|
+
// on severity. Next iteration: hash the section under the anchor and
|
|
199
|
+
// require staged change to that section's lines specifically.
|
|
200
|
+
violations.push({
|
|
201
|
+
severity: rule.severity,
|
|
202
|
+
sourcePaths: rule.paths,
|
|
203
|
+
matchedFiles,
|
|
204
|
+
requiresSection: rule.requires_section,
|
|
205
|
+
reason: "doc-not-staged-and-section-unchanged",
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
return violations;
|
|
209
|
+
}
|
|
210
|
+
// ---------------------------------------------------------------------------
|
|
211
|
+
// Doc section hash (foundation for future v2 staged-section-change check)
|
|
212
|
+
// ---------------------------------------------------------------------------
|
|
213
|
+
/**
|
|
214
|
+
* Compute a stable SHA-256 of the section content under a markdown anchor.
|
|
215
|
+
* "Section" = lines from `## Anchor` (or `### Anchor` etc.) up to the next
|
|
216
|
+
* heading at the same or higher level. Used by the next-iteration v2 check.
|
|
217
|
+
*
|
|
218
|
+
* Exposed now so the CLI can surface a stable hash for compliance evidence
|
|
219
|
+
* (e.g., "as of this commit, the firewall section is hash X").
|
|
220
|
+
*/
|
|
221
|
+
export function hashDocSection(filePath, anchor) {
|
|
222
|
+
if (!existsSync(filePath))
|
|
223
|
+
return null;
|
|
224
|
+
const lines = readFileSync(filePath, "utf-8").split("\n");
|
|
225
|
+
const slug = (s) => s.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
226
|
+
let inSection = false;
|
|
227
|
+
let sectionLevel = -1;
|
|
228
|
+
const section = [];
|
|
229
|
+
for (const line of lines) {
|
|
230
|
+
const heading = line.match(/^(#+)\s+(.+?)\s*$/);
|
|
231
|
+
if (heading) {
|
|
232
|
+
const level = heading[1].length;
|
|
233
|
+
const headSlug = slug(heading[2]);
|
|
234
|
+
if (!inSection && headSlug === slug(anchor)) {
|
|
235
|
+
inSection = true;
|
|
236
|
+
sectionLevel = level;
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
if (inSection && level <= sectionLevel) {
|
|
240
|
+
// Hit a sibling or higher heading — section ended
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if (inSection)
|
|
245
|
+
section.push(line);
|
|
246
|
+
}
|
|
247
|
+
if (!inSection)
|
|
248
|
+
return null;
|
|
249
|
+
return createHash("sha256").update(section.join("\n")).digest("hex");
|
|
250
|
+
}
|
|
251
|
+
// ---------------------------------------------------------------------------
|
|
252
|
+
// Formatters
|
|
253
|
+
// ---------------------------------------------------------------------------
|
|
254
|
+
export function formatSecretViolations(violations) {
|
|
255
|
+
if (violations.length === 0)
|
|
256
|
+
return "✅ No policy secret patterns matched.";
|
|
257
|
+
const lines = [];
|
|
258
|
+
const blocking = violations.filter((v) => v.severity === "block").length;
|
|
259
|
+
const warning = violations.filter((v) => v.severity === "warn").length;
|
|
260
|
+
lines.push(`🔒 SECRET POLICY: ${violations.length} violation(s) — ${blocking} blocking, ${warning} warning(s).`);
|
|
261
|
+
for (const v of violations) {
|
|
262
|
+
lines.push(` [${v.severity}] ${v.patternId} at ${v.file}:${v.lineNumber}`);
|
|
263
|
+
}
|
|
264
|
+
return lines.join("\n");
|
|
265
|
+
}
|
|
266
|
+
export function formatDocCoverageViolations(violations) {
|
|
267
|
+
if (violations.length === 0)
|
|
268
|
+
return "✅ All doc-coverage rules satisfied.";
|
|
269
|
+
const lines = [];
|
|
270
|
+
const blocking = violations.filter((v) => v.severity === "block").length;
|
|
271
|
+
const warning = violations.filter((v) => v.severity === "warn").length;
|
|
272
|
+
lines.push(`📄 DOC COVERAGE: ${violations.length} violation(s) — ${blocking} blocking, ${warning} warning(s).`);
|
|
273
|
+
for (const v of violations) {
|
|
274
|
+
const reason = v.reason === "doc-section-not-found"
|
|
275
|
+
? "doc file does not exist"
|
|
276
|
+
: "doc not staged in this commit (and section content unchanged)";
|
|
277
|
+
lines.push(` [${v.severity}] ${v.matchedFiles.join(", ")} → ${v.requiresSection} (${reason})`);
|
|
278
|
+
}
|
|
279
|
+
return lines.join("\n");
|
|
280
|
+
}
|
|
281
|
+
// ---------------------------------------------------------------------------
|
|
282
|
+
// Machine-readable output (for CI pipelines)
|
|
283
|
+
// ---------------------------------------------------------------------------
|
|
284
|
+
export function formatSecretViolationsJson(violations) {
|
|
285
|
+
return JSON.stringify({
|
|
286
|
+
check: "secret-scan",
|
|
287
|
+
violations_total: violations.length,
|
|
288
|
+
blocking: violations.filter((v) => v.severity === "block").length,
|
|
289
|
+
warnings: violations.filter((v) => v.severity === "warn").length,
|
|
290
|
+
violations: violations.map((v) => ({
|
|
291
|
+
pattern_id: v.patternId,
|
|
292
|
+
severity: v.severity,
|
|
293
|
+
file: v.file,
|
|
294
|
+
line: v.lineNumber,
|
|
295
|
+
})),
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
export function formatDocCoverageViolationsJson(violations) {
|
|
299
|
+
return JSON.stringify({
|
|
300
|
+
check: "doc-coverage",
|
|
301
|
+
violations_total: violations.length,
|
|
302
|
+
blocking: violations.filter((v) => v.severity === "block").length,
|
|
303
|
+
warnings: violations.filter((v) => v.severity === "warn").length,
|
|
304
|
+
violations: violations.map((v) => ({
|
|
305
|
+
severity: v.severity,
|
|
306
|
+
source_paths: v.sourcePaths,
|
|
307
|
+
matched_files: v.matchedFiles,
|
|
308
|
+
requires_section: v.requiresSection,
|
|
309
|
+
reason: v.reason,
|
|
310
|
+
})),
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
//# sourceMappingURL=hooks.js.map
|
package/dist/index.d.ts
ADDED