depcurfew 1.2.0 → 1.4.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/README.md +398 -80
- package/dist/cli.js +20 -3
- package/dist/index.d.ts +1458 -302
- package/dist/index.js +3 -1
- package/dist/io-eBMDYKV0.js +52 -0
- package/dist/{run-BBZUJJq-.js → run-NcyQxpTd.js} +34678 -13162
- package/package.json +16 -8
- package/schema.json +42 -11
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
import { Readable, Writable } from "node:stream";
|
|
2
|
+
type ToolId = 'npm' | 'pnpm' | 'yarn' | 'bun' | 'deno' | 'uv' | 'pip' | 'poetry' | 'pdm' | 'pipenv' | 'composer' | 'renovate' | 'dependabot';
|
|
2
3
|
type ToolKind = 'package-manager' | 'update-bot';
|
|
3
4
|
/** The registry a package manager installs from; package names in the policy are compared per ecosystem. */
|
|
4
|
-
type Ecosystem = 'npm' | 'pypi';
|
|
5
|
+
type Ecosystem = 'npm' | 'pypi' | 'packagist';
|
|
5
6
|
interface Evidence$1<T> {
|
|
6
7
|
value: T;
|
|
7
8
|
/** URL of official documentation or changelog. */
|
|
@@ -111,6 +112,7 @@ interface UserDirectory {
|
|
|
111
112
|
env: string;
|
|
112
113
|
absolute?: boolean;
|
|
113
114
|
relativeToCwd?: boolean;
|
|
115
|
+
emptyCounts?: boolean;
|
|
114
116
|
} | 'home';
|
|
115
117
|
/** Path below it, with / separators; empty for the directory itself. */
|
|
116
118
|
path: string;
|
|
@@ -179,10 +181,35 @@ interface UserVariable {
|
|
|
179
181
|
/** The file the tool reads when the variable is unset, from the home directory (~/.npmrc): naming it changes nothing. */
|
|
180
182
|
defaultPath?: string;
|
|
181
183
|
}
|
|
184
|
+
/**
|
|
185
|
+
* Directories of the user file that a library the tool calls decides, by the library's version (platformdirs for
|
|
186
|
+
* Poetry and PDM on macOS). depcurfew reads that version from the library's .dist-info next to the tool's own.
|
|
187
|
+
*/
|
|
188
|
+
interface LibraryDirectories {
|
|
189
|
+
/** The distribution name of the library, as its .dist-info folder spells it (platformdirs). */
|
|
190
|
+
library: string;
|
|
191
|
+
/** The platforms whose directories it decides; the other platforms keep those of `files`. */
|
|
192
|
+
platforms: readonly UserPlatform[];
|
|
193
|
+
/** The directories per library version, oldest first; they replace the directories of `files` on those platforms. */
|
|
194
|
+
directories: Versioned<readonly UserDirectory[]>;
|
|
195
|
+
}
|
|
196
|
+
/** The unit of the release-age setting in the user file, when it differs from the tool's own (`cooldown.unit`). */
|
|
197
|
+
interface UserCooldownUnit {
|
|
198
|
+
key: string;
|
|
199
|
+
unit: 'seconds' | 'minutes' | 'hours' | 'days';
|
|
200
|
+
/** Only whole numbers count; depcurfew rounds up, to the longer delay. */
|
|
201
|
+
wholeNumbers: boolean;
|
|
202
|
+
/** What the tool does with any other value: ignores it without a word, or stops. */
|
|
203
|
+
otherValues: 'ignored' | 'fail';
|
|
204
|
+
}
|
|
182
205
|
/** The tool's user-level configuration, for `depcurfew apply --user` and `doctor --user`. */
|
|
183
206
|
interface UserConfigKnowledge {
|
|
184
207
|
/** The user file per version, oldest first; null for versions without a user file. */
|
|
185
208
|
files: Fact<Versioned<UserFile | null>>;
|
|
209
|
+
/** Directories of the user file that depend on the version of a library the tool calls. */
|
|
210
|
+
libraryDirectories?: Fact<LibraryDirectories>;
|
|
211
|
+
/** The unit of the release-age setting there, when it is not the unit of the tool's project setting. */
|
|
212
|
+
cooldownUnit?: Fact<UserCooldownUnit>;
|
|
186
213
|
/** Settings depcurfew can write there. */
|
|
187
214
|
settings: Fact<readonly UserSetting[]>;
|
|
188
215
|
/** What a version does with a key it does not know in the user file: ignore it, warn, or fail. */
|
|
@@ -211,6 +238,32 @@ interface ToolKnowledge {
|
|
|
211
238
|
/** Further sourced facts that matter for generators and `doctor`, keyed by a short name. */
|
|
212
239
|
extra?: Readonly<Record<string, Fact<unknown>>>;
|
|
213
240
|
}
|
|
241
|
+
/**
|
|
242
|
+
* The values with which a switch weakens a protection: `delay` is a release-age delay in the tool's unit
|
|
243
|
+
* (cooldown.unit) or a duration string (durationStrings), which depcurfew compares with the policy; `any` is every
|
|
244
|
+
* value; otherwise the values listed, or every value except those (fail closed). A flag without a value is "true".
|
|
245
|
+
*/
|
|
246
|
+
type OverrideValues = 'delay' | 'any' | readonly string[] | {
|
|
247
|
+
readonly except: readonly string[];
|
|
248
|
+
};
|
|
249
|
+
/** An option or environment variable of one command that weakens what depcurfew apply configured. */
|
|
250
|
+
interface OverrideSwitch {
|
|
251
|
+
/** The option as the scanner resolves it (--name) or the variable, compared without regard to case. */
|
|
252
|
+
name: string;
|
|
253
|
+
/**
|
|
254
|
+
* The release-age delay, the rule on dependency install scripts, or a protection of the package manager that the
|
|
255
|
+
* policy does not manage (Composer's blocking of versions flagged as malware), which always counts (decision D-133).
|
|
256
|
+
*/
|
|
257
|
+
weakens: 'delay' | 'scripts' | 'protection';
|
|
258
|
+
values: OverrideValues;
|
|
259
|
+
/** protection: what it turns off, for messages ("turns off the blocking of versions flagged as malware"). */
|
|
260
|
+
effect?: string;
|
|
261
|
+
/**
|
|
262
|
+
* protection: what that protection does, as a clause of its own, when it covers less than every package the command
|
|
263
|
+
* fetches ("secure-http lets Composer download only over HTTPS, …"; decision D-142).
|
|
264
|
+
*/
|
|
265
|
+
protects?: string;
|
|
266
|
+
}
|
|
214
267
|
/** A tool that environment facts refer to: Node.js, Python or a package manager. */
|
|
215
268
|
type RuntimeOrTool = 'node' | 'python' | ToolId;
|
|
216
269
|
interface PackageManagerFieldKnowledge {
|
|
@@ -287,6 +340,18 @@ interface NpmRegistryKnowledge {
|
|
|
287
340
|
installScripts: readonly string[];
|
|
288
341
|
}
|
|
289
342
|
/** How the Python interpreter runs a module (python -m pip) or a command string. */
|
|
343
|
+
/**
|
|
344
|
+
* A Python virtual environment as venv creates it: `marker` is the file in the environment's directory, and the
|
|
345
|
+
* activation scripts sit in a binary directory right under it, per platform (bin on POSIX, Scripts on Windows).
|
|
346
|
+
*/
|
|
347
|
+
interface VirtualEnvironmentKnowledge {
|
|
348
|
+
marker: string;
|
|
349
|
+
scripts: readonly {
|
|
350
|
+
platform: 'posix' | 'windows';
|
|
351
|
+
directory: string;
|
|
352
|
+
names: readonly string[];
|
|
353
|
+
}[];
|
|
354
|
+
}
|
|
290
355
|
interface PythonInterpreterKnowledge {
|
|
291
356
|
/** Executable names; versioned names such as python3.12 are the same. */
|
|
292
357
|
executables: readonly string[];
|
|
@@ -331,6 +396,71 @@ interface ProjectStatusKnowledge {
|
|
|
331
396
|
/** Installers may warn about the project. */
|
|
332
397
|
warned: readonly string[];
|
|
333
398
|
}
|
|
399
|
+
/** The batch query of the OSV.dev API (decision D-129): where to send it, how its JSON is spelled, and its limit. */
|
|
400
|
+
interface OsvApiKnowledge {
|
|
401
|
+
/** Base URL of the API. */
|
|
402
|
+
url: string;
|
|
403
|
+
queryBatchPath: string;
|
|
404
|
+
/** GET: one record by id ({id}). */
|
|
405
|
+
vulnerabilityPath: string;
|
|
406
|
+
/** The web page of a record ({id}). */
|
|
407
|
+
recordPage: string;
|
|
408
|
+
queriesField: string;
|
|
409
|
+
resultsField: string;
|
|
410
|
+
vulnsField: string;
|
|
411
|
+
idField: string;
|
|
412
|
+
/** A result with more pages carries this token; the next request sends it back as pageTokenField of that query. */
|
|
413
|
+
nextPageTokenField: string;
|
|
414
|
+
pageTokenField: string;
|
|
415
|
+
/** Most queries in one batch request. */
|
|
416
|
+
maxQueriesPerBatch: number;
|
|
417
|
+
}
|
|
418
|
+
/** When a batch result is paged: more vulnerabilities than these for one query, or for the whole batch. */
|
|
419
|
+
interface OsvPagingKnowledge {
|
|
420
|
+
perQuery: number;
|
|
421
|
+
perBatch: number;
|
|
422
|
+
}
|
|
423
|
+
/** What OSV.dev promises about its service: no rate limit (null), how fresh the data is, the largest response over HTTP/1.1. */
|
|
424
|
+
interface OsvServiceKnowledge {
|
|
425
|
+
rateLimit: number | null;
|
|
426
|
+
freshnessMinutes: number;
|
|
427
|
+
freshnessShare: number;
|
|
428
|
+
responseLimitBytes: number;
|
|
429
|
+
}
|
|
430
|
+
/** Withdrawn records: the field that marks them, and which responses still carry them. */
|
|
431
|
+
interface OsvWithdrawnKnowledge {
|
|
432
|
+
field: string;
|
|
433
|
+
inQueryResponses: boolean;
|
|
434
|
+
inRecordResponses: boolean;
|
|
435
|
+
}
|
|
436
|
+
/** The records of known malicious packages in OSV.dev: their id prefix and where they come from. */
|
|
437
|
+
interface OsvMaliciousKnowledge {
|
|
438
|
+
idPrefix: string;
|
|
439
|
+
database: string;
|
|
440
|
+
license: string;
|
|
441
|
+
}
|
|
442
|
+
/** The characters a record id may have (a regular expression for the whole id), from the OSV schema (decision D-135). */
|
|
443
|
+
interface OsvIdKnowledge {
|
|
444
|
+
pattern: string;
|
|
445
|
+
}
|
|
446
|
+
/** OSV.dev, asked about known malicious package versions with --osv (decision D-129). */
|
|
447
|
+
interface OsvKnowledge {
|
|
448
|
+
api: Fact<OsvApiKnowledge>;
|
|
449
|
+
paging: Fact<OsvPagingKnowledge>;
|
|
450
|
+
ids: Fact<OsvIdKnowledge>;
|
|
451
|
+
service: Fact<OsvServiceKnowledge>;
|
|
452
|
+
withdrawn: Fact<OsvWithdrawnKnowledge>;
|
|
453
|
+
malicious: Fact<OsvMaliciousKnowledge>;
|
|
454
|
+
/** The OSV ecosystem name of each ecosystem of depcurfew (npm, pypi, ...). */
|
|
455
|
+
ecosystems: Fact<Readonly<Record<string, string>>>;
|
|
456
|
+
/** How the API compares the version of a query with the versions of a record, by depcurfew's ecosystem (decision D-142). */
|
|
457
|
+
versionMatching: Fact<Readonly<Record<string, OsvVersionMatchingKnowledge>>>;
|
|
458
|
+
}
|
|
459
|
+
/** What OSV.dev leaves out of a version before it compares it. */
|
|
460
|
+
interface OsvVersionMatchingKnowledge {
|
|
461
|
+
/** A leading character left out of both versions (Packagist: v and V). */
|
|
462
|
+
ignoredPrefixes: readonly string[];
|
|
463
|
+
}
|
|
334
464
|
/** Claude Code hooks that run before a tool call and can block it (used by depcurfew check --hook claude-code). */
|
|
335
465
|
interface ClaudeCodeHooksKnowledge {
|
|
336
466
|
/** Project settings file that can be committed. */
|
|
@@ -377,11 +507,172 @@ interface ClaudeCodeHooksKnowledge {
|
|
|
377
507
|
args: string;
|
|
378
508
|
};
|
|
379
509
|
}
|
|
510
|
+
/** additionalContext in hookSpecificOutput of a PreToolUse hook: text Claude reads next to the tool result. */
|
|
511
|
+
interface ClaudeCodeHookContextKnowledge {
|
|
512
|
+
field: string;
|
|
513
|
+
/** Whether the field works without a permission decision (which leaves the call to the normal permission flow). */
|
|
514
|
+
withoutDecision: boolean;
|
|
515
|
+
/** Longer text is written to a file and Claude gets its path with a preview. */
|
|
516
|
+
inlineLimit: number;
|
|
517
|
+
}
|
|
518
|
+
/** The SessionStart hook that can install the project, and what happens to a guard whose script is missing. */
|
|
519
|
+
interface ClaudeCodeSessionStartKnowledge {
|
|
520
|
+
event: string;
|
|
521
|
+
/** The matchers for a new and a resumed session. */
|
|
522
|
+
matchers: readonly string[];
|
|
523
|
+
/** The variable a shell-form hook command reads for the project root. */
|
|
524
|
+
projectDirVariable: string;
|
|
525
|
+
/** Whether Claude's first response waits for SessionStart hooks to finish. */
|
|
526
|
+
firstResponseWaits: boolean;
|
|
527
|
+
/** What a PreToolUse hook with onFailure "block" does when its script is missing. */
|
|
528
|
+
missingScriptWithBlock: 'blocks';
|
|
529
|
+
/** The part of the error Claude Code shows then, before the output of the command that failed. */
|
|
530
|
+
blockedNotice: string;
|
|
531
|
+
/** Whether a cloud environment's setup script runs before Claude Code starts. */
|
|
532
|
+
cloudSetupScriptRunsBeforeClaudeCode: boolean;
|
|
533
|
+
}
|
|
380
534
|
/** A Claude Code tool that runs shell commands, by the name a matcher and the event use, and the shell it runs them in. */
|
|
381
535
|
interface ClaudeCodeShellTool {
|
|
382
536
|
tool: string;
|
|
383
537
|
shell: 'bash' | 'powershell';
|
|
384
538
|
}
|
|
539
|
+
/** A shell tool call that carries another input in place of the command (a Monitor WebSocket watch). */
|
|
540
|
+
interface ClaudeCodeCommandlessInputKnowledge {
|
|
541
|
+
tool: string;
|
|
542
|
+
/** The field of the event, next to the command field, that takes the place of the command. */
|
|
543
|
+
input: string;
|
|
544
|
+
}
|
|
545
|
+
/** Model Context Protocol revisions: the legacy ones open a session with initialize, the modern ones carry the version on every request. */
|
|
546
|
+
interface McpRevisionsKnowledge {
|
|
547
|
+
/** Newest first. */
|
|
548
|
+
legacy: readonly string[];
|
|
549
|
+
/** Newest first. */
|
|
550
|
+
modern: readonly string[];
|
|
551
|
+
}
|
|
552
|
+
/** The initialize handshake of the legacy revisions. */
|
|
553
|
+
interface McpLifecycleKnowledge {
|
|
554
|
+
initialize: string;
|
|
555
|
+
initialized: string;
|
|
556
|
+
ping: string;
|
|
557
|
+
/** Fields of the initialize request and result. */
|
|
558
|
+
request: {
|
|
559
|
+
protocolVersion: string;
|
|
560
|
+
capabilities: string;
|
|
561
|
+
clientInfo: string;
|
|
562
|
+
};
|
|
563
|
+
result: {
|
|
564
|
+
protocolVersion: string;
|
|
565
|
+
capabilities: string;
|
|
566
|
+
serverInfo: string;
|
|
567
|
+
instructions: string;
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
/** What a modern (stateless) request and result carry in `_meta`, and the discovery method. */
|
|
571
|
+
interface McpStatelessKnowledge {
|
|
572
|
+
metaField: string;
|
|
573
|
+
protocolVersion: string;
|
|
574
|
+
clientCapabilities: string;
|
|
575
|
+
clientInfo: string;
|
|
576
|
+
serverInfo: string;
|
|
577
|
+
discover: string;
|
|
578
|
+
/** The fields of a discovery result. */
|
|
579
|
+
discoverResult: {
|
|
580
|
+
supportedVersions: string;
|
|
581
|
+
capabilities: string;
|
|
582
|
+
instructions: string;
|
|
583
|
+
};
|
|
584
|
+
/** Every modern result has this field, with `complete` for a finished result. */
|
|
585
|
+
resultType: {
|
|
586
|
+
field: string;
|
|
587
|
+
complete: string;
|
|
588
|
+
};
|
|
589
|
+
/** Caching hints that discovery and tools/list results must carry. */
|
|
590
|
+
caching: {
|
|
591
|
+
ttl: string;
|
|
592
|
+
scope: string;
|
|
593
|
+
publicScope: string;
|
|
594
|
+
};
|
|
595
|
+
/** The data of an unsupported-version error. */
|
|
596
|
+
unsupportedVersionData: {
|
|
597
|
+
supported: string;
|
|
598
|
+
requested: string;
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
/** Tools: the methods and the fields of a tool and of a call result. */
|
|
602
|
+
interface McpToolsKnowledge {
|
|
603
|
+
list: string;
|
|
604
|
+
call: string;
|
|
605
|
+
capability: string;
|
|
606
|
+
/** The revision that added each optional field of a tool or a result; older clients do not know them. */
|
|
607
|
+
fieldsSince: {
|
|
608
|
+
annotations: string;
|
|
609
|
+
title: string;
|
|
610
|
+
structuredContent: string;
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
interface McpCancellationKnowledge {
|
|
614
|
+
method: string;
|
|
615
|
+
requestId: string;
|
|
616
|
+
}
|
|
617
|
+
/** The JSON-RPC error codes the server uses. */
|
|
618
|
+
interface McpErrorCodesKnowledge {
|
|
619
|
+
parseError: number;
|
|
620
|
+
invalidRequest: number;
|
|
621
|
+
methodNotFound: number;
|
|
622
|
+
invalidParams: number;
|
|
623
|
+
internalError: number;
|
|
624
|
+
unsupportedProtocolVersion: number;
|
|
625
|
+
}
|
|
626
|
+
/** The id of an error response: required (a string or a number) in the schemas before `optionalSince`, optional from it. */
|
|
627
|
+
interface McpErrorIdKnowledge {
|
|
628
|
+
optionalSince: string;
|
|
629
|
+
}
|
|
630
|
+
/** stdio: messages are UTF-8 JSON-RPC, one per line, without embedded newlines; stderr is free for logs. */
|
|
631
|
+
interface McpStdioKnowledge {
|
|
632
|
+
delimiter: string;
|
|
633
|
+
/** Revisions whose servers must accept JSON-RPC batches (arrays). */
|
|
634
|
+
batchRevisions: readonly string[];
|
|
635
|
+
}
|
|
636
|
+
/** How Claude Code is told about a stdio server: `claude mcp add` and the project file. */
|
|
637
|
+
interface McpClaudeCodeKnowledge {
|
|
638
|
+
/** claude mcp add [options] <name> -- <command> [args...] */
|
|
639
|
+
addCommand: readonly string[];
|
|
640
|
+
separator: string;
|
|
641
|
+
scopeOption: string;
|
|
642
|
+
projectScope: string;
|
|
643
|
+
transportOption: string;
|
|
644
|
+
stdioTransport: string;
|
|
645
|
+
projectFile: string;
|
|
646
|
+
serversKey: string;
|
|
647
|
+
/** Set in the server's environment to the project root; in command and args it needs a default. */
|
|
648
|
+
projectDirVariable: string;
|
|
649
|
+
projectDirReference: string;
|
|
650
|
+
}
|
|
651
|
+
/** Where Claude Desktop keeps its configuration, per platform, and the key of its servers. */
|
|
652
|
+
interface McpClaudeDesktopKnowledge {
|
|
653
|
+
configFiles: {
|
|
654
|
+
darwin: string;
|
|
655
|
+
win32: string;
|
|
656
|
+
linux: string;
|
|
657
|
+
};
|
|
658
|
+
serversKey: string;
|
|
659
|
+
}
|
|
660
|
+
/** VS Code's own format (.vscode/mcp.json) and its command line. */
|
|
661
|
+
interface McpVsCodeKnowledge {
|
|
662
|
+
file: string;
|
|
663
|
+
serversKey: string;
|
|
664
|
+
typeField: string;
|
|
665
|
+
stdioType: string;
|
|
666
|
+
cwdField: string;
|
|
667
|
+
workspaceFolder: string;
|
|
668
|
+
/** code --add-mcp '{"name": …, …}' adds a server to the user profile. */
|
|
669
|
+
addOption: string;
|
|
670
|
+
}
|
|
671
|
+
interface McpClientsKnowledge {
|
|
672
|
+
claudeCode: Fact<McpClaudeCodeKnowledge>;
|
|
673
|
+
claudeDesktop: Fact<McpClaudeDesktopKnowledge>;
|
|
674
|
+
vscode: Fact<McpVsCodeKnowledge>;
|
|
675
|
+
}
|
|
385
676
|
/**
|
|
386
677
|
* Annotation workflow commands: `::{command} {property}={value},…::{message}` on a line of its own. Values are
|
|
387
678
|
* escaped by replacing each character listed in `escape` (in order), properties also with `escapeProperty`.
|
|
@@ -408,13 +699,32 @@ interface AnnotationLimitsKnowledge {
|
|
|
408
699
|
/** The Markdown job summary of a step: the environment variable naming its file and its size limit. */
|
|
409
700
|
/** Creating a check run with the workflow token, so that the result shows in the pull request's Checks tab. */
|
|
410
701
|
interface CheckRunsKnowledge {
|
|
411
|
-
/**
|
|
412
|
-
|
|
702
|
+
/**
|
|
703
|
+
* Paths with {repository} as owner/name: create (POST), update (PATCH, {id} the check run), and list the check runs
|
|
704
|
+
* of a commit (GET, {ref} a SHA), which takes the query parameters checkName and filter (with the value latest).
|
|
705
|
+
*/
|
|
706
|
+
endpoints: {
|
|
707
|
+
create: string;
|
|
708
|
+
update: string;
|
|
709
|
+
list: string;
|
|
710
|
+
};
|
|
711
|
+
query: {
|
|
712
|
+
checkName: string;
|
|
713
|
+
filter: string;
|
|
714
|
+
latest: string;
|
|
715
|
+
};
|
|
413
716
|
apiVersion: string;
|
|
414
|
-
/** The workflow permission the token needs. */
|
|
717
|
+
/** The workflow permission the token needs; it covers the list endpoint too (write includes read). */
|
|
415
718
|
permission: Record<string, string>;
|
|
416
719
|
/** Limit of output.summary, in characters. */
|
|
417
720
|
summaryMaxLength: number;
|
|
721
|
+
/** output.annotations: at most this many per request; further ones are appended by update requests. */
|
|
722
|
+
annotationsPerRequest: number;
|
|
723
|
+
/** annotation_level for each doctor level. */
|
|
724
|
+
annotationLevels: Record<'error' | 'warning' | 'note', string>;
|
|
725
|
+
/** Limits of an annotation: title in characters, message in bytes (64 KB, taken as 64000). */
|
|
726
|
+
annotationTitleMaxLength: number;
|
|
727
|
+
annotationMessageMaxBytes: number;
|
|
418
728
|
/** Variables the runner sets: API URL, owner/name, commit and the event payload file. */
|
|
419
729
|
variables: {
|
|
420
730
|
apiUrl: string;
|
|
@@ -456,12 +766,194 @@ interface GitIgnoreCaseKnowledge {
|
|
|
456
766
|
/** Values read as true (case-insensitive); a key without a value is true too. */
|
|
457
767
|
trueValues: readonly string[];
|
|
458
768
|
}
|
|
769
|
+
/** The file in which Corepack records the version of each package manager it runs outside a project. */
|
|
770
|
+
interface CorepackDefaultsKnowledge {
|
|
771
|
+
/** The first Corepack version with this layout. */
|
|
772
|
+
since: string;
|
|
773
|
+
/** Where the file is: the first directory that applies is used. */
|
|
774
|
+
locations: readonly UserLocation[];
|
|
775
|
+
}
|
|
776
|
+
/** The version of a tool each release of a runtime bundles, for one release line (Node.js 22, CPython 3.12). */
|
|
777
|
+
interface BundledVersionsLine {
|
|
778
|
+
/** The release line: 22 for Node.js, 3.12 for CPython. */
|
|
779
|
+
line: string;
|
|
780
|
+
/** The newest release of the line on the day the fact was verified. */
|
|
781
|
+
latest: string;
|
|
782
|
+
/** [first release, bundled version]: the first release of the line that bundles each version, oldest first. */
|
|
783
|
+
bundles: readonly (readonly [string, string])[];
|
|
784
|
+
}
|
|
785
|
+
/** One Node.js release line in the release schedule. */
|
|
786
|
+
interface ReleaseLineSchedule {
|
|
787
|
+
line: number;
|
|
788
|
+
/** The first release (YYYY-MM-DD). */
|
|
789
|
+
start: string;
|
|
790
|
+
/** When the line became LTS; absent for lines that never do. */
|
|
791
|
+
lts?: string;
|
|
792
|
+
/** End of life: no more releases, security fixes included. */
|
|
793
|
+
end: string;
|
|
794
|
+
/** The LTS codename in lower case, as nvm, setup-node and the node image tags write it. */
|
|
795
|
+
codename?: string;
|
|
796
|
+
}
|
|
797
|
+
/** Names of moving Node.js versions in a version file or a setup-node input. */
|
|
798
|
+
interface NodeVersionNamesKnowledge {
|
|
799
|
+
/** Names of the newest release. */
|
|
800
|
+
newest: readonly string[];
|
|
801
|
+
/** The name of the newest LTS line. */
|
|
802
|
+
newestLts: string;
|
|
803
|
+
/** Followed by a codename: that LTS line (lts/jod). */
|
|
804
|
+
ltsPrefix: string;
|
|
805
|
+
/** Followed by a number n: the n-th LTS line before the newest one (lts/-1); absent when not supported. */
|
|
806
|
+
previousLtsPrefix?: string;
|
|
807
|
+
}
|
|
808
|
+
interface NvmrcKnowledge extends NodeVersionNamesKnowledge {
|
|
809
|
+
file: string;
|
|
810
|
+
/** Starts a comment that runs to the end of the line. */
|
|
811
|
+
comment: string;
|
|
812
|
+
}
|
|
813
|
+
/** How setup-node reads the file named by node-version-file. */
|
|
814
|
+
interface SetupNodeVersionFileKnowledge {
|
|
815
|
+
/** A JSON manifest whose keys are read in order: dotted paths, devEngines.runtime means its entry named node. */
|
|
816
|
+
packageJson: string;
|
|
817
|
+
packageJsonOrder: readonly string[];
|
|
818
|
+
/** Any other file: the first line that matches (multiline), group "version"; otherwise the whole text, trimmed. */
|
|
819
|
+
textPattern: string;
|
|
820
|
+
}
|
|
821
|
+
/** How an image reference names the registry, the namespace and the tag when they are left out. */
|
|
822
|
+
interface ImageReferenceKnowledge {
|
|
823
|
+
defaultHost: string;
|
|
824
|
+
/** Hosts that mean the default registry. */
|
|
825
|
+
hosts: readonly string[];
|
|
826
|
+
/** The namespace of the official images, implied when a Docker Hub name has no namespace. */
|
|
827
|
+
officialNamespace: string;
|
|
828
|
+
defaultTag: string;
|
|
829
|
+
}
|
|
830
|
+
/** The Dockerfile syntax the reader relies on. */
|
|
831
|
+
interface DockerfileSyntaxKnowledge {
|
|
832
|
+
/** Starts a comment line, and a parser directive at the top of the file. */
|
|
833
|
+
comment: string;
|
|
834
|
+
/** The parser directive that sets the escape character, and the characters it accepts (the default first). */
|
|
835
|
+
escapeDirective: string;
|
|
836
|
+
escapeCharacters: readonly string[];
|
|
837
|
+
/** The only instruction allowed before the first FROM. */
|
|
838
|
+
globalArgInstruction: string;
|
|
839
|
+
}
|
|
840
|
+
/** An official runtime image: which tool version it ships and how its tags are built. */
|
|
841
|
+
interface RuntimeImageKnowledge {
|
|
842
|
+
/** The repository name on Docker Hub, in the official namespace. */
|
|
843
|
+
image: string;
|
|
844
|
+
/** The tool the image ships as the runtime release bundles it, unchanged. */
|
|
845
|
+
bundles: 'npm' | 'pip';
|
|
846
|
+
/** Variant names a tag may carry after the version (22-alpine, 3.12-slim-bookworm), or on its own. */
|
|
847
|
+
variants: readonly string[];
|
|
848
|
+
}
|
|
849
|
+
/** Tags of an official image that move to another release line: tag (without variant) to release line. */
|
|
850
|
+
interface MovingTagsKnowledge {
|
|
851
|
+
tags: Readonly<Record<string, string>>;
|
|
852
|
+
}
|
|
853
|
+
/**
|
|
854
|
+
* An npm package with install scripts whose effect is known (decision D-127), read in its source at the releases in
|
|
855
|
+
* `versions`: `not-needed` when the package works without the script, `project-task` when the script does a task of
|
|
856
|
+
* the project (installing git hooks) that the project can run in its own lifecycle script (`projectScript`).
|
|
857
|
+
*/
|
|
858
|
+
interface InstallScriptPackageKnowledge {
|
|
859
|
+
name: string;
|
|
860
|
+
/** The releases the source was read at. */
|
|
861
|
+
versions: readonly string[];
|
|
862
|
+
/** The install script, as the package declares it or as npm adds it, for messages. */
|
|
863
|
+
script: string;
|
|
864
|
+
kind: 'not-needed' | 'project-task';
|
|
865
|
+
/** What the script does, as a clause for messages ("it runs lefthook install, which installs the git hooks"). */
|
|
866
|
+
does: string;
|
|
867
|
+
/** What changes when the script does not run, as a clause for messages ("nothing changes"). */
|
|
868
|
+
withoutScript: string;
|
|
869
|
+
/**
|
|
870
|
+
* Without its install script the package can take its binary only from an optional dependency for the platform (the
|
|
871
|
+
* script fetches it otherwise), so a denied one fails where optional dependencies are left out.
|
|
872
|
+
*/
|
|
873
|
+
needsOptionalDependencies: boolean;
|
|
874
|
+
/**
|
|
875
|
+
* project-task: how the project does the same task without the install script, as the package documents it for
|
|
876
|
+
* projects that do not use that script: a command to run after each clone of the repository (decision D-135).
|
|
877
|
+
*/
|
|
878
|
+
projectStep?: {
|
|
879
|
+
command: string;
|
|
880
|
+
after: 'clone';
|
|
881
|
+
};
|
|
882
|
+
}
|
|
459
883
|
interface EnvironmentKnowledge {
|
|
460
884
|
corepack: {
|
|
461
885
|
packageManagerField: Fact<PackageManagerFieldKnowledge>;
|
|
462
886
|
devEnginesSince: Fact<string>;
|
|
463
887
|
envFileSince: Fact<string>;
|
|
464
888
|
bundledWithNode: Fact<Versioned<boolean>>;
|
|
889
|
+
/** The npm package of Corepack, whose package.json declares the shims of the package managers (yarn, pnpm). */
|
|
890
|
+
package: Fact<string>;
|
|
891
|
+
/** The npm packages that install a package manager itself, for the tools Corepack knows. */
|
|
892
|
+
toolPackages: Fact<Readonly<Partial<Record<ToolId, readonly string[]>>>>;
|
|
893
|
+
/** lastKnownGood.json: the version of each package manager Corepack runs outside a project. */
|
|
894
|
+
defaults: Fact<CorepackDefaultsKnowledge>;
|
|
895
|
+
/** corepack enable installs an npm shim only when npm is named. */
|
|
896
|
+
enableNpm: Fact<{
|
|
897
|
+
command: string;
|
|
898
|
+
npmOnlyWhenNamed: boolean;
|
|
899
|
+
}>;
|
|
900
|
+
/**
|
|
901
|
+
* Installing Corepack where Node.js does not come with it (decision D-128): the npm package and the command; then
|
|
902
|
+
* corepack enable puts the shims on the path.
|
|
903
|
+
*/
|
|
904
|
+
manualInstall: Fact<{
|
|
905
|
+
package: string;
|
|
906
|
+
command: string;
|
|
907
|
+
enable: string;
|
|
908
|
+
}>;
|
|
909
|
+
};
|
|
910
|
+
/** npm packages whose install scripts are known to be unneeded or to do a task of the project (decision D-127). */
|
|
911
|
+
installScriptPackages: Readonly<Record<string, Fact<InstallScriptPackageKnowledge>>>;
|
|
912
|
+
node: {
|
|
913
|
+
/** The npm each Node.js release bundles, per release line. */
|
|
914
|
+
bundledNpm: Fact<readonly BundledVersionsLine[]>;
|
|
915
|
+
releaseSchedule: Fact<readonly ReleaseLineSchedule[]>;
|
|
916
|
+
nvmrc: Fact<NvmrcKnowledge>;
|
|
917
|
+
/** The version file of nodenv, also read by fnm and setup-node. */
|
|
918
|
+
nodeVersionFile: Fact<{
|
|
919
|
+
file: string;
|
|
920
|
+
}>;
|
|
921
|
+
/** engines.node in package.json: the Node.js versions a package works with, advisory. */
|
|
922
|
+
engines: Fact<{
|
|
923
|
+
key: string;
|
|
924
|
+
runtimeKey: string;
|
|
925
|
+
}>;
|
|
926
|
+
/** The Node.js version files that version managers also use in subdirectories of their directory. */
|
|
927
|
+
inheritedVersionFiles: Fact<readonly string[]>;
|
|
928
|
+
};
|
|
929
|
+
docker: {
|
|
930
|
+
imageReference: Fact<ImageReferenceKnowledge>;
|
|
931
|
+
syntax: Fact<DockerfileSyntaxKnowledge>;
|
|
932
|
+
/** How COPY and ADD read a source path in the build context: from its root, with ../ and trailing slashes removed. */
|
|
933
|
+
copySources: Fact<{
|
|
934
|
+
relativeTo: 'context';
|
|
935
|
+
parentNavigation: 'removed';
|
|
936
|
+
trailingSlash: 'ignored';
|
|
937
|
+
}>;
|
|
938
|
+
/** ENV: variables for the later instructions of the stage and the stages that start from it (decision D-115). */
|
|
939
|
+
environment: Fact<{
|
|
940
|
+
instruction: string;
|
|
941
|
+
inheritedByStages: boolean;
|
|
942
|
+
spaceForm: boolean;
|
|
943
|
+
}>;
|
|
944
|
+
/** The default file name, and the suffix of other Dockerfiles (build.Dockerfile). */
|
|
945
|
+
dockerfileNames: Fact<{
|
|
946
|
+
defaultName: string;
|
|
947
|
+
suffix: string;
|
|
948
|
+
}>;
|
|
949
|
+
/** Dockerfile.<name>: a common convention without an official source. */
|
|
950
|
+
dockerfilePrefix: Fact<string>;
|
|
951
|
+
/** The file name Buildah and Podman read by default. */
|
|
952
|
+
containerfile: Fact<string>;
|
|
953
|
+
nodeImage: Fact<RuntimeImageKnowledge>;
|
|
954
|
+
nodeImageTags: Fact<MovingTagsKnowledge>;
|
|
955
|
+
pythonImage: Fact<RuntimeImageKnowledge>;
|
|
956
|
+
pythonImageTags: Fact<MovingTagsKnowledge>;
|
|
465
957
|
};
|
|
466
958
|
devEngines: Fact<DevEnginesKnowledge>;
|
|
467
959
|
devEnginesSince: Fact<string>;
|
|
@@ -477,7 +969,32 @@ interface EnvironmentKnowledge {
|
|
|
477
969
|
};
|
|
478
970
|
githubActions: {
|
|
479
971
|
workflows: Fact<WorkflowsKnowledge>;
|
|
972
|
+
/** The ids GitHub accepts as keys of jobs, as a regular expression (decision D-135). */
|
|
973
|
+
jobId: Fact<{
|
|
974
|
+
pattern: string;
|
|
975
|
+
}>;
|
|
480
976
|
setupNode: Fact<SetupActionKnowledge>;
|
|
977
|
+
/** The versions node-version accepts besides semver ranges. */
|
|
978
|
+
setupNodeVersions: Fact<NodeVersionNamesKnowledge>;
|
|
979
|
+
setupNodeVersionFile: Fact<SetupNodeVersionFileKnowledge>;
|
|
980
|
+
/**
|
|
981
|
+
* jobs.<job_id>.container: the image the job's run steps run in, as a string or under image, with its env
|
|
982
|
+
* (decision D-115).
|
|
983
|
+
*/
|
|
984
|
+
jobContainer: Fact<{
|
|
985
|
+
key: string;
|
|
986
|
+
imageKey: string;
|
|
987
|
+
envKey: string;
|
|
988
|
+
}>;
|
|
989
|
+
/** The env maps of a workflow, a job and a step; the most specific one wins (decision D-115). */
|
|
990
|
+
environmentKeys: Fact<{
|
|
991
|
+
key: string;
|
|
992
|
+
levels: readonly ('workflow' | 'job' | 'step')[];
|
|
993
|
+
}>;
|
|
994
|
+
/** Whether workflows may use YAML anchors and aliases, which GitHub expands (decision D-141). */
|
|
995
|
+
yamlAliases: Fact<{
|
|
996
|
+
accepted: boolean;
|
|
997
|
+
}>;
|
|
481
998
|
pnpmActionSetup: Fact<SetupActionKnowledge>;
|
|
482
999
|
pnpmSetup: Fact<SetupActionKnowledge>;
|
|
483
1000
|
setupBun: Fact<SetupActionKnowledge>;
|
|
@@ -512,11 +1029,36 @@ interface EnvironmentKnowledge {
|
|
|
512
1029
|
hooks: Fact<ClaudeCodeHooksKnowledge>;
|
|
513
1030
|
/** The tools whose PreToolUse events carry a shell command in the hooks fact's input.command. */
|
|
514
1031
|
shellTools: Fact<readonly ClaudeCodeShellTool[]>;
|
|
1032
|
+
sessionStart: Fact<ClaudeCodeSessionStartKnowledge>;
|
|
1033
|
+
/** The field of a PreToolUse hook's output that adds text to Claude's context, with or without a decision. */
|
|
1034
|
+
hookContext: Fact<ClaudeCodeHookContextKnowledge>;
|
|
1035
|
+
/** Shell tool calls without a command: a Monitor WebSocket watch. */
|
|
1036
|
+
commandlessInputs: Fact<readonly ClaudeCodeCommandlessInputKnowledge[]>;
|
|
1037
|
+
};
|
|
1038
|
+
/** The Model Context Protocol, for depcurfew mcp, and how its clients start a stdio server. */
|
|
1039
|
+
mcp: {
|
|
1040
|
+
revisions: Fact<McpRevisionsKnowledge>;
|
|
1041
|
+
lifecycle: Fact<McpLifecycleKnowledge>;
|
|
1042
|
+
stateless: Fact<McpStatelessKnowledge>;
|
|
1043
|
+
tools: Fact<McpToolsKnowledge>;
|
|
1044
|
+
cancellation: Fact<McpCancellationKnowledge>;
|
|
1045
|
+
errorCodes: Fact<McpErrorCodesKnowledge>;
|
|
1046
|
+
errorId: Fact<McpErrorIdKnowledge>;
|
|
1047
|
+
stdio: Fact<McpStdioKnowledge>;
|
|
1048
|
+
clients: McpClientsKnowledge;
|
|
515
1049
|
};
|
|
516
1050
|
git: {
|
|
517
1051
|
/** Where git reads the patterns of files it does not pick up, and the index that lists the tracked files. */
|
|
518
1052
|
ignore: Fact<GitIgnoreKnowledge>;
|
|
519
1053
|
ignoreCase: Fact<GitIgnoreCaseKnowledge>;
|
|
1054
|
+
/**
|
|
1055
|
+
* The entry at the root of a working tree that makes a directory a repository of its own: a .git directory, or in
|
|
1056
|
+
* a submodule a .git file pointing to its git directory in the superproject (decision D-115).
|
|
1057
|
+
*/
|
|
1058
|
+
repositoryEntry: Fact<{
|
|
1059
|
+
entry: string;
|
|
1060
|
+
submoduleEntry: 'file';
|
|
1061
|
+
}>;
|
|
520
1062
|
};
|
|
521
1063
|
windows: {
|
|
522
1064
|
commandShims: Fact<CommandShimsKnowledge>;
|
|
@@ -525,6 +1067,8 @@ interface EnvironmentKnowledge {
|
|
|
525
1067
|
extensions: readonly string[];
|
|
526
1068
|
}>;
|
|
527
1069
|
};
|
|
1070
|
+
/** OSV.dev, for --osv (decision D-129). */
|
|
1071
|
+
osv: OsvKnowledge;
|
|
528
1072
|
python: {
|
|
529
1073
|
/** How Python package names are compared: lowercased, with every run of these characters replaced by `replacement`. */
|
|
530
1074
|
nameNormalization: Fact<{
|
|
@@ -540,6 +1084,10 @@ interface EnvironmentKnowledge {
|
|
|
540
1084
|
/** File extensions of source distributions on an index, which are built when installed. */
|
|
541
1085
|
sourceDistributionExtensions: Fact<readonly string[]>;
|
|
542
1086
|
interpreter: Fact<PythonInterpreterKnowledge>;
|
|
1087
|
+
/** A virtual environment made by venv: the file in its directory that marks it, and its activation scripts. */
|
|
1088
|
+
virtualEnvironment: Fact<VirtualEnvironmentKnowledge>;
|
|
1089
|
+
/** The pip each CPython release bundles in ensurepip, per release line. */
|
|
1090
|
+
bundledPip: Fact<readonly BundledVersionsLine[]>;
|
|
543
1091
|
/** Hatch, which has no supply-chain settings of its own: how a project shows it uses Hatch. */
|
|
544
1092
|
hatch: Fact<{
|
|
545
1093
|
pyprojectTable: string;
|
|
@@ -547,7 +1095,7 @@ interface EnvironmentKnowledge {
|
|
|
547
1095
|
}>;
|
|
548
1096
|
};
|
|
549
1097
|
}
|
|
550
|
-
type PackageManagerId = Extract<ToolId, 'npm' | 'pnpm' | 'yarn' | 'bun' | 'deno' | 'uv' | 'pip' | 'poetry' | 'pdm' | 'pipenv'>;
|
|
1098
|
+
type PackageManagerId = Extract<ToolId, 'npm' | 'pnpm' | 'yarn' | 'bun' | 'deno' | 'uv' | 'pip' | 'poetry' | 'pdm' | 'pipenv' | 'composer'>;
|
|
551
1099
|
type BotId = Extract<ToolId, 'renovate' | 'dependabot'>;
|
|
552
1100
|
/** Why a package manager is considered in use. Ordered from the strongest signal. */
|
|
553
1101
|
type EvidenceKind = 'lockfile' | 'packageManager' | 'devEngines' | 'config' | 'ci' | 'default';
|
|
@@ -556,18 +1104,140 @@ interface Evidence {
|
|
|
556
1104
|
/** Repository-relative path with `/` separators. */
|
|
557
1105
|
file: string;
|
|
558
1106
|
}
|
|
559
|
-
/**
|
|
560
|
-
|
|
561
|
-
|
|
1107
|
+
/**
|
|
1108
|
+
* tool-config: a version the tool's own configuration requires, e.g. required-version in [tool.uv]. nvmrc and
|
|
1109
|
+
* node-version: the files .nvmrc and .node-version; engines: engines.node in package.json (a range of the Node.js
|
|
1110
|
+
* versions the package works on, advisory); dockerfile: a Dockerfile (FROM node:22, RUN npm install -g npm@11).
|
|
1111
|
+
*/
|
|
1112
|
+
type VersionSource = 'packageManager' | 'tool-config' | 'devEngines' | 'volta' | 'tool-versions' | 'nvmrc' | 'node-version' | 'engines' | 'github-actions' | 'dockerfile';
|
|
1113
|
+
/**
|
|
1114
|
+
* A version declared somewhere in the repository, as written (it may be a range or an expression). A CI step that
|
|
1115
|
+
* names a version file has `versionFile`, and also `version` when depcurfew could read the version from that file.
|
|
1116
|
+
*/
|
|
562
1117
|
type VersionDeclaration = ({
|
|
563
1118
|
version: string;
|
|
1119
|
+
versionFile?: string;
|
|
564
1120
|
} | {
|
|
565
1121
|
versionFile: string;
|
|
566
1122
|
}) & {
|
|
567
1123
|
source: VersionSource;
|
|
568
1124
|
file: string;
|
|
569
1125
|
line?: number;
|
|
1126
|
+
/** github-actions: the key of the job under jobs. */
|
|
1127
|
+
job?: string;
|
|
1128
|
+
/** github-actions: the line of the step that sets the version up; installs after it and before the next such step use it. */
|
|
1129
|
+
step?: number;
|
|
1130
|
+
/** github-actions: the version is one value of this matrix key, from ${{ matrix.<key> }}; the line is the value's. */
|
|
1131
|
+
matrix?: string;
|
|
1132
|
+
/** Why depcurfew does not read the version written (a matrix with too many values); the version is then as written. */
|
|
1133
|
+
unknown?: string;
|
|
1134
|
+
/** dockerfile: the build stage, by its name or its number from 0. */
|
|
1135
|
+
stage?: string;
|
|
1136
|
+
/**
|
|
1137
|
+
* github-actions: the image of jobs.<id>.container (node:20) the version comes from, read like a FROM of a Dockerfile
|
|
1138
|
+
* (decision D-115); the line is the image's, and it stands for the job's steps until a setup step.
|
|
1139
|
+
*/
|
|
1140
|
+
container?: string;
|
|
570
1141
|
};
|
|
1142
|
+
/**
|
|
1143
|
+
* A command in a CI run step or a Dockerfile RUN instruction. install: installs packages with the tool (npm ci,
|
|
1144
|
+
* npm install, pip install -r requirements.txt, pnpm install, yarn, bun add); upgrade: replaces the tool itself
|
|
1145
|
+
* (npm install -g npm@11.17.0, pip install --upgrade pip, corepack enable npm). npm install -g with another package is
|
|
1146
|
+
* neither: npm does not read the project .npmrc in global mode.
|
|
1147
|
+
*/
|
|
1148
|
+
interface ToolCommand {
|
|
1149
|
+
kind: 'install' | 'upgrade';
|
|
1150
|
+
/**
|
|
1151
|
+
* pnpm, Yarn and Bun only install (their versions come from packageManager and the setup actions); poetry only
|
|
1152
|
+
* upgrades: pipx install poetry==2.3.0 or pip install poetry==2.3.0 installs that Poetry (decision D-115).
|
|
1153
|
+
*/
|
|
1154
|
+
tool: 'npm' | 'pip' | 'pnpm' | 'yarn' | 'bun' | 'poetry';
|
|
1155
|
+
/** upgrade: the version asked for as written, latest when none is; absent when the command does not say (corepack enable npm). */
|
|
1156
|
+
version?: string;
|
|
1157
|
+
/** npm ci and its aliases, which install exactly the versions of package-lock.json (decision D-115). */
|
|
1158
|
+
lockfile?: true;
|
|
1159
|
+
/** npm runs it with dependency install scripts off (--ignore-scripts, npm_config_ignore_scripts=true; decision D-115). */
|
|
1160
|
+
scriptsOff?: true;
|
|
1161
|
+
/**
|
|
1162
|
+
* npm install: its command line and variables leave out optional dependencies (--omit=optional, --no-optional,
|
|
1163
|
+
* npm_config_omit=optional) or keep them whatever .npmrc says (--include=optional); absent when they do not say
|
|
1164
|
+
* (decision D-127).
|
|
1165
|
+
*/
|
|
1166
|
+
optionalDependencies?: 'omitted' | 'kept';
|
|
1167
|
+
/** pip install: the release-age delay it gets, as written (--uploaded-prior-to, PIP_UPLOADED_PRIOR_TO; decision D-115). */
|
|
1168
|
+
delay?: string;
|
|
1169
|
+
/** pip install: PIP_CONFIG_FILE names a file that may set the delay, or the value is a variable depcurfew does not read. */
|
|
1170
|
+
delayUnknown?: true;
|
|
1171
|
+
line: number;
|
|
1172
|
+
/**
|
|
1173
|
+
* A command that installs or runs Corepack comes before it in the same job (an earlier run step, or earlier in its
|
|
1174
|
+
* own) or Dockerfile stage (an earlier RUN, or earlier in its own), also on the same line (decision D-141).
|
|
1175
|
+
*/
|
|
1176
|
+
afterCorepack?: true;
|
|
1177
|
+
/**
|
|
1178
|
+
* github-actions: the working directory of the run step as written (working-directory of the step, or
|
|
1179
|
+
* defaults.run.working-directory of the job or the workflow), without ./ and a trailing /, joined with the literal
|
|
1180
|
+
* cd before the command (cd frontend && npm ci); absent for the root. dockerfile: the directory of a literal cd before
|
|
1181
|
+
* the command, relative to the working directory of the instruction. A cd depcurfew does not follow ($DIR, an
|
|
1182
|
+
* absolute path) gives a directory that is no project (decision D-115).
|
|
1183
|
+
*/
|
|
1184
|
+
directory?: string;
|
|
1185
|
+
}
|
|
1186
|
+
/** A GitHub Actions job, with what its run steps do with npm and pip. */
|
|
1187
|
+
interface WorkflowJob {
|
|
1188
|
+
file: string;
|
|
1189
|
+
/** The key of the job under jobs. */
|
|
1190
|
+
id: string;
|
|
1191
|
+
line: number;
|
|
1192
|
+
/** Installs and package manager changes, in order. */
|
|
1193
|
+
commands: ToolCommand[];
|
|
1194
|
+
/** A run step calls node, npm, npx, pnpm, yarn, bun or corepack, so the job uses the Node.js it sets up. */
|
|
1195
|
+
runsNode: boolean;
|
|
1196
|
+
/**
|
|
1197
|
+
* A run step may change pip's configuration (pip config set, a pip.conf it writes, PIP_CONFIG_FILE in env), so the
|
|
1198
|
+
* release-age delay of its pip installs cannot be told (decision D-115).
|
|
1199
|
+
*/
|
|
1200
|
+
pipConfig?: true;
|
|
1201
|
+
/** The line of the first run step command that installs Corepack (npm install -g corepack) or runs it (decision D-128). */
|
|
1202
|
+
corepack?: number;
|
|
1203
|
+
}
|
|
1204
|
+
/** The official runtime image a build stage starts from. */
|
|
1205
|
+
interface StageRuntime {
|
|
1206
|
+
tool: 'node' | 'python';
|
|
1207
|
+
/** The tag as written after ARG substitution; absent when the image has none (latest, or a digest alone). */
|
|
1208
|
+
tag?: string;
|
|
1209
|
+
/** The version the tag names: 22, 22.11.0, lts, jod, latest (moving tags stay names); absent when it cannot be told. */
|
|
1210
|
+
version?: string;
|
|
1211
|
+
/** Why the version cannot be told. */
|
|
1212
|
+
unknown?: string;
|
|
1213
|
+
}
|
|
1214
|
+
interface DockerStage {
|
|
1215
|
+
/** The line of its FROM instruction. */
|
|
1216
|
+
line: number;
|
|
1217
|
+
/** The name after AS, in lower case. */
|
|
1218
|
+
name?: string;
|
|
1219
|
+
/** The image after ARG substitution; absent when an ARG it uses has no value. */
|
|
1220
|
+
image?: string;
|
|
1221
|
+
/** The earlier stage this one starts from (FROM <name>), by its number from 0. */
|
|
1222
|
+
from?: number;
|
|
1223
|
+
/** The official node or python image of this stage or of the stage it starts from. */
|
|
1224
|
+
runtime?: StageRuntime;
|
|
1225
|
+
commands: ToolCommand[];
|
|
1226
|
+
/** A RUN may change pip's configuration (pip config set, a pip.conf it writes), as for jobs (decision D-115). */
|
|
1227
|
+
pipConfig?: true;
|
|
1228
|
+
/** The line of the first RUN command that installs Corepack (npm install -g corepack) or runs it (decision D-128). */
|
|
1229
|
+
corepack?: number;
|
|
1230
|
+
/**
|
|
1231
|
+
* The source paths of the stage's COPY and ADD instructions that copy from the build context (not --from), as
|
|
1232
|
+
* written; absent when there are none. A root Dockerfile whose stage copies only files of one directory listed in
|
|
1233
|
+
* projects builds that project (decision D-109). At most 256.
|
|
1234
|
+
*/
|
|
1235
|
+
copies?: string[];
|
|
1236
|
+
}
|
|
1237
|
+
interface DockerfileInfo {
|
|
1238
|
+
file: string;
|
|
1239
|
+
stages: DockerStage[];
|
|
1240
|
+
}
|
|
571
1241
|
interface PackageManagerInfo {
|
|
572
1242
|
id: PackageManagerId;
|
|
573
1243
|
/** Yarn only: Berry (2+) or Classic (1.x). */
|
|
@@ -595,9 +1265,18 @@ interface DetectionWarning {
|
|
|
595
1265
|
code: string;
|
|
596
1266
|
message: string;
|
|
597
1267
|
files: string[];
|
|
1268
|
+
/** The line in the first file the warning is about, when it names one (decision D-124). */
|
|
1269
|
+
line?: number;
|
|
598
1270
|
}
|
|
599
1271
|
interface Detection {
|
|
600
1272
|
packageManagers: PackageManagerInfo[];
|
|
1273
|
+
/**
|
|
1274
|
+
* Detected with the directories listed in projects (decision D-137): package managers only CI setup steps of the root
|
|
1275
|
+
* show, while the root holds no manifest of their registry, or every install with them in its workflows runs in a
|
|
1276
|
+
* listed directory and no Dockerfile of the root installs with them. The steps set them up for the projects, so they
|
|
1277
|
+
* are no package managers of the root; the versions the steps install are still checked. Absent when there are none.
|
|
1278
|
+
*/
|
|
1279
|
+
ciForProjects?: PackageManagerInfo[];
|
|
601
1280
|
node: {
|
|
602
1281
|
versions: VersionDeclaration[];
|
|
603
1282
|
};
|
|
@@ -607,6 +1286,10 @@ interface Detection {
|
|
|
607
1286
|
npmrcFiles: string[];
|
|
608
1287
|
/** GitHub Actions workflow files that were read. */
|
|
609
1288
|
workflows: string[];
|
|
1289
|
+
/** The jobs of those workflows. */
|
|
1290
|
+
jobs: WorkflowJob[];
|
|
1291
|
+
/** Dockerfiles and Containerfiles of the root, its direct subdirectories without a manifest, and workspace packages. */
|
|
1292
|
+
dockerfiles: DockerfileInfo[];
|
|
610
1293
|
warnings: DetectionWarning[];
|
|
611
1294
|
}
|
|
612
1295
|
interface UserEnvironment {
|
|
@@ -619,14 +1302,36 @@ interface UserEnvironment {
|
|
|
619
1302
|
/** Whether a directory exists; replaceable in tests. */
|
|
620
1303
|
isDirectory?: (path: string) => boolean;
|
|
621
1304
|
}
|
|
1305
|
+
/** fetch as depcurfew calls it: a GET of a registry or index, or the POST of a batch query to OSV.dev (--osv, decision D-129). */
|
|
622
1306
|
type FetchLike = (url: string, init: {
|
|
623
1307
|
headers: Record<string, string>;
|
|
624
1308
|
signal: AbortSignal;
|
|
625
1309
|
redirect?: 'manual';
|
|
1310
|
+
method?: 'POST';
|
|
1311
|
+
body?: string;
|
|
626
1312
|
}) => Promise<Response>;
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
1313
|
+
/**
|
|
1314
|
+
* Lookups shared by the checks of one run, by registry or index and name: a command checked against two policies
|
|
1315
|
+
* fetches and reads each document once (decision D-113).
|
|
1316
|
+
*/
|
|
1317
|
+
type LookupCache = Map<string, Promise<unknown>>;
|
|
1318
|
+
interface McpLimits {
|
|
1319
|
+
/** The longest line, in bytes; a longer one is answered with an error and skipped. */
|
|
1320
|
+
lineBytes: number;
|
|
1321
|
+
/** The deepest nesting of arrays and objects in a message. */
|
|
1322
|
+
depth: number;
|
|
1323
|
+
/** Tool calls that run at once. */
|
|
1324
|
+
concurrentCalls: number;
|
|
1325
|
+
/** Tool calls that wait for one of them; a call beyond that gets a result that says to try again. */
|
|
1326
|
+
queuedCalls: number;
|
|
1327
|
+
/** Messages in one batch (2025-03-26 only). */
|
|
1328
|
+
batch: number;
|
|
1329
|
+
/** How long calls may still run after stdin ends before they are stopped. */
|
|
1330
|
+
graceMs: number;
|
|
1331
|
+
}
|
|
1332
|
+
export declare const EXIT_OK = 0;
|
|
1333
|
+
export declare const EXIT_FAILURE = 1;
|
|
1334
|
+
export declare const EXIT_USAGE = 2;
|
|
630
1335
|
interface CliIo {
|
|
631
1336
|
stdout(text: string): void;
|
|
632
1337
|
stderr(text: string): void;
|
|
@@ -641,18 +1346,41 @@ interface CliIo {
|
|
|
641
1346
|
stderr: boolean;
|
|
642
1347
|
};
|
|
643
1348
|
}
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
1349
|
+
/** What the server talks over and the process around it; tests replace it. */
|
|
1350
|
+
interface McpProcess {
|
|
1351
|
+
input: Readable;
|
|
1352
|
+
output: Writable;
|
|
1353
|
+
env: Readonly<Record<string, string | undefined>>;
|
|
1354
|
+
cwd: string;
|
|
1355
|
+
/** Calls `stop` on SIGINT and SIGTERM; returns what removes the handlers. Left out in tests. */
|
|
1356
|
+
onSignal?: (stop: () => void) => () => void;
|
|
1357
|
+
limits?: Partial<McpLimits>;
|
|
1358
|
+
}
|
|
1359
|
+
declare class Repository {
|
|
1360
|
+
#private;
|
|
1361
|
+
readonly root: string;
|
|
1362
|
+
readonly warnings: DetectionWarning[];
|
|
1363
|
+
/** Repository-relative paths of the files that were read, for the log of what doctor checked. */
|
|
1364
|
+
readonly read: Set<string>;
|
|
1365
|
+
constructor(root: string);
|
|
1366
|
+
warn(code: string, message: string, files: readonly string[], line?: number): void;
|
|
1367
|
+
isFile(path: string): boolean;
|
|
1368
|
+
/** Whether the path exists in the repository as something other than a regular file or a directory (a named pipe, a socket, a device). */
|
|
1369
|
+
isSpecialFile(path: string): boolean;
|
|
1370
|
+
/** The text of a file, without a byte order mark; undefined (with a warning when useful) if it cannot be read. */
|
|
1371
|
+
readText(path: string, limit?: number): string | undefined;
|
|
1372
|
+
/** The first bytes of a file, e.g. to recognize a lockfile format without reading a large file. */
|
|
1373
|
+
readHead(path: string, bytes?: number): string | undefined;
|
|
1374
|
+
/** Names of the files in a directory (following symlinks only inside the repository). */
|
|
1375
|
+
listFiles(path: string): string[];
|
|
1376
|
+
/** Names of the subdirectories of a directory, skipping node_modules, .git and symlinks that lead outside. */
|
|
1377
|
+
listDirectories(path: string): string[];
|
|
1378
|
+
/** Parses a file with the given parser, reporting failures as warnings. */
|
|
1379
|
+
parse<T>(path: string, parser: (text: string) => T): T | undefined;
|
|
1380
|
+
/** Like parse, but each file is read and parsed once per parser: several rules read the same file. */
|
|
1381
|
+
parseOnce<T>(path: string, parser: (text: string) => T): T | undefined;
|
|
1382
|
+
relative(absolute: string): string;
|
|
653
1383
|
}
|
|
654
|
-
declare function run(argv: readonly string[], output: CliIo, dependencies?: CheckDependencies): Promise<number>;
|
|
655
|
-
declare function getVersion(): string;
|
|
656
1384
|
interface Duration {
|
|
657
1385
|
/** The duration as written in the policy. */
|
|
658
1386
|
text: string;
|
|
@@ -666,8 +1394,8 @@ type DurationResult = {
|
|
|
666
1394
|
message: string;
|
|
667
1395
|
};
|
|
668
1396
|
/** Writes minutes with the largest unit that represents them exactly, e.g. 2160 → "36h". */
|
|
669
|
-
declare function formatDuration(minutes: number): string;
|
|
670
|
-
declare function parseDuration(input: string): DurationResult;
|
|
1397
|
+
export declare function formatDuration(minutes: number): string;
|
|
1398
|
+
export declare function parseDuration(input: string): DurationResult;
|
|
671
1399
|
interface Issue {
|
|
672
1400
|
/** Path to the field, e.g. `cooldown.exclude[2]`; `(root)` for the document itself. */
|
|
673
1401
|
path: string;
|
|
@@ -712,14 +1440,10 @@ interface Shape<In, Out = In> {
|
|
|
712
1440
|
}
|
|
713
1441
|
type Input<S> = S extends Shape<infer In, unknown> ? In : never;
|
|
714
1442
|
type Output<S> = S extends Shape<unknown, infer Out> ? Out : never;
|
|
1443
|
+
/** Which findings make depcurfew doctor exit with 1 (decision D-112). */
|
|
1444
|
+
declare const FAIL_ON_LEVELS: readonly ["error", "warning"];
|
|
715
1445
|
declare const policyShape: Shape<{
|
|
716
1446
|
version: 1;
|
|
717
|
-
$schema?: string;
|
|
718
|
-
cooldown?: {
|
|
719
|
-
minimum: string;
|
|
720
|
-
exclude?: readonly string[];
|
|
721
|
-
securityUpdates?: "bypass" | "delay";
|
|
722
|
-
};
|
|
723
1447
|
installScripts?: {
|
|
724
1448
|
default: "allow" | "deny";
|
|
725
1449
|
allow?: readonly string[];
|
|
@@ -731,16 +1455,21 @@ declare const policyShape: Shape<{
|
|
|
731
1455
|
file?: "allow" | "deny";
|
|
732
1456
|
directory?: "allow" | "deny";
|
|
733
1457
|
};
|
|
1458
|
+
$schema?: string;
|
|
1459
|
+
cooldown?: {
|
|
1460
|
+
minimum: string;
|
|
1461
|
+
exclude?: readonly string[];
|
|
1462
|
+
securityUpdates?: "bypass" | "delay";
|
|
1463
|
+
};
|
|
734
1464
|
targets?: "auto" | readonly ToolId[];
|
|
1465
|
+
projects?: readonly string[];
|
|
1466
|
+
doctor?: {
|
|
1467
|
+
failOn?: "error" | "warning";
|
|
1468
|
+
};
|
|
735
1469
|
}, {
|
|
736
1470
|
version: 1;
|
|
737
1471
|
targets: "auto" | readonly ToolId[];
|
|
738
|
-
|
|
739
|
-
cooldown?: {
|
|
740
|
-
minimum: Duration;
|
|
741
|
-
exclude: readonly string[];
|
|
742
|
-
securityUpdates: "bypass" | "delay";
|
|
743
|
-
};
|
|
1472
|
+
projects: readonly string[];
|
|
744
1473
|
installScripts?: {
|
|
745
1474
|
default: "allow" | "deny";
|
|
746
1475
|
allow: readonly string[];
|
|
@@ -752,203 +1481,27 @@ declare const policyShape: Shape<{
|
|
|
752
1481
|
file?: "allow" | "deny";
|
|
753
1482
|
directory?: "allow" | "deny";
|
|
754
1483
|
};
|
|
1484
|
+
$schema?: string;
|
|
1485
|
+
cooldown?: {
|
|
1486
|
+
minimum: Duration;
|
|
1487
|
+
exclude: readonly string[];
|
|
1488
|
+
securityUpdates: "bypass" | "delay";
|
|
1489
|
+
};
|
|
1490
|
+
doctor?: {
|
|
1491
|
+
failOn?: "error" | "warning";
|
|
1492
|
+
};
|
|
755
1493
|
}>;
|
|
756
1494
|
/** The policy as written in depcurfew.json. */
|
|
757
1495
|
type Policy = Input<typeof policyShape>;
|
|
758
1496
|
/** The policy after validation: defaults filled in, durations parsed. */
|
|
759
1497
|
type ResolvedPolicy = Output<typeof policyShape>;
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
readonly 'tool-not-supported': "A detected or installed tool is not supported (Yarn Classic), so nothing is written for it.";
|
|
766
|
-
readonly 'tool-only-in-ci': "A tool appears only in a CI setup step, with no manifest, lockfile or configuration in the repository, so targets \"auto\" does not configure it.";
|
|
767
|
-
readonly 'version-not-pinned': "No exact version of a package manager is pinned, so depcurfew configures for the latest version it knows.";
|
|
768
|
-
readonly 'version-too-old': "The pinned version of a tool (with apply --user, the installed one) lacks a setting the policy needs, so that setting is not written.";
|
|
769
|
-
readonly 'version-unknown': "apply --user: the installed version of a tool cannot be read from its files, and a value it does not understand would stop every command, so nothing is written for it.";
|
|
770
|
-
readonly 'no-user-config': "apply --user: the tool has no user-level configuration file, so nothing is written for it.";
|
|
771
|
-
readonly 'user-setting-project-only': "apply --user: the tool reads a part of the policy only from project files, so it is not written into the user file.";
|
|
772
|
-
readonly 'user-directory-relative': "apply --user: the environment variable that names the directory of the user file (XDG_CONFIG_HOME, APPDATA) is a relative path, so the tool looks in another place from each directory, or where it looks then is not known; nothing is written for it.";
|
|
773
|
-
readonly 'user-setting-overridden': "apply --user: a file the tool reads ahead of its user file (~/.npmrc for pnpm 10) sets a key apply writes there to another value, so the value written has no effect.";
|
|
774
|
-
readonly 'version-not-used': "apply --user: --for gives the version of a tool that apply --user does not configure (not in PATH with targets \"auto\", or not listed under targets).";
|
|
775
|
-
readonly 'cooldown-not-written': "The release-age delay cannot be written for a tool.";
|
|
776
|
-
readonly 'cooldown-shortened': "A release-age delay set outside depcurfew's markers (in JSON files also one an earlier apply wrote) is longer than the policy, and apply replaces it with the shorter one.";
|
|
777
|
-
readonly 'cooldown-environment-only': "pip reads no project configuration, so the release-age delay has to be set in the environment where pip runs.";
|
|
778
|
-
readonly 'exclude-pattern-unsupported': "A cooldown exception uses a wildcard the tool does not support, so it is not written.";
|
|
779
|
-
readonly 'exclude-unsupported': "The tool has no setting for cooldown exceptions, so the release-age delay applies to every package.";
|
|
780
|
-
readonly 'allow-pattern-unsupported': "An installScripts.allow entry is a pattern, but the tool takes package names only, so it is not written.";
|
|
781
|
-
readonly 'deny-pattern-unsupported': "An installScripts.deny entry is a pattern, but the tool records denials by package name only, so it is not written.";
|
|
782
|
-
readonly 'allow-all-unsupported': "The tool has no setting that allows every install script, so installScripts \"allow\" is not written.";
|
|
783
|
-
readonly 'allow-all-replaced': "A setting that allowed every install script (with any value the tool reads as true, also one that a YAML merge key brings in, or pnpm 10's dangerously-allow-all-builds in .npmrc) was set to false, because the policy denies install scripts.";
|
|
784
|
-
readonly 'allow-list-entry-outside-policy': "An install-script allow-list in the repository (with apply --user, in a user file) lets a package run scripts the policy does not allow; apply does not remove it.";
|
|
785
|
-
readonly 'deny-conflicts-with-file': "An entry in a tool's own list lets a package of installScripts.deny run its install scripts, or blocks a package of installScripts.allow; apply does not change entries it did not write.";
|
|
786
|
-
readonly 'unexpected-value': "A setting apply adds to has a value of a type the tool does not accept there, so apply leaves it as it is and writes nothing into it.";
|
|
787
|
-
readonly 'built-in-list-replaced': "Bun's built-in list of trusted packages is replaced by the policy's allow-list.";
|
|
788
|
-
readonly 'scripts-not-enforceable': "The pinned (with apply --user, the installed) npm or pnpm runs every install script and has no allow-list, so \"deny\" cannot be enforced.";
|
|
789
|
-
readonly 'before-overrides-cooldown': "before= in .npmrc takes the place of the release-age delay apply writes, or makes npm stop with an error next to it (apply --user then does not write the delay).";
|
|
790
|
-
readonly 'exact-version-gap': "The pinned (with apply --user, the installed) pnpm skips the release-age delay for packages requested by exact version.";
|
|
791
|
-
readonly 'source-not-enforceable': "The tool has no setting for a dependency source the policy denies; doctor checks the lockfile instead.";
|
|
792
|
-
readonly 'source-switch-indirect-only': "The pnpm setting for git and tarball sources stops them only for indirect dependencies.";
|
|
793
|
-
readonly 'source-switch-combined': "One pnpm setting covers git and tarball sources, so denying one denies both.";
|
|
794
|
-
readonly 'forced-source-build': "Python packages on the allow-list are always built from source, because no tool can only allow a build.";
|
|
795
|
-
readonly 'build-switch-scope': "The tool's switch forbids building only packages from an index; git, local and archive dependencies are still built.";
|
|
796
|
-
readonly 'build-switch-install-only': "Poetry applies its build switch when installing; poetry lock may still build source distributions.";
|
|
797
|
-
readonly 'build-switch-environment-only': "The tool has no project setting that forbids building from source, so it has to be set in the environment where the tool runs.";
|
|
798
|
-
readonly 'requirements-file-missing': "There is no requirements file to write pip options into.";
|
|
799
|
-
readonly 'manifest-missing': "A setting belongs in a project manifest (package.json, deno.json, pyproject.toml, Pipfile) that does not exist, and apply does not create manifests.";
|
|
800
|
-
readonly 'lockfile-out-of-date': "The lockfile records, or is checked against, a setting apply changed, so it has to be locked again.";
|
|
801
|
-
readonly 'file-ignored-by-git': "A file apply writes matches a .gitignore rule, so the change may never reach the repository.";
|
|
802
|
-
readonly 'bot-config-missing': "A bot has no configuration file, and depcurfew does not create one.";
|
|
803
|
-
readonly 'bot-config-not-edited': "A bot configuration file is in a place or format depcurfew does not edit; a snippet shows what to add.";
|
|
804
|
-
readonly 'bot-entries-missing': "The Dependabot configuration has no entries for the package managers in use.";
|
|
805
|
-
readonly 'security-delay-unsupported': "Dependabot cannot delay security updates, so securityUpdates \"delay\" is not applied.";
|
|
806
|
-
};
|
|
807
|
-
declare const APPLY_ERRORS: {
|
|
808
|
-
readonly 'cannot-edit': "A file cannot be read or edited safely (invalid syntax, a symlink, a style depcurfew does not edit), so nothing is written.";
|
|
809
|
-
readonly 'cannot-write': "A file cannot be written (no permission, a read-only file system, a full disk, a directory or symlink in its place); the files already written are put back, so nothing changes.";
|
|
810
|
-
readonly 'policy-missing': "The directory has no depcurfew.json.";
|
|
811
|
-
readonly 'policy-invalid': "depcurfew.json does not follow the schema; issues lists the problems.";
|
|
812
|
-
readonly 'policy-unreadable': "depcurfew.json cannot be read.";
|
|
813
|
-
readonly 'policy-exists': "depcurfew init found a depcurfew.json and writes it only with --force.";
|
|
814
|
-
readonly 'directory-not-found': "The directory given on the command line does not exist or cannot be opened.";
|
|
815
|
-
readonly 'not-a-directory': "The path given on the command line is a file, not a directory.";
|
|
816
|
-
readonly 'home-missing': "apply --user and init --user: the home directory (HOME, USERPROFILE on Windows) does not exist or is not an absolute path, so nothing is written.";
|
|
817
|
-
};
|
|
818
|
-
type ApplyWarningCode = keyof typeof APPLY_WARNINGS;
|
|
819
|
-
type ApplyErrorCode = keyof typeof APPLY_ERRORS;
|
|
820
|
-
interface ApplyWarning {
|
|
821
|
-
code: ApplyWarningCode;
|
|
822
|
-
/** The tool it concerns, when there is one. */
|
|
823
|
-
tool?: ToolId;
|
|
824
|
-
/** The file it concerns, when there is one, and the line when it is known. */
|
|
825
|
-
location?: {
|
|
826
|
-
file: string;
|
|
827
|
-
line?: number;
|
|
828
|
-
};
|
|
829
|
-
/** What happened, for people; it starts with the tool's name when it concerns one. */
|
|
830
|
-
message: string;
|
|
831
|
-
/** What to do about it, when there is something to do. */
|
|
832
|
-
fix?: string;
|
|
833
|
-
}
|
|
834
|
-
/** A warning as one line of text, as ApplyReport.warnings has it. */
|
|
835
|
-
declare function warningText(warning: ApplyWarning): string;
|
|
836
|
-
interface FileChange {
|
|
837
|
-
/** Repository-relative path with `/` separators. */
|
|
838
|
-
file: string;
|
|
839
|
-
/** null for a new file. */
|
|
840
|
-
before: string | null;
|
|
841
|
-
after: string;
|
|
1498
|
+
interface SimilarName {
|
|
1499
|
+
/** The popular package the name resembles. */
|
|
1500
|
+
match: string;
|
|
1501
|
+
/** edit: a letter or two differ; separators: only -, _ or . differ. */
|
|
1502
|
+
reason: 'edit' | 'separators';
|
|
842
1503
|
}
|
|
843
|
-
|
|
844
|
-
* A file could not be written. writeChanges has removed its temporary files and put back the files it had already
|
|
845
|
-
* replaced, so nothing changed, except for the files in `unrestored` (whose earlier text is in `backups` when it could
|
|
846
|
-
* be written) and the temporary files in `leftovers`. The message says why, without the file name.
|
|
847
|
-
*/
|
|
848
|
-
declare class WriteFailed extends Error {
|
|
849
|
-
readonly file: string;
|
|
850
|
-
/** Why, in plain words. */
|
|
851
|
-
readonly reason: string;
|
|
852
|
-
/** Files already replaced that could not be put back (normally none). */
|
|
853
|
-
readonly unrestored: string[];
|
|
854
|
-
/** For files in `unrestored`: the temporary file, next to it, that holds its earlier text. */
|
|
855
|
-
readonly backups: Readonly<Record<string, string>>;
|
|
856
|
-
/** Temporary files that could not be removed, named like `file` (relative to the root, or as WriteOptions.name says). */
|
|
857
|
-
readonly leftovers: string[];
|
|
858
|
-
/** Another program holds the file, or (on Windows) it is read-only. */
|
|
859
|
-
readonly busy: boolean;
|
|
860
|
-
readonly platform: NodeJS.Platform;
|
|
861
|
-
constructor(file: string, error: unknown, unrestored: string[], details?: {
|
|
862
|
-
platform?: NodeJS.Platform;
|
|
863
|
-
backups?: Record<string, string>;
|
|
864
|
-
leftovers?: string[];
|
|
865
|
-
});
|
|
866
|
-
}
|
|
867
|
-
interface Snippet {
|
|
868
|
-
tool: ToolId;
|
|
869
|
-
file: string;
|
|
870
|
-
text: string;
|
|
871
|
-
}
|
|
872
|
-
/** A file a generator could not read or edit; nothing is written when there is one. */
|
|
873
|
-
interface EditFailure {
|
|
874
|
-
tool: ToolId;
|
|
875
|
-
file: string;
|
|
876
|
-
message: string;
|
|
877
|
-
}
|
|
878
|
-
interface ApplyReport {
|
|
879
|
-
changes: FileChange[];
|
|
880
|
-
/** The warnings as one line of text each. */
|
|
881
|
-
warnings: string[];
|
|
882
|
-
/** The same warnings with a code, the tool, the file and what to do (apply --json). */
|
|
883
|
-
warningDetails: ApplyWarning[];
|
|
884
|
-
snippets: Snippet[];
|
|
885
|
-
/** Files that could not be edited, as messages. When there are any, nothing is written. */
|
|
886
|
-
errors: string[];
|
|
887
|
-
/** The same failures with the tool and the file. */
|
|
888
|
-
failures: EditFailure[];
|
|
889
|
-
}
|
|
890
|
-
/** The bots' delay: the policy's, or longer if a package manager rounded its delay up further. */
|
|
891
|
-
declare function botDuration(policy: Duration, managerMinutes: readonly number[]): Duration;
|
|
892
|
-
/** Computes every change without writing anything. */
|
|
893
|
-
declare function planApply(root: string, policy: ResolvedPolicy, detection: Detection, tools?: readonly ToolKnowledge[]): ApplyReport;
|
|
894
|
-
declare class PolicyError extends Error {
|
|
895
|
-
readonly issues: string[];
|
|
896
|
-
readonly code: ApplyErrorCode;
|
|
897
|
-
/** What to do, when the message does not say it. */
|
|
898
|
-
readonly fix: string | undefined;
|
|
899
|
-
constructor(message: string, issues?: string[], code?: ApplyErrorCode, fix?: string);
|
|
900
|
-
/** The message and what to do, as one line of text. */
|
|
901
|
-
get text(): string;
|
|
902
|
-
}
|
|
903
|
-
declare function loadPolicy(root: string): ResolvedPolicy;
|
|
904
|
-
/**
|
|
905
|
-
* A file could not be written (code cannot-write). The files apply had already replaced were put back, so the
|
|
906
|
-
* repository is as it was, unless `unrestored` lists files. `report` is the plan that was being written.
|
|
907
|
-
*/
|
|
908
|
-
declare class ApplyWriteError extends Error {
|
|
909
|
-
readonly code: "cannot-write";
|
|
910
|
-
/** The file that could not be written, relative to the repository. */
|
|
911
|
-
readonly file: string;
|
|
912
|
-
/** What to do. */
|
|
913
|
-
readonly fix: string;
|
|
914
|
-
/** Files already replaced that could not be put back (normally none). */
|
|
915
|
-
readonly unrestored: string[];
|
|
916
|
-
readonly report: ApplyReport;
|
|
917
|
-
/**
|
|
918
|
-
* `command` is what the fix tells the user to run again (depcurfew apply --user for user files), and
|
|
919
|
-
* `versionControl` whether the files may be in version control, to restore them from.
|
|
920
|
-
*/
|
|
921
|
-
constructor(failure: WriteFailed, report: ApplyReport, options?: {
|
|
922
|
-
command?: string;
|
|
923
|
-
versionControl?: boolean;
|
|
924
|
-
});
|
|
925
|
-
}
|
|
926
|
-
/**
|
|
927
|
-
* Reads the policy, detects the project, plans the changes and (unless told otherwise) writes them. If any file
|
|
928
|
-
* could not be edited, nothing is written, so a run either applies the whole policy or leaves the repository as it was.
|
|
929
|
-
* A file that cannot be written throws ApplyWriteError after the files already written are put back.
|
|
930
|
-
*/
|
|
931
|
-
declare function applyProject(root: string, options?: {
|
|
932
|
-
write?: boolean;
|
|
933
|
-
}): ApplyReport;
|
|
934
|
-
interface DiffOptions {
|
|
935
|
-
/** Shows the values of secrets as <redacted> in every line (redactSecrets), for a diff that people or logs see. */
|
|
936
|
-
redact?: boolean;
|
|
937
|
-
}
|
|
938
|
-
/**
|
|
939
|
-
* A unified diff with three lines of context; empty when the texts are equal. `before` is null for a new file.
|
|
940
|
-
* Control characters in the file name are shown as text (printable), so that a name cannot start a line of its own
|
|
941
|
-
* and forge a header or a hunk. A file with an absolute path (a user file outside the home directory) is named as it
|
|
942
|
-
* is, without the a/ and b/ of repository paths.
|
|
943
|
-
*/
|
|
944
|
-
declare function unifiedDiff(file: string, before: string | null, after: string, context?: number, options?: DiffOptions): string;
|
|
945
|
-
interface SimilarName {
|
|
946
|
-
/** The popular package the name resembles. */
|
|
947
|
-
match: string;
|
|
948
|
-
/** edit: a letter or two differ; separators: only -, _ or . differ. */
|
|
949
|
-
reason: 'edit' | 'separators';
|
|
950
|
-
}
|
|
951
|
-
declare class PopularNames {
|
|
1504
|
+
export declare class PopularNames {
|
|
952
1505
|
#private;
|
|
953
1506
|
constructor(names: readonly string[]);
|
|
954
1507
|
has(name: string): boolean;
|
|
@@ -956,7 +1509,9 @@ declare class PopularNames {
|
|
|
956
1509
|
similar(name: string): SimilarName | undefined;
|
|
957
1510
|
}
|
|
958
1511
|
type SpecKind = 'registry' | 'git' | 'remoteTarball' | 'file' | 'directory' | 'workspace' | 'other';
|
|
959
|
-
type FindingCode = 'invalid-spec' | 'forbidden-source' | 'unchecked-source' | 'unverified' | 'not-found' | 'no-version' | 'too-young' | 'install-scripts' | 'similar-name' | 'deprecated' | 'yanked' | 'archived' | 'quarantined' | 'insecure-registry'
|
|
1512
|
+
type FindingCode = 'invalid-spec' | 'forbidden-source' | 'unchecked-source' | 'unverified' | 'not-found' | 'no-version' | 'too-young' | 'install-scripts' | 'similar-name' | 'deprecated' | 'yanked' | 'archived' | 'quarantined' | 'insecure-registry' |
|
|
1513
|
+
/** --osv: OSV.dev has a record of the OpenSSF malicious-packages database for the version (decision D-129). */
|
|
1514
|
+
'known-malicious';
|
|
960
1515
|
interface CheckFinding {
|
|
961
1516
|
level: 'error' | 'warning';
|
|
962
1517
|
code: FindingCode;
|
|
@@ -985,6 +1540,8 @@ interface PackageReport {
|
|
|
985
1540
|
/** A spec with what the install command changes about the version or the build. */
|
|
986
1541
|
interface CheckRequest {
|
|
987
1542
|
spec: string;
|
|
1543
|
+
/** The package manager of the command, when a command names one (the hook): its own registry settings apply. */
|
|
1544
|
+
tool?: ToolId;
|
|
988
1545
|
/** npm: the dist-tag npm prefers for a name without a version or with a range; the registry's default when left out. */
|
|
989
1546
|
tag?: string;
|
|
990
1547
|
/** The package manager is not npm, so tag= in .npmrc (CheckOptions.defaultTag) does not choose its version. */
|
|
@@ -993,6 +1550,35 @@ interface CheckRequest {
|
|
|
993
1550
|
prereleases?: 'allow' | 'deny';
|
|
994
1551
|
/** Python: the package is built from source even when it has wheels (true), or only wheels are used (false). */
|
|
995
1552
|
buildFromSource?: boolean;
|
|
1553
|
+
/**
|
|
1554
|
+
* npm: the command line has an option that may set workspaces (--no-workspaces, --workspaces=false), with which npm
|
|
1555
|
+
* reads the .npmrc of a workspace package as its project configuration (decision D-124).
|
|
1556
|
+
*/
|
|
1557
|
+
workspacesOption?: boolean;
|
|
1558
|
+
/**
|
|
1559
|
+
* Composer (decision D-133): the command does not read the composer.json of the directory it runs in (create-project,
|
|
1560
|
+
* global), so the repositories declared there do not apply to it.
|
|
1561
|
+
*/
|
|
1562
|
+
readsManifest?: false;
|
|
1563
|
+
/**
|
|
1564
|
+
* Composer: composer update names the package without a constraint, so the constraint the root composer.json
|
|
1565
|
+
* requires applies; a package that composer.json does not require is not checked.
|
|
1566
|
+
*/
|
|
1567
|
+
fromManifest?: true;
|
|
1568
|
+
/**
|
|
1569
|
+
* Composer (decision D-136): the command word as written and the command of Composer it stands for, when it is not
|
|
1570
|
+
* that command's exact name (an alias, an abbreviation, another case), so that the scripts of the composer.json the
|
|
1571
|
+
* command runs with, which Composer then registers as commands, may make it stand for a script or for nothing.
|
|
1572
|
+
*/
|
|
1573
|
+
composerCommand?: {
|
|
1574
|
+
word: string;
|
|
1575
|
+
command: string;
|
|
1576
|
+
};
|
|
1577
|
+
/**
|
|
1578
|
+
* Composer: picks the version at the minimum stability stable, without the minimum-stability and prefer-stable of
|
|
1579
|
+
* the root composer.json, for a file of a parent directory that Composer may not use (decision D-142).
|
|
1580
|
+
*/
|
|
1581
|
+
rootStability?: false;
|
|
996
1582
|
}
|
|
997
1583
|
interface CheckOptions {
|
|
998
1584
|
policy: ResolvedPolicy;
|
|
@@ -1013,63 +1599,55 @@ interface CheckOptions {
|
|
|
1013
1599
|
* written by the package's publisher and should not reach the model as part of depcurfew's reason.
|
|
1014
1600
|
*/
|
|
1015
1601
|
scriptBodies?: boolean;
|
|
1602
|
+
/** Stops the lookups: checkSpecs then rejects with the signal's reason (a cancelled call of depcurfew mcp). */
|
|
1603
|
+
signal?: AbortSignal;
|
|
1604
|
+
/** Lookups shared with other checks of the same run (the two policies of the hook), by registry and name. */
|
|
1605
|
+
lookups?: LookupCache;
|
|
1606
|
+
/**
|
|
1607
|
+
* npm packages not to look up: their scope's registry is set in a file depcurfew does not take (decision D-113), so
|
|
1608
|
+
* the name may be private and the public registry may hold a look-alike. The package is reported as unverified.
|
|
1609
|
+
*/
|
|
1610
|
+
skipLookup?: (name: string, tool: ToolId | undefined, request?: Omit<CheckRequest, 'spec'>) => boolean;
|
|
1611
|
+
/**
|
|
1612
|
+
* Composer: minimum-stability and prefer-stable of the root composer.json (decision D-136), for the requests that read
|
|
1613
|
+
* it; without them, the defaults (stable, prefer-stable off).
|
|
1614
|
+
*/
|
|
1615
|
+
composerStability?: {
|
|
1616
|
+
minimum?: string;
|
|
1617
|
+
preferStable: boolean;
|
|
1618
|
+
file: string;
|
|
1619
|
+
};
|
|
1016
1620
|
}
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
/** The command as written, for messages. */
|
|
1023
|
-
command: string;
|
|
1024
|
-
spec: string;
|
|
1025
|
-
/** npm: the dist-tag the command makes npm prefer (--tag, npm_config_tag). */
|
|
1026
|
-
tag?: string;
|
|
1027
|
-
/** Python: the command allows pre-releases of the package, or forbids them; left out, the tool's default applies. */
|
|
1028
|
-
prereleases?: 'allow' | 'deny';
|
|
1029
|
-
/** Python: the command makes the tool build the package from source (true) or install wheels only (false). */
|
|
1030
|
-
buildFromSource?: boolean;
|
|
1031
|
-
}
|
|
1032
|
-
interface CommandScan {
|
|
1033
|
-
requests: InstallRequest[];
|
|
1034
|
-
/** Why an install command could not be read; undefined when every one could. */
|
|
1035
|
-
unreadable: string | undefined;
|
|
1621
|
+
/** A package version to look up; `ecosystem` is depcurfew's (npm, pypi, packagist), whose names the policy writes without a prefix, with pypi: and with composer:. */
|
|
1622
|
+
interface OsvPackage {
|
|
1623
|
+
ecosystem: string;
|
|
1624
|
+
name: string;
|
|
1625
|
+
version: string;
|
|
1036
1626
|
}
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1627
|
+
/** What OSV.dev says about one version: the ids of its MAL- records (none: no known malware), or why it was not checked. */
|
|
1628
|
+
type OsvVerdict = {
|
|
1629
|
+
status: 'checked';
|
|
1630
|
+
malicious: string[];
|
|
1631
|
+
} | {
|
|
1632
|
+
status: 'unchecked';
|
|
1633
|
+
reason: string;
|
|
1634
|
+
};
|
|
1635
|
+
interface OsvOptions {
|
|
1636
|
+
fetch: FetchLike;
|
|
1637
|
+
signal?: AbortSignal | undefined;
|
|
1638
|
+
/** Per request; OSV_TIMEOUT_MS when left out. */
|
|
1639
|
+
timeoutMs?: number;
|
|
1640
|
+
/** Largest answer read; the response limit of OSV.dev over HTTP/1.1 when left out. */
|
|
1641
|
+
maxBytes?: number;
|
|
1642
|
+
/** Most distinct versions looked up in one run; the others stay unchecked. MAX_OSV_PACKAGES when left out. */
|
|
1643
|
+
limit?: number;
|
|
1040
1644
|
}
|
|
1041
|
-
/** The packages a command line installs or downloads to run with npm, pnpm, Yarn, Bun, Deno, pip, uv, Poetry, PDM, Pipenv or pipx. */
|
|
1042
|
-
declare function scanCommandLine(line: string, options?: ScanOptions): CommandScan;
|
|
1043
1645
|
/**
|
|
1044
|
-
*
|
|
1045
|
-
*
|
|
1646
|
+
* What OSV.dev says about each package version, in the order given. Each distinct version is asked about once; the
|
|
1647
|
+
* batches go one after the other, and once one fails the rest is not asked (OSV.dev is out of reach, or answers
|
|
1648
|
+
* wrong), so every version left is unchecked with that reason. When `options.signal` aborts, the promise rejects.
|
|
1046
1649
|
*/
|
|
1047
|
-
declare function
|
|
1048
|
-
declare class Repository {
|
|
1049
|
-
#private;
|
|
1050
|
-
readonly root: string;
|
|
1051
|
-
readonly warnings: DetectionWarning[];
|
|
1052
|
-
/** Repository-relative paths of the files that were read, for the log of what doctor checked. */
|
|
1053
|
-
readonly read: Set<string>;
|
|
1054
|
-
constructor(root: string);
|
|
1055
|
-
warn(code: string, message: string, files: readonly string[]): void;
|
|
1056
|
-
isFile(path: string): boolean;
|
|
1057
|
-
/** Whether the path exists in the repository as something other than a regular file or a directory (a named pipe, a socket, a device). */
|
|
1058
|
-
isSpecialFile(path: string): boolean;
|
|
1059
|
-
/** The text of a file, without a byte order mark; undefined (with a warning when useful) if it cannot be read. */
|
|
1060
|
-
readText(path: string, limit?: number): string | undefined;
|
|
1061
|
-
/** The first bytes of a file, e.g. to recognize a lockfile format without reading a large file. */
|
|
1062
|
-
readHead(path: string, bytes?: number): string | undefined;
|
|
1063
|
-
/** Names of the files in a directory (following symlinks only inside the repository). */
|
|
1064
|
-
listFiles(path: string): string[];
|
|
1065
|
-
/** Names of the subdirectories of a directory, skipping node_modules, .git and symlinks that lead outside. */
|
|
1066
|
-
listDirectories(path: string): string[];
|
|
1067
|
-
/** Parses a file with the given parser, reporting failures as warnings. */
|
|
1068
|
-
parse<T>(path: string, parser: (text: string) => T): T | undefined;
|
|
1069
|
-
/** Like parse, but each file is read and parsed once per parser: several rules read the same file. */
|
|
1070
|
-
parseOnce<T>(path: string, parser: (text: string) => T): T | undefined;
|
|
1071
|
-
relative(absolute: string): string;
|
|
1072
|
-
}
|
|
1650
|
+
export declare function queryOsv(packages: readonly OsvPackage[], options: OsvOptions): Promise<OsvVerdict[]>;
|
|
1073
1651
|
/** The SARIF levels: an error fails `doctor`, warnings and notes do not. */
|
|
1074
1652
|
type Level = 'error' | 'warning' | 'note';
|
|
1075
1653
|
interface Location {
|
|
@@ -1077,6 +1655,8 @@ interface Location {
|
|
|
1077
1655
|
file: string;
|
|
1078
1656
|
/** 1-based; absent when the finding concerns the whole file. */
|
|
1079
1657
|
line?: number;
|
|
1658
|
+
/** 1-based column on `line`, when the problem has one (a syntax error); absent otherwise (decision D-116). */
|
|
1659
|
+
column?: number;
|
|
1080
1660
|
/** doctor --user: the environment variable, as it is set, of a finding whose file is "environment". */
|
|
1081
1661
|
variable?: string;
|
|
1082
1662
|
}
|
|
@@ -1088,10 +1668,23 @@ interface Finding {
|
|
|
1088
1668
|
/** A fix for this finding; the rule's general help applies when absent. */
|
|
1089
1669
|
fix?: string;
|
|
1090
1670
|
}
|
|
1091
|
-
/**
|
|
1671
|
+
/**
|
|
1672
|
+
* Something doctor could not check, such as an unreadable file. Not a problem in the repository itself, so it never
|
|
1673
|
+
* fails doctor. It has the fields of a finding (decision D-116): the level, always note; a code, the rule that could not
|
|
1674
|
+
* check something or the code of the warning or state that gave it; where it is; the message; and what to do.
|
|
1675
|
+
*/
|
|
1092
1676
|
interface Notice {
|
|
1677
|
+
level: 'note';
|
|
1678
|
+
code: string;
|
|
1093
1679
|
message: string;
|
|
1680
|
+
/** The file it concerns, the same as location.file; kept for programs that read it before location was added. */
|
|
1094
1681
|
file?: string;
|
|
1682
|
+
/** The line, when the notice is about one (a CI run step or RUN instruction depcurfew did not follow, decision D-124). */
|
|
1683
|
+
location?: {
|
|
1684
|
+
file: string;
|
|
1685
|
+
line?: number;
|
|
1686
|
+
};
|
|
1687
|
+
fix?: string;
|
|
1095
1688
|
}
|
|
1096
1689
|
interface RuleContext {
|
|
1097
1690
|
root: string;
|
|
@@ -1103,8 +1696,43 @@ interface RuleContext {
|
|
|
1103
1696
|
report(finding: Omit<Finding, 'rule' | 'level'> & {
|
|
1104
1697
|
level?: Level;
|
|
1105
1698
|
}): void;
|
|
1106
|
-
|
|
1699
|
+
/** Something the rule could not check; its code is the rule's id (decision D-116). */
|
|
1700
|
+
notice(message: string, file?: string, fix?: string): void;
|
|
1701
|
+
/**
|
|
1702
|
+
* Set when the rules run on a directory listed in projects in depcurfew.json (decision D-100): its path relative to
|
|
1703
|
+
* the repository root. `root`, `repository` and `detection` are then the project's, and file names relative to it.
|
|
1704
|
+
*/
|
|
1705
|
+
project?: string;
|
|
1706
|
+
/**
|
|
1707
|
+
* At the repository root: the directories listed in projects that could be read, for the rules that compare the
|
|
1708
|
+
* bots, which are configured from the root, with the package managers of every project.
|
|
1709
|
+
*/
|
|
1710
|
+
projects?: readonly ProjectView[];
|
|
1711
|
+
/** How apply plans this directory, for the rules that compare with that plan (config-drift). */
|
|
1712
|
+
plan?: PlanOptions;
|
|
1713
|
+
/** doctor --osv (decision D-129): absent without it; see OsvDoctor. */
|
|
1714
|
+
osv?: OsvDoctor;
|
|
1107
1715
|
}
|
|
1716
|
+
/**
|
|
1717
|
+
* What known-malicious gets with --osv: a list to fill with the locked versions (the first run, before OSV.dev is
|
|
1718
|
+
* asked), or the answers of OSV.dev by osvKey (the second run, which reports).
|
|
1719
|
+
*/
|
|
1720
|
+
type OsvDoctor = {
|
|
1721
|
+
collect: OsvPackage[];
|
|
1722
|
+
} | {
|
|
1723
|
+
answers: ReadonlyMap<string, OsvVerdict>;
|
|
1724
|
+
};
|
|
1725
|
+
/** A directory listed in projects, as the rules at the repository root see it. */
|
|
1726
|
+
interface ProjectView {
|
|
1727
|
+
/** Relative to the repository root, as listed. */
|
|
1728
|
+
directory: string;
|
|
1729
|
+
/** The absolute path. */
|
|
1730
|
+
root: string;
|
|
1731
|
+
repository: Repository;
|
|
1732
|
+
detection: Detection;
|
|
1733
|
+
}
|
|
1734
|
+
/** What a rule may look at to decide whether it concerns the project: the context without the reporting functions. */
|
|
1735
|
+
type RuleScope = Omit<RuleContext, 'report' | 'notice'>;
|
|
1108
1736
|
interface Rule {
|
|
1109
1737
|
/** Stable kebab-case identifier, used in SARIF and in the documentation. */
|
|
1110
1738
|
id: string;
|
|
@@ -1120,23 +1748,38 @@ interface Rule {
|
|
|
1120
1748
|
level: Level;
|
|
1121
1749
|
/** Rules that compare the repository with depcurfew.json do not run without one. */
|
|
1122
1750
|
requiresPolicy: boolean;
|
|
1751
|
+
/**
|
|
1752
|
+
* Whether the rule concerns the project at all, from detection: the Python rules without a Python tool, the bot rules
|
|
1753
|
+
* without a bot. false shows the rule as "not applicable" in the list of what doctor checked (decision D-098). It
|
|
1754
|
+
* never changes findings: check still runs, and a rule that reports one has problems. Absent: it always applies.
|
|
1755
|
+
*/
|
|
1756
|
+
applies?(scope: RuleScope): boolean;
|
|
1123
1757
|
check(context: RuleContext): void;
|
|
1124
1758
|
}
|
|
1125
1759
|
/** A package manager or bot doctor checked, with the version it checked against when one is known. */
|
|
1126
1760
|
interface CheckedTool {
|
|
1127
1761
|
id: string;
|
|
1128
1762
|
name: string;
|
|
1763
|
+
/** The directory listed in projects the tool was found in; absent for the repository root. */
|
|
1764
|
+
project?: string;
|
|
1129
1765
|
version?: string;
|
|
1130
1766
|
/** Where the version comes from: a repository file, or PATH for doctor --user. */
|
|
1131
1767
|
versionFrom?: string;
|
|
1768
|
+
/** A bot configuration in a directory listed in projects, which the bot does not read (it reads the root's, decision D-109). */
|
|
1769
|
+
unread?: true;
|
|
1770
|
+
/** A package manager that a CI setup step of the root sets up for the directories listed in projects (decisions D-137 and D-141). */
|
|
1771
|
+
setUpInCi?: true;
|
|
1132
1772
|
}
|
|
1133
1773
|
/** One check of the list of what doctor checked: a rule and what came of it. */
|
|
1134
1774
|
interface RuleResult {
|
|
1135
1775
|
rule: string;
|
|
1136
1776
|
/** What the rule checks, in plain words (Rule.check). */
|
|
1137
1777
|
check: string;
|
|
1138
|
-
/**
|
|
1139
|
-
|
|
1778
|
+
/**
|
|
1779
|
+
* passed: it ran and found nothing; problems: it reported findings; skipped: it needs a policy that is missing or
|
|
1780
|
+
* invalid; not-applicable: it does not concern the project, e.g. a Python rule without a Python tool (decision D-098).
|
|
1781
|
+
*/
|
|
1782
|
+
result: 'passed' | 'problems' | 'skipped' | 'not-applicable';
|
|
1140
1783
|
errors: number;
|
|
1141
1784
|
warnings: number;
|
|
1142
1785
|
notes: number;
|
|
@@ -1160,8 +1803,285 @@ interface DoctorReport {
|
|
|
1160
1803
|
/** What was checked. */
|
|
1161
1804
|
checked?: Checked;
|
|
1162
1805
|
}
|
|
1163
|
-
/**
|
|
1164
|
-
|
|
1806
|
+
/** What a directory listed in projects takes from the repository root besides its CI installs (decision D-109). */
|
|
1807
|
+
interface RootPlaces {
|
|
1808
|
+
/** The project; the stages of root Dockerfiles are then read for it (decision D-115). */
|
|
1809
|
+
project?: string;
|
|
1810
|
+
/** The root's Node.js version files (.nvmrc, .node-version, .tool-versions) of the kinds the project has none of. */
|
|
1811
|
+
versions: readonly VersionDeclaration[];
|
|
1812
|
+
/**
|
|
1813
|
+
* Root Dockerfiles with the stages that build the project (they copy only its files, decision D-109) or install in its
|
|
1814
|
+
* directory after copying the whole build context (cd frontend && npm ci, decision D-115).
|
|
1815
|
+
*/
|
|
1816
|
+
dockerfiles: readonly {
|
|
1817
|
+
file: DockerfileInfo;
|
|
1818
|
+
stages: readonly number[];
|
|
1819
|
+
}[];
|
|
1820
|
+
}
|
|
1821
|
+
export declare const APPLY_WARNINGS: {
|
|
1822
|
+
readonly 'nothing-to-configure': "depcurfew found no supported package manager or bot, so nothing is configured.";
|
|
1823
|
+
readonly 'environment-overrides': "apply --user: an environment variable makes a tool read another user file or none, or overrides a value apply writes there.";
|
|
1824
|
+
readonly 'depcurfew-outdated': "The project installs a newer depcurfew than the one running, which may not know settings that version writes.";
|
|
1825
|
+
readonly 'file-unreadable': "A file that shows which tools a project uses cannot be read or parsed (too large, a symlink out of the repository, no read permission, invalid), so depcurfew may configure fewer tools than the project uses.";
|
|
1826
|
+
readonly 'tool-not-supported': "A detected or installed tool is not supported (Yarn Classic), so nothing is written for it.";
|
|
1827
|
+
readonly 'target-not-found': "With projects, a package manager listed under targets is configured in each directory (the root or a project) that uses it, and none uses it, so nothing is written for it.";
|
|
1828
|
+
readonly 'tool-only-in-ci': "A tool appears only in a CI setup step, with no manifest, lockfile or configuration in the repository, so targets \"auto\" does not configure it.";
|
|
1829
|
+
readonly 'version-not-pinned': "No exact version of a package manager is pinned, so depcurfew configures for the latest version it knows.";
|
|
1830
|
+
readonly 'version-too-old': "The pinned version of a tool (with apply --user, the installed one) lacks a setting the policy needs, so that setting is not written.";
|
|
1831
|
+
readonly 'version-unknown': "apply --user: the installed version of a tool cannot be read from its files, and a value it does not understand would stop every command, so nothing is written for it.";
|
|
1832
|
+
readonly 'no-user-config': "apply --user: the tool has no user-level configuration file, so nothing is written for it.";
|
|
1833
|
+
readonly 'user-setting-project-only': "apply --user: the tool reads a part of the policy only from project files, so it is not written into the user file.";
|
|
1834
|
+
readonly 'user-directory-relative': "apply --user: the environment variable that names the directory of the user file (XDG_CONFIG_HOME, APPDATA) is a relative path, so the tool looks in another place from each directory, or where it looks then is not known; nothing is written for it.";
|
|
1835
|
+
readonly 'user-file-uncertain': "apply --user: a library the tool calls decides where its user file is (platformdirs for Poetry and PDM on macOS), and its version cannot be read from files, so apply --user writes where the tool's docs say and names the other files the tool may read.";
|
|
1836
|
+
readonly 'user-setting-overridden': "apply --user: a file the tool reads ahead of its user file (~/.npmrc for pnpm 10) sets a key apply writes there to another value, so the value written has no effect.";
|
|
1837
|
+
readonly 'version-not-used': "apply --user: --for gives the version of a tool that apply --user does not configure (not in PATH with targets \"auto\", or not listed under targets).";
|
|
1838
|
+
readonly 'cooldown-not-written': "The release-age delay cannot be written for a tool.";
|
|
1839
|
+
readonly 'cooldown-shortened': "A release-age delay set outside depcurfew's markers (in JSON files also one an earlier apply wrote) is longer than the policy, and apply replaces it with the shorter one.";
|
|
1840
|
+
readonly 'cooldown-environment-only': "pip reads no project configuration, so the release-age delay has to be set in the environment where pip runs.";
|
|
1841
|
+
readonly 'exclude-pattern-unsupported': "A cooldown exception uses a wildcard the tool does not support, so it is not written.";
|
|
1842
|
+
readonly 'exclude-unsupported': "The tool has no setting for cooldown exceptions, so the release-age delay applies to every package.";
|
|
1843
|
+
readonly 'exclude-not-written': "The setting that holds the cooldown exceptions (Composer's config.policy.cooldown.ignore) has a value of a type the tool does not accept there, so apply leaves it as it is and does not add the exceptions; the release-age delay is written.";
|
|
1844
|
+
readonly 'exclude-entry-outside-policy': "A cooldown exception in a file without depcurfew's markers (Composer's config.policy.cooldown.ignore) exempts a package that cooldown.exclude does not; apply removes only the entries it wrote, recognised by their reason, and leaves this one.";
|
|
1845
|
+
readonly 'allow-pattern-unsupported': "An installScripts.allow entry is a pattern, but the tool takes package names only, so it is not written.";
|
|
1846
|
+
readonly 'deny-pattern-unsupported': "An installScripts.deny entry is a pattern, but the tool records denials by package name only, so it is not written.";
|
|
1847
|
+
readonly 'allow-all-unsupported': "The tool has no setting that allows every install script, so installScripts \"allow\" is not written.";
|
|
1848
|
+
readonly 'allow-all-replaced': "A setting that allowed every install script (with any value the tool reads as true, also one that a YAML merge key brings in, or pnpm 10's dangerously-allow-all-builds in .npmrc) was set to false, or replaced with the policy's allow-list where the setting is the list itself (Deno's allowScripts, Composer's allow-plugins, which then may be {}), because the policy denies install scripts.";
|
|
1849
|
+
readonly 'allow-list-entry-outside-policy': "An install-script allow-list in the repository (with apply --user, in a user file) lets a package run scripts the policy does not allow; apply does not remove it.";
|
|
1850
|
+
readonly 'deny-conflicts-with-file': "An entry in a tool's own list lets a package of installScripts.deny run its install scripts, or blocks a package of installScripts.allow; apply does not change entries it did not write.";
|
|
1851
|
+
readonly 'deny-needs-optional-dependencies': "A package of installScripts.deny takes its binary from an optional dependency (esbuild), but npm leaves optional dependencies out (omit=optional or optional=false in .npmrc, --omit=optional or --no-optional on an install of a CI job or Dockerfile, NPM_CONFIG_OMIT=optional), so the package cannot find its binary after that install.";
|
|
1852
|
+
readonly 'unexpected-value': "A setting apply adds to has a value of a type the tool does not accept there, so apply leaves it as it is and writes nothing into it.";
|
|
1853
|
+
readonly 'built-in-list-replaced': "Bun's built-in list of trusted packages is replaced by the policy's allow-list.";
|
|
1854
|
+
readonly 'scripts-not-enforceable': "The pinned (with apply --user, the installed) npm or pnpm, or another npm the repository runs (one a declared Node.js comes with, or a CI job or Dockerfile installs), runs every install script and has no allow-list, so \"deny\" cannot be enforced; also given for an npm the repository runs whose version depcurfew cannot tell, which counts as one that runs them.";
|
|
1855
|
+
readonly 'strict-allow-scripts-for-npm-in-use': "An npm the repository runs (the pinned one, one a CI job or Dockerfile installs, or one a declared Node.js comes with, for a project also through the root version files and root Dockerfiles that build it) is 11.16 to 11.x and runs install scripts the allow-list does not cover unless strict-allow-scripts is set, so apply writes strict-allow-scripts=true, which makes npm 12 stop the install at such a script; not given when that npm is the pinned one and no place runs npm 12.";
|
|
1856
|
+
readonly 'before-overrides-cooldown': "before= in .npmrc takes the place of the release-age delay apply writes, or makes npm stop with an error next to it (apply --user then does not write the delay).";
|
|
1857
|
+
readonly 'exact-version-gap': "The pinned (with apply --user, the installed) pnpm skips the release-age delay for packages requested by exact version.";
|
|
1858
|
+
readonly 'source-not-enforceable': "The tool has no setting for a dependency source the policy denies; doctor checks the lockfile instead.";
|
|
1859
|
+
readonly 'source-switch-indirect-only': "The pnpm setting for git and tarball sources stops them only for indirect dependencies.";
|
|
1860
|
+
readonly 'source-switch-combined': "One pnpm setting covers git and tarball sources, so denying one denies both.";
|
|
1861
|
+
readonly 'forced-source-build': "Python packages on the allow-list are always built from source, because no tool can only allow a build.";
|
|
1862
|
+
readonly 'build-switch-scope': "The tool's switch forbids building only packages from an index; git, local and archive dependencies are still built.";
|
|
1863
|
+
readonly 'build-switch-install-only': "Poetry applies its build switch when installing; poetry lock may still build source distributions.";
|
|
1864
|
+
readonly 'build-switch-environment-only': "The tool has no project setting that forbids building from source, so it has to be set in the environment where the tool runs.";
|
|
1865
|
+
readonly 'requirements-file-missing': "There is no requirements file to write pip options into.";
|
|
1866
|
+
readonly 'manifest-missing': "A setting belongs in a project manifest (package.json, deno.json, pyproject.toml, Pipfile, composer.json) that does not exist, and apply does not create manifests.";
|
|
1867
|
+
readonly 'lockfile-out-of-date': "The lockfile records, or is checked against, a setting apply changed, so it has to be locked again.";
|
|
1868
|
+
readonly 'file-ignored-by-git': "A file apply writes matches a .gitignore rule, so the change may never reach the repository.";
|
|
1869
|
+
readonly 'bot-config-missing': "A bot has no configuration file, and depcurfew does not create one.";
|
|
1870
|
+
readonly 'bot-config-not-edited': "A bot configuration file is in a place or format depcurfew does not edit; a snippet shows what to add.";
|
|
1871
|
+
readonly 'bot-entries-missing': "The Dependabot configuration has no entries for the package managers in use.";
|
|
1872
|
+
readonly 'bot-config-unreadable': "The Dependabot configuration uses a YAML alias, which Dependabot does not accept, so it reads no configuration from the file (the cooldown apply writes included), and depcurfew does not read the delays the file sets.";
|
|
1873
|
+
readonly 'bot-rule-shortens-delay': "A Renovate packageRules entry apply does not manage gives every package of a registry, Renovate manager or category in use, or the actions of the workflows, or some update types of them, a shorter release-age delay than the one apply writes; Renovate uses it, and apply does not change it.";
|
|
1874
|
+
readonly 'security-delay-unsupported': "Dependabot cannot delay security updates, so securityUpdates \"delay\" is not applied.";
|
|
1875
|
+
};
|
|
1876
|
+
export declare const APPLY_ERRORS: {
|
|
1877
|
+
readonly 'cannot-edit': "A file cannot be read or edited safely (invalid syntax, a symlink, a style depcurfew does not edit), so nothing is written.";
|
|
1878
|
+
readonly 'cannot-write': "A file cannot be written (no permission, a read-only file system, a full disk, a directory or symlink in its place); the files already written are put back, so nothing changes.";
|
|
1879
|
+
readonly 'policy-missing': "The directory has no depcurfew.json.";
|
|
1880
|
+
readonly 'policy-invalid': "depcurfew.json does not follow the schema; issues lists the problems.";
|
|
1881
|
+
readonly 'policy-unreadable': "depcurfew.json cannot be read.";
|
|
1882
|
+
readonly 'policy-exists': "depcurfew init found a depcurfew.json and writes it only with --force.";
|
|
1883
|
+
readonly 'directory-not-found': "The directory given on the command line does not exist or cannot be opened.";
|
|
1884
|
+
readonly 'not-a-directory': "The path given on the command line is a file, not a directory.";
|
|
1885
|
+
readonly usage: "The command line is not valid: an unknown command, option or hook, a missing or extra argument, an option without its value or with one it does not take (such as a --registry that is not an http:// or https:// URL), or options that cannot go together (exit code 2); with --json the document holds only schemaVersion and this error.";
|
|
1886
|
+
readonly 'project-not-found': "A directory listed in projects in depcurfew.json does not exist, so nothing is written.";
|
|
1887
|
+
readonly 'project-not-a-directory': "A path listed in projects in depcurfew.json is a file, not a directory, so nothing is written.";
|
|
1888
|
+
readonly 'project-symlink': "A directory listed in projects in depcurfew.json is a symlink or lies under one, which may lead out of the repository, so nothing is written.";
|
|
1889
|
+
readonly 'project-has-policy': "A directory listed in projects has a depcurfew.json of its own, a second policy for the same files, so nothing is written.";
|
|
1890
|
+
readonly 'project-is-repository': "A directory listed in projects is a separate git repository or lies in one (it has a .git of its own, as a git submodule does), whose files belong to that repository, so nothing is written.";
|
|
1891
|
+
readonly 'project-in-workspace': "A directory listed in projects is, holds or lies in a workspace package of the repository root, which the root configuration already covers, so nothing is written.";
|
|
1892
|
+
readonly 'home-missing': "apply --user and init --user: the home directory (HOME, USERPROFILE on Windows) does not exist or is not an absolute path, so nothing is written.";
|
|
1893
|
+
};
|
|
1894
|
+
type ApplyWarningCode = keyof typeof APPLY_WARNINGS;
|
|
1895
|
+
type ApplyErrorCode = keyof typeof APPLY_ERRORS;
|
|
1896
|
+
interface ApplyWarning {
|
|
1897
|
+
code: ApplyWarningCode;
|
|
1898
|
+
/** The directory listed in projects that the warning concerns; absent for the repository root. */
|
|
1899
|
+
project?: string;
|
|
1900
|
+
/** The tool it concerns, when there is one. */
|
|
1901
|
+
tool?: ToolId;
|
|
1902
|
+
/** The file it concerns, when there is one, and the line when it is known. */
|
|
1903
|
+
location?: {
|
|
1904
|
+
file: string;
|
|
1905
|
+
line?: number;
|
|
1906
|
+
};
|
|
1907
|
+
/** What happened, for people; it starts with the tool's name when it concerns one. */
|
|
1908
|
+
message: string;
|
|
1909
|
+
/** What to do about it, when there is something to do. */
|
|
1910
|
+
fix?: string;
|
|
1911
|
+
}
|
|
1912
|
+
/** A warning as one line of text, as ApplyReport.warnings has it. */
|
|
1913
|
+
export declare function warningText(warning: ApplyWarning): string;
|
|
1914
|
+
interface FileChange {
|
|
1915
|
+
/** Repository-relative path with `/` separators. */
|
|
1916
|
+
file: string;
|
|
1917
|
+
/** null for a new file. */
|
|
1918
|
+
before: string | null;
|
|
1919
|
+
after: string;
|
|
1920
|
+
}
|
|
1921
|
+
/**
|
|
1922
|
+
* A file could not be written. writeChanges has removed its temporary files and put back the files it had already
|
|
1923
|
+
* replaced, so nothing changed, except for the files in `unrestored` (whose earlier text is in `backups` when it could
|
|
1924
|
+
* be written) and the temporary files in `leftovers`. The message says why, without the file name.
|
|
1925
|
+
*/
|
|
1926
|
+
declare class WriteFailed extends Error {
|
|
1927
|
+
readonly file: string;
|
|
1928
|
+
/** Why, in plain words. */
|
|
1929
|
+
readonly reason: string;
|
|
1930
|
+
/** Files already replaced that could not be put back (normally none). */
|
|
1931
|
+
readonly unrestored: string[];
|
|
1932
|
+
/** For files in `unrestored`: the temporary file, next to it, that holds its earlier text. */
|
|
1933
|
+
readonly backups: Readonly<Record<string, string>>;
|
|
1934
|
+
/** Temporary files that could not be removed, named like `file` (relative to the root, or as WriteOptions.name says). */
|
|
1935
|
+
readonly leftovers: string[];
|
|
1936
|
+
/** Another program holds the file, or (on Windows) it is read-only. */
|
|
1937
|
+
readonly busy: boolean;
|
|
1938
|
+
readonly platform: NodeJS.Platform;
|
|
1939
|
+
constructor(file: string, error: unknown, unrestored: string[], details?: {
|
|
1940
|
+
platform?: NodeJS.Platform;
|
|
1941
|
+
backups?: Record<string, string>;
|
|
1942
|
+
leftovers?: string[];
|
|
1943
|
+
});
|
|
1944
|
+
}
|
|
1945
|
+
interface Snippet {
|
|
1946
|
+
tool: ToolId;
|
|
1947
|
+
file: string;
|
|
1948
|
+
text: string;
|
|
1949
|
+
}
|
|
1950
|
+
/** A file a generator could not read or edit; nothing is written when there is one. */
|
|
1951
|
+
interface EditFailure {
|
|
1952
|
+
tool: ToolId;
|
|
1953
|
+
file: string;
|
|
1954
|
+
message: string;
|
|
1955
|
+
/** What to do, when the generic fix of cannot-edit does not say it. */
|
|
1956
|
+
fix?: string;
|
|
1957
|
+
}
|
|
1958
|
+
/** Why a directory listed in projects cannot be used; nothing is written and doctor reports it (invalid-project). */
|
|
1959
|
+
interface ProjectProblem {
|
|
1960
|
+
code: Extract<ApplyErrorCode, `project-${string}`>;
|
|
1961
|
+
/** The entry of projects it concerns. */
|
|
1962
|
+
project: string;
|
|
1963
|
+
location: {
|
|
1964
|
+
file: string;
|
|
1965
|
+
line?: number;
|
|
1966
|
+
};
|
|
1967
|
+
message: string;
|
|
1968
|
+
fix: string;
|
|
1969
|
+
}
|
|
1970
|
+
interface ApplyReport {
|
|
1971
|
+
/** Repository-relative paths, also for the files of the directories listed in projects. */
|
|
1972
|
+
changes: FileChange[];
|
|
1973
|
+
/** The warnings as one line of text each. */
|
|
1974
|
+
warnings: string[];
|
|
1975
|
+
/** The same warnings with a code, the tool, the file and what to do (apply --json). */
|
|
1976
|
+
warningDetails: ApplyWarning[];
|
|
1977
|
+
snippets: Snippet[];
|
|
1978
|
+
/** Files that could not be edited, as messages. When there are any, nothing is written. */
|
|
1979
|
+
errors: string[];
|
|
1980
|
+
/** The same failures with the tool and the file. */
|
|
1981
|
+
failures: EditFailure[];
|
|
1982
|
+
/**
|
|
1983
|
+
* Directories listed in projects that cannot be used (decision D-100); their messages come first in `errors`, and
|
|
1984
|
+
* nothing is written when there is one.
|
|
1985
|
+
*/
|
|
1986
|
+
problems?: ApplyProblem[];
|
|
1987
|
+
}
|
|
1988
|
+
/** An error that concerns no file being edited (a directory listed in projects, the home directory of apply --user). */
|
|
1989
|
+
interface ApplyProblem {
|
|
1990
|
+
code: ApplyErrorCode;
|
|
1991
|
+
location: {
|
|
1992
|
+
file: string;
|
|
1993
|
+
line?: number;
|
|
1994
|
+
};
|
|
1995
|
+
message: string;
|
|
1996
|
+
fix: string;
|
|
1997
|
+
}
|
|
1998
|
+
/** How a directory is planned in a repository with projects (decision D-100). */
|
|
1999
|
+
interface PlanOptions {
|
|
2000
|
+
/** The directory is the project listed so in projects (relative to the repository root): bots are not configured there. */
|
|
2001
|
+
project?: string;
|
|
2002
|
+
/** The directory is the root of a repository with projects: what the projects configure, which bots serve too. */
|
|
2003
|
+
projects?: {
|
|
2004
|
+
managerMinutes: readonly number[];
|
|
2005
|
+
targets: ReadonlySet<ToolId>;
|
|
2006
|
+
};
|
|
2007
|
+
/**
|
|
2008
|
+
* The project's installs in CI jobs of the repository root (working-directory: frontend), with their Node.js setup
|
|
2009
|
+
* steps, for the npm the project runs (decision D-105); jobsInProject gives them.
|
|
2010
|
+
*/
|
|
2011
|
+
ci?: {
|
|
2012
|
+
jobs: readonly WorkflowJob[];
|
|
2013
|
+
versions: readonly VersionDeclaration[];
|
|
2014
|
+
};
|
|
2015
|
+
/**
|
|
2016
|
+
* What the project takes from the repository root (decision D-109): the root's Node.js version files when it has none
|
|
2017
|
+
* of its own, and the stages of root Dockerfiles that build it; rootPlaces gives them.
|
|
2018
|
+
*/
|
|
2019
|
+
root?: RootPlaces;
|
|
2020
|
+
}
|
|
2021
|
+
/** The bots' delay: the policy's, or longer if a package manager rounded its delay up further. */
|
|
2022
|
+
export declare function botDuration(policy: Duration, managerMinutes: readonly number[]): Duration;
|
|
2023
|
+
/**
|
|
2024
|
+
* Computes every change of one directory without writing anything. With projects in the policy, `options` says which
|
|
2025
|
+
* part of the repository this directory is (planRepository plans them all).
|
|
2026
|
+
*/
|
|
2027
|
+
export declare function planApply(root: string, policy: ResolvedPolicy, detection: Detection, tools?: readonly ToolKnowledge[], options?: PlanOptions): ApplyReport;
|
|
2028
|
+
/**
|
|
2029
|
+
* Plans the whole repository: the root as planApply does, and each directory listed in projects as a project of its
|
|
2030
|
+
* own with the same policy (decision D-100). Bots are configured only from the root, with a delay at least as long
|
|
2031
|
+
* as any package manager of any project gets. Every path is relative to the root. A listed directory that cannot be
|
|
2032
|
+
* used is an error (`problems`), so that nothing is written; the other directories are still planned, so one run
|
|
2033
|
+
* reports everything.
|
|
2034
|
+
*/
|
|
2035
|
+
export declare function planRepository(root: string, policy: ResolvedPolicy, tools?: readonly ToolKnowledge[], rootDetection?: Detection): ApplyReport;
|
|
2036
|
+
export declare class PolicyError extends Error {
|
|
2037
|
+
readonly issues: string[];
|
|
2038
|
+
readonly code: ApplyErrorCode;
|
|
2039
|
+
/** What to do, when the message does not say it. */
|
|
2040
|
+
readonly fix: string | undefined;
|
|
2041
|
+
/** The line of the policy file the first problem concerns, when it can be found (decision D-116). */
|
|
2042
|
+
readonly line: number | undefined;
|
|
2043
|
+
constructor(message: string, issues?: string[], code?: ApplyErrorCode, fix?: string, line?: number);
|
|
2044
|
+
/** The message and what to do, as one line of text. */
|
|
2045
|
+
get text(): string;
|
|
2046
|
+
}
|
|
2047
|
+
export declare function loadPolicy(root: string): ResolvedPolicy;
|
|
2048
|
+
/**
|
|
2049
|
+
* A file could not be written (code cannot-write). The files apply had already replaced were put back, so the
|
|
2050
|
+
* repository is as it was, unless `unrestored` lists files. `report` is the plan that was being written.
|
|
2051
|
+
*/
|
|
2052
|
+
export declare class ApplyWriteError extends Error {
|
|
2053
|
+
readonly code: "cannot-write";
|
|
2054
|
+
/** The file that could not be written, relative to the repository. */
|
|
2055
|
+
readonly file: string;
|
|
2056
|
+
/** What to do. */
|
|
2057
|
+
readonly fix: string;
|
|
2058
|
+
/** Files already replaced that could not be put back (normally none). */
|
|
2059
|
+
readonly unrestored: string[];
|
|
2060
|
+
readonly report: ApplyReport;
|
|
2061
|
+
/**
|
|
2062
|
+
* `command` is what the fix tells the user to run again (depcurfew apply --user for user files), and
|
|
2063
|
+
* `versionControl` whether the files may be in version control, to restore them from.
|
|
2064
|
+
*/
|
|
2065
|
+
constructor(failure: WriteFailed, report: ApplyReport, options?: {
|
|
2066
|
+
command?: string;
|
|
2067
|
+
versionControl?: boolean;
|
|
2068
|
+
});
|
|
2069
|
+
}
|
|
2070
|
+
/**
|
|
2071
|
+
* Reads the policy, detects the project, plans the changes and (unless told otherwise) writes them. If any file
|
|
2072
|
+
* could not be edited, nothing is written, so a run either applies the whole policy or leaves the repository as it was.
|
|
2073
|
+
* A file that cannot be written throws ApplyWriteError after the files already written are put back.
|
|
2074
|
+
*/
|
|
2075
|
+
export declare function applyProject(root: string, options?: {
|
|
2076
|
+
write?: boolean;
|
|
2077
|
+
beforeWrite?: () => void;
|
|
2078
|
+
}): ApplyReport;
|
|
2079
|
+
export declare function checkSpec(request: string | CheckRequest, options: CheckOptions): Promise<PackageReport>;
|
|
2080
|
+
/**
|
|
2081
|
+
* Checks the specs a few at a time; the reports keep the order of the specs. When `options.signal` aborts, no further
|
|
2082
|
+
* spec is looked up and the promise rejects with the signal's reason.
|
|
2083
|
+
*/
|
|
2084
|
+
export declare function checkSpecs(specs: readonly (string | CheckRequest)[], options: CheckOptions): Promise<PackageReport[]>;
|
|
1165
2085
|
interface Paint {
|
|
1166
2086
|
red(text: string): string;
|
|
1167
2087
|
yellow(text: string): string;
|
|
@@ -1170,8 +2090,231 @@ interface Paint {
|
|
|
1170
2090
|
bold(text: string): string;
|
|
1171
2091
|
dim(text: string): string;
|
|
1172
2092
|
}
|
|
1173
|
-
|
|
1174
|
-
|
|
2093
|
+
interface CheckDependencies {
|
|
2094
|
+
fetch: FetchLike;
|
|
2095
|
+
now: () => Date;
|
|
2096
|
+
/** The whole of stdin, for hook mode. */
|
|
2097
|
+
stdin: () => Promise<string>;
|
|
2098
|
+
/** The user's home directory, with the user's .npmrc. */
|
|
2099
|
+
home?: () => string;
|
|
2100
|
+
/** The project root Claude Code gives hooks (CLAUDE_PROJECT_DIR); tests replace it. */
|
|
2101
|
+
projectDir?: () => string | undefined;
|
|
2102
|
+
/** --user: platform, home directory and environment variables (tests replace them). */
|
|
2103
|
+
userEnvironment?: () => UserEnvironment;
|
|
2104
|
+
/**
|
|
2105
|
+
* The platform the hook runs on: on Windows a POSIX shell (Git Bash) hands its variables to programs that do not tell
|
|
2106
|
+
* their names apart by case (decision D-143); tests replace it.
|
|
2107
|
+
*/
|
|
2108
|
+
platform?: () => NodeJS.Platform;
|
|
2109
|
+
/** depcurfew mcp: the streams the server talks over, its environment and signals (tests replace them). */
|
|
2110
|
+
mcpProcess?: () => McpProcess;
|
|
2111
|
+
}
|
|
2112
|
+
export declare function run(argv: readonly string[], output: CliIo, dependencies?: CheckDependencies): Promise<number>;
|
|
2113
|
+
export declare function getVersion(): string;
|
|
2114
|
+
interface DiffOptions {
|
|
2115
|
+
/** Shows the values of secrets as <redacted> in every line (redactSecrets), for a diff that people or logs see. */
|
|
2116
|
+
redact?: boolean;
|
|
2117
|
+
}
|
|
2118
|
+
/**
|
|
2119
|
+
* A unified diff with three lines of context; empty when the texts are equal. `before` is null for a new file.
|
|
2120
|
+
* Control characters in the file name are shown as text (printable), so that a name cannot start a line of its own
|
|
2121
|
+
* and forge a header or a hunk. A file with an absolute path (a user file outside the home directory) is named as it
|
|
2122
|
+
* is, without the a/ and b/ of repository paths.
|
|
2123
|
+
*/
|
|
2124
|
+
export declare function unifiedDiff(file: string, before: string | null, after: string, context?: number, options?: DiffOptions): string;
|
|
2125
|
+
/**
|
|
2126
|
+
* Where a command runs, relative to the directory the command line starts in: the directories it changes to, in order
|
|
2127
|
+
* (an absolute one starts over). `localPrefix`: npm --prefix names npm's local prefix, whose .npmrc npm reads without
|
|
2128
|
+
* looking for a package.json above it. `entered`: the indexes of the steps that the shell changes to (cd, pushd,
|
|
2129
|
+
* Set-Location), which fail and leave the shell where it was when the directory does not exist, so the hook checks that
|
|
2130
|
+
* it does (decision D-119); a directory that a mkdir earlier on the line creates is left out. `physical`: the indexes of
|
|
2131
|
+
* the steps resolved through symlinks (cd -P, set -P), as the system resolves them. `assumes`: directories the shell
|
|
2132
|
+
* changed to earlier on the line and left again (cd -, popd, cd /path), each of which must exist for this directory to
|
|
2133
|
+
* be right, as a change that fails leaves OLDPWD and the stack of pushd as they were (decision D-123); the hook checks
|
|
2134
|
+
* their entered steps too.
|
|
2135
|
+
*/
|
|
2136
|
+
interface KnownDirectory {
|
|
2137
|
+
steps: readonly string[];
|
|
2138
|
+
localPrefix?: true;
|
|
2139
|
+
entered?: readonly number[];
|
|
2140
|
+
physical?: readonly number[];
|
|
2141
|
+
assumes?: readonly KnownDirectory[];
|
|
2142
|
+
/**
|
|
2143
|
+
* In `assumes`: a Python virtual environment whose activation script the command runs with . or source, which the
|
|
2144
|
+
* hook trusts not to change directory only when this directory holds pyvenv.cfg (decision D-123).
|
|
2145
|
+
*/
|
|
2146
|
+
venv?: true;
|
|
2147
|
+
}
|
|
2148
|
+
/** A directory as KnownDirectory gives it, or why it cannot be told. */
|
|
2149
|
+
type CommandDirectory = KnownDirectory | {
|
|
2150
|
+
unknown: string;
|
|
2151
|
+
};
|
|
2152
|
+
/** Why a relative directory that the shell finds when it gets to it (pushd -n) cannot be followed, by the variables of the line. */
|
|
2153
|
+
interface RelativeDoubt {
|
|
2154
|
+
variables: ShellVariables;
|
|
2155
|
+
}
|
|
2156
|
+
/**
|
|
2157
|
+
* A change of directory by one command (decision D-123 for the stack): cd; cd - (`back`, with `push` for pushd -);
|
|
2158
|
+
* pushd dir (`keep`: pushd -n, which only puts dir on the stack; no path: Push-Location alone, which pushes the location
|
|
2159
|
+
* without changing it); pushd without a directory (`swap`); pushd +N or -N (`rotate`, `fromRight` for -N); popd with
|
|
2160
|
+
* +N or -N (`count`); dirs -c (`clear`); set -P or set +P (`mode`), which make later changes resolve the path physically
|
|
2161
|
+
* or not. `physical`: cd -P or cd -L chose. `stack`: the stack of pushd is unknown too. `lasting`: the directory of every
|
|
2162
|
+
* later command is unknown, whatever changes it later. `doubt`: the variables of the line, for a directory pushd -n put
|
|
2163
|
+
* on the stack, which the shell finds relative to where it is when it gets to it. `activate`: . or source of the
|
|
2164
|
+
* activation script of the virtual environment in `root`, which changes no directory when `root` holds pyvenv.cfg;
|
|
2165
|
+
* `reason` is why the directory is unknown when it cannot be trusted (decision D-123).
|
|
2166
|
+
*/
|
|
2167
|
+
type Move = {
|
|
2168
|
+
kind: 'cd';
|
|
2169
|
+
path: string;
|
|
2170
|
+
physical?: boolean;
|
|
2171
|
+
} | {
|
|
2172
|
+
kind: 'back';
|
|
2173
|
+
command: string;
|
|
2174
|
+
push?: true;
|
|
2175
|
+
} | {
|
|
2176
|
+
kind: 'push';
|
|
2177
|
+
path: string | undefined;
|
|
2178
|
+
keep?: true;
|
|
2179
|
+
} | {
|
|
2180
|
+
kind: 'swap';
|
|
2181
|
+
command: string;
|
|
2182
|
+
doubt?: RelativeDoubt;
|
|
2183
|
+
} | {
|
|
2184
|
+
kind: 'rotate';
|
|
2185
|
+
command: string;
|
|
2186
|
+
count: number;
|
|
2187
|
+
fromRight: boolean;
|
|
2188
|
+
keep: boolean;
|
|
2189
|
+
doubt?: RelativeDoubt;
|
|
2190
|
+
} | {
|
|
2191
|
+
kind: 'pop';
|
|
2192
|
+
command: string;
|
|
2193
|
+
count?: number;
|
|
2194
|
+
fromRight?: boolean;
|
|
2195
|
+
keep?: boolean;
|
|
2196
|
+
doubt?: RelativeDoubt;
|
|
2197
|
+
} | {
|
|
2198
|
+
kind: 'clear';
|
|
2199
|
+
} | {
|
|
2200
|
+
kind: 'mode';
|
|
2201
|
+
physical: boolean;
|
|
2202
|
+
} | {
|
|
2203
|
+
kind: 'unknown';
|
|
2204
|
+
reason: string;
|
|
2205
|
+
stack?: true;
|
|
2206
|
+
lasting?: true;
|
|
2207
|
+
} | {
|
|
2208
|
+
kind: 'activate';
|
|
2209
|
+
root: string;
|
|
2210
|
+
reason: string;
|
|
2211
|
+
};
|
|
2212
|
+
/**
|
|
2213
|
+
* Variables the command line sets that change where cd goes: CDPATH, OLDPWD (cd -), PWD and DIRSTACK (popd).
|
|
2214
|
+
* `dynamic`: why a variable whose name the shell builds may be set, which may be any of them (decision D-123).
|
|
2215
|
+
*/
|
|
2216
|
+
interface ShellVariables {
|
|
2217
|
+
cdPath: boolean;
|
|
2218
|
+
oldPwd: boolean;
|
|
2219
|
+
pwd: boolean;
|
|
2220
|
+
dirStack: boolean;
|
|
2221
|
+
dynamic?: string;
|
|
2222
|
+
}
|
|
2223
|
+
interface InstallRequest {
|
|
2224
|
+
tool: ToolId;
|
|
2225
|
+
/** The command as written, for messages. */
|
|
2226
|
+
command: string;
|
|
2227
|
+
spec: string;
|
|
2228
|
+
/** npm: the dist-tag the command makes npm prefer (--tag, npm_config_tag). */
|
|
2229
|
+
tag?: string;
|
|
2230
|
+
/** Python: the command allows pre-releases of the package, or forbids them; left out, the tool's default applies. */
|
|
2231
|
+
prereleases?: 'allow' | 'deny';
|
|
2232
|
+
/** Python: the command makes the tool build the package from source (true) or install wheels only (false). */
|
|
2233
|
+
buildFromSource?: boolean;
|
|
2234
|
+
/** Where the install runs, when the command changes directory before it or moves it with an option (decision D-118). */
|
|
2235
|
+
directory?: CommandDirectory;
|
|
2236
|
+
/** Composer: the command does not read the composer.json of the directory it runs in (create-project, global; decision D-133). */
|
|
2237
|
+
readsManifest?: false;
|
|
2238
|
+
/** Composer: composer update names the package without a constraint, so the one of the root composer.json applies. */
|
|
2239
|
+
fromManifest?: true;
|
|
2240
|
+
/** Composer: the command word, when the scripts of composer.json may change the command it stands for (decision D-136). */
|
|
2241
|
+
composerCommand?: {
|
|
2242
|
+
word: string;
|
|
2243
|
+
command: string;
|
|
2244
|
+
};
|
|
2245
|
+
}
|
|
2246
|
+
/** An option or variable of a command that weakens the release-age delay or the rule on install scripts (installOverrides). */
|
|
2247
|
+
interface Override {
|
|
2248
|
+
tool: ToolId;
|
|
2249
|
+
/** The command it is on, for messages. */
|
|
2250
|
+
command: string;
|
|
2251
|
+
/** As written: --min-release-age=0, npm_config_ignore_scripts=false. */
|
|
2252
|
+
written: string;
|
|
2253
|
+
weakens: OverrideSwitch['weakens'];
|
|
2254
|
+
/** protection: what it turns off (OverrideSwitch.effect). */
|
|
2255
|
+
effect?: string;
|
|
2256
|
+
/** protection: what that protection does, when it covers less than every package the command fetches (OverrideSwitch.protects). */
|
|
2257
|
+
protects?: string;
|
|
2258
|
+
/** The delay it sets, to compare with the policy; left out when any value weakens it. */
|
|
2259
|
+
delay?: string;
|
|
2260
|
+
/** The shell expands its value. */
|
|
2261
|
+
expanded?: boolean;
|
|
2262
|
+
/** A shell builtin or another form sets it, whose value depcurfew does not read (decision D-143); written is the name. */
|
|
2263
|
+
unread?: true;
|
|
2264
|
+
/** Where its command runs, when that is not where the command line starts (decision D-118). */
|
|
2265
|
+
directory?: CommandDirectory;
|
|
2266
|
+
}
|
|
2267
|
+
interface CommandScan {
|
|
2268
|
+
requests: InstallRequest[];
|
|
2269
|
+
/** Why an install command could not be read; undefined when every one could. */
|
|
2270
|
+
unreadable: string | undefined;
|
|
2271
|
+
/** Options and variables that weaken what depcurfew apply configured, to be compared with the policy. */
|
|
2272
|
+
overrides?: Override[];
|
|
2273
|
+
/** A command on the line changes the package manager's configuration; it counts when the line installs packages. */
|
|
2274
|
+
changes?: string;
|
|
2275
|
+
/** A package manager runs on the line, so variables that weaken it count. */
|
|
2276
|
+
manager?: boolean;
|
|
2277
|
+
/** How the command changes the directory of the shell that runs it (cd, pushd, eval 'cd …'), in order. */
|
|
2278
|
+
moves?: Move[];
|
|
2279
|
+
}
|
|
2280
|
+
interface ScanOptions {
|
|
2281
|
+
/** The shell that runs the line: a POSIX shell (the default; also Git Bash) or PowerShell. */
|
|
2282
|
+
shell?: 'posix' | 'powershell';
|
|
2283
|
+
/**
|
|
2284
|
+
* The line runs on Windows (by default where depcurfew runs), whose programs do not tell the names of environment
|
|
2285
|
+
* variables apart by case, so a POSIX shell's Composer=… sets COMPOSER there too (decision D-143).
|
|
2286
|
+
*/
|
|
2287
|
+
windows?: boolean;
|
|
2288
|
+
}
|
|
2289
|
+
/** The packages a command line installs or downloads to run with npm, pnpm, Yarn, Bun, Deno, pip, uv, Poetry, PDM, Pipenv or pipx. */
|
|
2290
|
+
export declare function scanCommandLine(line: string, options?: ScanOptions): CommandScan;
|
|
2291
|
+
interface DetectOptions {
|
|
2292
|
+
/** The directories listed in projects in depcurfew.json, relative to the root (decision D-137). */
|
|
2293
|
+
projects?: readonly string[];
|
|
2294
|
+
}
|
|
2295
|
+
export declare function detectProject(root: string, tools?: readonly ToolKnowledge[], environment?: EnvironmentKnowledge, options?: DetectOptions): Detection;
|
|
2296
|
+
/** Every doctor rule, in the order they are documented. unreadable-config runs last, after every other rule has read its files. */
|
|
2297
|
+
export declare const RULES: readonly Rule[];
|
|
2298
|
+
type FailOn = (typeof FAIL_ON_LEVELS)[number];
|
|
2299
|
+
/** Where the setting came from: the command line (or the Action input), doctor.failOn of the policy, or neither. */
|
|
2300
|
+
type FailOnFrom = 'option' | 'policy' | 'default';
|
|
2301
|
+
interface FailOnSetting {
|
|
2302
|
+
failOn: FailOn;
|
|
2303
|
+
from: FailOnFrom;
|
|
2304
|
+
/** from option: the option as given, "--strict" or "--fail-on warning". */
|
|
2305
|
+
option?: string;
|
|
2306
|
+
/** from policy: the policy file as messages show it, e.g. depcurfew.json. */
|
|
2307
|
+
file?: string;
|
|
2308
|
+
}
|
|
2309
|
+
export declare function countLevels(findings: readonly Finding[]): Record<'error' | 'warning' | 'note', number>;
|
|
2310
|
+
/**
|
|
2311
|
+
* `failOn`: which findings fail doctor and where that was set (decision D-112); when warnings make it fail, a line under
|
|
2312
|
+
* the counts says why, with how to fail on errors only. `strict` alone (before D-112) adds STRICT_NOTE.
|
|
2313
|
+
*/
|
|
2314
|
+
export declare function renderText(report: DoctorReport, rules: readonly Rule[], colors?: Paint, options?: {
|
|
2315
|
+
strict?: boolean;
|
|
2316
|
+
failOn?: FailOnSetting;
|
|
2317
|
+
}): string;
|
|
1175
2318
|
/** The text of a line in a repository file, to give regions the end column GitHub asks for. */
|
|
1176
2319
|
type LineReader = (file: string, line: number) => string | undefined;
|
|
1177
2320
|
/** Why doctor could not run, with a stable code. */
|
|
@@ -1180,16 +2323,26 @@ interface SarifError {
|
|
|
1180
2323
|
message: string;
|
|
1181
2324
|
fix?: string;
|
|
1182
2325
|
}
|
|
1183
|
-
declare function toSarif(report: DoctorReport, rules: readonly Rule[], version: string, readLine?: LineReader, errors?: readonly SarifError[]): object;
|
|
2326
|
+
export declare function toSarif(report: DoctorReport, rules: readonly Rule[], version: string, readLine?: LineReader, errors?: readonly SarifError[]): object;
|
|
1184
2327
|
/**
|
|
1185
2328
|
* Runs the rules on a repository. When depcurfew.json exists but is invalid, it throws PolicyError, or with
|
|
1186
2329
|
* invalidPolicy: 'report' runs the rules that need no policy and reports the problems as an invalid-policy finding.
|
|
2330
|
+
* `policy` passes the policy in instead of reading depcurfew.json (null: run as without one). With projects in the
|
|
2331
|
+
* policy, the rules also run on every listed directory with the same policy, and the report covers them all, with
|
|
2332
|
+
* paths relative to the root (decision D-100).
|
|
1187
2333
|
*/
|
|
1188
|
-
declare function runDoctor(root: string, options?: {
|
|
2334
|
+
export declare function runDoctor(root: string, options?: {
|
|
1189
2335
|
rules?: readonly Rule[];
|
|
1190
2336
|
tools?: readonly ToolKnowledge[];
|
|
1191
2337
|
invalidPolicy?: 'throw' | 'report';
|
|
2338
|
+
policy?: ResolvedPolicy | null;
|
|
2339
|
+
osv?: OsvDoctor;
|
|
1192
2340
|
}): DoctorReport;
|
|
2341
|
+
/**
|
|
2342
|
+
* runDoctor with known-malicious: the locked versions are looked up in OSV.dev first. Throws PolicyError as runDoctor
|
|
2343
|
+
* does for an invalid depcurfew.json, unless asked to report it.
|
|
2344
|
+
*/
|
|
2345
|
+
export declare function runDoctorWithOsv(root: string, options: Omit<Parameters<typeof runDoctor>[1] & object, 'osv'>, osv: OsvOptions): Promise<DoctorReport>;
|
|
1193
2346
|
type NumericDelayUnit = Exclude<DelayUnit, 'duration-string'>;
|
|
1194
2347
|
declare const DURATION_STRING_UNITS: readonly ["minutes", "hours", "days", "weeks"];
|
|
1195
2348
|
type DurationStringUnit = (typeof DURATION_STRING_UNITS)[number];
|
|
@@ -1210,19 +2363,19 @@ type CooldownConversion = {
|
|
|
1210
2363
|
reason: string;
|
|
1211
2364
|
fix: string;
|
|
1212
2365
|
};
|
|
1213
|
-
declare function convertMinutes(minutes: number, unit: NumericDelayUnit): Converted<number>;
|
|
2366
|
+
export declare function convertMinutes(minutes: number, unit: NumericDelayUnit): Converted<number>;
|
|
1214
2367
|
/** Writes minutes as a duration string such as `36 hours` or `3 days`. */
|
|
1215
|
-
declare function toDurationString(minutes: number, units: readonly DurationStringUnit[]): Converted<string>;
|
|
2368
|
+
export declare function toDurationString(minutes: number, units: readonly DurationStringUnit[]): Converted<string>;
|
|
1216
2369
|
/**
|
|
1217
2370
|
* Converts the policy's minimum release age into the value for one tool. When official sources disagree on the
|
|
1218
2371
|
* unit, the smallest unit wins: its larger number is long enough under every reading.
|
|
1219
2372
|
*/
|
|
1220
|
-
declare function convertCooldown(tool: ToolKnowledge, duration: Duration): CooldownConversion;
|
|
1221
|
-
declare const POLICY_FILE = "depcurfew.json";
|
|
2373
|
+
export declare function convertCooldown(tool: ToolKnowledge, duration: Duration): CooldownConversion;
|
|
2374
|
+
export declare const POLICY_FILE = "depcurfew.json";
|
|
1222
2375
|
/** Where the published package serves schema.json; init writes it into `$schema`. */
|
|
1223
|
-
declare const SCHEMA_URL = "https://unpkg.com/depcurfew/schema.json";
|
|
2376
|
+
export declare const SCHEMA_URL = "https://unpkg.com/depcurfew/schema.json";
|
|
1224
2377
|
/** What `depcurfew init` writes. Every section is explicit, so the file shows what can be configured. */
|
|
1225
|
-
declare const DEFAULT_POLICY: {
|
|
2378
|
+
export declare const DEFAULT_POLICY: {
|
|
1226
2379
|
readonly $schema: "https://unpkg.com/depcurfew/schema.json";
|
|
1227
2380
|
readonly version: 1;
|
|
1228
2381
|
readonly cooldown: {
|
|
@@ -1242,8 +2395,11 @@ declare const DEFAULT_POLICY: {
|
|
|
1242
2395
|
readonly directory: "allow";
|
|
1243
2396
|
};
|
|
1244
2397
|
readonly targets: "auto";
|
|
2398
|
+
readonly doctor: {
|
|
2399
|
+
readonly failOn: "warning";
|
|
2400
|
+
};
|
|
1245
2401
|
};
|
|
1246
|
-
declare function renderPolicy(policy: Policy): string;
|
|
2402
|
+
export declare function renderPolicy(policy: Policy): string;
|
|
1247
2403
|
type PolicyIssue = Issue;
|
|
1248
2404
|
type PolicyResult = {
|
|
1249
2405
|
ok: true;
|
|
@@ -1253,8 +2409,8 @@ type PolicyResult = {
|
|
|
1253
2409
|
issues: PolicyIssue[];
|
|
1254
2410
|
};
|
|
1255
2411
|
/** Validates a parsed policy document and fills in defaults. Reports every problem, each with the path to its field. */
|
|
1256
|
-
declare function validatePolicy(document: unknown, tools?: readonly ToolKnowledge[]): PolicyResult;
|
|
2412
|
+
export declare function validatePolicy(document: unknown, tools?: readonly ToolKnowledge[]): PolicyResult;
|
|
1257
2413
|
/** Parses and validates the text of a policy file. */
|
|
1258
|
-
declare function parsePolicy(source: string, tools?: readonly ToolKnowledge[]): PolicyResult;
|
|
1259
|
-
declare function formatIssue(issue: PolicyIssue): string;
|
|
1260
|
-
export {
|
|
2414
|
+
export declare function parsePolicy(source: string, tools?: readonly ToolKnowledge[]): PolicyResult;
|
|
2415
|
+
export declare function formatIssue(issue: PolicyIssue): string;
|
|
2416
|
+
export type { ApplyErrorCode, ApplyProblem, ApplyReport, ApplyWarning, ApplyWarningCode, CheckFinding, CheckOptions, CliIo, CommandDirectory, CommandScan, Converted, CooldownConversion, DetectOptions, Detection, DetectionWarning, DoctorReport, Duration, DurationResult, DurationStringUnit, FileChange, Finding, FindingCode, InstallRequest, KnownDirectory, Level, Location, Notice, NumericDelayUnit, OsvOptions, OsvPackage, OsvVerdict, PackageManagerInfo, PackageReport, PlanOptions, Policy, PolicyIssue, PolicyResult, ProjectProblem, ResolvedPolicy, Rule, ScanOptions, Snippet, VersionDeclaration };
|