depcurfew 1.0.0 → 1.2.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 +349 -46
- package/dist/cli.js +28 -4
- package/dist/index.d.ts +679 -28
- package/dist/index.js +1 -1
- package/dist/run-BBZUJJq-.js +33143 -0
- package/package.json +13 -2
- package/schema.json +24 -8
- package/dist/run-D7KMlsVh.js +0 -6595
package/dist/index.d.ts
CHANGED
|
@@ -1,19 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
declare const EXIT_FAILURE = 1;
|
|
3
|
-
declare const EXIT_USAGE = 2;
|
|
4
|
-
interface CliIo {
|
|
5
|
-
stdout(text: string): void;
|
|
6
|
-
stderr(text: string): void;
|
|
7
|
-
/** Whether each stream may be colored (see color.ts); absent means plain text. */
|
|
8
|
-
colors?: {
|
|
9
|
-
stdout: boolean;
|
|
10
|
-
stderr: boolean;
|
|
11
|
-
};
|
|
12
|
-
}
|
|
13
|
-
declare function run(argv: readonly string[], output: CliIo): Promise<number>;
|
|
14
|
-
declare function getVersion(): string;
|
|
15
|
-
type ToolId = 'npm' | 'pnpm' | 'yarn' | 'bun' | 'deno' | 'renovate' | 'dependabot';
|
|
1
|
+
type ToolId = 'npm' | 'pnpm' | 'yarn' | 'bun' | 'deno' | 'uv' | 'pip' | 'poetry' | 'pdm' | 'pipenv' | 'renovate' | 'dependabot';
|
|
16
2
|
type ToolKind = 'package-manager' | 'update-bot';
|
|
3
|
+
/** The registry a package manager installs from; package names in the policy are compared per ecosystem. */
|
|
4
|
+
type Ecosystem = 'npm' | 'pypi';
|
|
17
5
|
interface Evidence$1<T> {
|
|
18
6
|
value: T;
|
|
19
7
|
/** URL of official documentation or changelog. */
|
|
@@ -95,6 +83,8 @@ interface SourcesKnowledge {
|
|
|
95
83
|
interface WorkspaceDeclaration {
|
|
96
84
|
files: readonly string[];
|
|
97
85
|
key: string;
|
|
86
|
+
/** Key of a list of patterns that exclude packages, next to the package list; such patterns win. */
|
|
87
|
+
excludeKey?: string;
|
|
98
88
|
}
|
|
99
89
|
/** Facts for recognising a package manager in a repository. */
|
|
100
90
|
interface ProjectKnowledge {
|
|
@@ -102,25 +92,127 @@ interface ProjectKnowledge {
|
|
|
102
92
|
lockfiles: Fact<Versioned<readonly string[]>>;
|
|
103
93
|
/** Files that make a directory a project or a workspace package for this tool. */
|
|
104
94
|
manifests: Fact<readonly string[]>;
|
|
105
|
-
workspaces
|
|
95
|
+
/** `null` when the tool has no workspaces. In TOML files the key is a dotted path to a table. */
|
|
96
|
+
workspaces: Fact<WorkspaceDeclaration | null>;
|
|
106
97
|
/** Key of the package list when the workspace declaration is an object instead of a list; `null` if only lists are accepted. */
|
|
107
98
|
workspacesObjectKey: Fact<string | null>;
|
|
108
99
|
}
|
|
100
|
+
/** A platform family for user-level paths. */
|
|
101
|
+
type UserPlatform = 'linux' | 'darwin' | 'win32';
|
|
102
|
+
/** A base directory for a user-level file. */
|
|
103
|
+
interface UserDirectory {
|
|
104
|
+
/**
|
|
105
|
+
* Where it starts: an environment variable (used only when it is set and not empty, and with `absolute` only when
|
|
106
|
+
* it is an absolute path), or the home directory (HOME on Linux and macOS, USERPROFILE on Windows). With
|
|
107
|
+
* `relativeToCwd` the tool takes a relative value as it is, so it reads the file relative to the directory each
|
|
108
|
+
* command runs in; without it and without `absolute`, what the tool does with a relative value is not known.
|
|
109
|
+
*/
|
|
110
|
+
from: {
|
|
111
|
+
env: string;
|
|
112
|
+
absolute?: boolean;
|
|
113
|
+
relativeToCwd?: boolean;
|
|
114
|
+
} | 'home';
|
|
115
|
+
/** Path below it, with / separators; empty for the directory itself. */
|
|
116
|
+
path: string;
|
|
117
|
+
/** Use this directory only when it already exists (pip on macOS). */
|
|
118
|
+
ifExists?: boolean;
|
|
119
|
+
/** Skip this directory while this environment variable is set and not empty (pip 26.2+ on macOS and XDG_DATA_HOME). */
|
|
120
|
+
unlessEnv?: string;
|
|
121
|
+
}
|
|
122
|
+
/** Where the user file is on some platforms: the first directory that applies is used. */
|
|
123
|
+
interface UserLocation {
|
|
124
|
+
platforms: readonly UserPlatform[];
|
|
125
|
+
directories: readonly UserDirectory[];
|
|
126
|
+
file: string;
|
|
127
|
+
}
|
|
128
|
+
/** The user-level configuration file of a range of versions. */
|
|
129
|
+
interface UserFile {
|
|
130
|
+
format: 'ini' | 'yaml' | 'toml';
|
|
131
|
+
locations: readonly UserLocation[];
|
|
132
|
+
/** INI only: the [sections] whose keys the tool reads, '' for the keys above the first header; absent: every key. */
|
|
133
|
+
sections?: readonly string[];
|
|
134
|
+
/** Other user-level files this version reads the same settings from, whose keys win over this file's. */
|
|
135
|
+
overriddenBy?: readonly UserFile[];
|
|
136
|
+
/** INI only: the tool reads a key with underscores as the same key with dashes (pip: only_binary is only-binary). */
|
|
137
|
+
underscoresAsDashes?: boolean;
|
|
138
|
+
/** YAML only: the tool's loader applies merge keys (<<: *defaults), so merged keys count as keys of the map. */
|
|
139
|
+
mergeKeys?: boolean;
|
|
140
|
+
}
|
|
141
|
+
/** What depcurfew writes with a user-level setting. */
|
|
142
|
+
type UserSettingPurpose = 'cooldown' | 'cooldownExclude' | 'cooldownStrict' | 'scriptsAllow' | 'scriptsStrict' | 'buildsDeny' | 'buildsAllow' | 'sourceGit' | 'sourceRemote' | 'sourceFile' | 'sourceDirectory' | 'sourcesExotic';
|
|
143
|
+
/** A setting the tool reads from its user file: the key there and the versions that read it from that file. */
|
|
144
|
+
interface UserSetting {
|
|
145
|
+
purpose: UserSettingPurpose;
|
|
146
|
+
key: string;
|
|
147
|
+
since: string;
|
|
148
|
+
/** First version that no longer reads it from the user file. */
|
|
149
|
+
until?: string;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* The values of a switch that turns a protection off, given by the values the tool reads as off: every other value
|
|
153
|
+
* turns it on (fail closed), so a string "false" or "yes" that the tool takes for true is not missed. In INI files and
|
|
154
|
+
* environment variables values are compared as text after trimming, elsewhere with their type (false, 0, null, "").
|
|
155
|
+
*/
|
|
156
|
+
interface UnlessOff<T> {
|
|
157
|
+
except: readonly T[];
|
|
158
|
+
}
|
|
159
|
+
/** A value in the user file that weakens every project that does not set the key itself (doctor --user). */
|
|
160
|
+
interface UserWeakening {
|
|
161
|
+
key: string;
|
|
162
|
+
/** 'any' for every value, the values that weaken, or every value except those the tool reads as off. */
|
|
163
|
+
values: 'any' | readonly (string | number | boolean)[] | UnlessOff<string | number | boolean | null>;
|
|
164
|
+
/** What it does, for the message. */
|
|
165
|
+
effect: string;
|
|
166
|
+
}
|
|
167
|
+
/** An environment variable that bypasses or overrides the user file or the policy (doctor --user). */
|
|
168
|
+
interface UserVariable {
|
|
169
|
+
/** The variable name; matched without regard to case when `anyCase`. */
|
|
170
|
+
name: string;
|
|
171
|
+
anyCase?: boolean;
|
|
172
|
+
/** 'any' when every value matters, the values that weaken, or every value except those the tool reads as off. */
|
|
173
|
+
values: 'any' | readonly string[] | UnlessOff<string>;
|
|
174
|
+
effect: string;
|
|
175
|
+
/** First version that reads the variable; every version when absent. */
|
|
176
|
+
since?: string;
|
|
177
|
+
/** First version that no longer reads it. */
|
|
178
|
+
until?: string;
|
|
179
|
+
/** The file the tool reads when the variable is unset, from the home directory (~/.npmrc): naming it changes nothing. */
|
|
180
|
+
defaultPath?: string;
|
|
181
|
+
}
|
|
182
|
+
/** The tool's user-level configuration, for `depcurfew apply --user` and `doctor --user`. */
|
|
183
|
+
interface UserConfigKnowledge {
|
|
184
|
+
/** The user file per version, oldest first; null for versions without a user file. */
|
|
185
|
+
files: Fact<Versioned<UserFile | null>>;
|
|
186
|
+
/** Settings depcurfew can write there. */
|
|
187
|
+
settings: Fact<readonly UserSetting[]>;
|
|
188
|
+
/** What a version does with a key it does not know in the user file: ignore it, warn, or fail. */
|
|
189
|
+
unknownKeys: Fact<'ignore' | 'warn' | 'fail'>;
|
|
190
|
+
/** Whether a value the installed version cannot parse makes every command fail; depcurfew then needs the exact version. */
|
|
191
|
+
invalidValueFails: Fact<boolean>;
|
|
192
|
+
/** Values in the user file that weaken projects. */
|
|
193
|
+
weakening: Fact<readonly UserWeakening[]>;
|
|
194
|
+
/** Environment variables that move or skip the user file or override settings. */
|
|
195
|
+
variables: Fact<readonly UserVariable[]>;
|
|
196
|
+
}
|
|
109
197
|
interface ToolKnowledge {
|
|
110
198
|
id: ToolId;
|
|
111
199
|
name: string;
|
|
112
200
|
kind: ToolKind;
|
|
201
|
+
/** Package managers only; `npm` when absent. */
|
|
202
|
+
ecosystem?: Ecosystem;
|
|
113
203
|
cooldown: CooldownKnowledge;
|
|
114
204
|
installScripts: InstallScriptsKnowledge;
|
|
115
205
|
/** Package managers only: bots do not resolve dependency sources themselves. */
|
|
116
206
|
sources?: SourcesKnowledge;
|
|
117
|
-
/** Package managers only: how to
|
|
207
|
+
/** Package managers only: how to recognize the tool in a repository. */
|
|
118
208
|
project?: ProjectKnowledge;
|
|
209
|
+
/** Package managers only: the user-level configuration (`--user`). */
|
|
210
|
+
user?: UserConfigKnowledge;
|
|
119
211
|
/** Further sourced facts that matter for generators and `doctor`, keyed by a short name. */
|
|
120
212
|
extra?: Readonly<Record<string, Fact<unknown>>>;
|
|
121
213
|
}
|
|
122
|
-
/** A tool that environment facts refer to: Node.js or a package manager. */
|
|
123
|
-
type RuntimeOrTool = 'node' | ToolId;
|
|
214
|
+
/** A tool that environment facts refer to: Node.js, Python or a package manager. */
|
|
215
|
+
type RuntimeOrTool = 'node' | 'python' | ToolId;
|
|
124
216
|
interface PackageManagerFieldKnowledge {
|
|
125
217
|
/** Key in package.json, e.g. `packageManager`. */
|
|
126
218
|
key: string;
|
|
@@ -174,6 +266,122 @@ interface StopCommandsKnowledge {
|
|
|
174
266
|
stop: string;
|
|
175
267
|
resume: string;
|
|
176
268
|
}
|
|
269
|
+
/** The public npm registry, as depcurfew check reads it. */
|
|
270
|
+
interface NpmRegistryKnowledge {
|
|
271
|
+
url: string;
|
|
272
|
+
/** Path of a package document; scoped names have their slash escaped as %2f. */
|
|
273
|
+
packagePath: string;
|
|
274
|
+
/** Field of the full document that maps versions to publish times; the abbreviated install document lacks it. */
|
|
275
|
+
timeField: string;
|
|
276
|
+
/** Field of the document that maps tags (latest, next) to versions. */
|
|
277
|
+
distTagsField: string;
|
|
278
|
+
/** The tag every package has, which a spec without a version installs. */
|
|
279
|
+
defaultTag: string;
|
|
280
|
+
/** Field of the document that maps versions to their version objects. */
|
|
281
|
+
versionsField: string;
|
|
282
|
+
/** Field of a version object with the lifecycle scripts of its package.json. */
|
|
283
|
+
scriptsField: string;
|
|
284
|
+
/** Field of a version object that holds the deprecation message. */
|
|
285
|
+
deprecatedField: string;
|
|
286
|
+
/** Lifecycle scripts a package runs when it is installed as a dependency. */
|
|
287
|
+
installScripts: readonly string[];
|
|
288
|
+
}
|
|
289
|
+
/** How the Python interpreter runs a module (python -m pip) or a command string. */
|
|
290
|
+
interface PythonInterpreterKnowledge {
|
|
291
|
+
/** Executable names; versioned names such as python3.12 are the same. */
|
|
292
|
+
executables: readonly string[];
|
|
293
|
+
/** Runs the named module as a script; the options end there. */
|
|
294
|
+
moduleOption: string;
|
|
295
|
+
/** Runs a command string; the options end there. */
|
|
296
|
+
commandOption: string;
|
|
297
|
+
/** Other options followed by a separate value. */
|
|
298
|
+
valueOptions: readonly string[];
|
|
299
|
+
/**
|
|
300
|
+
* One-letter options may be grouped after one dash (-Im pip): flags, then perhaps an option with a value, which is
|
|
301
|
+
* the rest of the word or the next word.
|
|
302
|
+
*/
|
|
303
|
+
groupedShortOptions: boolean;
|
|
304
|
+
}
|
|
305
|
+
/** The JSON form of the simple repository API of a Python package index (PEP 691, PEP 700, PEP 792). */
|
|
306
|
+
interface PypiIndexKnowledge {
|
|
307
|
+
/** Base URL; a project's page is the normalized name followed by a slash. */
|
|
308
|
+
url: string;
|
|
309
|
+
/** Accept header that asks for the JSON form. */
|
|
310
|
+
accept: string;
|
|
311
|
+
/** Status of a project the index does not know. */
|
|
312
|
+
notFoundStatus: number;
|
|
313
|
+
/** Key of the list of distribution files, and the keys of a file. */
|
|
314
|
+
filesField: string;
|
|
315
|
+
filenameField: string;
|
|
316
|
+
/** ISO 8601 upload time in UTC; optional, since API version 1.1. */
|
|
317
|
+
uploadTimeField: string;
|
|
318
|
+
/** true or a reason string when the file is yanked. */
|
|
319
|
+
yankedField: string;
|
|
320
|
+
/** Key of the list of all versions of the project, since API version 1.1. */
|
|
321
|
+
versionsField: string;
|
|
322
|
+
/** Key of the project status object, its status and its reason, since API version 1.4. */
|
|
323
|
+
statusField: string;
|
|
324
|
+
statusValueField: string;
|
|
325
|
+
statusReasonField: string;
|
|
326
|
+
}
|
|
327
|
+
/** Project status markers (PEP 792): which mean that no file may be downloaded, and which installers may warn about. */
|
|
328
|
+
interface ProjectStatusKnowledge {
|
|
329
|
+
/** The index offers no file of the project. */
|
|
330
|
+
blocked: readonly string[];
|
|
331
|
+
/** Installers may warn about the project. */
|
|
332
|
+
warned: readonly string[];
|
|
333
|
+
}
|
|
334
|
+
/** Claude Code hooks that run before a tool call and can block it (used by depcurfew check --hook claude-code). */
|
|
335
|
+
interface ClaudeCodeHooksKnowledge {
|
|
336
|
+
/** Project settings file that can be committed. */
|
|
337
|
+
settingsFile: string;
|
|
338
|
+
event: string;
|
|
339
|
+
/** Separates the tool names of a matcher that lists several, each matched exactly. */
|
|
340
|
+
matcherSeparator: string;
|
|
341
|
+
/** Fields of the JSON a command hook reads on stdin. */
|
|
342
|
+
input: {
|
|
343
|
+
event: string;
|
|
344
|
+
tool: string;
|
|
345
|
+
command: string;
|
|
346
|
+
cwd: string;
|
|
347
|
+
};
|
|
348
|
+
/** Fields of the JSON object a hook prints to decide. */
|
|
349
|
+
output: {
|
|
350
|
+
container: string;
|
|
351
|
+
event: string;
|
|
352
|
+
decision: string;
|
|
353
|
+
reason: string;
|
|
354
|
+
};
|
|
355
|
+
decisions: {
|
|
356
|
+
deny: string;
|
|
357
|
+
ask: string;
|
|
358
|
+
};
|
|
359
|
+
/** A command hook that exits with this code blocks the call; stderr is the reason. */
|
|
360
|
+
blockingExitCode: number;
|
|
361
|
+
/** The handler field that blocks the call when the hook fails or times out, and the version that has it. */
|
|
362
|
+
onFailure: {
|
|
363
|
+
field: string;
|
|
364
|
+
value: string;
|
|
365
|
+
since: string;
|
|
366
|
+
};
|
|
367
|
+
/** Placeholder for the project root in a hook command. */
|
|
368
|
+
projectDir: string;
|
|
369
|
+
/** Keys of a settings file: the hooks map, a matcher group and a command handler run without a shell. */
|
|
370
|
+
settings: {
|
|
371
|
+
hooks: string;
|
|
372
|
+
matcher: string;
|
|
373
|
+
handlers: string;
|
|
374
|
+
type: string;
|
|
375
|
+
commandType: string;
|
|
376
|
+
command: string;
|
|
377
|
+
args: string;
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
/** 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
|
+
interface ClaudeCodeShellTool {
|
|
382
|
+
tool: string;
|
|
383
|
+
shell: 'bash' | 'powershell';
|
|
384
|
+
}
|
|
177
385
|
/**
|
|
178
386
|
* Annotation workflow commands: `::{command} {property}={value},…::{message}` on a line of its own. Values are
|
|
179
387
|
* escaped by replacing each character listed in `escape` (in order), properties also with `escapeProperty`.
|
|
@@ -198,10 +406,56 @@ interface AnnotationLimitsKnowledge {
|
|
|
198
406
|
perJob: number;
|
|
199
407
|
}
|
|
200
408
|
/** The Markdown job summary of a step: the environment variable naming its file and its size limit. */
|
|
409
|
+
/** Creating a check run with the workflow token, so that the result shows in the pull request's Checks tab. */
|
|
410
|
+
interface CheckRunsKnowledge {
|
|
411
|
+
/** POST endpoint, with {repository} as owner/name. */
|
|
412
|
+
endpoint: string;
|
|
413
|
+
apiVersion: string;
|
|
414
|
+
/** The workflow permission the token needs. */
|
|
415
|
+
permission: Record<string, string>;
|
|
416
|
+
/** Limit of output.summary, in characters. */
|
|
417
|
+
summaryMaxLength: number;
|
|
418
|
+
/** Variables the runner sets: API URL, owner/name, commit and the event payload file. */
|
|
419
|
+
variables: {
|
|
420
|
+
apiUrl: string;
|
|
421
|
+
repository: string;
|
|
422
|
+
sha: string;
|
|
423
|
+
eventPath: string;
|
|
424
|
+
};
|
|
425
|
+
/** Path in the event payload of the commit a pull request check belongs to (GITHUB_SHA is the merge commit there). */
|
|
426
|
+
pullRequestHeadSha: readonly string[];
|
|
427
|
+
}
|
|
201
428
|
interface StepSummaryKnowledge {
|
|
202
429
|
variable: string;
|
|
203
430
|
maxBytes: number;
|
|
204
431
|
}
|
|
432
|
+
interface GitIgnoreKnowledge {
|
|
433
|
+
/** The per-directory pattern file, e.g. .gitignore. */
|
|
434
|
+
file: string;
|
|
435
|
+
/** The repository's own pattern file, relative to the working tree. */
|
|
436
|
+
excludeFile: string;
|
|
437
|
+
/** The index, whose entries are the tracked files. */
|
|
438
|
+
indexFile: string;
|
|
439
|
+
/** Whether a pattern also hides a file git already tracks (it does not). */
|
|
440
|
+
trackedFilesIgnored: boolean;
|
|
441
|
+
}
|
|
442
|
+
/** npm's shims for the programs of packages on Windows. */
|
|
443
|
+
interface CommandShimsKnowledge {
|
|
444
|
+
/** The files written next to each program (besides a sh script). */
|
|
445
|
+
extensions: readonly string[];
|
|
446
|
+
/** The shim cmd.exe runs, which passes its arguments on with `argumentsVariable`, so they are read once more. */
|
|
447
|
+
batchExtension: string;
|
|
448
|
+
argumentsVariable: string;
|
|
449
|
+
}
|
|
450
|
+
/** The git setting that makes .gitignore patterns and index lookups ignore case. */
|
|
451
|
+
interface GitIgnoreCaseKnowledge {
|
|
452
|
+
/** The repository's configuration file, relative to the working tree. */
|
|
453
|
+
configFile: string;
|
|
454
|
+
section: string;
|
|
455
|
+
key: string;
|
|
456
|
+
/** Values read as true (case-insensitive); a key without a value is true too. */
|
|
457
|
+
trueValues: readonly string[];
|
|
458
|
+
}
|
|
205
459
|
interface EnvironmentKnowledge {
|
|
206
460
|
corepack: {
|
|
207
461
|
packageManagerField: Fact<PackageManagerFieldKnowledge>;
|
|
@@ -228,14 +482,72 @@ interface EnvironmentKnowledge {
|
|
|
228
482
|
pnpmSetup: Fact<SetupActionKnowledge>;
|
|
229
483
|
setupBun: Fact<SetupActionKnowledge>;
|
|
230
484
|
setupDeno: Fact<SetupActionKnowledge>;
|
|
485
|
+
setupPython: Fact<SetupActionKnowledge>;
|
|
486
|
+
setupUv: Fact<SetupActionKnowledge>;
|
|
487
|
+
/** Third-party actions that install Poetry; Poetry has no official action. */
|
|
488
|
+
installPoetry: Fact<SetupActionKnowledge>;
|
|
489
|
+
actionsPoetry: Fact<SetupActionKnowledge>;
|
|
231
490
|
codeScanningUpload: Fact<CodeScanningUploadKnowledge>;
|
|
232
491
|
stopCommands: Fact<StopCommandsKnowledge>;
|
|
233
492
|
annotations: Fact<AnnotationsKnowledge>;
|
|
234
493
|
annotationLimits: Fact<AnnotationLimitsKnowledge>;
|
|
235
494
|
stepSummary: Fact<StepSummaryKnowledge>;
|
|
495
|
+
checkRuns: Fact<CheckRunsKnowledge>;
|
|
496
|
+
};
|
|
497
|
+
npmRegistry: {
|
|
498
|
+
api: Fact<NpmRegistryKnowledge>;
|
|
499
|
+
notFoundStatus: Fact<number>;
|
|
500
|
+
/** Search endpoint, its result limit and the field with weekly downloads. */
|
|
501
|
+
search: Fact<{
|
|
502
|
+
path: string;
|
|
503
|
+
maxSize: number;
|
|
504
|
+
weeklyDownloadsField: string;
|
|
505
|
+
}>;
|
|
506
|
+
};
|
|
507
|
+
pypiIndex: {
|
|
508
|
+
api: Fact<PypiIndexKnowledge>;
|
|
509
|
+
projectStatus: Fact<ProjectStatusKnowledge>;
|
|
510
|
+
};
|
|
511
|
+
claudeCode: {
|
|
512
|
+
hooks: Fact<ClaudeCodeHooksKnowledge>;
|
|
513
|
+
/** The tools whose PreToolUse events carry a shell command in the hooks fact's input.command. */
|
|
514
|
+
shellTools: Fact<readonly ClaudeCodeShellTool[]>;
|
|
515
|
+
};
|
|
516
|
+
git: {
|
|
517
|
+
/** Where git reads the patterns of files it does not pick up, and the index that lists the tracked files. */
|
|
518
|
+
ignore: Fact<GitIgnoreKnowledge>;
|
|
519
|
+
ignoreCase: Fact<GitIgnoreCaseKnowledge>;
|
|
520
|
+
};
|
|
521
|
+
windows: {
|
|
522
|
+
commandShims: Fact<CommandShimsKnowledge>;
|
|
523
|
+
/** Files whose arguments PowerShell hands to cmd.exe as a raw command line. */
|
|
524
|
+
batchArguments: Fact<{
|
|
525
|
+
extensions: readonly string[];
|
|
526
|
+
}>;
|
|
527
|
+
};
|
|
528
|
+
python: {
|
|
529
|
+
/** How Python package names are compared: lowercased, with every run of these characters replaced by `replacement`. */
|
|
530
|
+
nameNormalization: Fact<{
|
|
531
|
+
separators: string;
|
|
532
|
+
replacement: string;
|
|
533
|
+
}>;
|
|
534
|
+
/** Python package names: ASCII letters and digits, `.`, `_` and `-`, starting and ending with a letter or digit. */
|
|
535
|
+
namePattern: Fact<string>;
|
|
536
|
+
/** The project file of a Python project, whose [tool.<name>] tables hold tool settings. */
|
|
537
|
+
projectFile: Fact<string>;
|
|
538
|
+
/** The file extension of a wheel, a built distribution that installs without running package code. */
|
|
539
|
+
wheelExtension: Fact<string>;
|
|
540
|
+
/** File extensions of source distributions on an index, which are built when installed. */
|
|
541
|
+
sourceDistributionExtensions: Fact<readonly string[]>;
|
|
542
|
+
interpreter: Fact<PythonInterpreterKnowledge>;
|
|
543
|
+
/** Hatch, which has no supply-chain settings of its own: how a project shows it uses Hatch. */
|
|
544
|
+
hatch: Fact<{
|
|
545
|
+
pyprojectTable: string;
|
|
546
|
+
configFile: string;
|
|
547
|
+
}>;
|
|
236
548
|
};
|
|
237
549
|
}
|
|
238
|
-
type PackageManagerId = Extract<ToolId, 'npm' | 'pnpm' | 'yarn' | 'bun' | 'deno'>;
|
|
550
|
+
type PackageManagerId = Extract<ToolId, 'npm' | 'pnpm' | 'yarn' | 'bun' | 'deno' | 'uv' | 'pip' | 'poetry' | 'pdm' | 'pipenv'>;
|
|
239
551
|
type BotId = Extract<ToolId, 'renovate' | 'dependabot'>;
|
|
240
552
|
/** Why a package manager is considered in use. Ordered from the strongest signal. */
|
|
241
553
|
type EvidenceKind = 'lockfile' | 'packageManager' | 'devEngines' | 'config' | 'ci' | 'default';
|
|
@@ -244,7 +556,8 @@ interface Evidence {
|
|
|
244
556
|
/** Repository-relative path with `/` separators. */
|
|
245
557
|
file: string;
|
|
246
558
|
}
|
|
247
|
-
|
|
559
|
+
/** tool-config: a version the tool's own configuration requires, e.g. required-version in [tool.uv]. */
|
|
560
|
+
type VersionSource = 'packageManager' | 'tool-config' | 'devEngines' | 'volta' | 'tool-versions' | 'github-actions';
|
|
248
561
|
/** A version declared somewhere in the repository, as written (it may be a range or an expression). */
|
|
249
562
|
type VersionDeclaration = ({
|
|
250
563
|
version: string;
|
|
@@ -296,6 +609,50 @@ interface Detection {
|
|
|
296
609
|
workflows: string[];
|
|
297
610
|
warnings: DetectionWarning[];
|
|
298
611
|
}
|
|
612
|
+
interface UserEnvironment {
|
|
613
|
+
platform: NodeJS.Platform;
|
|
614
|
+
/** HOME on Linux and macOS, USERPROFILE on Windows: what os.homedir() returns. */
|
|
615
|
+
home: string;
|
|
616
|
+
env: Readonly<Record<string, string | undefined>>;
|
|
617
|
+
/** The current directory, against which a tool resolves a relative XDG_CONFIG_HOME (relativeToCwd); process.cwd() by default. */
|
|
618
|
+
cwd?: string;
|
|
619
|
+
/** Whether a directory exists; replaceable in tests. */
|
|
620
|
+
isDirectory?: (path: string) => boolean;
|
|
621
|
+
}
|
|
622
|
+
type FetchLike = (url: string, init: {
|
|
623
|
+
headers: Record<string, string>;
|
|
624
|
+
signal: AbortSignal;
|
|
625
|
+
redirect?: 'manual';
|
|
626
|
+
}) => Promise<Response>;
|
|
627
|
+
declare const EXIT_OK = 0;
|
|
628
|
+
declare const EXIT_FAILURE = 1;
|
|
629
|
+
declare const EXIT_USAGE = 2;
|
|
630
|
+
interface CliIo {
|
|
631
|
+
stdout(text: string): void;
|
|
632
|
+
stderr(text: string): void;
|
|
633
|
+
/** Whether each stream may be colored (see color.ts); absent means plain text. */
|
|
634
|
+
colors?: {
|
|
635
|
+
stdout: boolean;
|
|
636
|
+
stderr: boolean;
|
|
637
|
+
};
|
|
638
|
+
/** Whether each stream is a terminal; absent means it is not. */
|
|
639
|
+
terminal?: {
|
|
640
|
+
stdout: boolean;
|
|
641
|
+
stderr: boolean;
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
interface CheckDependencies {
|
|
645
|
+
fetch: FetchLike;
|
|
646
|
+
now: () => Date;
|
|
647
|
+
/** The whole of stdin, for hook mode. */
|
|
648
|
+
stdin: () => Promise<string>;
|
|
649
|
+
/** The user's home directory, with the user's .npmrc. */
|
|
650
|
+
home?: () => string;
|
|
651
|
+
/** --user: platform, home directory and environment variables (tests replace them). */
|
|
652
|
+
userEnvironment?: () => UserEnvironment;
|
|
653
|
+
}
|
|
654
|
+
declare function run(argv: readonly string[], output: CliIo, dependencies?: CheckDependencies): Promise<number>;
|
|
655
|
+
declare function getVersion(): string;
|
|
299
656
|
interface Duration {
|
|
300
657
|
/** The duration as written in the policy. */
|
|
301
658
|
text: string;
|
|
@@ -366,6 +723,7 @@ declare const policyShape: Shape<{
|
|
|
366
723
|
installScripts?: {
|
|
367
724
|
default: "allow" | "deny";
|
|
368
725
|
allow?: readonly string[];
|
|
726
|
+
deny?: readonly string[];
|
|
369
727
|
};
|
|
370
728
|
sources?: {
|
|
371
729
|
git?: "allow" | "deny";
|
|
@@ -386,6 +744,7 @@ declare const policyShape: Shape<{
|
|
|
386
744
|
installScripts?: {
|
|
387
745
|
default: "allow" | "deny";
|
|
388
746
|
allow: readonly string[];
|
|
747
|
+
deny: readonly string[];
|
|
389
748
|
};
|
|
390
749
|
sources?: {
|
|
391
750
|
git?: "allow" | "deny";
|
|
@@ -398,6 +757,82 @@ declare const policyShape: Shape<{
|
|
|
398
757
|
type Policy = Input<typeof policyShape>;
|
|
399
758
|
/** The policy after validation: defaults filled in, durations parsed. */
|
|
400
759
|
type ResolvedPolicy = Output<typeof policyShape>;
|
|
760
|
+
declare const APPLY_WARNINGS: {
|
|
761
|
+
readonly 'nothing-to-configure': "depcurfew found no supported package manager or bot, so nothing is configured.";
|
|
762
|
+
readonly 'environment-overrides': "apply --user: an environment variable makes a tool read another user file or none, or overrides a value apply writes there.";
|
|
763
|
+
readonly 'depcurfew-outdated': "The project installs a newer depcurfew than the one running, which may not know settings that version writes.";
|
|
764
|
+
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.";
|
|
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;
|
|
401
836
|
interface FileChange {
|
|
402
837
|
/** Repository-relative path with `/` separators. */
|
|
403
838
|
file: string;
|
|
@@ -405,6 +840,30 @@ interface FileChange {
|
|
|
405
840
|
before: string | null;
|
|
406
841
|
after: string;
|
|
407
842
|
}
|
|
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
|
+
}
|
|
408
867
|
interface Snippet {
|
|
409
868
|
tool: ToolId;
|
|
410
869
|
file: string;
|
|
@@ -418,7 +877,10 @@ interface EditFailure {
|
|
|
418
877
|
}
|
|
419
878
|
interface ApplyReport {
|
|
420
879
|
changes: FileChange[];
|
|
880
|
+
/** The warnings as one line of text each. */
|
|
421
881
|
warnings: string[];
|
|
882
|
+
/** The same warnings with a code, the tool, the file and what to do (apply --json). */
|
|
883
|
+
warningDetails: ApplyWarning[];
|
|
422
884
|
snippets: Snippet[];
|
|
423
885
|
/** Files that could not be edited, as messages. When there are any, nothing is written. */
|
|
424
886
|
errors: string[];
|
|
@@ -431,18 +893,153 @@ declare function botDuration(policy: Duration, managerMinutes: readonly number[]
|
|
|
431
893
|
declare function planApply(root: string, policy: ResolvedPolicy, detection: Detection, tools?: readonly ToolKnowledge[]): ApplyReport;
|
|
432
894
|
declare class PolicyError extends Error {
|
|
433
895
|
readonly issues: string[];
|
|
434
|
-
|
|
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;
|
|
435
902
|
}
|
|
436
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
|
+
}
|
|
437
926
|
/**
|
|
438
927
|
* Reads the policy, detects the project, plans the changes and (unless told otherwise) writes them. If any file
|
|
439
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.
|
|
440
930
|
*/
|
|
441
931
|
declare function applyProject(root: string, options?: {
|
|
442
932
|
write?: boolean;
|
|
443
933
|
}): ApplyReport;
|
|
444
|
-
|
|
445
|
-
|
|
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 {
|
|
952
|
+
#private;
|
|
953
|
+
constructor(names: readonly string[]);
|
|
954
|
+
has(name: string): boolean;
|
|
955
|
+
/** The popular package `name` resembles, or undefined (also when `name` is itself popular). */
|
|
956
|
+
similar(name: string): SimilarName | undefined;
|
|
957
|
+
}
|
|
958
|
+
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';
|
|
960
|
+
interface CheckFinding {
|
|
961
|
+
level: 'error' | 'warning';
|
|
962
|
+
code: FindingCode;
|
|
963
|
+
message: string;
|
|
964
|
+
/** What to do about it. */
|
|
965
|
+
fix: string;
|
|
966
|
+
}
|
|
967
|
+
interface PackageReport {
|
|
968
|
+
spec: string;
|
|
969
|
+
kind: SpecKind | 'invalid';
|
|
970
|
+
name?: string;
|
|
971
|
+
version?: string;
|
|
972
|
+
publishedAt?: string;
|
|
973
|
+
latest?: {
|
|
974
|
+
version: string;
|
|
975
|
+
publishedAt?: string;
|
|
976
|
+
};
|
|
977
|
+
/** npm: the dist-tag the version was picked by, when it is not the default one (--tag, npm_config_tag, tag in .npmrc). */
|
|
978
|
+
tag?: string;
|
|
979
|
+
/** Python: pre-releases allowed or forbidden by the command. */
|
|
980
|
+
prereleases?: 'allow' | 'deny';
|
|
981
|
+
/** Python: the command builds the package from source (true) or installs wheels only (false). */
|
|
982
|
+
buildFromSource?: boolean;
|
|
983
|
+
findings: CheckFinding[];
|
|
984
|
+
}
|
|
985
|
+
/** A spec with what the install command changes about the version or the build. */
|
|
986
|
+
interface CheckRequest {
|
|
987
|
+
spec: string;
|
|
988
|
+
/** npm: the dist-tag npm prefers for a name without a version or with a range; the registry's default when left out. */
|
|
989
|
+
tag?: string;
|
|
990
|
+
/** The package manager is not npm, so tag= in .npmrc (CheckOptions.defaultTag) does not choose its version. */
|
|
991
|
+
ignoresConfigTag?: boolean;
|
|
992
|
+
/** Python: pre-releases allowed or forbidden; PEP 440's default when left out. */
|
|
993
|
+
prereleases?: 'allow' | 'deny';
|
|
994
|
+
/** Python: the package is built from source even when it has wheels (true), or only wheels are used (false). */
|
|
995
|
+
buildFromSource?: boolean;
|
|
996
|
+
}
|
|
997
|
+
interface CheckOptions {
|
|
998
|
+
policy: ResolvedPolicy;
|
|
999
|
+
fetch: FetchLike;
|
|
1000
|
+
now: Date;
|
|
1001
|
+
registry: string;
|
|
1002
|
+
popular: PopularNames;
|
|
1003
|
+
/** Registries the project assigns to scopes (@scope:registry in .npmrc); those packages are not looked up. */
|
|
1004
|
+
scopeRegistries?: ReadonlyMap<string, string>;
|
|
1005
|
+
/** The Python package index pypi: packages are looked up in; PyPI when not given. */
|
|
1006
|
+
index?: string;
|
|
1007
|
+
/** Popular Python projects, by normalized name, for look-alike names. */
|
|
1008
|
+
popularPython?: PopularNames;
|
|
1009
|
+
/** npm: the dist-tag npm prefers when a request names none (tag in .npmrc); the registry's default when left out. */
|
|
1010
|
+
defaultTag?: string;
|
|
1011
|
+
/**
|
|
1012
|
+
* Whether a finding about install scripts quotes them (the default) or names them only, for the hook: their body is
|
|
1013
|
+
* written by the package's publisher and should not reach the model as part of depcurfew's reason.
|
|
1014
|
+
*/
|
|
1015
|
+
scriptBodies?: boolean;
|
|
1016
|
+
}
|
|
1017
|
+
declare function checkSpec(request: string | CheckRequest, options: CheckOptions): Promise<PackageReport>;
|
|
1018
|
+
/** Checks the specs a few at a time; the reports keep the order of the specs. */
|
|
1019
|
+
declare function checkSpecs(specs: readonly (string | CheckRequest)[], options: CheckOptions): Promise<PackageReport[]>;
|
|
1020
|
+
interface InstallRequest {
|
|
1021
|
+
tool: ToolId;
|
|
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;
|
|
1036
|
+
}
|
|
1037
|
+
interface ScanOptions {
|
|
1038
|
+
/** The shell that runs the line: a POSIX shell (the default; also Git Bash) or PowerShell. */
|
|
1039
|
+
shell?: 'posix' | 'powershell';
|
|
1040
|
+
}
|
|
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;
|
|
446
1043
|
/**
|
|
447
1044
|
* Describes the repository at `root`. Never executes anything from the repository and never leaves it;
|
|
448
1045
|
* problems with individual files become warnings.
|
|
@@ -452,9 +1049,13 @@ declare class Repository {
|
|
|
452
1049
|
#private;
|
|
453
1050
|
readonly root: string;
|
|
454
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>;
|
|
455
1054
|
constructor(root: string);
|
|
456
1055
|
warn(code: string, message: string, files: readonly string[]): void;
|
|
457
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;
|
|
458
1059
|
/** The text of a file, without a byte order mark; undefined (with a warning when useful) if it cannot be read. */
|
|
459
1060
|
readText(path: string, limit?: number): string | undefined;
|
|
460
1061
|
/** The first bytes of a file, e.g. to recognize a lockfile format without reading a large file. */
|
|
@@ -465,6 +1066,8 @@ declare class Repository {
|
|
|
465
1066
|
listDirectories(path: string): string[];
|
|
466
1067
|
/** Parses a file with the given parser, reporting failures as warnings. */
|
|
467
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;
|
|
468
1071
|
relative(absolute: string): string;
|
|
469
1072
|
}
|
|
470
1073
|
/** The SARIF levels: an error fails `doctor`, warnings and notes do not. */
|
|
@@ -474,6 +1077,8 @@ interface Location {
|
|
|
474
1077
|
file: string;
|
|
475
1078
|
/** 1-based; absent when the finding concerns the whole file. */
|
|
476
1079
|
line?: number;
|
|
1080
|
+
/** doctor --user: the environment variable, as it is set, of a finding whose file is "environment". */
|
|
1081
|
+
variable?: string;
|
|
477
1082
|
}
|
|
478
1083
|
interface Finding {
|
|
479
1084
|
rule: string;
|
|
@@ -505,6 +1110,8 @@ interface Rule {
|
|
|
505
1110
|
id: string;
|
|
506
1111
|
/** One sentence: what the rule finds. */
|
|
507
1112
|
summary: string;
|
|
1113
|
+
/** What the rule checks, said as what holds when it passes, for the list of what doctor checked. */
|
|
1114
|
+
checks?: string;
|
|
508
1115
|
/** Why it matters. */
|
|
509
1116
|
description: string;
|
|
510
1117
|
/** How to fix it in general. */
|
|
@@ -515,11 +1122,43 @@ interface Rule {
|
|
|
515
1122
|
requiresPolicy: boolean;
|
|
516
1123
|
check(context: RuleContext): void;
|
|
517
1124
|
}
|
|
1125
|
+
/** A package manager or bot doctor checked, with the version it checked against when one is known. */
|
|
1126
|
+
interface CheckedTool {
|
|
1127
|
+
id: string;
|
|
1128
|
+
name: string;
|
|
1129
|
+
version?: string;
|
|
1130
|
+
/** Where the version comes from: a repository file, or PATH for doctor --user. */
|
|
1131
|
+
versionFrom?: string;
|
|
1132
|
+
}
|
|
1133
|
+
/** One check of the list of what doctor checked: a rule and what came of it. */
|
|
1134
|
+
interface RuleResult {
|
|
1135
|
+
rule: string;
|
|
1136
|
+
/** What the rule checks, in plain words (Rule.check). */
|
|
1137
|
+
check: string;
|
|
1138
|
+
/** passed: it ran and found nothing; problems: it reported findings; skipped: it needs a policy that is missing or invalid. */
|
|
1139
|
+
result: 'passed' | 'problems' | 'skipped';
|
|
1140
|
+
errors: number;
|
|
1141
|
+
warnings: number;
|
|
1142
|
+
notes: number;
|
|
1143
|
+
}
|
|
1144
|
+
/** What doctor looked at, shown below the result, so that a clean report says what it covers. */
|
|
1145
|
+
interface Checked {
|
|
1146
|
+
tools: CheckedTool[];
|
|
1147
|
+
/** Files that were read, repository-relative (doctor) or as shown (~/.npmrc, doctor --user). */
|
|
1148
|
+
files: string[];
|
|
1149
|
+
/** Every rule, in the order it ran, with its result. */
|
|
1150
|
+
rules: RuleResult[];
|
|
1151
|
+
/** The policy that skipped rules need, as messages show it. */
|
|
1152
|
+
policyFile: string;
|
|
1153
|
+
}
|
|
518
1154
|
interface DoctorReport {
|
|
519
1155
|
schemaVersion: 1;
|
|
520
|
-
|
|
1156
|
+
/** invalid: depcurfew.json does not follow the schema or cannot be read (only with invalidPolicy: 'report'). */
|
|
1157
|
+
policy: 'valid' | 'missing' | 'invalid';
|
|
521
1158
|
findings: Finding[];
|
|
522
1159
|
notices: Notice[];
|
|
1160
|
+
/** What was checked. */
|
|
1161
|
+
checked?: Checked;
|
|
523
1162
|
}
|
|
524
1163
|
/** Every doctor rule, in the order they are documented. unreadable-config runs last, after every other rule has read its files. */
|
|
525
1164
|
declare const RULES: readonly Rule[];
|
|
@@ -535,11 +1174,21 @@ declare function countLevels(findings: readonly Finding[]): Record<'error' | 'wa
|
|
|
535
1174
|
declare function renderText(report: DoctorReport, rules: readonly Rule[], colors?: Paint): string;
|
|
536
1175
|
/** The text of a line in a repository file, to give regions the end column GitHub asks for. */
|
|
537
1176
|
type LineReader = (file: string, line: number) => string | undefined;
|
|
538
|
-
|
|
539
|
-
|
|
1177
|
+
/** Why doctor could not run, with a stable code. */
|
|
1178
|
+
interface SarifError {
|
|
1179
|
+
code: string;
|
|
1180
|
+
message: string;
|
|
1181
|
+
fix?: string;
|
|
1182
|
+
}
|
|
1183
|
+
declare function toSarif(report: DoctorReport, rules: readonly Rule[], version: string, readLine?: LineReader, errors?: readonly SarifError[]): object;
|
|
1184
|
+
/**
|
|
1185
|
+
* Runs the rules on a repository. When depcurfew.json exists but is invalid, it throws PolicyError, or with
|
|
1186
|
+
* invalidPolicy: 'report' runs the rules that need no policy and reports the problems as an invalid-policy finding.
|
|
1187
|
+
*/
|
|
540
1188
|
declare function runDoctor(root: string, options?: {
|
|
541
1189
|
rules?: readonly Rule[];
|
|
542
1190
|
tools?: readonly ToolKnowledge[];
|
|
1191
|
+
invalidPolicy?: 'throw' | 'report';
|
|
543
1192
|
}): DoctorReport;
|
|
544
1193
|
type NumericDelayUnit = Exclude<DelayUnit, 'duration-string'>;
|
|
545
1194
|
declare const DURATION_STRING_UNITS: readonly ["minutes", "hours", "days", "weeks"];
|
|
@@ -559,6 +1208,7 @@ type CooldownConversion = {
|
|
|
559
1208
|
ok: false;
|
|
560
1209
|
tool: ToolId;
|
|
561
1210
|
reason: string;
|
|
1211
|
+
fix: string;
|
|
562
1212
|
};
|
|
563
1213
|
declare function convertMinutes(minutes: number, unit: NumericDelayUnit): Converted<number>;
|
|
564
1214
|
/** Writes minutes as a duration string such as `36 hours` or `3 days`. */
|
|
@@ -583,6 +1233,7 @@ declare const DEFAULT_POLICY: {
|
|
|
583
1233
|
readonly installScripts: {
|
|
584
1234
|
readonly default: "deny";
|
|
585
1235
|
readonly allow: readonly [];
|
|
1236
|
+
readonly deny: readonly [];
|
|
586
1237
|
};
|
|
587
1238
|
readonly sources: {
|
|
588
1239
|
readonly git: "deny";
|
|
@@ -606,4 +1257,4 @@ declare function validatePolicy(document: unknown, tools?: readonly ToolKnowledg
|
|
|
606
1257
|
/** Parses and validates the text of a policy file. */
|
|
607
1258
|
declare function parsePolicy(source: string, tools?: readonly ToolKnowledge[]): PolicyResult;
|
|
608
1259
|
declare function formatIssue(issue: PolicyIssue): string;
|
|
609
|
-
export { type ApplyReport, type CliIo, type Converted, type CooldownConversion, DEFAULT_POLICY, type Detection, type DetectionWarning, type DoctorReport, type Duration, type DurationResult, type DurationStringUnit, EXIT_FAILURE, EXIT_OK, EXIT_USAGE, type FileChange, type Finding, type Level, type Location, type Notice, type NumericDelayUnit, POLICY_FILE, type PackageManagerInfo, type Policy, PolicyError, type PolicyIssue, type PolicyResult, RULES, type ResolvedPolicy, type Rule, SCHEMA_URL, type Snippet, type VersionDeclaration, applyProject, botDuration, convertCooldown, convertMinutes, countLevels, detectProject, formatDuration, formatIssue, getVersion, loadPolicy, parseDuration, parsePolicy, planApply, renderPolicy, renderText, run, runDoctor, toDurationString, toSarif, unifiedDiff, validatePolicy };
|
|
1260
|
+
export { APPLY_ERRORS, APPLY_WARNINGS, type ApplyErrorCode, type ApplyReport, type ApplyWarning, type ApplyWarningCode, ApplyWriteError, type CheckFinding, type CheckOptions, type CliIo, type CommandScan, type Converted, type CooldownConversion, DEFAULT_POLICY, type Detection, type DetectionWarning, type DoctorReport, type Duration, type DurationResult, type DurationStringUnit, EXIT_FAILURE, EXIT_OK, EXIT_USAGE, type FileChange, type Finding, type FindingCode, type InstallRequest, type Level, type Location, type Notice, type NumericDelayUnit, POLICY_FILE, type PackageManagerInfo, type PackageReport, type Policy, PolicyError, type PolicyIssue, type PolicyResult, PopularNames, RULES, type ResolvedPolicy, type Rule, SCHEMA_URL, type ScanOptions, type Snippet, type VersionDeclaration, applyProject, botDuration, checkSpec, checkSpecs, convertCooldown, convertMinutes, countLevels, detectProject, formatDuration, formatIssue, getVersion, loadPolicy, parseDuration, parsePolicy, planApply, renderPolicy, renderText, run, runDoctor, scanCommandLine, toDurationString, toSarif, unifiedDiff, validatePolicy, warningText };
|