@stll/oxlint-config 1.4.0 → 1.5.1

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.
Files changed (69) hide show
  1. package/README.md +90 -14
  2. package/dist/cloud-setup-cli.cjs +3 -3
  3. package/dist/cloud-setup-cli.mjs +3 -3
  4. package/dist/consumer-compat-cli.cjs +2 -2
  5. package/dist/consumer-compat-cli.mjs +2 -2
  6. package/dist/dependabot-cli.cjs +34 -10
  7. package/dist/dependabot-cli.cjs.map +1 -1
  8. package/dist/dependabot-cli.mjs +35 -11
  9. package/dist/dependabot-cli.mjs.map +1 -1
  10. package/dist/dependabot.cjs +2 -0
  11. package/dist/dependabot.d.cts +12 -2
  12. package/dist/dependabot.d.mts +12 -2
  13. package/dist/dependabot.mjs +2 -2
  14. package/dist/generated-file.cjs +2 -2
  15. package/dist/generated-file.mjs +2 -2
  16. package/dist/git-tracked-output.cjs +2 -2
  17. package/dist/git-tracked-output.mjs +1 -1
  18. package/dist/jsonc.cjs +66 -0
  19. package/dist/jsonc.cjs.map +1 -0
  20. package/dist/jsonc.mjs +61 -0
  21. package/dist/jsonc.mjs.map +1 -0
  22. package/dist/publish-artifact-syntax.cjs +2 -2
  23. package/dist/publish-artifact-syntax.mjs +2 -2
  24. package/dist/publish-build-target-vite-dts-helper2.cjs +1 -1
  25. package/dist/publish-build-target-vite-dts-helper2.mjs +1 -1
  26. package/dist/publish-contract-cli.cjs +2 -2
  27. package/dist/publish-contract-cli.mjs +1 -1
  28. package/dist/quarantine-cli.cjs +66 -0
  29. package/dist/quarantine-cli.cjs.map +1 -0
  30. package/dist/quarantine-cli.d.cts +1 -0
  31. package/dist/quarantine-cli.d.mts +1 -0
  32. package/dist/quarantine-cli.mjs +66 -0
  33. package/dist/quarantine-cli.mjs.map +1 -0
  34. package/dist/quarantine-policy.cjs +10 -0
  35. package/dist/quarantine-policy.d.cts +27 -0
  36. package/dist/quarantine-policy.d.mts +27 -0
  37. package/dist/quarantine-policy.mjs +1 -0
  38. package/dist/quarantine-policy2.cjs +399 -0
  39. package/dist/quarantine-policy2.cjs.map +1 -0
  40. package/dist/quarantine-policy2.mjs +352 -0
  41. package/dist/quarantine-policy2.mjs.map +1 -0
  42. package/dist/toolchain-changed.cjs +11 -66
  43. package/dist/toolchain-changed.cjs.map +1 -1
  44. package/dist/toolchain-changed.mjs +13 -68
  45. package/dist/toolchain-changed.mjs.map +1 -1
  46. package/dist/toolchain-check-cli.cjs +1 -0
  47. package/dist/toolchain-check-cli.cjs.map +1 -1
  48. package/dist/toolchain-check-cli.mjs +1 -0
  49. package/dist/toolchain-check-cli.mjs.map +1 -1
  50. package/dist/toolchain-dependabot.cjs +77 -3
  51. package/dist/toolchain-dependabot.cjs.map +1 -1
  52. package/dist/toolchain-dependabot.mjs +66 -4
  53. package/dist/toolchain-dependabot.mjs.map +1 -1
  54. package/dist/toolchain-guard.cjs +49 -10
  55. package/dist/toolchain-guard.cjs.map +1 -1
  56. package/dist/toolchain-guard.mjs +51 -12
  57. package/dist/toolchain-guard.mjs.map +1 -1
  58. package/dist/toolchain-inputs.cjs +104 -18
  59. package/dist/toolchain-inputs.cjs.map +1 -1
  60. package/dist/toolchain-inputs.mjs +106 -20
  61. package/dist/toolchain-inputs.mjs.map +1 -1
  62. package/dist/typecheck-parity.cjs +4 -3
  63. package/dist/typecheck-parity.cjs.map +1 -1
  64. package/dist/typecheck-parity.mjs +4 -3
  65. package/dist/typecheck-parity.mjs.map +1 -1
  66. package/dist/typecheck-probe-cli.cjs +2 -2
  67. package/dist/typecheck-probe-cli.mjs +2 -2
  68. package/package.json +13 -1
  69. package/toolchain.json +11 -4
package/README.md CHANGED
@@ -300,8 +300,33 @@ deleted from the working tree. Conflicting indexed or existing spellings are
300
300
  rejected, and unrelated unreadable inputs still stop generation. Repeated runs
301
301
  produce identical bytes; linked output files and directories are rejected.
302
302
 
303
+ Use `stll-dependabot --check` in CI to compare bytes without writing. Missing or
304
+ stale output exits with status 1; current output exits with status 0. Pass the
305
+ same `--config` argument to write and check commands when using explicit input.
306
+
307
+ An optional `--config stll-dependabot.json` input declares only coverage:
308
+
309
+ ```json
310
+ {
311
+ "updates": [
312
+ { "ecosystem": "bun", "directories": ["/"] },
313
+ { "ecosystem": "github-actions", "directories": ["/"] },
314
+ { "ecosystem": "cargo", "directories": ["/native"] }
315
+ ]
316
+ }
317
+ ```
318
+
319
+ Each ecosystem appears once, and directories must be unique literal absolute
320
+ repository paths. Declarations must cover every detected update root; they may
321
+ also declare ecosystems whose manifests are outside automatic discovery.
322
+ Shared `toolchain.json` owns the schedule, cooldown, groups, and tooling-owned
323
+ package/action/image ignores. The input rejects policy overrides. Without
324
+ `--config`, the tracked manifest inventory supplies ecosystem roots.
325
+
303
326
  For programmatic generation, the `@stll/oxlint-config/dependabot` subpath exports
304
- `generateDependabotConfig({ files, policy })` and `parseToolchainPolicy`:
327
+ `generateDependabotConfig({ files, policy, updates? })`, `parseDependabotInput`,
328
+ and `parseToolchainPolicy`. `parseDependabotInput` validates the JSON shape above
329
+ and returns the `updates` array:
305
330
 
306
331
  ```ts
307
332
  import {
@@ -359,6 +384,18 @@ repository-relative path; its existing parent must be safe at runtime. Track an
359
384
  bunx --no-install stll-cloud-setup
360
385
  ```
361
386
 
387
+ When PostgreSQL is enabled, the selected environment file contains `DATABASE_URL`
388
+ and `POSTGRES_HOST`, `POSTGRES_USER`, `POSTGRES_PASSWORD`, `POSTGRES_DB_NAME`,
389
+ and `POSTGRES_ADDITIONAL_CONNECTION_DATA`. `DATABASE_URL` retains the
390
+ `postgresql://` spelling; the component values assemble the same endpoint with
391
+ `postgres://`. Additional connection data is empty by default.
392
+ Remote Claude sessions receive these generated values as quoted `export`
393
+ assignments after service startup succeeds. SessionStart does not source the
394
+ selected environment file, and it preserves the configured `envFile` path for
395
+ tools that load that file directly. Settings generation migrates only exact
396
+ SessionStart hooks from releases 1.1.0 and 1.3.1 and the prior main-branch form
397
+ at 783bc97; edited or unknown hook forms fail with the canonical repair.
398
+
362
399
  Rust is optional. A tracked root `rust-toolchain.toml` enables it automatically;
363
400
  otherwise declare `cloud.rust` to enable it. `cloud.rust.toolchain`, when given,
364
401
  must be an exact stable Rust patch. The root toolchain file supplies the pin when
@@ -409,21 +446,26 @@ binary DEBs for the declared release and architecture, verifies the policy's
409
446
  SHA256 pins, and installs those local files with apt. It does not resolve Valkey
410
447
  from a mutable package index.
411
448
  The command writes `.agents/cloud-setup.sh` with executable permissions and
412
- merges a `SessionStart` hook into `.claude/settings.json`. The hook runs
413
- `bash "$CLAUDE_PROJECT_DIR/.agents/cloud-setup.sh" start` only when
414
- `CLAUDE_CODE_REMOTE=true`, including sessions that reuse cached filesystems and
415
- sessions started from a repository subdirectory. Generation replaces the exact hooks written by 1.1.0 and 1.3.1 in place.
416
- After successful startup, it appends the
417
- managed shell profile's literal exports to `CLAUDE_ENV_FILE` when available, so
418
- subsequent Claude commands select pinned runtimes and tools before preinstalled
419
- versions while retaining their inherited `PATH`. The settings also set
449
+ merges a `SessionStart` hook into `.claude/settings.json`. Only when
450
+ `CLAUDE_CODE_REMOTE=true`, the hook runs
451
+ `bash "$CLAUDE_PROJECT_DIR/.agents/cloud-setup.sh" session` if `CLAUDE_ENV_FILE`
452
+ is nonempty, and `start` otherwise. This includes sessions that reuse cached
453
+ filesystems and sessions started from a repository subdirectory. Generation
454
+ replaces the exact hooks written by 1.1.0 and 1.3.1 and the prior main-branch
455
+ form in place.
456
+ After successful startup, the hook copies existing `CLAUDE_ENV_FILE` content
457
+ into a temporary file in the same directory, adds the generated service exports
458
+ and managed shell profile's literal exports, then renames it over the destination.
459
+ The resulting file has mode `0600`; an existing destination must be a regular,
460
+ non-symlink file. Subsequent Claude commands select pinned runtimes and tools
461
+ before preinstalled versions while retaining their inherited `PATH`. The settings also set
420
462
  `"attribution": {"commit": "", "pr": ""}`, so cloud sessions add no tool
421
463
  attribution to commits or pull requests. Existing environment-file
422
464
  content, unrelated settings, and hooks are preserved. Invalid JSON, disabled
423
465
  hooks, conflicting cloud startup entries, or a different `attribution` value stop
424
466
  generation with a repair instruction. Startup or environment persistence failures
425
467
  produce actionable session context and a stderr reason; SessionStart cannot block
426
- a session. Successful startup is silent on stdout. Commit both generated files.
468
+ a session. Successful hook execution is silent on stdout. Commit both generated files.
427
469
  Generation refuses symlink parents or destinations and hard-linked destinations.
428
470
  Generation recreates its deleted tracked output files and requires unrelated
429
471
  tracked inputs to remain readable.
@@ -435,21 +477,26 @@ prints usage without reading repository configuration.
435
477
 
436
478
  | Environment phase | Command |
437
479
  | ----------------------- | ------------------------------------- |
438
- | Networked install | `bash .agents/cloud-setup.sh install` |
439
- | Offline session startup | `bash .agents/cloud-setup.sh start` |
480
+ | Networked install | `bash .agents/cloud-setup.sh install` |
481
+ | Offline session startup | `bash .agents/cloud-setup.sh start` |
482
+ | Startup with exports | `bash .agents/cloud-setup.sh session` |
440
483
 
441
484
  Runtime installation repairs and verifies a missing or empty system CA bundle
442
485
  offline before any apt refresh or download, including when the certificate package
443
486
  is already current. If the certificate updater is unavailable or cannot restore
444
487
  trust, restore the base image offline before rerunning installation.
445
488
 
446
- Both commands can be rerun. Missing or unknown subcommands print usage and
489
+ All three commands can be rerun. Passwordless sudo rules limited to specific
490
+ script arguments must authorize `install`, `start`, and `session`.
491
+ Missing or unknown subcommands print usage and
447
492
  exit 2; there is no implicit lifecycle mode. The script supports Ubuntu 24.04
448
493
  with root access or passwordless sudo and fails when required capabilities
449
494
  or pinned installations are unavailable. `install` provisions runtimes,
450
495
  dependencies, and declared services; `start` refreshes the managed shell environment
451
496
  and restores local services after
452
- cached environments lose running processes. Services bind only to localhost.
497
+ cached environments lose running processes. `session` performs the same offline
498
+ startup, sends startup output to stderr, and emits the declared service environment
499
+ exports on stdout for the hook to capture. Services bind only to localhost.
453
500
  System provisioning runs as root; frozen dependencies run as the invoking user
454
501
  with that user's home and a private, user-owned Bun cache. Package installation
455
502
  does not permit automatic downgrades.
@@ -938,3 +985,32 @@ fail closed. This is an ownership check for ordinary authored package scripts,
938
985
  not a security boundary against deliberately obfuscated code or lint invocations
939
986
  hidden inside arbitrary program bodies. Unsupported forms require an explicit
940
987
  supported script invocation or a separately tested extension to the model.
988
+
989
+ ## Quarantine preset and expiry checks
990
+
991
+ Run `bunx --no-install stll-quarantine --sync` before installation to create the
992
+ five-day Bun preset and synchronize permanent exact-name `@stll/` exemptions.
993
+ The command derives registry identities from dependency declarations and the
994
+ root Bun lockfile, including transitive platform packages. Existing first-party
995
+ creation timestamps remain stable across runs; they are not expiries.
996
+
997
+ Third-party entries require `# quarantine-expires: YYYY-MM-DDTHH:mm:ss.sssZ`.
998
+ `stll-toolchain-check` warns 48 hours before expiry; warnings continue through
999
+ the existing 24-hour removal grace, after which expired entries fail. The
1000
+ quarantine rule cannot be opted out. `stll-quarantine --check` is read-only;
1001
+ `--prune` removes expired third-party entries without removing first-party ones.
1002
+ `--help` lists modes. Linked output files are rejected.
1003
+
1004
+ `@stll/oxlint-config/quarantine` exports the Node-compatible repository helpers
1005
+ `checkRepositoryQuarantine`, `synchronizeRepositoryQuarantine`, and
1006
+ `pruneRepositoryQuarantine`, accepting a tracked `files` snapshot and optional
1007
+ `now`. The toolchain API reports advisory expiry messages through `onWarning`;
1008
+ its returned diagnostics remain failures. Snapshot readers keep lockfiles
1009
+ opaque by default; `{ bunLock: "content" }` explicitly requests the root graph
1010
+ when quarantine identity checks need it.
1011
+
1012
+ The shared scheduled workflow imports the same dependency-free policy core
1013
+ from an exact tooling commit; it does not install consumer dependencies to
1014
+ perform enforcement or pruning. Its stable removal branch updates one PR per
1015
+ repository. See the [root guide](../../README.md#package-quarantine) for the
1016
+ inline annotation format and adoption boundary.
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const require_generated_file = require("./generated-file.cjs");
3
4
  const require_toolchain_inputs = require("./toolchain-inputs.cjs");
4
5
  const require_toolchain_guard = require("./toolchain-guard.cjs");
5
6
  const require_toolchain_schema = require("./toolchain-schema.cjs");
6
- const require_generated_file = require("./generated-file.cjs");
7
- let node_fs = require("node:fs");
8
- let node_path = require("node:path");
9
7
  let node_fs_promises = require("node:fs/promises");
8
+ let node_path = require("node:path");
9
+ let node_fs = require("node:fs");
10
10
  //#region src/cloud-setup-cli.ts
11
11
  const usage = "Usage: stll-cloud-setup (run from repository root)\n stll-cloud-setup --help";
12
12
  const readClaudeSettings = async (destination) => {
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
+ import { n as generationDiagnostics, r as writeGeneratedFile, t as checkGeneratedOutput } from "./generated-file.mjs";
2
3
  import { S as generateCloudSetup, _ as cloudSessionStartFix, b as mergeCloudClaudeSettings, g as cloudClaudeSettingsPath, x as cloudSetupPath } from "./toolchain-inputs.mjs";
3
4
  import { n as parseToolchainConfiguration, r as readToolchainInputs } from "./toolchain-guard.mjs";
4
5
  import { t as parseToolchainPolicy } from "./toolchain-schema.mjs";
5
- import { n as generationDiagnostics, r as writeGeneratedFile, t as checkGeneratedOutput } from "./generated-file.mjs";
6
- import { constants } from "node:fs";
7
- import { join } from "node:path";
8
6
  import { open, readFile, realpath } from "node:fs/promises";
7
+ import { join } from "node:path";
8
+ import { constants } from "node:fs";
9
9
  //#region src/cloud-setup-cli.ts
10
10
  const usage = "Usage: stll-cloud-setup (run from repository root)\n stll-cloud-setup --help";
11
11
  const readClaudeSettings = async (destination) => {
@@ -4,14 +4,14 @@ const require_rolldown_runtime = require("./rolldown-runtime.cjs");
4
4
  const require_consumer_node_support = require("./consumer-node-support.cjs");
5
5
  const require_toolchain_schema = require("./toolchain-schema.cjs");
6
6
  const require_publish_artifact_syntax = require("./publish-artifact-syntax.cjs");
7
- let node_child_process = require("node:child_process");
7
+ let node_fs_promises = require("node:fs/promises");
8
8
  let node_path = require("node:path");
9
9
  node_path = require_rolldown_runtime.__toESM(node_path, 1);
10
+ let node_child_process = require("node:child_process");
10
11
  let node_crypto = require("node:crypto");
11
12
  let semver = require("semver");
12
13
  let yaml = require("yaml");
13
14
  let node_os = require("node:os");
14
- let node_fs_promises = require("node:fs/promises");
15
15
  //#region src/consumer-build-approvals.ts
16
16
  const consumerBuildApprovals = async ({ manifest, lock, manager, directory }) => {
17
17
  if (!require_consumer_node_support.consumerRecord(lock)) throw new Error("invalid consumer dependency lock");
@@ -2,13 +2,13 @@
2
2
  import { _ as bunCatalogState, a as assertConsumerFixtureSelection, c as consumerDependencyConfigFiles, d as consumerRecord, f as consumerRelativePath, g as parseConsumerFixtures, h as discoverConsumerPackages, i as assertConsumerFixtureManifest, l as consumerPackRootManifest, m as discoverConsumerManifests, p as consumerStagingPaths, r as assertConsumerFixtureKind, s as bindConsumerManifest, t as assertConsumerNodeSupport, u as consumerPackageClosure, v as catalogState, y as pnpmWorkspaceState } from "./consumer-node-support.mjs";
3
3
  import { t as parseToolchainPolicy } from "./toolchain-schema.mjs";
4
4
  import { i as parsePublishContract, n as readPackedSyntaxFiles, o as consumerReleasePackArguments, r as checkPublishContract, s as resolveConsumerReleasePack, t as checkPackedJavaScriptWithNode } from "./publish-artifact-syntax.mjs";
5
- import { spawn } from "node:child_process";
5
+ import { cp, lstat, mkdir, mkdtemp, readFile, readdir, realpath, rm, writeFile } from "node:fs/promises";
6
6
  import path from "node:path";
7
+ import { spawn } from "node:child_process";
7
8
  import { createHash } from "node:crypto";
8
9
  import { compare, satisfies, valid, validRange } from "semver";
9
10
  import { parse as parse$1, stringify } from "yaml";
10
11
  import { tmpdir } from "node:os";
11
- import { cp, lstat, mkdir, mkdtemp, readFile, readdir, realpath, rm, writeFile } from "node:fs/promises";
12
12
  //#region src/consumer-build-approvals.ts
13
13
  const consumerBuildApprovals = async ({ manifest, lock, manager, directory }) => {
14
14
  if (!consumerRecord(lock)) throw new Error("invalid consumer dependency lock");
@@ -1,21 +1,37 @@
1
1
  #!/usr/bin/env node
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const require_generated_file = require("./generated-file.cjs");
3
4
  const require_toolchain_guard = require("./toolchain-guard.cjs");
4
5
  const require_toolchain_dependabot = require("./toolchain-dependabot.cjs");
5
6
  const require_toolchain_schema = require("./toolchain-schema.cjs");
6
7
  require("./dependabot.cjs");
7
- const require_generated_file = require("./generated-file.cjs");
8
- let node_path = require("node:path");
9
8
  let node_fs_promises = require("node:fs/promises");
9
+ let node_path = require("node:path");
10
10
  //#region src/dependabot-cli.ts
11
- const usage = "Usage: stll-dependabot (run from repository root)\n stll-dependabot --help";
11
+ const usage = "Usage: stll-dependabot [--check] [--config <path>] (run from repository root)\n stll-dependabot --help";
12
12
  const main = async () => {
13
13
  const args = process.argv.slice(2);
14
14
  if (args.length === 1 && args.at(0) === "--help") {
15
15
  process.stdout.write(`${usage}\n`);
16
16
  return;
17
17
  }
18
- if (args.length !== 0) throw new Error(usage);
18
+ let mode = "write";
19
+ let config;
20
+ for (let index = 0; index < args.length; index++) {
21
+ const arg = args[index];
22
+ if (arg === "--check" && mode === "write") {
23
+ mode = "check";
24
+ continue;
25
+ }
26
+ if (arg === "--config" && config === void 0) {
27
+ const next = args.at(index + 1);
28
+ if (next === void 0 || next.startsWith("--")) throw new Error(usage);
29
+ config = next;
30
+ index++;
31
+ continue;
32
+ }
33
+ throw new Error(usage);
34
+ }
19
35
  const root = await (0, node_fs_promises.realpath)(process.cwd());
20
36
  const { files, trackedFiles, diagnostics } = require_toolchain_guard.readToolchainInputs(root);
21
37
  const policy = require_toolchain_schema.parseToolchainPolicy(JSON.parse(await (0, node_fs_promises.readFile)(new URL("../toolchain.json", require("url").pathToFileURL(__filename).href), "utf8")));
@@ -46,15 +62,23 @@ const main = async () => {
46
62
  }]
47
63
  });
48
64
  if (remainingDiagnostics.length !== 0) throw new Error(remainingDiagnostics.map(({ path, message }) => `${path}: ${message}`).join("\n"));
65
+ const declarations = config === void 0 ? void 0 : require_toolchain_dependabot.parseDependabotInput(JSON.parse(await (0, node_fs_promises.readFile)((0, node_path.resolve)(root, config), "utf8")));
66
+ const text = require_toolchain_dependabot.generateDependabotConfig({
67
+ files,
68
+ policy: policy.dependabot,
69
+ updates: declarations
70
+ });
49
71
  const destination = selected.path;
50
- const mode = selected.stat?.mode;
72
+ if (mode === "check") {
73
+ if (selected.stat === void 0 || await (0, node_fs_promises.readFile)(destination, "utf8") !== text) throw new Error(`${selected.relative} has drifted; run stll-dependabot${config === void 0 ? "" : ` --config ${config}`}`);
74
+ process.stdout.write(`Verified ${selected.relative}\n`);
75
+ return;
76
+ }
77
+ const permissions = selected.stat?.mode;
51
78
  await require_generated_file.writeGeneratedFile({
52
79
  destination,
53
- text: require_toolchain_dependabot.generateDependabotConfig({
54
- files,
55
- policy: policy.dependabot
56
- }),
57
- mode: mode === void 0 ? 420 : mode & 511
80
+ text,
81
+ mode: permissions === void 0 ? 420 : permissions & 511
58
82
  });
59
83
  process.stdout.write(`Generated ${selected.relative}\n`);
60
84
  };
@@ -1 +1 @@
1
- {"version":3,"file":"dependabot-cli.cjs","names":["realpath","readToolchainInputs","parseToolchainPolicy","readFile","join","checkGeneratedOutput","generationDiagnostics","writeGeneratedFile","generateDependabotConfig"],"sources":["../src/dependabot-cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { readFile, realpath } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { generateDependabotConfig, parseToolchainPolicy } from \"./dependabot\";\nimport {\n checkGeneratedOutput,\n generationDiagnostics,\n writeGeneratedFile,\n} from \"./generated-file\";\nimport { readToolchainInputs } from \"./toolchain-guard\";\n\nconst usage =\n \"Usage: stll-dependabot (run from repository root)\\n stll-dependabot --help\";\nconst main = async () => {\n const args = process.argv.slice(2);\n if (args.length === 1 && args.at(0) === \"--help\") {\n process.stdout.write(`${usage}\\n`);\n return;\n }\n if (args.length !== 0) throw new Error(usage);\n const root = await realpath(process.cwd());\n const { files, trackedFiles, diagnostics } = readToolchainInputs(root);\n const policy = parseToolchainPolicy(\n JSON.parse(\n await readFile(new URL(\"../toolchain.json\", import.meta.url), \"utf8\"),\n ),\n );\n const yaml = join(root, \".github/dependabot.yaml\");\n const yml = join(root, \".github/dependabot.yml\");\n const yamlStat = await checkGeneratedOutput(yaml);\n const ymlStat = await checkGeneratedOutput(yml);\n const candidates = [\n { relative: \".github/dependabot.yaml\", path: yaml, stat: yamlStat },\n { relative: \".github/dependabot.yml\", path: yml, stat: ymlStat },\n ].filter(\n ({ relative, stat }) => stat !== undefined || trackedFiles.has(relative),\n );\n if (candidates.length > 1)\n throw new Error(\"keep exactly one Dependabot configuration: .yml or .yaml\");\n const selected = candidates.at(0) ?? {\n relative: \".github/dependabot.yml\",\n path: yml,\n stat: ymlStat,\n };\n const remainingDiagnostics = await generationDiagnostics({\n diagnostics,\n outputs: [{ relative: selected.relative, destination: selected.path }],\n });\n if (remainingDiagnostics.length !== 0)\n throw new Error(\n remainingDiagnostics\n .map(({ path, message }) => `${path}: ${message}`)\n .join(\"\\n\"),\n );\n const destination = selected.path;\n const mode = selected.stat?.mode;\n await writeGeneratedFile({\n destination,\n text: generateDependabotConfig({ files, policy: policy.dependabot }),\n mode: mode === undefined ? 0o644 : mode & 0o777,\n });\n process.stdout.write(`Generated ${selected.relative}\\n`);\n};\n\nmain().catch((error: unknown) => {\n process.stderr.write(\n `dependabot: ${error instanceof Error ? error.message : String(error)}\\n`,\n );\n process.exitCode = 1;\n});\n"],"mappings":";;;;;;;;;;AAYA,MAAM,QACJ;AACF,MAAM,OAAO,YAAY;CACvB,MAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;CACjC,IAAI,KAAK,WAAW,KAAK,KAAK,GAAG,CAAC,MAAM,UAAU;EAChD,QAAQ,OAAO,MAAM,GAAG,MAAM,GAAG;EACjC;CACF;CACA,IAAI,KAAK,WAAW,GAAG,MAAM,IAAI,MAAM,KAAK;CAC5C,MAAM,OAAO,OAAA,GAAMA,iBAAAA,SAAAA,CAAS,QAAQ,IAAI,CAAC;CACzC,MAAM,EAAE,OAAO,cAAc,gBAAgBC,wBAAAA,oBAAoB,IAAI;CACrE,MAAM,SAASC,yBAAAA,qBACb,KAAK,MACH,OAAA,GAAMC,iBAAAA,SAAAA,CAAS,IAAI,IAAI,qBAAA,QAAA,KAAA,CAAA,CAAA,cAAA,UAAA,CAAA,CAAA,IAAoC,GAAG,MAAM,CACtE,CACF;CACA,MAAM,QAAA,GAAOC,UAAAA,KAAAA,CAAK,MAAM,yBAAyB;CACjD,MAAM,OAAA,GAAMA,UAAAA,KAAAA,CAAK,MAAM,wBAAwB;CAC/C,MAAM,WAAW,MAAMC,uBAAAA,qBAAqB,IAAI;CAChD,MAAM,UAAU,MAAMA,uBAAAA,qBAAqB,GAAG;CAC9C,MAAM,aAAa,CACjB;EAAE,UAAU;EAA2B,MAAM;EAAM,MAAM;CAAS,GAClE;EAAE,UAAU;EAA0B,MAAM;EAAK,MAAM;CAAQ,CACjE,CAAC,CAAC,QACC,EAAE,UAAU,WAAW,SAAS,KAAA,KAAa,aAAa,IAAI,QAAQ,CACzE;CACA,IAAI,WAAW,SAAS,GACtB,MAAM,IAAI,MAAM,0DAA0D;CAC5E,MAAM,WAAW,WAAW,GAAG,CAAC,KAAK;EACnC,UAAU;EACV,MAAM;EACN,MAAM;CACR;CACA,MAAM,uBAAuB,MAAMC,uBAAAA,sBAAsB;EACvD;EACA,SAAS,CAAC;GAAE,UAAU,SAAS;GAAU,aAAa,SAAS;EAAK,CAAC;CACvE,CAAC;CACD,IAAI,qBAAqB,WAAW,GAClC,MAAM,IAAI,MACR,qBACG,KAAK,EAAE,MAAM,cAAc,GAAG,KAAK,IAAI,SAAS,CAAC,CACjD,KAAK,IAAI,CACd;CACF,MAAM,cAAc,SAAS;CAC7B,MAAM,OAAO,SAAS,MAAM;CAC5B,MAAMC,uBAAAA,mBAAmB;EACvB;EACA,MAAMC,6BAAAA,yBAAyB;GAAE;GAAO,QAAQ,OAAO;EAAW,CAAC;EACnE,MAAM,SAAS,KAAA,IAAY,MAAQ,OAAO;CAC5C,CAAC;CACD,QAAQ,OAAO,MAAM,aAAa,SAAS,SAAS,GAAG;AACzD;AAEA,KAAK,CAAC,CAAC,OAAO,UAAmB;CAC/B,QAAQ,OAAO,MACb,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE,GACxE;CACA,QAAQ,WAAW;AACrB,CAAC"}
1
+ {"version":3,"file":"dependabot-cli.cjs","names":["realpath","readToolchainInputs","parseToolchainPolicy","readFile","join","checkGeneratedOutput","generationDiagnostics","parseDependabotInput","resolve","generateDependabotConfig","writeGeneratedFile"],"sources":["../src/dependabot-cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { readFile, realpath } from \"node:fs/promises\";\nimport { join, resolve } from \"node:path\";\n\nimport {\n generateDependabotConfig,\n parseDependabotInput,\n parseToolchainPolicy,\n} from \"./dependabot\";\nimport {\n checkGeneratedOutput,\n generationDiagnostics,\n writeGeneratedFile,\n} from \"./generated-file\";\nimport { readToolchainInputs } from \"./toolchain-guard\";\n\nconst usage =\n \"Usage: stll-dependabot [--check] [--config <path>] (run from repository root)\\n stll-dependabot --help\";\nconst main = async () => {\n const args = process.argv.slice(2);\n if (args.length === 1 && args.at(0) === \"--help\") {\n process.stdout.write(`${usage}\\n`);\n return;\n }\n let mode: \"write\" | \"check\" = \"write\";\n let config: string | undefined;\n for (let index = 0; index < args.length; index++) {\n const arg = args[index];\n if (arg === \"--check\" && mode === \"write\") {\n mode = \"check\";\n continue;\n }\n if (arg === \"--config\" && config === undefined) {\n const next = args.at(index + 1);\n if (next === undefined || next.startsWith(\"--\")) throw new Error(usage);\n config = next;\n index++;\n continue;\n }\n throw new Error(usage);\n }\n const root = await realpath(process.cwd());\n const { files, trackedFiles, diagnostics } = readToolchainInputs(root);\n const policy = parseToolchainPolicy(\n JSON.parse(\n await readFile(new URL(\"../toolchain.json\", import.meta.url), \"utf8\"),\n ),\n );\n const yaml = join(root, \".github/dependabot.yaml\");\n const yml = join(root, \".github/dependabot.yml\");\n const yamlStat = await checkGeneratedOutput(yaml);\n const ymlStat = await checkGeneratedOutput(yml);\n const candidates = [\n { relative: \".github/dependabot.yaml\", path: yaml, stat: yamlStat },\n { relative: \".github/dependabot.yml\", path: yml, stat: ymlStat },\n ].filter(\n ({ relative, stat }) => stat !== undefined || trackedFiles.has(relative),\n );\n if (candidates.length > 1)\n throw new Error(\"keep exactly one Dependabot configuration: .yml or .yaml\");\n const selected = candidates.at(0) ?? {\n relative: \".github/dependabot.yml\",\n path: yml,\n stat: ymlStat,\n };\n const remainingDiagnostics = await generationDiagnostics({\n diagnostics,\n outputs: [{ relative: selected.relative, destination: selected.path }],\n });\n if (remainingDiagnostics.length !== 0)\n throw new Error(\n remainingDiagnostics\n .map(({ path, message }) => `${path}: ${message}`)\n .join(\"\\n\"),\n );\n const declarations =\n config === undefined\n ? undefined\n : parseDependabotInput(\n JSON.parse(await readFile(resolve(root, config), \"utf8\")),\n );\n const text = generateDependabotConfig({\n files,\n policy: policy.dependabot,\n updates: declarations,\n });\n const destination = selected.path;\n if (mode === \"check\") {\n if (\n selected.stat === undefined ||\n (await readFile(destination, \"utf8\")) !== text\n )\n throw new Error(\n `${selected.relative} has drifted; run stll-dependabot${config === undefined ? \"\" : ` --config ${config}`}`,\n );\n process.stdout.write(`Verified ${selected.relative}\\n`);\n return;\n }\n const permissions = selected.stat?.mode;\n await writeGeneratedFile({\n destination,\n text,\n mode: permissions === undefined ? 0o644 : permissions & 0o777,\n });\n process.stdout.write(`Generated ${selected.relative}\\n`);\n};\n\nmain().catch((error: unknown) => {\n process.stderr.write(\n `dependabot: ${error instanceof Error ? error.message : String(error)}\\n`,\n );\n process.exitCode = 1;\n});\n"],"mappings":";;;;;;;;;;AAgBA,MAAM,QACJ;AACF,MAAM,OAAO,YAAY;CACvB,MAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;CACjC,IAAI,KAAK,WAAW,KAAK,KAAK,GAAG,CAAC,MAAM,UAAU;EAChD,QAAQ,OAAO,MAAM,GAAG,MAAM,GAAG;EACjC;CACF;CACA,IAAI,OAA0B;CAC9B,IAAI;CACJ,KAAK,IAAI,QAAQ,GAAG,QAAQ,KAAK,QAAQ,SAAS;EAChD,MAAM,MAAM,KAAK;EACjB,IAAI,QAAQ,aAAa,SAAS,SAAS;GACzC,OAAO;GACP;EACF;EACA,IAAI,QAAQ,cAAc,WAAW,KAAA,GAAW;GAC9C,MAAM,OAAO,KAAK,GAAG,QAAQ,CAAC;GAC9B,IAAI,SAAS,KAAA,KAAa,KAAK,WAAW,IAAI,GAAG,MAAM,IAAI,MAAM,KAAK;GACtE,SAAS;GACT;GACA;EACF;EACA,MAAM,IAAI,MAAM,KAAK;CACvB;CACA,MAAM,OAAO,OAAA,GAAMA,iBAAAA,SAAAA,CAAS,QAAQ,IAAI,CAAC;CACzC,MAAM,EAAE,OAAO,cAAc,gBAAgBC,wBAAAA,oBAAoB,IAAI;CACrE,MAAM,SAASC,yBAAAA,qBACb,KAAK,MACH,OAAA,GAAMC,iBAAAA,SAAAA,CAAS,IAAI,IAAI,qBAAA,QAAA,KAAA,CAAA,CAAA,cAAA,UAAA,CAAA,CAAA,IAAoC,GAAG,MAAM,CACtE,CACF;CACA,MAAM,QAAA,GAAOC,UAAAA,KAAAA,CAAK,MAAM,yBAAyB;CACjD,MAAM,OAAA,GAAMA,UAAAA,KAAAA,CAAK,MAAM,wBAAwB;CAC/C,MAAM,WAAW,MAAMC,uBAAAA,qBAAqB,IAAI;CAChD,MAAM,UAAU,MAAMA,uBAAAA,qBAAqB,GAAG;CAC9C,MAAM,aAAa,CACjB;EAAE,UAAU;EAA2B,MAAM;EAAM,MAAM;CAAS,GAClE;EAAE,UAAU;EAA0B,MAAM;EAAK,MAAM;CAAQ,CACjE,CAAC,CAAC,QACC,EAAE,UAAU,WAAW,SAAS,KAAA,KAAa,aAAa,IAAI,QAAQ,CACzE;CACA,IAAI,WAAW,SAAS,GACtB,MAAM,IAAI,MAAM,0DAA0D;CAC5E,MAAM,WAAW,WAAW,GAAG,CAAC,KAAK;EACnC,UAAU;EACV,MAAM;EACN,MAAM;CACR;CACA,MAAM,uBAAuB,MAAMC,uBAAAA,sBAAsB;EACvD;EACA,SAAS,CAAC;GAAE,UAAU,SAAS;GAAU,aAAa,SAAS;EAAK,CAAC;CACvE,CAAC;CACD,IAAI,qBAAqB,WAAW,GAClC,MAAM,IAAI,MACR,qBACG,KAAK,EAAE,MAAM,cAAc,GAAG,KAAK,IAAI,SAAS,CAAC,CACjD,KAAK,IAAI,CACd;CACF,MAAM,eACJ,WAAW,KAAA,IACP,KAAA,IACAC,6BAAAA,qBACE,KAAK,MAAM,OAAA,GAAMJ,iBAAAA,SAAAA,EAAAA,GAASK,UAAAA,QAAAA,CAAQ,MAAM,MAAM,GAAG,MAAM,CAAC,CAC1D;CACN,MAAM,OAAOC,6BAAAA,yBAAyB;EACpC;EACA,QAAQ,OAAO;EACf,SAAS;CACX,CAAC;CACD,MAAM,cAAc,SAAS;CAC7B,IAAI,SAAS,SAAS;EACpB,IACE,SAAS,SAAS,KAAA,KACjB,OAAA,GAAMN,iBAAAA,SAAAA,CAAS,aAAa,MAAM,MAAO,MAE1C,MAAM,IAAI,MACR,GAAG,SAAS,SAAS,mCAAmC,WAAW,KAAA,IAAY,KAAK,aAAa,UACnG;EACF,QAAQ,OAAO,MAAM,YAAY,SAAS,SAAS,GAAG;EACtD;CACF;CACA,MAAM,cAAc,SAAS,MAAM;CACnC,MAAMO,uBAAAA,mBAAmB;EACvB;EACA;EACA,MAAM,gBAAgB,KAAA,IAAY,MAAQ,cAAc;CAC1D,CAAC;CACD,QAAQ,OAAO,MAAM,aAAa,SAAS,SAAS,GAAG;AACzD;AAEA,KAAK,CAAC,CAAC,OAAO,UAAmB;CAC/B,QAAQ,OAAO,MACb,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE,GACxE;CACA,QAAQ,WAAW;AACrB,CAAC"}
@@ -1,20 +1,36 @@
1
1
  #!/usr/bin/env node
2
+ import { n as generationDiagnostics, r as writeGeneratedFile, t as checkGeneratedOutput } from "./generated-file.mjs";
2
3
  import { r as readToolchainInputs } from "./toolchain-guard.mjs";
3
- import { r as generateDependabotConfig } from "./toolchain-dependabot.mjs";
4
+ import { i as parseDependabotInput, r as generateDependabotConfig } from "./toolchain-dependabot.mjs";
4
5
  import { t as parseToolchainPolicy } from "./toolchain-schema.mjs";
5
6
  import "./dependabot.mjs";
6
- import { n as generationDiagnostics, r as writeGeneratedFile, t as checkGeneratedOutput } from "./generated-file.mjs";
7
- import { join } from "node:path";
8
7
  import { readFile, realpath } from "node:fs/promises";
8
+ import { join, resolve } from "node:path";
9
9
  //#region src/dependabot-cli.ts
10
- const usage = "Usage: stll-dependabot (run from repository root)\n stll-dependabot --help";
10
+ const usage = "Usage: stll-dependabot [--check] [--config <path>] (run from repository root)\n stll-dependabot --help";
11
11
  const main = async () => {
12
12
  const args = process.argv.slice(2);
13
13
  if (args.length === 1 && args.at(0) === "--help") {
14
14
  process.stdout.write(`${usage}\n`);
15
15
  return;
16
16
  }
17
- if (args.length !== 0) throw new Error(usage);
17
+ let mode = "write";
18
+ let config;
19
+ for (let index = 0; index < args.length; index++) {
20
+ const arg = args[index];
21
+ if (arg === "--check" && mode === "write") {
22
+ mode = "check";
23
+ continue;
24
+ }
25
+ if (arg === "--config" && config === void 0) {
26
+ const next = args.at(index + 1);
27
+ if (next === void 0 || next.startsWith("--")) throw new Error(usage);
28
+ config = next;
29
+ index++;
30
+ continue;
31
+ }
32
+ throw new Error(usage);
33
+ }
18
34
  const root = await realpath(process.cwd());
19
35
  const { files, trackedFiles, diagnostics } = readToolchainInputs(root);
20
36
  const policy = parseToolchainPolicy(JSON.parse(await readFile(new URL("../toolchain.json", import.meta.url), "utf8")));
@@ -45,15 +61,23 @@ const main = async () => {
45
61
  }]
46
62
  });
47
63
  if (remainingDiagnostics.length !== 0) throw new Error(remainingDiagnostics.map(({ path, message }) => `${path}: ${message}`).join("\n"));
64
+ const declarations = config === void 0 ? void 0 : parseDependabotInput(JSON.parse(await readFile(resolve(root, config), "utf8")));
65
+ const text = generateDependabotConfig({
66
+ files,
67
+ policy: policy.dependabot,
68
+ updates: declarations
69
+ });
48
70
  const destination = selected.path;
49
- const mode = selected.stat?.mode;
71
+ if (mode === "check") {
72
+ if (selected.stat === void 0 || await readFile(destination, "utf8") !== text) throw new Error(`${selected.relative} has drifted; run stll-dependabot${config === void 0 ? "" : ` --config ${config}`}`);
73
+ process.stdout.write(`Verified ${selected.relative}\n`);
74
+ return;
75
+ }
76
+ const permissions = selected.stat?.mode;
50
77
  await writeGeneratedFile({
51
78
  destination,
52
- text: generateDependabotConfig({
53
- files,
54
- policy: policy.dependabot
55
- }),
56
- mode: mode === void 0 ? 420 : mode & 511
79
+ text,
80
+ mode: permissions === void 0 ? 420 : permissions & 511
57
81
  });
58
82
  process.stdout.write(`Generated ${selected.relative}\n`);
59
83
  };
@@ -1 +1 @@
1
- {"version":3,"file":"dependabot-cli.mjs","names":[],"sources":["../src/dependabot-cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { readFile, realpath } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { generateDependabotConfig, parseToolchainPolicy } from \"./dependabot\";\nimport {\n checkGeneratedOutput,\n generationDiagnostics,\n writeGeneratedFile,\n} from \"./generated-file\";\nimport { readToolchainInputs } from \"./toolchain-guard\";\n\nconst usage =\n \"Usage: stll-dependabot (run from repository root)\\n stll-dependabot --help\";\nconst main = async () => {\n const args = process.argv.slice(2);\n if (args.length === 1 && args.at(0) === \"--help\") {\n process.stdout.write(`${usage}\\n`);\n return;\n }\n if (args.length !== 0) throw new Error(usage);\n const root = await realpath(process.cwd());\n const { files, trackedFiles, diagnostics } = readToolchainInputs(root);\n const policy = parseToolchainPolicy(\n JSON.parse(\n await readFile(new URL(\"../toolchain.json\", import.meta.url), \"utf8\"),\n ),\n );\n const yaml = join(root, \".github/dependabot.yaml\");\n const yml = join(root, \".github/dependabot.yml\");\n const yamlStat = await checkGeneratedOutput(yaml);\n const ymlStat = await checkGeneratedOutput(yml);\n const candidates = [\n { relative: \".github/dependabot.yaml\", path: yaml, stat: yamlStat },\n { relative: \".github/dependabot.yml\", path: yml, stat: ymlStat },\n ].filter(\n ({ relative, stat }) => stat !== undefined || trackedFiles.has(relative),\n );\n if (candidates.length > 1)\n throw new Error(\"keep exactly one Dependabot configuration: .yml or .yaml\");\n const selected = candidates.at(0) ?? {\n relative: \".github/dependabot.yml\",\n path: yml,\n stat: ymlStat,\n };\n const remainingDiagnostics = await generationDiagnostics({\n diagnostics,\n outputs: [{ relative: selected.relative, destination: selected.path }],\n });\n if (remainingDiagnostics.length !== 0)\n throw new Error(\n remainingDiagnostics\n .map(({ path, message }) => `${path}: ${message}`)\n .join(\"\\n\"),\n );\n const destination = selected.path;\n const mode = selected.stat?.mode;\n await writeGeneratedFile({\n destination,\n text: generateDependabotConfig({ files, policy: policy.dependabot }),\n mode: mode === undefined ? 0o644 : mode & 0o777,\n });\n process.stdout.write(`Generated ${selected.relative}\\n`);\n};\n\nmain().catch((error: unknown) => {\n process.stderr.write(\n `dependabot: ${error instanceof Error ? error.message : String(error)}\\n`,\n );\n process.exitCode = 1;\n});\n"],"mappings":";;;;;;;;;AAYA,MAAM,QACJ;AACF,MAAM,OAAO,YAAY;CACvB,MAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;CACjC,IAAI,KAAK,WAAW,KAAK,KAAK,GAAG,CAAC,MAAM,UAAU;EAChD,QAAQ,OAAO,MAAM,GAAG,MAAM,GAAG;EACjC;CACF;CACA,IAAI,KAAK,WAAW,GAAG,MAAM,IAAI,MAAM,KAAK;CAC5C,MAAM,OAAO,MAAM,SAAS,QAAQ,IAAI,CAAC;CACzC,MAAM,EAAE,OAAO,cAAc,gBAAgB,oBAAoB,IAAI;CACrE,MAAM,SAAS,qBACb,KAAK,MACH,MAAM,SAAS,IAAI,IAAI,qBAAqB,YAAY,GAAG,GAAG,MAAM,CACtE,CACF;CACA,MAAM,OAAO,KAAK,MAAM,yBAAyB;CACjD,MAAM,MAAM,KAAK,MAAM,wBAAwB;CAC/C,MAAM,WAAW,MAAM,qBAAqB,IAAI;CAChD,MAAM,UAAU,MAAM,qBAAqB,GAAG;CAC9C,MAAM,aAAa,CACjB;EAAE,UAAU;EAA2B,MAAM;EAAM,MAAM;CAAS,GAClE;EAAE,UAAU;EAA0B,MAAM;EAAK,MAAM;CAAQ,CACjE,CAAC,CAAC,QACC,EAAE,UAAU,WAAW,SAAS,KAAA,KAAa,aAAa,IAAI,QAAQ,CACzE;CACA,IAAI,WAAW,SAAS,GACtB,MAAM,IAAI,MAAM,0DAA0D;CAC5E,MAAM,WAAW,WAAW,GAAG,CAAC,KAAK;EACnC,UAAU;EACV,MAAM;EACN,MAAM;CACR;CACA,MAAM,uBAAuB,MAAM,sBAAsB;EACvD;EACA,SAAS,CAAC;GAAE,UAAU,SAAS;GAAU,aAAa,SAAS;EAAK,CAAC;CACvE,CAAC;CACD,IAAI,qBAAqB,WAAW,GAClC,MAAM,IAAI,MACR,qBACG,KAAK,EAAE,MAAM,cAAc,GAAG,KAAK,IAAI,SAAS,CAAC,CACjD,KAAK,IAAI,CACd;CACF,MAAM,cAAc,SAAS;CAC7B,MAAM,OAAO,SAAS,MAAM;CAC5B,MAAM,mBAAmB;EACvB;EACA,MAAM,yBAAyB;GAAE;GAAO,QAAQ,OAAO;EAAW,CAAC;EACnE,MAAM,SAAS,KAAA,IAAY,MAAQ,OAAO;CAC5C,CAAC;CACD,QAAQ,OAAO,MAAM,aAAa,SAAS,SAAS,GAAG;AACzD;AAEA,KAAK,CAAC,CAAC,OAAO,UAAmB;CAC/B,QAAQ,OAAO,MACb,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE,GACxE;CACA,QAAQ,WAAW;AACrB,CAAC"}
1
+ {"version":3,"file":"dependabot-cli.mjs","names":[],"sources":["../src/dependabot-cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { readFile, realpath } from \"node:fs/promises\";\nimport { join, resolve } from \"node:path\";\n\nimport {\n generateDependabotConfig,\n parseDependabotInput,\n parseToolchainPolicy,\n} from \"./dependabot\";\nimport {\n checkGeneratedOutput,\n generationDiagnostics,\n writeGeneratedFile,\n} from \"./generated-file\";\nimport { readToolchainInputs } from \"./toolchain-guard\";\n\nconst usage =\n \"Usage: stll-dependabot [--check] [--config <path>] (run from repository root)\\n stll-dependabot --help\";\nconst main = async () => {\n const args = process.argv.slice(2);\n if (args.length === 1 && args.at(0) === \"--help\") {\n process.stdout.write(`${usage}\\n`);\n return;\n }\n let mode: \"write\" | \"check\" = \"write\";\n let config: string | undefined;\n for (let index = 0; index < args.length; index++) {\n const arg = args[index];\n if (arg === \"--check\" && mode === \"write\") {\n mode = \"check\";\n continue;\n }\n if (arg === \"--config\" && config === undefined) {\n const next = args.at(index + 1);\n if (next === undefined || next.startsWith(\"--\")) throw new Error(usage);\n config = next;\n index++;\n continue;\n }\n throw new Error(usage);\n }\n const root = await realpath(process.cwd());\n const { files, trackedFiles, diagnostics } = readToolchainInputs(root);\n const policy = parseToolchainPolicy(\n JSON.parse(\n await readFile(new URL(\"../toolchain.json\", import.meta.url), \"utf8\"),\n ),\n );\n const yaml = join(root, \".github/dependabot.yaml\");\n const yml = join(root, \".github/dependabot.yml\");\n const yamlStat = await checkGeneratedOutput(yaml);\n const ymlStat = await checkGeneratedOutput(yml);\n const candidates = [\n { relative: \".github/dependabot.yaml\", path: yaml, stat: yamlStat },\n { relative: \".github/dependabot.yml\", path: yml, stat: ymlStat },\n ].filter(\n ({ relative, stat }) => stat !== undefined || trackedFiles.has(relative),\n );\n if (candidates.length > 1)\n throw new Error(\"keep exactly one Dependabot configuration: .yml or .yaml\");\n const selected = candidates.at(0) ?? {\n relative: \".github/dependabot.yml\",\n path: yml,\n stat: ymlStat,\n };\n const remainingDiagnostics = await generationDiagnostics({\n diagnostics,\n outputs: [{ relative: selected.relative, destination: selected.path }],\n });\n if (remainingDiagnostics.length !== 0)\n throw new Error(\n remainingDiagnostics\n .map(({ path, message }) => `${path}: ${message}`)\n .join(\"\\n\"),\n );\n const declarations =\n config === undefined\n ? undefined\n : parseDependabotInput(\n JSON.parse(await readFile(resolve(root, config), \"utf8\")),\n );\n const text = generateDependabotConfig({\n files,\n policy: policy.dependabot,\n updates: declarations,\n });\n const destination = selected.path;\n if (mode === \"check\") {\n if (\n selected.stat === undefined ||\n (await readFile(destination, \"utf8\")) !== text\n )\n throw new Error(\n `${selected.relative} has drifted; run stll-dependabot${config === undefined ? \"\" : ` --config ${config}`}`,\n );\n process.stdout.write(`Verified ${selected.relative}\\n`);\n return;\n }\n const permissions = selected.stat?.mode;\n await writeGeneratedFile({\n destination,\n text,\n mode: permissions === undefined ? 0o644 : permissions & 0o777,\n });\n process.stdout.write(`Generated ${selected.relative}\\n`);\n};\n\nmain().catch((error: unknown) => {\n process.stderr.write(\n `dependabot: ${error instanceof Error ? error.message : String(error)}\\n`,\n );\n process.exitCode = 1;\n});\n"],"mappings":";;;;;;;;;AAgBA,MAAM,QACJ;AACF,MAAM,OAAO,YAAY;CACvB,MAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;CACjC,IAAI,KAAK,WAAW,KAAK,KAAK,GAAG,CAAC,MAAM,UAAU;EAChD,QAAQ,OAAO,MAAM,GAAG,MAAM,GAAG;EACjC;CACF;CACA,IAAI,OAA0B;CAC9B,IAAI;CACJ,KAAK,IAAI,QAAQ,GAAG,QAAQ,KAAK,QAAQ,SAAS;EAChD,MAAM,MAAM,KAAK;EACjB,IAAI,QAAQ,aAAa,SAAS,SAAS;GACzC,OAAO;GACP;EACF;EACA,IAAI,QAAQ,cAAc,WAAW,KAAA,GAAW;GAC9C,MAAM,OAAO,KAAK,GAAG,QAAQ,CAAC;GAC9B,IAAI,SAAS,KAAA,KAAa,KAAK,WAAW,IAAI,GAAG,MAAM,IAAI,MAAM,KAAK;GACtE,SAAS;GACT;GACA;EACF;EACA,MAAM,IAAI,MAAM,KAAK;CACvB;CACA,MAAM,OAAO,MAAM,SAAS,QAAQ,IAAI,CAAC;CACzC,MAAM,EAAE,OAAO,cAAc,gBAAgB,oBAAoB,IAAI;CACrE,MAAM,SAAS,qBACb,KAAK,MACH,MAAM,SAAS,IAAI,IAAI,qBAAqB,YAAY,GAAG,GAAG,MAAM,CACtE,CACF;CACA,MAAM,OAAO,KAAK,MAAM,yBAAyB;CACjD,MAAM,MAAM,KAAK,MAAM,wBAAwB;CAC/C,MAAM,WAAW,MAAM,qBAAqB,IAAI;CAChD,MAAM,UAAU,MAAM,qBAAqB,GAAG;CAC9C,MAAM,aAAa,CACjB;EAAE,UAAU;EAA2B,MAAM;EAAM,MAAM;CAAS,GAClE;EAAE,UAAU;EAA0B,MAAM;EAAK,MAAM;CAAQ,CACjE,CAAC,CAAC,QACC,EAAE,UAAU,WAAW,SAAS,KAAA,KAAa,aAAa,IAAI,QAAQ,CACzE;CACA,IAAI,WAAW,SAAS,GACtB,MAAM,IAAI,MAAM,0DAA0D;CAC5E,MAAM,WAAW,WAAW,GAAG,CAAC,KAAK;EACnC,UAAU;EACV,MAAM;EACN,MAAM;CACR;CACA,MAAM,uBAAuB,MAAM,sBAAsB;EACvD;EACA,SAAS,CAAC;GAAE,UAAU,SAAS;GAAU,aAAa,SAAS;EAAK,CAAC;CACvE,CAAC;CACD,IAAI,qBAAqB,WAAW,GAClC,MAAM,IAAI,MACR,qBACG,KAAK,EAAE,MAAM,cAAc,GAAG,KAAK,IAAI,SAAS,CAAC,CACjD,KAAK,IAAI,CACd;CACF,MAAM,eACJ,WAAW,KAAA,IACP,KAAA,IACA,qBACE,KAAK,MAAM,MAAM,SAAS,QAAQ,MAAM,MAAM,GAAG,MAAM,CAAC,CAC1D;CACN,MAAM,OAAO,yBAAyB;EACpC;EACA,QAAQ,OAAO;EACf,SAAS;CACX,CAAC;CACD,MAAM,cAAc,SAAS;CAC7B,IAAI,SAAS,SAAS;EACpB,IACE,SAAS,SAAS,KAAA,KACjB,MAAM,SAAS,aAAa,MAAM,MAAO,MAE1C,MAAM,IAAI,MACR,GAAG,SAAS,SAAS,mCAAmC,WAAW,KAAA,IAAY,KAAK,aAAa,UACnG;EACF,QAAQ,OAAO,MAAM,YAAY,SAAS,SAAS,GAAG;EACtD;CACF;CACA,MAAM,cAAc,SAAS,MAAM;CACnC,MAAM,mBAAmB;EACvB;EACA;EACA,MAAM,gBAAgB,KAAA,IAAY,MAAQ,cAAc;CAC1D,CAAC;CACD,QAAQ,OAAO,MAAM,aAAa,SAAS,SAAS,GAAG;AACzD;AAEA,KAAK,CAAC,CAAC,OAAO,UAAmB;CAC/B,QAAQ,OAAO,MACb,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE,GACxE;CACA,QAAQ,WAAW;AACrB,CAAC"}
@@ -2,4 +2,6 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_toolchain_dependabot = require("./toolchain-dependabot.cjs");
3
3
  const require_toolchain_schema = require("./toolchain-schema.cjs");
4
4
  exports.generateDependabotConfig = require_toolchain_dependabot.generateDependabotConfig;
5
+ exports.parseDependabotInput = require_toolchain_dependabot.parseDependabotInput;
5
6
  exports.parseToolchainPolicy = require_toolchain_schema.parseToolchainPolicy;
7
+ exports.supportedDependabotEcosystems = require_toolchain_dependabot.supportedDependabotEcosystems;
@@ -1,4 +1,6 @@
1
1
  //#region src/toolchain-dependabot.d.ts
2
+ declare const supportedDependabotEcosystems: readonly ["bazel", "bun", "bundler", "cargo", "composer", "conda", "deno", "devcontainers", "docker", "docker-compose", "dotnet-sdk", "elm", "gitsubmodule", "github-actions", "gomod", "gradle", "helm", "julia", "maven", "mix", "nix", "npm", "nuget", "opentofu", "pip", "pre-commit", "pub", "rust-toolchain", "sbt", "swift", "terraform", "uv", "vcpkg"];
3
+ type DependabotEcosystem = (typeof supportedDependabotEcosystems)[number];
2
4
  type DependabotPolicy = {
3
5
  schedule: {
4
6
  interval: string;
@@ -21,8 +23,16 @@ type CheckDependabotOptions = {
21
23
  files: Record<string, string>;
22
24
  policy: DependabotPolicy;
23
25
  };
26
+ /** Repository declarations own coverage only; shared policy owns every update option. */
27
+ declare const parseDependabotInput: (input: unknown) => {
28
+ ecosystem: "bazel" | "bun" | "bundler" | "cargo" | "composer" | "conda" | "deno" | "devcontainers" | "docker" | "docker-compose" | "dotnet-sdk" | "elm" | "github-actions" | "gitsubmodule" | "gomod" | "gradle" | "helm" | "julia" | "maven" | "mix" | "nix" | "npm" | "nuget" | "opentofu" | "pip" | "pre-commit" | "pub" | "rust-toolchain" | "sbt" | "swift" | "terraform" | "uv" | "vcpkg";
29
+ directories: string[];
30
+ }[];
31
+ type GenerateDependabotOptions = CheckDependabotOptions & {
32
+ updates?: ReturnType<typeof parseDependabotInput> | undefined;
33
+ };
24
34
  /** Generate canonical YAML from the same ecosystem enumeration used by the guard. */
25
- declare const generateDependabotConfig: ({ files, policy }: CheckDependabotOptions) => string;
35
+ declare const generateDependabotConfig: ({ files, policy, updates: declarations }: GenerateDependabotOptions) => string;
26
36
  //#endregion
27
37
  //#region src/toolchain-schema.d.ts
28
38
  /** Reject unsupported or incomplete installed policies before inspecting a consumer. */
@@ -104,5 +114,5 @@ declare const parseToolchainPolicy: (input: unknown) => {
104
114
  };
105
115
  };
106
116
  //#endregion
107
- export { generateDependabotConfig, parseToolchainPolicy };
117
+ export { type DependabotEcosystem, generateDependabotConfig, parseDependabotInput, parseToolchainPolicy, supportedDependabotEcosystems };
108
118
  //# sourceMappingURL=dependabot.d.cts.map
@@ -1,4 +1,6 @@
1
1
  //#region src/toolchain-dependabot.d.ts
2
+ declare const supportedDependabotEcosystems: readonly ["bazel", "bun", "bundler", "cargo", "composer", "conda", "deno", "devcontainers", "docker", "docker-compose", "dotnet-sdk", "elm", "gitsubmodule", "github-actions", "gomod", "gradle", "helm", "julia", "maven", "mix", "nix", "npm", "nuget", "opentofu", "pip", "pre-commit", "pub", "rust-toolchain", "sbt", "swift", "terraform", "uv", "vcpkg"];
3
+ type DependabotEcosystem = (typeof supportedDependabotEcosystems)[number];
2
4
  type DependabotPolicy = {
3
5
  schedule: {
4
6
  interval: string;
@@ -21,8 +23,16 @@ type CheckDependabotOptions = {
21
23
  files: Record<string, string>;
22
24
  policy: DependabotPolicy;
23
25
  };
26
+ /** Repository declarations own coverage only; shared policy owns every update option. */
27
+ declare const parseDependabotInput: (input: unknown) => {
28
+ ecosystem: "bazel" | "bun" | "bundler" | "cargo" | "composer" | "conda" | "deno" | "devcontainers" | "docker" | "docker-compose" | "dotnet-sdk" | "elm" | "github-actions" | "gitsubmodule" | "gomod" | "gradle" | "helm" | "julia" | "maven" | "mix" | "nix" | "npm" | "nuget" | "opentofu" | "pip" | "pre-commit" | "pub" | "rust-toolchain" | "sbt" | "swift" | "terraform" | "uv" | "vcpkg";
29
+ directories: string[];
30
+ }[];
31
+ type GenerateDependabotOptions = CheckDependabotOptions & {
32
+ updates?: ReturnType<typeof parseDependabotInput> | undefined;
33
+ };
24
34
  /** Generate canonical YAML from the same ecosystem enumeration used by the guard. */
25
- declare const generateDependabotConfig: ({ files, policy }: CheckDependabotOptions) => string;
35
+ declare const generateDependabotConfig: ({ files, policy, updates: declarations }: GenerateDependabotOptions) => string;
26
36
  //#endregion
27
37
  //#region src/toolchain-schema.d.ts
28
38
  /** Reject unsupported or incomplete installed policies before inspecting a consumer. */
@@ -104,5 +114,5 @@ declare const parseToolchainPolicy: (input: unknown) => {
104
114
  };
105
115
  };
106
116
  //#endregion
107
- export { generateDependabotConfig, parseToolchainPolicy };
117
+ export { type DependabotEcosystem, generateDependabotConfig, parseDependabotInput, parseToolchainPolicy, supportedDependabotEcosystems };
108
118
  //# sourceMappingURL=dependabot.d.mts.map
@@ -1,3 +1,3 @@
1
- import { r as generateDependabotConfig } from "./toolchain-dependabot.mjs";
1
+ import { a as supportedDependabotEcosystems, i as parseDependabotInput, r as generateDependabotConfig } from "./toolchain-dependabot.mjs";
2
2
  import { t as parseToolchainPolicy } from "./toolchain-schema.mjs";
3
- export { generateDependabotConfig, parseToolchainPolicy };
3
+ export { generateDependabotConfig, parseDependabotInput, parseToolchainPolicy, supportedDependabotEcosystems };
@@ -1,6 +1,6 @@
1
- let node_fs = require("node:fs");
2
- let node_path = require("node:path");
3
1
  let node_fs_promises = require("node:fs/promises");
2
+ let node_path = require("node:path");
3
+ let node_fs = require("node:fs");
4
4
  //#region src/generated-file.ts
5
5
  const optionalStat = async (path) => {
6
6
  try {
@@ -1,6 +1,6 @@
1
- import { constants } from "node:fs";
2
- import { dirname } from "node:path";
3
1
  import { lstat, mkdir, open } from "node:fs/promises";
2
+ import { dirname } from "node:path";
3
+ import { constants } from "node:fs";
4
4
  //#region src/generated-file.ts
5
5
  const optionalStat = async (path) => {
6
6
  try {
@@ -1,8 +1,8 @@
1
1
  const require_rolldown_runtime = require("./rolldown-runtime.cjs");
2
- let node_fs = require("node:fs");
3
- let node_child_process = require("node:child_process");
4
2
  let node_path = require("node:path");
5
3
  node_path = require_rolldown_runtime.__toESM(node_path, 1);
4
+ let node_fs = require("node:fs");
5
+ let node_child_process = require("node:child_process");
6
6
  let node_os = require("node:os");
7
7
  //#region src/git-tracked-output.ts
8
8
  /** Stream Git's file list to disk so consumer size cannot exhaust maxBuffer. */
@@ -1,6 +1,6 @@
1
+ import path from "node:path";
1
2
  import { closeSync, mkdtempSync, openSync, readFileSync, rmSync } from "node:fs";
2
3
  import { execFileSync } from "node:child_process";
3
- import path from "node:path";
4
4
  import { tmpdir } from "node:os";
5
5
  //#region src/git-tracked-output.ts
6
6
  /** Stream Git's file list to disk so consumer size cannot exhaust maxBuffer. */
package/dist/jsonc.cjs ADDED
@@ -0,0 +1,66 @@
1
+ //#region src/jsonc.ts
2
+ /** JSONC permits comments and trailing commas, never alterations inside strings. */
3
+ const parseJsonc = (text) => {
4
+ let clean = "";
5
+ let quote = false;
6
+ for (let index = 0; index < text.length; index++) {
7
+ const char = text[index];
8
+ if (quote) {
9
+ clean += char;
10
+ if (char === "\\") {
11
+ index++;
12
+ clean += text[index] ?? "";
13
+ } else if (char === "\"") quote = false;
14
+ continue;
15
+ }
16
+ if (char === "\"") {
17
+ quote = true;
18
+ clean += char;
19
+ continue;
20
+ }
21
+ if (char === "/" && text[index + 1] === "/") {
22
+ while (index < text.length && text[index] !== "\n") index++;
23
+ clean += "\n";
24
+ continue;
25
+ }
26
+ if (char === "/" && text[index + 1] === "*") {
27
+ index += 2;
28
+ while (index < text.length && !(text[index] === "*" && text[index + 1] === "/")) index++;
29
+ if (index >= text.length) throw new Error("Unterminated JSONC comment");
30
+ index++;
31
+ clean += " ";
32
+ continue;
33
+ }
34
+ clean += char;
35
+ }
36
+ let result = "";
37
+ quote = false;
38
+ for (let index = 0; index < clean.length; index++) {
39
+ const char = clean[index];
40
+ if (quote) {
41
+ result += char;
42
+ if (char === "\\") {
43
+ index++;
44
+ result += clean[index] ?? "";
45
+ } else if (char === "\"") quote = false;
46
+ continue;
47
+ }
48
+ if (char === "\"") quote = true;
49
+ if (char === ",") {
50
+ let next = index + 1;
51
+ while (/\s/.test(clean[next] ?? "") && next < clean.length) next++;
52
+ if (clean[next] === "}" || clean[next] === "]") continue;
53
+ }
54
+ result += char;
55
+ }
56
+ return JSON.parse(result);
57
+ };
58
+ //#endregion
59
+ Object.defineProperty(exports, "parseJsonc", {
60
+ enumerable: true,
61
+ get: function() {
62
+ return parseJsonc;
63
+ }
64
+ });
65
+
66
+ //# sourceMappingURL=jsonc.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsonc.cjs","names":[],"sources":["../src/jsonc.ts"],"sourcesContent":["/** JSONC permits comments and trailing commas, never alterations inside strings. */\nexport const parseJsonc = (text: string): unknown => {\n let clean = \"\";\n let quote = false;\n for (let index = 0; index < text.length; index++) {\n const char = text[index];\n if (quote) {\n clean += char;\n if (char === \"\\\\\") {\n index++;\n clean += text[index] ?? \"\";\n } else if (char === '\"') quote = false;\n continue;\n }\n if (char === '\"') {\n quote = true;\n clean += char;\n continue;\n }\n if (char === \"/\" && text[index + 1] === \"/\") {\n while (index < text.length && text[index] !== \"\\n\") index++;\n clean += \"\\n\";\n continue;\n }\n if (char === \"/\" && text[index + 1] === \"*\") {\n index += 2;\n while (\n index < text.length &&\n !(text[index] === \"*\" && text[index + 1] === \"/\")\n )\n index++;\n if (index >= text.length) throw new Error(\"Unterminated JSONC comment\");\n index++;\n clean += \" \";\n continue;\n }\n clean += char;\n }\n let result = \"\";\n quote = false;\n for (let index = 0; index < clean.length; index++) {\n const char = clean[index];\n if (quote) {\n result += char;\n if (char === \"\\\\\") {\n index++;\n result += clean[index] ?? \"\";\n } else if (char === '\"') quote = false;\n continue;\n }\n if (char === '\"') quote = true;\n if (char === \",\") {\n let next = index + 1;\n while (/\\s/.test(clean[next] ?? \"\") && next < clean.length) next++;\n if (clean[next] === \"}\" || clean[next] === \"]\") continue;\n }\n result += char;\n }\n return JSON.parse(result);\n};\n"],"mappings":";;AACA,MAAa,cAAc,SAA0B;CACnD,IAAI,QAAQ;CACZ,IAAI,QAAQ;CACZ,KAAK,IAAI,QAAQ,GAAG,QAAQ,KAAK,QAAQ,SAAS;EAChD,MAAM,OAAO,KAAK;EAClB,IAAI,OAAO;GACT,SAAS;GACT,IAAI,SAAS,MAAM;IACjB;IACA,SAAS,KAAK,UAAU;GAC1B,OAAO,IAAI,SAAS,MAAK,QAAQ;GACjC;EACF;EACA,IAAI,SAAS,MAAK;GAChB,QAAQ;GACR,SAAS;GACT;EACF;EACA,IAAI,SAAS,OAAO,KAAK,QAAQ,OAAO,KAAK;GAC3C,OAAO,QAAQ,KAAK,UAAU,KAAK,WAAW,MAAM;GACpD,SAAS;GACT;EACF;EACA,IAAI,SAAS,OAAO,KAAK,QAAQ,OAAO,KAAK;GAC3C,SAAS;GACT,OACE,QAAQ,KAAK,UACb,EAAE,KAAK,WAAW,OAAO,KAAK,QAAQ,OAAO,MAE7C;GACF,IAAI,SAAS,KAAK,QAAQ,MAAM,IAAI,MAAM,4BAA4B;GACtE;GACA,SAAS;GACT;EACF;EACA,SAAS;CACX;CACA,IAAI,SAAS;CACb,QAAQ;CACR,KAAK,IAAI,QAAQ,GAAG,QAAQ,MAAM,QAAQ,SAAS;EACjD,MAAM,OAAO,MAAM;EACnB,IAAI,OAAO;GACT,UAAU;GACV,IAAI,SAAS,MAAM;IACjB;IACA,UAAU,MAAM,UAAU;GAC5B,OAAO,IAAI,SAAS,MAAK,QAAQ;GACjC;EACF;EACA,IAAI,SAAS,MAAK,QAAQ;EAC1B,IAAI,SAAS,KAAK;GAChB,IAAI,OAAO,QAAQ;GACnB,OAAO,KAAK,KAAK,MAAM,SAAS,EAAE,KAAK,OAAO,MAAM,QAAQ;GAC5D,IAAI,MAAM,UAAU,OAAO,MAAM,UAAU,KAAK;EAClD;EACA,UAAU;CACZ;CACA,OAAO,KAAK,MAAM,MAAM;AAC1B"}