@pho9ubenaa/siro 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.3.0]
4
+
5
+ ### Features
6
+
7
+ - **Project policy selection**: `--project-type application|package`, `SiroConfig.projectType`, and `LintOptions.projectType` distinguish dependency-consuming applications from published packages. Omit the selection to infer policy per package-manager binding.
8
+ - **Scoped custom rules**: `Rule.projectTypes` follows explicit or inferred policy for npm-family and Deno bindings.
9
+
10
+ ### Fixes
11
+
12
+ - Programmatic `lintCommand` calls reject unsupported project type, package manager, and severity values instead of returning misleading results.
13
+ - Malformed custom rules from config files or untyped programmatic callers are rejected before rule merging and evaluation.
14
+ - Empty and whitespace-only package names no longer activate published-package policy.
15
+ - Unscoped Deno rules no longer read `deno.json` solely for project-type inference.
16
+ - The published `package.json#bin` preserves exit code 70 when a reporter or custom rule crashes, and subprocess tests now execute that declared bin directly.
17
+ - Malformed or cyclic YAML configurations are rejected, while TOML configurations with a leading UTF-8 BOM are accepted.
18
+ - Deno release-age validation accepts documented default forms and rejects invalid, partial, or zero ages.
19
+ - Repeated reporter selectors and non-boolean Yarn strict SSL values are rejected as invalid input.
20
+ - Findings without automatic remediation operations no longer report themselves as fixable.
21
+
3
22
  ## [0.2.0]
4
23
 
5
24
  ### Breaking Changes
package/README.md CHANGED
@@ -31,7 +31,7 @@ npx @pho9ubenaa/siro lint --reporter json # machine-readable output with fix ops
31
31
  - **PM-aware severities.** When a manager's documented default already satisfies a rule (e.g.
32
32
  Yarn's `enableScripts: false`, aube's `preferFrozenLockfile: true`), the finding is demoted
33
33
  to `info` so CI noise stays proportional to real risk.
34
- - **Machine-readable remediation.** Every finding carries `fix` operations and `manualSteps` in the
34
+ - **Machine-readable remediation.** Every finding carries `fix` operations or `manualSteps` in the
35
35
  JSON output — hand it to an agent skill or editor plugin that edits the files and re-runs
36
36
  `siro lint` until it exits `0`. See [JSON output](docs/json-output.md).
37
37
  - **Lint with severities.** `error` fails CI by default; `--severity warn` tightens the gate.
@@ -62,6 +62,7 @@ that backs those suffixes.
62
62
  siro <lint|check> [path] [options]
63
63
 
64
64
  --pm <npm|pnpm|yarn|bun|deno|aube> Target a specific package manager (auto-detected; required if detection finds nothing)
65
+ --project-type <application|package> Select application or published-package policy (default auto)
65
66
  --reporter <pretty|json|github> Output format (default pretty)
66
67
  --severity <error|warn|info> Show and fail on findings at or above this level
67
68
  --json Shortcut for --reporter json
@@ -70,7 +71,7 @@ siro <lint|check> [path] [options]
70
71
 
71
72
  `check` is an alias of `lint` (same flags, same exit codes) — provided so `siro check` reads naturally in CI scripts.
72
73
 
73
- Exit codes: `0` clean · `1` findings at/above threshold · `2` usage error.
74
+ Exit codes: `0` clean · `1` findings at/above threshold · `2` usage error · `70` uncaught exception (a siro bug or a throwing reporter/custom rule).
74
75
 
75
76
  ## How is this different from `npm audit` / `osv-scanner`?
76
77
 
package/dist/cli.js CHANGED
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import { run } from './cli.mjs';
2
+ import { runMain } from './cli.mjs';
3
3
 
4
- const code = await run(process.argv.slice(2));
5
- process.exitCode = code;
4
+ await runMain(process.argv.slice(2));
package/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { C as isPM, E as UsageError, O as SiroError, S as SEVERITIES, k as asAbsPath, m as BUILTIN_REPORTER_NAMES, n as lintCommand, p as isNodeError, t as nodeIO, v as version, w as isSeverity, x as PMS } from "./node-io-Cb8nixgE.mjs";
2
+ import { A as isSeverity, D as PMS, F as asAbsPath, M as UsageError, O as SEVERITIES, P as SiroError, _ as isSupportedNodeVersion, b as JSON_REPORTER_NAME, g as SUPPORTED_NODE_RANGE, h as isProjectType, k as isPM, m as PROJECT_TYPES, n as lintCommand, p as isNodeError, t as nodeIO, v as BUILTIN_REPORTER_NAMES, w as version, y as DEFAULT_REPORTER_NAME } from "./node-io-CUcMwlGH.mjs";
3
3
  import path from "node:path";
4
4
  import { pathToFileURL } from "node:url";
5
5
  import { cac } from "cac";
@@ -17,15 +17,25 @@ const isCommandName = (value) => COMMANDS.some((cmd) => cmd === value);
17
17
  //#endregion
18
18
  //#region src/cli/flags.ts
19
19
  const ARGS_OFFSET$1 = 2;
20
- /** Flags that consume the following argv token; pre-cac scanners skip that token. */
21
- const VALUE_FLAGS = new Set(Object.entries({
20
+ const FLAGS = {
22
21
  json: { takesValue: false },
23
22
  pm: { takesValue: true },
23
+ projectType: {
24
+ cliName: "project-type",
25
+ takesValue: true
26
+ },
24
27
  reporter: { takesValue: true },
25
28
  severity: { takesValue: true }
26
- }).filter(([, meta]) => meta.takesValue).map(([name]) => name));
29
+ };
30
+ const flagCliName = (name, meta) => {
31
+ if (typeof meta.cliName !== "undefined") return meta.cliName;
32
+ return name;
33
+ };
34
+ /** Flags that consume the following argv token; pre-cac scanners skip that token. */
35
+ const VALUE_FLAGS = new Set(Object.entries(FLAGS).filter(([, meta]) => meta.takesValue).flatMap(([name, meta]) => [name, flagCliName(name, meta)]));
27
36
  const LINT_FLAGS = [
28
37
  "pm",
38
+ "projectType",
29
39
  "reporter",
30
40
  "json",
31
41
  "severity"
@@ -84,7 +94,7 @@ const scanHelpTokens = (argv) => {
84
94
  }
85
95
  return {
86
96
  asksHelp: state.asksHelp,
87
- first: state.first || [].pop()
97
+ first: state.first || void 0
88
98
  };
89
99
  };
90
100
  const resolveTarget = (first) => {
@@ -97,7 +107,7 @@ const resolveTarget = (first) => {
97
107
  */
98
108
  const detectHelpFlag = (argv) => {
99
109
  const { asksHelp, first } = scanHelpTokens(argv);
100
- if (!asksHelp) return [].pop();
110
+ if (!asksHelp) return;
101
111
  return { target: resolveTarget(first) };
102
112
  };
103
113
  /**
@@ -121,8 +131,6 @@ const detectVersionFlag = (argv) => {
121
131
 
122
132
  //#endregion
123
133
  //#region src/cli/parsers.ts
124
- const FIRST_INDEX$1 = 0;
125
- const MIN_NODE_MAJOR = 20;
126
134
  const rejectUnknownFlags = (flags, allowed, scope) => {
127
135
  for (const key of Object.keys(flags)) if (!allowed.has(key)) {
128
136
  let where = "";
@@ -136,6 +144,12 @@ const parsePmFlag = (raw) => {
136
144
  if (typeof raw !== "string" || !isPM(raw)) throw new UsageError(`Unknown package manager: ${raw} (expected one of: ${PMS.join(", ")})`);
137
145
  return raw;
138
146
  };
147
+ const parseProjectTypeFlag = (raw) => {
148
+ if (typeof raw === "undefined") return;
149
+ if (raw === true) throw new UsageError(`--project-type requires a value (expected ${PROJECT_TYPES.join("|")})`);
150
+ if (typeof raw === "string" && isProjectType(raw)) return raw;
151
+ throw new UsageError(`Unknown project type: ${String(raw)} (expected ${PROJECT_TYPES.join("|")})`);
152
+ };
139
153
  const parseSeverityFlag = (raw) => {
140
154
  if (typeof raw === "undefined") return;
141
155
  if (raw === true) throw new UsageError(`--severity requires a value (expected ${SEVERITIES.join("|")})`);
@@ -144,26 +158,22 @@ const parseSeverityFlag = (raw) => {
144
158
  };
145
159
  const resolveReporter = (flags) => {
146
160
  if (flags.reporter === true) throw new UsageError(`--reporter requires a value (expected one of: ${BUILTIN_REPORTER_NAMES.join(", ")}, or a reporter registered via siro.config.ts)`);
147
- if (typeof flags.reporter === "string") {
148
- if (flags.json === true) throw new UsageError("Use either --reporter or --json, not both.");
149
- return flags.reporter;
150
- }
151
- if (flags.json === true) return "json";
152
- return "pretty";
161
+ if (Array.isArray(flags.reporter) || Array.isArray(flags.json) || typeof flags.reporter === "string" && flags.json === true) throw new UsageError("Invalid reporter selection: use either --reporter or --json, and specify it only once.");
162
+ if (typeof flags.reporter === "string") return flags.reporter;
163
+ if (flags.json === true) return JSON_REPORTER_NAME;
164
+ return DEFAULT_REPORTER_NAME;
153
165
  };
154
166
  const ensureNodeVersion = (nodeVersion) => {
155
- const major = Number(nodeVersion.replace(/^v/u, "").split(".")[FIRST_INDEX$1]);
156
- if (Number.isFinite(major) && major < MIN_NODE_MAJOR) throw new UsageError(`Node ${MIN_NODE_MAJOR}+ required (you are on ${nodeVersion}). Upgrade your runtime to use siro.`);
167
+ if (!isSupportedNodeVersion(nodeVersion)) throw new UsageError(`Node ${SUPPORTED_NODE_RANGE} required (you are on ${nodeVersion}). Upgrade your runtime to use siro.`);
157
168
  };
158
169
 
159
170
  //#endregion
160
171
  //#region src/cli/cac-bridge.ts
161
- const FIRST_INDEX = 0;
162
172
  const STEP = 1;
163
173
  const ARGS_OFFSET = 2;
164
174
  const buildCli = () => {
165
175
  const cli = cac("siro");
166
- cli.option("--pm <name>", "Target a specific package manager").option("--reporter <name>", `Reporter (${BUILTIN_REPORTER_NAMES.join("|")})`).option("--json", "Shortcut for --reporter json").option("--severity <level>", `Display/fail threshold (${SEVERITIES.join("|")})`).help().version(version);
176
+ cli.option("--pm <name>", "Target a specific package manager").option("--project-type <type>", "Project type (application|package)").option("--reporter <name>", `Reporter (${BUILTIN_REPORTER_NAMES.join("|")})`).option("--json", "Shortcut for --reporter json").option("--severity <level>", `Display/fail threshold (${SEVERITIES.join("|")})`).help().version(version);
167
177
  for (const name of COMMANDS) {
168
178
  let desc = `${name} a repository`;
169
179
  if (name === "check") desc = "Alias of lint";
@@ -172,12 +182,16 @@ const buildCli = () => {
172
182
  return cli;
173
183
  };
174
184
  const extractPositionalCwd = (matchedCommand, args) => {
175
- if (matchedCommand) return args[FIRST_INDEX];
185
+ if (matchedCommand) {
186
+ const [cwd] = args;
187
+ return cwd;
188
+ }
176
189
  return args[STEP];
177
190
  };
178
191
  const extractCommandCandidate = (matchedCommand, args) => {
179
192
  if (matchedCommand) return matchedCommand;
180
- return args[FIRST_INDEX];
193
+ const [candidate] = args;
194
+ return candidate;
181
195
  };
182
196
  const extractExtraPositionals = (matchedCommand, args) => {
183
197
  if (matchedCommand) return args.slice(STEP);
@@ -197,7 +211,6 @@ const parseCacOutput = (argv) => {
197
211
  commandCandidate: extractCommandCandidate(matchedCommand, parsed.args),
198
212
  extraPositionals: extractExtraPositionals(matchedCommand, parsed.args),
199
213
  flags,
200
- matchedCommand,
201
214
  positionalCwd
202
215
  };
203
216
  };
@@ -206,10 +219,12 @@ const parseCacOutput = (argv) => {
206
219
  //#region src/cli/help.ts
207
220
  const PMS_LIST = PMS.join("|");
208
221
  const REPORTERS_LIST = BUILTIN_REPORTER_NAMES.join("|");
222
+ const PROJECT_TYPES_LIST = PROJECT_TYPES.join("|");
209
223
  const SEVERITIES_LIST = SEVERITIES.join("|");
210
224
  const FLAG_LINES = {
211
225
  json: " --json Shortcut for --reporter json",
212
226
  pm: ` --pm <name> Target a specific package manager (${PMS_LIST})`,
227
+ projectType: ` --project-type <type> Project type (${PROJECT_TYPES_LIST}; default auto)`,
213
228
  reporter: ` --reporter <name> Reporter (${REPORTERS_LIST}; additional reporters can be registered via siro.config.ts)`,
214
229
  severity: ` --severity <level> Show + fail on findings at or above this level (${SEVERITIES_LIST})`
215
230
  };
@@ -224,6 +239,7 @@ const HELP_ROOT = [
224
239
  "",
225
240
  "GLOBAL FLAGS",
226
241
  FLAG_LINES.pm,
242
+ FLAG_LINES.projectType,
227
243
  " --version Print the siro version",
228
244
  " --help Show help for siro or a command",
229
245
  "",
@@ -251,6 +267,7 @@ const HELP_LINT = [
251
267
  "",
252
268
  "FLAGS",
253
269
  FLAG_LINES.pm,
270
+ FLAG_LINES.projectType,
254
271
  FLAG_LINES.reporter,
255
272
  FLAG_LINES.json,
256
273
  FLAG_LINES.severity,
@@ -274,13 +291,12 @@ const renderHelp = (target) => {
274
291
 
275
292
  //#endregion
276
293
  //#region src/cli.ts
277
- const EMPTY = 0;
278
294
  const EXIT_SUCCESS = 0;
279
295
  const EXIT_USAGE = 2;
280
296
  const EXIT_CRASH = 70;
281
297
  const rejectPassthrough = (flags) => {
282
298
  const passthrough = flags["--"];
283
- if (Array.isArray(passthrough) && passthrough.length > EMPTY) throw new UsageError("siro takes no passthrough arguments after `--`.");
299
+ if (Array.isArray(passthrough) && passthrough.length > 0) throw new UsageError("siro takes no passthrough arguments after `--`.");
284
300
  };
285
301
  const resolveCommandCandidate = (commandCandidate) => {
286
302
  if (typeof commandCandidate === "undefined") return { kind: "usage" };
@@ -299,7 +315,7 @@ const resolveCommandCandidate = (commandCandidate) => {
299
315
  };
300
316
  const SINGLE = 1;
301
317
  const rejectExtraPositionals = (extraPositionals) => {
302
- if (extraPositionals.length > EMPTY) {
318
+ if (extraPositionals.length > 0) {
303
319
  let plural = "";
304
320
  if (extraPositionals.length > SINGLE) plural = "s";
305
321
  throw new UsageError(`Unexpected extra argument${plural}: ${extraPositionals.join(" ")}`);
@@ -324,6 +340,7 @@ const buildLintCommand = (flags, positionalCwd, commandCandidate) => {
324
340
  cwd: resolveCwd(positionalCwd),
325
341
  kind: "lint",
326
342
  pm: parsePmFlag(flags.pm),
343
+ projectType: parseProjectTypeFlag(flags.projectType),
327
344
  reporter: resolveReporter(flags),
328
345
  severity: parseSeverityFlag(flags.severity)
329
346
  };
@@ -403,14 +420,11 @@ const onRunRejected = (error) => {
403
420
  process.stderr.write(`${errStr}\n`);
404
421
  process.exitCode = EXIT_CRASH;
405
422
  };
423
+ const runMain = (argv) => run(argv).then(onRunFulfilled, onRunRejected);
406
424
  const ARGV_SKIP = 2;
407
425
  const [, invokedPath] = process.argv;
408
- if (invokedPath && import.meta.url === pathToFileURL(invokedPath).href) try {
409
- onRunFulfilled(await run(process.argv.slice(ARGV_SKIP)));
410
- } catch (error) {
411
- onRunRejected(error);
412
- }
426
+ if (invokedPath && import.meta.url === pathToFileURL(invokedPath).href) await runMain(process.argv.slice(ARGV_SKIP));
413
427
 
414
428
  //#endregion
415
- export { run };
429
+ export { run, runMain };
416
430
  //# sourceMappingURL=cli.mjs.map
package/dist/index.d.mts CHANGED
@@ -47,7 +47,8 @@ interface IO {
47
47
  declare const nodeIO: IO;
48
48
  //#endregion
49
49
  //#region src/domain/entities/config-value.d.ts
50
- type CodecKind = 'npmrc' | 'yaml' | 'toml' | 'json';
50
+ declare const CODEC_KINDS: readonly ["json", "npmrc", "toml", "yaml"];
51
+ type CodecKind = (typeof CODEC_KINDS)[number];
51
52
  /** Scalar value that can be written back to a config file. */
52
53
  type ConfigValue = string | number | boolean;
53
54
  /** Scalar value as it appears when reading (codecs may produce `null`). */
@@ -106,6 +107,10 @@ declare const PackageJsonSchema: vb.LooseObjectSchema<{
106
107
  }, undefined>;
107
108
  type PackageJson = vb.InferOutput<typeof PackageJsonSchema>;
108
109
  //#endregion
110
+ //#region src/domain/entities/project-type.d.ts
111
+ declare const PROJECT_TYPES: readonly ["application", "package"];
112
+ type ProjectType = (typeof PROJECT_TYPES)[number];
113
+ //#endregion
109
114
  //#region src/domain/ports/repo-context.d.ts
110
115
  /** Read-only view of a repository, passed to every rule's `check` and `fix`. */
111
116
  interface RepoContext {
@@ -113,6 +118,7 @@ interface RepoContext {
113
118
  exists: (relPath: RelPath) => boolean;
114
119
  readText: (relPath: RelPath) => string | undefined;
115
120
  readonly packageJson: PackageJson | undefined;
121
+ readonly projectType?: ProjectType;
116
122
  }
117
123
  //#endregion
118
124
  //#region src/domain/entities/rule.d.ts
@@ -178,6 +184,12 @@ type FixOp = {
178
184
  * - `advisory`: `fix` only produces notes/ensureFileTracked hints.
179
185
  */
180
186
  type FixKind = 'auto' | 'advisory';
187
+ /** Display-only package-manager version metadata. */
188
+ interface VersionNote {
189
+ readonly configAvailableSince?: string;
190
+ readonly defaultSafeSince?: string;
191
+ readonly note?: string;
192
+ }
181
193
  type SetKeyOp = Extract<FixOp, {
182
194
  op: 'setKey';
183
195
  }>;
@@ -197,6 +209,7 @@ interface AutoRuleBinding {
197
209
  * override always wins over both.
198
210
  */
199
211
  readonly severity?: Severity;
212
+ readonly versionNote?: VersionNote;
200
213
  check: (ctx: RepoContext, config: ParsedConfig) => CheckStatus;
201
214
  fix: (ctx: RepoContext) => readonly SetKeyOp[];
202
215
  }
@@ -207,17 +220,20 @@ interface AdvisoryRuleBinding {
207
220
  readonly docs?: string;
208
221
  /** See {@link AutoRuleBinding.severity}. */
209
222
  readonly severity?: Severity;
223
+ readonly versionNote?: VersionNote;
210
224
  check: (ctx: RepoContext, config: ParsedConfig) => CheckStatus;
211
225
  fix: (ctx: RepoContext) => readonly AdvisoryOp[];
212
226
  }
213
227
  type RuleBinding = AutoRuleBinding | AdvisoryRuleBinding;
214
228
  /** A package-manager-agnostic security intent, realized per PM via `bindings`. */
215
- interface Rule {
216
- readonly id: string;
229
+ interface Rule<Id extends string = string> {
230
+ readonly id: Id;
217
231
  readonly title: string;
218
232
  readonly description: string;
219
233
  readonly severity: Severity;
220
234
  readonly docs?: string;
235
+ /** Project types this rule applies to; omission means both types. */
236
+ readonly projectTypes?: readonly ProjectType[];
221
237
  /** Absence of a PM key means the rule does not apply (N/A) to that PM. */
222
238
  readonly bindings: Partial<Record<PM, RuleBinding>>;
223
239
  }
@@ -317,6 +333,8 @@ interface LintOptions {
317
333
  cwd: AbsPath;
318
334
  /** Restrict to a single PM; otherwise PMs are auto-detected. */
319
335
  pm?: PM;
336
+ /** Select application or published-package policy; otherwise infer it. */
337
+ projectType?: ProjectType;
320
338
  /** Reporter name or instance; defaults to `pretty`. */
321
339
  reporter?: string | Reporter;
322
340
  /** Extra reporters to make available by name (merged with the user config). */
@@ -431,13 +449,9 @@ declare const PM_SIGNALS: {
431
449
  };
432
450
  };
433
451
  //#endregion
434
- //#region src/domain/entities/rule-id.d.ts
435
- /**
436
- * Closed set of built-in rule IDs. Declared in `domain/` so SiroConfig can
437
- * type `rules` keys without depending on the engine.
438
- */
439
- declare const BUILTIN_RULE_IDS: readonly ["advisory-check", "approved-git-repos", "audit-suppression", "block-auto-install", "block-exotic-subdeps", "bun-security-scanner", "checksum-verification", "commit-lockfile", "dependency-overrides", "disable-lifecycle-scripts", "enforce-strict-ssl", "files-field", "frozen-lockfile", "frozen-store", "hardened-mode", "minimum-release-age", "named-registries", "paranoid-mode", "patched-dependencies", "pin-exact-versions", "provenance", "publish-access", "store-server", "strict-allow-scripts", "strict-release-age", "strict-store-integrity", "trust-policy"];
440
- type BuiltinRuleId = (typeof BUILTIN_RULE_IDS)[number];
452
+ //#region src/domain/builtin-rules.d.ts
453
+ declare const rules: readonly [Rule<"advisory-check">, Rule<"approved-git-repos">, Rule<"audit-suppression">, Rule<"block-auto-install">, Rule<"block-exotic-subdeps">, Rule<"bun-security-scanner">, Rule<"checksum-verification">, Rule<"commit-lockfile">, Rule<"dependency-overrides">, Rule<"disable-lifecycle-scripts">, Rule<"enforce-strict-ssl">, Rule<"files-field">, Rule<"frozen-lockfile">, Rule<"frozen-store">, Rule<"hardened-mode">, Rule<"minimum-release-age">, Rule<"named-registries">, Rule<"paranoid-mode">, Rule<"patched-dependencies">, Rule<"pin-exact-versions">, Rule<"provenance">, Rule<"publish-access">, Rule<"store-server">, Rule<"strict-allow-scripts">, Rule<"strict-release-age">, Rule<"strict-store-integrity">, Rule<"trust-policy">];
454
+ type BuiltinRuleId = (typeof rules)[number]['id'];
441
455
  //#endregion
442
456
  //#region src/domain/entities/siro-config.d.ts
443
457
  /** Per-rule setting. `'off'` disables; a Severity overrides the default level. */
@@ -454,6 +468,7 @@ type RuleSetting = Severity | 'off';
454
468
  */
455
469
  interface SiroConfig {
456
470
  readonly pms?: readonly PM[];
471
+ readonly projectType?: ProjectType;
457
472
  readonly rules?: Readonly<Partial<Record<BuiltinRuleId | (string & Record<never, never>), RuleSetting>>>;
458
473
  readonly customRules?: readonly Rule[];
459
474
  readonly reporters?: readonly Reporter[];
@@ -470,21 +485,6 @@ interface ConfigCodec {
470
485
  type CodecFor = (kind: CodecKind) => ConfigCodec;
471
486
  //#endregion
472
487
  //#region src/domain/rules/builders/require-config-key.d.ts
473
- /**
474
- * Display-only metadata for a binding. siro itself is intentionally
475
- * version-agnostic (we never branch on the user's actual PM version) — these
476
- * fields exist so the rendered message can still tell the reader *when* a key
477
- * appeared and *when* the PM started shipping a safe default. They MUST NOT
478
- * be consulted by any code path that decides severity or applicability.
479
- */
480
- interface VersionNote {
481
- /** PM version that first shipped this config key (e.g. `'pnpm 10.16.0'`). */
482
- readonly configAvailableSince?: string;
483
- /** PM version whose built-in default first satisfies the rule (e.g. `'pnpm 11.0.0 (1440 minutes)'`). */
484
- readonly defaultSafeSince?: string;
485
- /** Free-form trailing note rendered last (e.g. `'replaces auto-install-peers'`). */
486
- readonly note?: string;
487
- }
488
488
  interface RequireConfigKeySpec {
489
489
  readonly file: ConfigFileRef;
490
490
  readonly keyPath: KeyPath;
@@ -524,12 +524,13 @@ interface RequireConfigKeySpec {
524
524
  readonly defaultSatisfiedSeverity?: Severity | 'off';
525
525
  readonly versionNote?: VersionNote;
526
526
  }
527
- interface RequireConfigKeyOptions {
528
- readonly id: string;
527
+ interface RequireConfigKeyOptions<Id extends string = string> {
528
+ readonly id: Id;
529
529
  readonly title: string;
530
530
  readonly description: string;
531
531
  readonly severity: Severity;
532
532
  readonly docs?: string;
533
+ readonly projectTypes?: Rule['projectTypes'];
533
534
  /** Bindings keyed by PM. PMs absent from this map are treated as N/A. */
534
535
  readonly bindings: Partial<Record<PM, RequireConfigKeySpec>>;
535
536
  /** Return false to short-circuit `check` as N/A (e.g. private packages). */
@@ -542,10 +543,12 @@ interface RequireConfigKeyOptions {
542
543
  * the hand-written bindings in via this helper rather than rebuilding from
543
544
  * scratch or mutating the source rule.
544
545
  */
545
- declare const overrideBindings: (rule: Rule, overrides: Partial<Rule["bindings"]>) => Rule;
546
- declare const renderVersionNoteMessage: (message: string, versionNote: VersionNote | undefined) => string;
546
+ declare const overrideBindings: <Id extends string>(rule: Rule<Id>, overrides: Partial<Rule["bindings"]>) => Rule<Id>;
547
547
  /** Build a Rule from a per-PM table of {file, keyPath, value, message}. */
548
- declare const requireConfigKey: (options: RequireConfigKeyOptions) => Rule;
548
+ declare const requireConfigKey: <const Id extends string>(options: RequireConfigKeyOptions<Id>) => Rule<Id>;
549
+ //#endregion
550
+ //#region src/domain/services/render-version-note.d.ts
551
+ declare const renderVersionNoteMessage: (message: string, versionNote: VersionNote | undefined) => string;
549
552
  //#endregion
550
553
  //#region src/domain/services/detect-pms.d.ts
551
554
  declare const detectPMs: (ctx: RepoContext) => PM[];
@@ -565,11 +568,6 @@ declare const exitCodeForLint: (result: LintResult, threshold?: Severity) => num
565
568
  * place so individual commands never have to think about exit semantics.
566
569
  * The exit-code table lives in docs/configuration.md S"Exit codes" and
567
570
  * src/cli/help.ts (HELP_LINT) -- not restated here.
568
- *
569
- * One mapping note that belongs at this altitude: filesystem errno errors
570
- * are routed to 2 by SHAPE (numeric `errno`) in cli run(), so an
571
- * errno-bearing throw from a user extension also lands on 2 rather than the
572
- * 70 crash path -- same remedy (fix the environment) either way.
573
571
  */
574
572
  declare class SiroError extends Error {
575
573
  /** Recommended process exit code for this error. */
@@ -599,7 +597,7 @@ declare class UsageError extends SiroError {
599
597
  declare const wrapCodecError: <TResult>(filePath: string, fn: () => TResult) => TResult;
600
598
  //#endregion
601
599
  //#region src/version.d.ts
602
- declare const version = "0.2.0";
600
+ declare const version = "0.3.0";
603
601
  //#endregion
604
- export { type AbsPath, type AdvisoryRuleBinding, type AutoRuleBinding, BUILTIN_REPORTER_NAMES, type BuiltinReporterName, CONFIG_FILES, type CheckStatus, type CodecFor, type ConfigCodec, ConfigError, type ConfigFileRef, type ConfigReadValue, type ConfigScalar, type ConfigValue, type FileSystem, type Finding, type FixKind, type FixOp, type IO, type KeyAssignment, type KeyPath, type LintOptions, type LintResult, type PM, PMS, type PMSignals, PM_SIGNALS, type PackageJson, type ParsedConfig, type RelPath, type RepoContext, type Reporter, type ReporterRegistry, type RequireConfigKeyOptions, type Rule, type RuleBinding, type RuleSetting, SEVERITIES, type Severity, type SiroConfig, SiroError, UsageError, type VersionNote, asAbsPath, asRelPath, createRegistry, defineConfig, detectPMs, exitCodeForLint, filterBySeverity, getByPath, githubReporter, isPM, isReporterShape, isSeverity, jsonReporter, lintCommand, nodeFileSystem, nodeIO, overrideBindings, prettyReporter, renderVersionNoteMessage, requireConfigKey, version, wrapCodecError };
602
+ export { type AbsPath, type AdvisoryRuleBinding, type AutoRuleBinding, BUILTIN_REPORTER_NAMES, type BuiltinReporterName, CONFIG_FILES, type CheckStatus, type CodecFor, type ConfigCodec, ConfigError, type ConfigFileRef, type ConfigReadValue, type ConfigScalar, type ConfigValue, type FileSystem, type Finding, type FixKind, type FixOp, type IO, type KeyAssignment, type KeyPath, type LintOptions, type LintResult, type PM, PMS, type PMSignals, PM_SIGNALS, PROJECT_TYPES, type PackageJson, type ParsedConfig, type ProjectType, type RelPath, type RepoContext, type Reporter, type ReporterRegistry, type RequireConfigKeyOptions, type Rule, type RuleBinding, type RuleSetting, SEVERITIES, type Severity, type SiroConfig, SiroError, UsageError, type VersionNote, asAbsPath, asRelPath, createRegistry, defineConfig, detectPMs, exitCodeForLint, filterBySeverity, getByPath, githubReporter, isPM, isReporterShape, isSeverity, jsonReporter, lintCommand, nodeFileSystem, nodeIO, overrideBindings, prettyReporter, renderVersionNoteMessage, requireConfigKey, version, wrapCodecError };
605
603
  //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs CHANGED
@@ -1,9 +1,9 @@
1
- import { A as asRelPath, C as isPM, D as ConfigError, E as UsageError, O as SiroError, S as SEVERITIES, T as wrapCodecError, _ as jsonReporter, a as PM_SIGNALS, b as isReporterShape, c as requireConfigKey, d as exitCodeForLint, f as filterBySeverity, g as prettyReporter, h as createRegistry, i as nodeFileSystem, k as asAbsPath, l as CONFIG_FILES, m as BUILTIN_REPORTER_NAMES, n as lintCommand, o as overrideBindings, r as detectPMs, s as renderVersionNoteMessage, t as nodeIO, u as getByPath, v as version, w as isSeverity, x as PMS, y as githubReporter } from "./node-io-Cb8nixgE.mjs";
1
+ import { A as isSeverity, C as jsonReporter, D as PMS, E as isReporterShape, F as asAbsPath, I as asRelPath, M as UsageError, N as ConfigError, O as SEVERITIES, P as SiroError, S as prettyReporter, T as githubReporter, a as nodeFileSystem, c as requireConfigKey, d as exitCodeForLint, f as filterBySeverity, i as detectPMs, j as wrapCodecError, k as isPM, l as CONFIG_FILES, m as PROJECT_TYPES, n as lintCommand, o as PM_SIGNALS, r as renderVersionNoteMessage, s as overrideBindings, t as nodeIO, u as getByPath, v as BUILTIN_REPORTER_NAMES, w as version, x as createRegistry } from "./node-io-CUcMwlGH.mjs";
2
2
 
3
3
  //#region src/domain/entities/siro-config.ts
4
4
  /** Identity helper for type-checked config files. */
5
5
  const defineConfig = (config) => config;
6
6
 
7
7
  //#endregion
8
- export { BUILTIN_REPORTER_NAMES, CONFIG_FILES, ConfigError, PMS, PM_SIGNALS, SEVERITIES, SiroError, UsageError, asAbsPath, asRelPath, createRegistry, defineConfig, detectPMs, exitCodeForLint, filterBySeverity, getByPath, githubReporter, isPM, isReporterShape, isSeverity, jsonReporter, lintCommand, nodeFileSystem, nodeIO, overrideBindings, prettyReporter, renderVersionNoteMessage, requireConfigKey, version, wrapCodecError };
8
+ export { BUILTIN_REPORTER_NAMES, CONFIG_FILES, ConfigError, PMS, PM_SIGNALS, PROJECT_TYPES, SEVERITIES, SiroError, UsageError, asAbsPath, asRelPath, createRegistry, defineConfig, detectPMs, exitCodeForLint, filterBySeverity, getByPath, githubReporter, isPM, isReporterShape, isSeverity, jsonReporter, lintCommand, nodeFileSystem, nodeIO, overrideBindings, prettyReporter, renderVersionNoteMessage, requireConfigKey, version, wrapCodecError };
9
9
  //# sourceMappingURL=index.mjs.map