@rainy-updates/cli 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/CHANGELOG.md +158 -0
  2. package/LICENSE +21 -0
  3. package/README.md +141 -0
  4. package/dist/bin/cli.d.ts +2 -0
  5. package/dist/bin/cli.js +146 -0
  6. package/dist/cache/cache.d.ts +9 -0
  7. package/dist/cache/cache.js +125 -0
  8. package/dist/config/loader.d.ts +22 -0
  9. package/dist/config/loader.js +35 -0
  10. package/dist/config/policy.d.ts +16 -0
  11. package/dist/config/policy.js +32 -0
  12. package/dist/core/check.d.ts +2 -0
  13. package/dist/core/check.js +141 -0
  14. package/dist/core/init-ci.d.ts +4 -0
  15. package/dist/core/init-ci.js +20 -0
  16. package/dist/core/options.d.ts +17 -0
  17. package/dist/core/options.js +238 -0
  18. package/dist/core/upgrade.d.ts +2 -0
  19. package/dist/core/upgrade.js +88 -0
  20. package/dist/core/warm-cache.d.ts +2 -0
  21. package/dist/core/warm-cache.js +89 -0
  22. package/dist/index.d.ts +8 -0
  23. package/dist/index.js +7 -0
  24. package/dist/output/format.d.ts +2 -0
  25. package/dist/output/format.js +56 -0
  26. package/dist/output/github.d.ts +3 -0
  27. package/dist/output/github.js +30 -0
  28. package/dist/output/pr-report.d.ts +2 -0
  29. package/dist/output/pr-report.js +38 -0
  30. package/dist/output/sarif.d.ts +2 -0
  31. package/dist/output/sarif.js +60 -0
  32. package/dist/parsers/package-json.d.ts +5 -0
  33. package/dist/parsers/package-json.js +35 -0
  34. package/dist/pm/detect.d.ts +1 -0
  35. package/dist/pm/detect.js +20 -0
  36. package/dist/pm/install.d.ts +1 -0
  37. package/dist/pm/install.js +21 -0
  38. package/dist/registry/npm.d.ts +14 -0
  39. package/dist/registry/npm.js +128 -0
  40. package/dist/types/index.d.ts +86 -0
  41. package/dist/types/index.js +1 -0
  42. package/dist/utils/async-pool.d.ts +1 -0
  43. package/dist/utils/async-pool.js +21 -0
  44. package/dist/utils/pattern.d.ts +1 -0
  45. package/dist/utils/pattern.js +11 -0
  46. package/dist/utils/semver.d.ts +13 -0
  47. package/dist/utils/semver.js +80 -0
  48. package/dist/workspace/discover.d.ts +1 -0
  49. package/dist/workspace/discover.js +88 -0
  50. package/dist/workspace/graph.d.ts +13 -0
  51. package/dist/workspace/graph.js +86 -0
  52. package/package.json +66 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,158 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ ## [0.4.0] - 2026-02-27
6
+
7
+ ### Added
8
+
9
+ - Production hardening for CLI UX:
10
+ - global and command-level help (`--help`, `-h`),
11
+ - version output (`--version`, `-v`),
12
+ - strict unknown command rejection.
13
+ - OSS/release infrastructure:
14
+ - `LICENSE` (MIT),
15
+ - `CONTRIBUTING.md`,
16
+ - project CI workflow (`.github/workflows/ci.yml`),
17
+ - npm release workflow (`.github/workflows/release.yml`).
18
+ - Packaging stabilization:
19
+ - `types` export path,
20
+ - production scripts (`clean`, `test:prod`, `prepublishOnly`),
21
+ - publish config with npm provenance and public access.
22
+
23
+ ### Changed
24
+
25
+ - Registry client now retries latest-version resolution with backoff for transient failures.
26
+ - Output formatting now shows cache warming summary when relevant.
27
+
28
+ ### Fixed
29
+
30
+ - Parser now fails fast for unknown commands instead of silently defaulting to `check`.
31
+
32
+ ## [0.3.0] - 2026-02-27
33
+
34
+ ### Added
35
+
36
+ - `warm-cache` command:
37
+ - pre-fetches package metadata into cache,
38
+ - supports workspace scanning,
39
+ - supports offline behavior with explicit cache-miss reporting.
40
+ - `init-ci` command:
41
+ - scaffolds `.github/workflows/rainy-updates.yml`,
42
+ - supports `--force` overwrite behavior.
43
+ - Policy engine:
44
+ - `--policy-file` to load package update rules,
45
+ - default discovery of `.rainyupdates-policy.json` and `rainy-updates.policy.json`,
46
+ - rule-level controls:
47
+ - global ignore patterns,
48
+ - per-package ignore,
49
+ - per-package `maxTarget` update ceiling.
50
+ - PR report output:
51
+ - `--pr-report-file` emits markdown report for pull request comments.
52
+ - New summary metric:
53
+ - `warmedPackages` in results and GitHub output values.
54
+ - New tests:
55
+ - warm cache behavior,
56
+ - policy loading,
57
+ - CI workflow scaffolding,
58
+ - PR markdown report rendering,
59
+ - parser support for new commands/flags.
60
+
61
+ ### Changed
62
+
63
+ - Check pipeline now applies policy constraints before update proposals.
64
+ - Output summary now includes warmed cache package count.
65
+ - CLI command parser supports additional commands (`warm-cache`, `init-ci`) and options (`--policy-file`, `--pr-report-file`, `--force`).
66
+
67
+ ## [0.2.0] - 2026-02-27
68
+
69
+ ### Added
70
+
71
+ - High-throughput registry resolution architecture:
72
+ - batched unique package resolution,
73
+ - configurable concurrency with `--concurrency`,
74
+ - optional `undici` pool + HTTP/2 path when available,
75
+ - automatic fallback to native `fetch` when `undici` is unavailable.
76
+ - Offline execution mode:
77
+ - `--offline` runs in cache-only mode,
78
+ - reports cache misses explicitly for deterministic CI behavior.
79
+ - Workspace graph module:
80
+ - detects local package graph,
81
+ - computes topological order,
82
+ - detects simple cycle groups and surfaces warnings.
83
+ - Graph-aware sync in upgrade flow:
84
+ - `--sync` now aligns versions following workspace graph order,
85
+ - preserves `workspace:*` protocol references.
86
+ - Additional test coverage:
87
+ - workspace graph ordering,
88
+ - workspace protocol edge handling,
89
+ - offline cache miss behavior.
90
+
91
+ ### Changed
92
+
93
+ - `check` pipeline now resolves versions by unique dependency name first, then applies results across all manifests.
94
+ - stale cache fallback is applied after registry failures to reduce flaky CI checks.
95
+ - options/config surface expanded with `offline` and stronger CI-oriented controls.
96
+
97
+ ### OSS Quality
98
+
99
+ - README expanded with performance/runtime notes and complete options matrix.
100
+ - Output and artifact model reinforced for CI systems (JSON, GitHub outputs, SARIF).
101
+
102
+ ## [0.1.0] - 2026-02-27
103
+
104
+ ### Added
105
+
106
+ - New npm package identity: `@rainy-updates/cli` with CLI binary `rainy-updates`.
107
+ - Core commands:
108
+ - `check` for update detection.
109
+ - `upgrade` for manifest rewriting with optional installation step.
110
+ - Multi-format output system:
111
+ - `table`, `json`, `minimal`, and `github` annotation output.
112
+ - CI artifact outputs:
113
+ - `--json-file` for machine-readable check results.
114
+ - `--github-output` for key-value GitHub Actions outputs.
115
+ - `--sarif-file` for SARIF 2.1.0 compatible reports.
116
+ - Configuration support:
117
+ - `.rainyupdatesrc`
118
+ - `.rainyupdatesrc.json`
119
+ - `package.json` field: `rainyUpdates`
120
+ - Workspace-aware scanning (`--workspace`):
121
+ - `package.json` workspaces detection.
122
+ - `pnpm-workspace.yaml` package pattern detection.
123
+ - Workspace upgrade harmonization:
124
+ - `--sync` aligns dependency versions across scanned manifests.
125
+ - Dependency category filtering:
126
+ - `--dep-kinds deps,dev,optional,peer`
127
+ - Runtime controls:
128
+ - `--concurrency` for parallel dependency checks.
129
+ - `--cache-ttl` for cache freshness tuning.
130
+ - Cache layer improvements:
131
+ - SQLite-first cache backend when available.
132
+ - JSON fallback cache backend.
133
+ - stale cache fallback path when registry requests fail.
134
+ - Programmatic exports:
135
+ - `check`, `upgrade`, SARIF and GitHub output helpers.
136
+ - Test suite expanded with coverage for:
137
+ - semver utilities,
138
+ - CLI option parsing,
139
+ - config loading,
140
+ - workspace discovery,
141
+ - SARIF generation,
142
+ - GitHub output writing.
143
+
144
+ ### Changed
145
+
146
+ - Project structure refactored into modular layers (`core`, `config`, `workspace`, `output`, `cache`, `registry`, `pm`, `utils`, `types`).
147
+ - CLI parser upgraded to async workflow to support config loading and cwd-based config resolution.
148
+ - Upgrade pipeline now supports writing updates across multiple workspace package manifests.
149
+
150
+ ### Fixed
151
+
152
+ - Argument parsing now correctly handles flags when command is omitted (`check` default mode).
153
+ - Type-safe discriminated command parsing for `check` vs `upgrade` options.
154
+
155
+ ### Notes
156
+
157
+ - In network-restricted environments, registry lookups fail gracefully and return exit code `2` with detailed error output.
158
+ - `pnpm` and `npm` are the official package-manager scope for this release.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Rainy Updates
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,141 @@
1
+ # @rainy-updates/cli
2
+
3
+ Agentic OSS CLI for dependency updates focused on speed, CI automation, and workspace-scale maintenance.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm i -D @rainy-updates/cli
9
+ # or
10
+ pnpm add -D @rainy-updates/cli
11
+ ```
12
+
13
+ ## Commands
14
+
15
+ - `check`: detect available dependency updates.
16
+ - `upgrade`: rewrite dependency ranges (optional install + workspace sync).
17
+ - `warm-cache`: pre-fetch package metadata into local cache for faster CI checks.
18
+ - `init-ci`: scaffold `.github/workflows/rainy-updates.yml`.
19
+
20
+ ## Quick start
21
+
22
+ ```bash
23
+ # check and fail CI if updates are found
24
+ npx @rainy-updates/cli check --ci --format json --json-file .artifacts/deps-report.json
25
+
26
+ # pre-warm cache before strict offline checks
27
+ npx @rainy-updates/cli warm-cache --workspace --concurrency 32
28
+ npx @rainy-updates/cli check --workspace --offline --ci
29
+
30
+ # upgrade ranges and install lockfiles
31
+ npx @rainy-updates/cli upgrade --target latest --workspace --sync --install
32
+
33
+ # scaffold GitHub Actions workflow
34
+ npx @rainy-updates/cli init-ci
35
+ ```
36
+
37
+ ## Core options
38
+
39
+ - `--target patch|minor|major|latest`
40
+ - `--filter <pattern>`
41
+ - `--reject <pattern>`
42
+ - `--dep-kinds deps,dev,optional,peer`
43
+ - `--workspace`
44
+ - `--concurrency <n>`
45
+ - `--cache-ttl <seconds>`
46
+ - `--offline` (cache-only mode)
47
+ - `--cwd <path>`
48
+
49
+ ## Output options
50
+
51
+ - `--format table|json|minimal|github`
52
+ - `--json-file <path>`
53
+ - `--github-output <path>`
54
+ - `--sarif-file <path>`
55
+ - `--pr-report-file <path>` (generates markdown report for PR comments)
56
+
57
+ ## Upgrade options
58
+
59
+ - `--install`
60
+ - `--pm auto|npm|pnpm`
61
+ - `--sync` (graph-aware version alignment across workspace packages)
62
+
63
+ ## Policy controls
64
+
65
+ - `--policy-file <path>` to apply package-level policy rules.
66
+ - default policy discovery:
67
+ - `.rainyupdates-policy.json`
68
+ - `rainy-updates.policy.json`
69
+
70
+ Policy example:
71
+
72
+ ```json
73
+ {
74
+ "ignore": ["@types/*", "eslint*"],
75
+ "packageRules": {
76
+ "react": { "maxTarget": "minor" },
77
+ "typescript": { "ignore": true }
78
+ }
79
+ }
80
+ ```
81
+
82
+ ## CLI help
83
+
84
+ ```bash
85
+ rainy-updates --help
86
+ rainy-updates <command> --help
87
+ rainy-updates --version
88
+ ```
89
+
90
+ ## CI behavior
91
+
92
+ - `--ci`: returns exit code `1` when updates are found.
93
+ - returns exit code `2` for operational errors (registry/IO/runtime failures).
94
+
95
+ ## Config files
96
+
97
+ Supported:
98
+
99
+ - `.rainyupdatesrc`
100
+ - `.rainyupdatesrc.json`
101
+ - `package.json` -> `rainyUpdates`
102
+
103
+ Example:
104
+
105
+ ```json
106
+ {
107
+ "rainyUpdates": {
108
+ "target": "minor",
109
+ "workspace": true,
110
+ "concurrency": 24,
111
+ "offline": false,
112
+ "format": "json",
113
+ "cacheTtlSeconds": 1800,
114
+ "jsonFile": ".artifacts/deps.json",
115
+ "sarifFile": ".artifacts/deps.sarif",
116
+ "prReportFile": ".artifacts/deps.md",
117
+ "policyFile": ".rainyupdates-policy.json"
118
+ }
119
+ }
120
+ ```
121
+
122
+ ## Production release
123
+
124
+ ```bash
125
+ bun run prepublishOnly
126
+ node scripts/release-preflight.mjs
127
+ npm publish --provenance --access public
128
+ ```
129
+
130
+ If publishing in GitHub Actions, set `NPM_TOKEN` in repository secrets.
131
+
132
+ The repository includes:
133
+
134
+ - `.github/workflows/ci.yml` for test/typecheck/build/smoke checks.
135
+ - `.github/workflows/release.yml` for tag-driven npm publishing.
136
+
137
+ ## Performance and runtime notes
138
+
139
+ - Resolves dependency metadata by unique package name to avoid duplicate network calls.
140
+ - Uses `undici` pool with HTTP/2 when available; falls back to native `fetch` automatically.
141
+ - Uses layered cache with stale fallback for resilient CI runs.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,146 @@
1
+ #!/usr/bin/env node
2
+ import { promises as fs } from "node:fs";
3
+ import path from "node:path";
4
+ import process from "node:process";
5
+ import { fileURLToPath } from "node:url";
6
+ import { parseCliArgs } from "../core/options.js";
7
+ import { check } from "../core/check.js";
8
+ import { upgrade } from "../core/upgrade.js";
9
+ import { warmCache } from "../core/warm-cache.js";
10
+ import { initCiWorkflow } from "../core/init-ci.js";
11
+ import { renderResult } from "../output/format.js";
12
+ import { writeGitHubOutput } from "../output/github.js";
13
+ import { createSarifReport } from "../output/sarif.js";
14
+ import { renderPrReport } from "../output/pr-report.js";
15
+ async function main() {
16
+ try {
17
+ const argv = process.argv.slice(2);
18
+ if (argv.includes("--version") || argv.includes("-v")) {
19
+ process.stdout.write((await readPackageVersion()) + "\n");
20
+ return;
21
+ }
22
+ if (argv.includes("--help") || argv.includes("-h")) {
23
+ process.stdout.write(renderHelp(argv[0]) + "\n");
24
+ return;
25
+ }
26
+ const parsed = await parseCliArgs(argv);
27
+ if (parsed.command === "init-ci") {
28
+ const workflow = await initCiWorkflow(parsed.options.cwd, parsed.options.force);
29
+ process.stdout.write(workflow.created
30
+ ? `Created CI workflow at ${workflow.path}\n`
31
+ : `CI workflow already exists at ${workflow.path}. Use --force to overwrite.\n`);
32
+ return;
33
+ }
34
+ const result = parsed.command === "upgrade"
35
+ ? await upgrade(parsed.options)
36
+ : parsed.command === "warm-cache"
37
+ ? await warmCache(parsed.options)
38
+ : await check(parsed.options);
39
+ const rendered = renderResult(result, parsed.options.format);
40
+ process.stdout.write(rendered + "\n");
41
+ if (parsed.options.jsonFile) {
42
+ await fs.mkdir(path.dirname(parsed.options.jsonFile), { recursive: true });
43
+ await fs.writeFile(parsed.options.jsonFile, JSON.stringify(result, null, 2) + "\n", "utf8");
44
+ }
45
+ if (parsed.options.prReportFile) {
46
+ const markdown = renderPrReport(result);
47
+ await fs.mkdir(path.dirname(parsed.options.prReportFile), { recursive: true });
48
+ await fs.writeFile(parsed.options.prReportFile, markdown + "\n", "utf8");
49
+ }
50
+ if (parsed.options.githubOutputFile) {
51
+ await writeGitHubOutput(parsed.options.githubOutputFile, result);
52
+ }
53
+ if (parsed.options.sarifFile) {
54
+ const sarif = createSarifReport(result);
55
+ await fs.mkdir(path.dirname(parsed.options.sarifFile), { recursive: true });
56
+ await fs.writeFile(parsed.options.sarifFile, JSON.stringify(sarif, null, 2) + "\n", "utf8");
57
+ }
58
+ if (parsed.options.ci && result.updates.length > 0) {
59
+ process.exitCode = 1;
60
+ return;
61
+ }
62
+ if (result.errors.length > 0) {
63
+ process.exitCode = 2;
64
+ }
65
+ }
66
+ catch (error) {
67
+ process.stderr.write(`rainy-updates: ${String(error)}\n`);
68
+ process.exitCode = 2;
69
+ }
70
+ }
71
+ void main();
72
+ function renderHelp(command) {
73
+ const isCommand = command && !command.startsWith("-");
74
+ if (isCommand && command === "warm-cache") {
75
+ return `rainy-updates warm-cache [options]
76
+
77
+ Pre-warm local metadata cache for faster CI checks.
78
+
79
+ Options:
80
+ --workspace
81
+ --target patch|minor|major|latest
82
+ --filter <pattern>
83
+ --reject <pattern>
84
+ --dep-kinds deps,dev,optional,peer
85
+ --concurrency <n>
86
+ --cache-ttl <seconds>
87
+ --offline
88
+ --json-file <path>
89
+ --github-output <path>
90
+ --sarif-file <path>
91
+ --pr-report-file <path>`;
92
+ }
93
+ if (isCommand && command === "upgrade") {
94
+ return `rainy-updates upgrade [options]
95
+
96
+ Apply dependency updates to package.json manifests.
97
+
98
+ Options:
99
+ --workspace
100
+ --sync
101
+ --install
102
+ --pm auto|npm|pnpm
103
+ --target patch|minor|major|latest
104
+ --policy-file <path>
105
+ --concurrency <n>
106
+ --json-file <path>
107
+ --pr-report-file <path>`;
108
+ }
109
+ if (isCommand && command === "init-ci") {
110
+ return `rainy-updates init-ci [--force]
111
+
112
+ Create a GitHub Actions workflow template at:
113
+ .github/workflows/rainy-updates.yml`;
114
+ }
115
+ return `rainy-updates <command> [options]
116
+
117
+ Commands:
118
+ check Detect available updates
119
+ upgrade Apply updates to manifests
120
+ warm-cache Warm local cache for fast/offline checks
121
+ init-ci Scaffold GitHub Actions workflow
122
+
123
+ Global options:
124
+ --cwd <path>
125
+ --workspace
126
+ --target patch|minor|major|latest
127
+ --format table|json|minimal|github
128
+ --json-file <path>
129
+ --github-output <path>
130
+ --sarif-file <path>
131
+ --pr-report-file <path>
132
+ --policy-file <path>
133
+ --concurrency <n>
134
+ --cache-ttl <seconds>
135
+ --offline
136
+ --ci
137
+ --help, -h
138
+ --version, -v`;
139
+ }
140
+ async function readPackageVersion() {
141
+ const currentFile = fileURLToPath(import.meta.url);
142
+ const packageJsonPath = path.resolve(path.dirname(currentFile), "../../package.json");
143
+ const content = await fs.readFile(packageJsonPath, "utf8");
144
+ const parsed = JSON.parse(content);
145
+ return parsed.version ?? "0.0.0";
146
+ }
@@ -0,0 +1,9 @@
1
+ import type { CachedVersion, TargetLevel } from "../types/index.js";
2
+ export declare class VersionCache {
3
+ private readonly store;
4
+ private constructor();
5
+ static create(customPath?: string): Promise<VersionCache>;
6
+ getValid(packageName: string, target: TargetLevel): Promise<CachedVersion | null>;
7
+ getAny(packageName: string, target: TargetLevel): Promise<CachedVersion | null>;
8
+ set(packageName: string, target: TargetLevel, latestVersion: string, ttlSeconds: number): Promise<void>;
9
+ }
@@ -0,0 +1,125 @@
1
+ import { promises as fs } from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ class FileCacheStore {
5
+ filePath;
6
+ constructor(filePath) {
7
+ this.filePath = filePath;
8
+ }
9
+ async get(packageName, target) {
10
+ const entries = await this.readEntries();
11
+ const key = this.getKey(packageName, target);
12
+ return entries[key] ?? null;
13
+ }
14
+ async set(entry) {
15
+ const entries = await this.readEntries();
16
+ entries[this.getKey(entry.packageName, entry.target)] = entry;
17
+ await fs.mkdir(path.dirname(this.filePath), { recursive: true });
18
+ await fs.writeFile(this.filePath, JSON.stringify(entries), "utf8");
19
+ }
20
+ async readEntries() {
21
+ try {
22
+ const content = await fs.readFile(this.filePath, "utf8");
23
+ return JSON.parse(content);
24
+ }
25
+ catch {
26
+ return {};
27
+ }
28
+ }
29
+ getKey(packageName, target) {
30
+ return `${packageName}:${target}`;
31
+ }
32
+ }
33
+ class SqliteCacheStore {
34
+ db;
35
+ constructor(db) {
36
+ this.db = db;
37
+ this.db.exec(`
38
+ CREATE TABLE IF NOT EXISTS versions (
39
+ package_name TEXT NOT NULL,
40
+ target TEXT NOT NULL,
41
+ latest_version TEXT NOT NULL,
42
+ fetched_at INTEGER NOT NULL,
43
+ ttl_seconds INTEGER NOT NULL,
44
+ PRIMARY KEY (package_name, target)
45
+ );
46
+ `);
47
+ }
48
+ async get(packageName, target) {
49
+ const row = this.db
50
+ .prepare(`SELECT package_name, target, latest_version, fetched_at, ttl_seconds FROM versions WHERE package_name = ? AND target = ?`)
51
+ .get(packageName, target);
52
+ if (!row)
53
+ return null;
54
+ return {
55
+ packageName: row.package_name,
56
+ target: row.target,
57
+ latestVersion: row.latest_version,
58
+ fetchedAt: row.fetched_at,
59
+ ttlSeconds: row.ttl_seconds,
60
+ };
61
+ }
62
+ async set(entry) {
63
+ this.db
64
+ .prepare(`INSERT OR REPLACE INTO versions (package_name, target, latest_version, fetched_at, ttl_seconds)
65
+ VALUES (?, ?, ?, ?, ?)`)
66
+ .run(entry.packageName, entry.target, entry.latestVersion, entry.fetchedAt, entry.ttlSeconds);
67
+ }
68
+ }
69
+ export class VersionCache {
70
+ store;
71
+ constructor(store) {
72
+ this.store = store;
73
+ }
74
+ static async create(customPath) {
75
+ const basePath = customPath ?? path.join(os.homedir(), ".cache", "rainy-updates");
76
+ const sqlitePath = path.join(basePath, "cache.db");
77
+ const sqliteStore = await tryCreateSqliteStore(sqlitePath);
78
+ if (sqliteStore)
79
+ return new VersionCache(sqliteStore);
80
+ const jsonPath = path.join(basePath, "cache.json");
81
+ return new VersionCache(new FileCacheStore(jsonPath));
82
+ }
83
+ async getValid(packageName, target) {
84
+ const entry = await this.store.get(packageName, target);
85
+ if (!entry)
86
+ return null;
87
+ const expiresAt = entry.fetchedAt + entry.ttlSeconds * 1000;
88
+ if (Date.now() > expiresAt)
89
+ return null;
90
+ return entry;
91
+ }
92
+ async getAny(packageName, target) {
93
+ return this.store.get(packageName, target);
94
+ }
95
+ async set(packageName, target, latestVersion, ttlSeconds) {
96
+ await this.store.set({
97
+ packageName,
98
+ target,
99
+ latestVersion,
100
+ fetchedAt: Date.now(),
101
+ ttlSeconds,
102
+ });
103
+ }
104
+ }
105
+ async function tryCreateSqliteStore(dbPath) {
106
+ try {
107
+ if (typeof Bun !== "undefined") {
108
+ const mod = await import("bun:sqlite");
109
+ const db = new mod.Database(dbPath, { create: true });
110
+ return new SqliteCacheStore(db);
111
+ }
112
+ }
113
+ catch {
114
+ // noop
115
+ }
116
+ try {
117
+ const maybeRequire = Function("return require")();
118
+ const Database = maybeRequire("better-sqlite3");
119
+ const db = new Database(dbPath);
120
+ return new SqliteCacheStore(db);
121
+ }
122
+ catch {
123
+ return null;
124
+ }
125
+ }
@@ -0,0 +1,22 @@
1
+ import type { DependencyKind, OutputFormat, TargetLevel } from "../types/index.js";
2
+ export interface FileConfig {
3
+ target?: TargetLevel;
4
+ filter?: string;
5
+ reject?: string;
6
+ cacheTtlSeconds?: number;
7
+ includeKinds?: DependencyKind[];
8
+ ci?: boolean;
9
+ format?: OutputFormat;
10
+ workspace?: boolean;
11
+ jsonFile?: string;
12
+ githubOutputFile?: string;
13
+ sarifFile?: string;
14
+ concurrency?: number;
15
+ offline?: boolean;
16
+ policyFile?: string;
17
+ prReportFile?: string;
18
+ install?: boolean;
19
+ packageManager?: "auto" | "npm" | "pnpm";
20
+ sync?: boolean;
21
+ }
22
+ export declare function loadConfig(cwd: string): Promise<FileConfig>;
@@ -0,0 +1,35 @@
1
+ import { promises as fs } from "node:fs";
2
+ import path from "node:path";
3
+ export async function loadConfig(cwd) {
4
+ const fromRc = await loadRcFile(cwd);
5
+ const fromPackage = await loadPackageConfig(cwd);
6
+ return {
7
+ ...fromPackage,
8
+ ...fromRc,
9
+ };
10
+ }
11
+ async function loadRcFile(cwd) {
12
+ const candidates = [".rainyupdatesrc", ".rainyupdatesrc.json"];
13
+ for (const candidate of candidates) {
14
+ const filePath = path.join(cwd, candidate);
15
+ try {
16
+ const content = await fs.readFile(filePath, "utf8");
17
+ return JSON.parse(content);
18
+ }
19
+ catch {
20
+ // noop
21
+ }
22
+ }
23
+ return {};
24
+ }
25
+ async function loadPackageConfig(cwd) {
26
+ const packagePath = path.join(cwd, "package.json");
27
+ try {
28
+ const content = await fs.readFile(packagePath, "utf8");
29
+ const parsed = JSON.parse(content);
30
+ return parsed.rainyUpdates ?? {};
31
+ }
32
+ catch {
33
+ return {};
34
+ }
35
+ }
@@ -0,0 +1,16 @@
1
+ import type { TargetLevel } from "../types/index.js";
2
+ export interface PolicyConfig {
3
+ ignore?: string[];
4
+ packageRules?: Record<string, {
5
+ maxTarget?: TargetLevel;
6
+ ignore?: boolean;
7
+ }>;
8
+ }
9
+ export interface ResolvedPolicy {
10
+ ignorePatterns: string[];
11
+ packageRules: Map<string, {
12
+ maxTarget?: TargetLevel;
13
+ ignore: boolean;
14
+ }>;
15
+ }
16
+ export declare function loadPolicy(cwd: string, policyFile?: string): Promise<ResolvedPolicy>;