@npm-safe/core-dsh 1.0.5

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 (86) hide show
  1. package/LICENSE +204 -0
  2. package/dist/index.d.ts +513 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +711 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/llm/anthropic.d.ts +47 -0
  7. package/dist/llm/anthropic.d.ts.map +1 -0
  8. package/dist/llm/anthropic.js +161 -0
  9. package/dist/llm/anthropic.js.map +1 -0
  10. package/dist/llm/gemini.d.ts +47 -0
  11. package/dist/llm/gemini.d.ts.map +1 -0
  12. package/dist/llm/gemini.js +165 -0
  13. package/dist/llm/gemini.js.map +1 -0
  14. package/dist/llm/llm-config.d.ts +97 -0
  15. package/dist/llm/llm-config.d.ts.map +1 -0
  16. package/dist/llm/llm-config.js +188 -0
  17. package/dist/llm/llm-config.js.map +1 -0
  18. package/dist/llm/parse.d.ts +95 -0
  19. package/dist/llm/parse.d.ts.map +1 -0
  20. package/dist/llm/parse.js +158 -0
  21. package/dist/llm/parse.js.map +1 -0
  22. package/dist/llm/provider.d.ts +122 -0
  23. package/dist/llm/provider.d.ts.map +1 -0
  24. package/dist/llm/provider.js +206 -0
  25. package/dist/llm/provider.js.map +1 -0
  26. package/dist/registry/client.d.ts +164 -0
  27. package/dist/registry/client.d.ts.map +1 -0
  28. package/dist/registry/client.js +378 -0
  29. package/dist/registry/client.js.map +1 -0
  30. package/dist/registry/types.d.ts +226 -0
  31. package/dist/registry/types.d.ts.map +1 -0
  32. package/dist/registry/types.js +32 -0
  33. package/dist/registry/types.js.map +1 -0
  34. package/dist/registry/validator.d.ts +87 -0
  35. package/dist/registry/validator.d.ts.map +1 -0
  36. package/dist/registry/validator.js +214 -0
  37. package/dist/registry/validator.js.map +1 -0
  38. package/dist/scanner/ci-scan.d.ts +82 -0
  39. package/dist/scanner/ci-scan.d.ts.map +1 -0
  40. package/dist/scanner/ci-scan.js +130 -0
  41. package/dist/scanner/ci-scan.js.map +1 -0
  42. package/dist/scanner/rule-config.d.ts +61 -0
  43. package/dist/scanner/rule-config.d.ts.map +1 -0
  44. package/dist/scanner/rule-config.js +103 -0
  45. package/dist/scanner/rule-config.js.map +1 -0
  46. package/dist/scanner/rule-loader.d.ts +28 -0
  47. package/dist/scanner/rule-loader.d.ts.map +1 -0
  48. package/dist/scanner/rule-loader.js +67 -0
  49. package/dist/scanner/rule-loader.js.map +1 -0
  50. package/dist/scanner/static-rules.d.ts +88 -0
  51. package/dist/scanner/static-rules.d.ts.map +1 -0
  52. package/dist/scanner/static-rules.js +723 -0
  53. package/dist/scanner/static-rules.js.map +1 -0
  54. package/dist/scanner/types.d.ts +177 -0
  55. package/dist/scanner/types.d.ts.map +1 -0
  56. package/dist/scanner/types.js +53 -0
  57. package/dist/scanner/types.js.map +1 -0
  58. package/dist/scheduler/rate-limiter.d.ts +74 -0
  59. package/dist/scheduler/rate-limiter.d.ts.map +1 -0
  60. package/dist/scheduler/rate-limiter.js +182 -0
  61. package/dist/scheduler/rate-limiter.js.map +1 -0
  62. package/dist/scheduler/refresh-scheduler.d.ts +201 -0
  63. package/dist/scheduler/refresh-scheduler.d.ts.map +1 -0
  64. package/dist/scheduler/refresh-scheduler.js +295 -0
  65. package/dist/scheduler/refresh-scheduler.js.map +1 -0
  66. package/dist/store/cache-manager.d.ts +166 -0
  67. package/dist/store/cache-manager.d.ts.map +1 -0
  68. package/dist/store/cache-manager.js +356 -0
  69. package/dist/store/cache-manager.js.map +1 -0
  70. package/dist/store/database.d.ts +81 -0
  71. package/dist/store/database.d.ts.map +1 -0
  72. package/dist/store/database.js +182 -0
  73. package/dist/store/database.js.map +1 -0
  74. package/dist/store/schema.d.ts +42 -0
  75. package/dist/store/schema.d.ts.map +1 -0
  76. package/dist/store/schema.js +126 -0
  77. package/dist/store/schema.js.map +1 -0
  78. package/dist/translator/provider.d.ts +152 -0
  79. package/dist/translator/provider.d.ts.map +1 -0
  80. package/dist/translator/provider.js +159 -0
  81. package/dist/translator/provider.js.map +1 -0
  82. package/dist/translator/types.d.ts +83 -0
  83. package/dist/translator/types.d.ts.map +1 -0
  84. package/dist/translator/types.js +58 -0
  85. package/dist/translator/types.js.map +1 -0
  86. package/package.json +42 -0
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Auto-refresh scheduler with rate-limited registry polling.
3
+ *
4
+ * {@link RefreshScheduler} orchestrates periodic re-fetching of npm package
5
+ * metadata and re-running the static analyzer for packages on the user's
6
+ * watchlist or with stale cache entries. It is a pure scheduling +
7
+ * orchestration layer: it contains no GUI integration and no CLI command
8
+ * logic. All registry calls are gated through the injected {@link TokenBucket}
9
+ * so the scheduler respects the configured rate limit, and progress is
10
+ * surfaced via three {@link EventEmitter} events:
11
+ *
12
+ * - `refresh:start` — emitted before each package refresh begins.
13
+ * - `refresh:complete`— emitted after a package refresh succeeds, carrying
14
+ * the freshly produced {@link StaticScanReport}.
15
+ * - `refresh:error` — emitted when a package refresh fails. The scheduler
16
+ * does **not** throw on per-package failures; it emits the error and
17
+ * continues with the next package.
18
+ *
19
+ * @module scheduler/refresh-scheduler
20
+ */
21
+ import { EventEmitter } from 'node:events';
22
+ import type { NpmRegistryClient } from '../registry/client.js';
23
+ import type { CacheManager } from '../store/cache-manager.js';
24
+ import type { TokenBucket } from './rate-limiter.js';
25
+ import type { StaticAnalyzer } from '../scanner/static-rules.js';
26
+ import type { StaticScanReport } from '../scanner/types.js';
27
+ import type { LlmScanReport } from '../scanner/types.js';
28
+ import type { LlmScanProvider } from '../llm/provider.js';
29
+ /**
30
+ * Payload of the `refresh:start` event.
31
+ */
32
+ export interface RefreshStartPayload {
33
+ /** Name of the package about to be refreshed. */
34
+ readonly packageName: string;
35
+ }
36
+ /**
37
+ * Payload of the `refresh:complete` event.
38
+ */
39
+ export interface RefreshCompletePayload {
40
+ /** Name of the package that was refreshed. */
41
+ readonly packageName: string;
42
+ /** The static scan report produced during the refresh. */
43
+ readonly report: StaticScanReport;
44
+ readonly llmScan?: LlmScanReport;
45
+ }
46
+ /**
47
+ * Payload of the `refresh:error` event.
48
+ */
49
+ export interface RefreshErrorPayload {
50
+ /** Name of the package whose refresh failed. */
51
+ readonly packageName: string;
52
+ /** The error that caused the refresh to fail. */
53
+ readonly error: unknown;
54
+ }
55
+ /**
56
+ * Auto-refresh scheduler that periodically polls the npm registry for
57
+ * updated package metadata, re-caches it, and re-runs the static analyzer.
58
+ *
59
+ * The scheduler is constructed with four collaborators:
60
+ *
61
+ * - {@link NpmRegistryClient} — used to fetch fresh packuments.
62
+ * - {@link CacheManager} — used to read the watchlist/stale set and persist
63
+ * refreshed metadata + security reports.
64
+ * - {@link TokenBucket} — gates every registry call so the configured rate
65
+ * limit is respected.
66
+ * - {@link StaticAnalyzer} — re-evaluates the refreshed package's README and
67
+ * latest version manifest.
68
+ *
69
+ * Call {@link RefreshScheduler.start} to begin the refresh loop and
70
+ * {@link RefreshScheduler.stop} to tear it down.
71
+ *
72
+ * @example
73
+ * ```ts
74
+ * const scheduler = new RefreshScheduler(client, cache, limiter, analyzer);
75
+ * scheduler.on('refresh:complete', ({ packageName, report }) => {
76
+ * console.log(`${packageName}: score=${report.score}`);
77
+ * });
78
+ * scheduler.start(); // refresh hourly
79
+ * // ... later
80
+ * scheduler.stop();
81
+ * ```
82
+ */
83
+ export declare class RefreshScheduler extends EventEmitter {
84
+ /** Registry client used to fetch fresh packuments. */
85
+ private readonly client;
86
+ /** Cache manager used to read the watchlist and persist results. */
87
+ private readonly cache;
88
+ /** Rate limiter gating every registry call. */
89
+ private readonly limiter;
90
+ /** Static analyzer re-run on each refreshed package. */
91
+ private readonly analyzer;
92
+ /** Optional semantic analyzer for refreshed packages (mutable at runtime). */
93
+ private llmProvider;
94
+ /** Handle to the recurring refresh interval, or `null` when stopped. */
95
+ private intervalId;
96
+ /**
97
+ * @param client - Registry client for fetching package metadata.
98
+ * @param cache - Cache manager for reading the watchlist and persisting
99
+ * refreshed metadata + reports.
100
+ * @param limiter - Token bucket rate limiter; one token is consumed per
101
+ * registry fetch.
102
+ * @param analyzer - Static analyzer re-run on each refreshed package.
103
+ * @param llmProvider - Optional semantic analyzer for refreshed packages, or
104
+ * a getter that returns the current provider. A getter is preferred when the
105
+ * provider can be changed at runtime.
106
+ */
107
+ constructor(client: NpmRegistryClient, cache: CacheManager, limiter: TokenBucket, analyzer: StaticAnalyzer, llmProvider?: LlmScanProvider | (() => LlmScanProvider | undefined));
108
+ /**
109
+ * Replace the LLM provider used during refreshes.
110
+ *
111
+ * Safe to call while the scheduler is running; the next refresh cycle uses
112
+ * the new provider.
113
+ *
114
+ * @param provider - The new provider, or `undefined` to disable LLM scanning.
115
+ */
116
+ setLlmProvider(provider?: LlmScanProvider): void;
117
+ /** Resolve the current LLM provider. */
118
+ private getLlmProvider;
119
+ /**
120
+ * Start the periodic refresh loop.
121
+ *
122
+ * Immediately kicks off a background {@link RefreshScheduler.refreshWatchlist}
123
+ * call (without awaiting it), then schedules the same call to repeat every
124
+ * `intervalMs` milliseconds. Safe to call multiple times; calling while
125
+ * already running clears the previous interval before starting a new one.
126
+ *
127
+ * @param intervalMs - Milliseconds between refresh cycles. Defaults to
128
+ * {@link DEFAULT_INTERVAL_MS} (1 hour).
129
+ */
130
+ start(intervalMs?: number): void;
131
+ /**
132
+ * Stop the periodic refresh loop.
133
+ *
134
+ * Clears the recurring interval. Any in-flight refresh that is already
135
+ * executing continues to completion; this method only prevents future
136
+ * cycles from being scheduled. Safe to call when not running.
137
+ */
138
+ stop(): void;
139
+ /**
140
+ * Refresh a single package: fetch its latest metadata from the registry,
141
+ * persist it to the cache, re-run the static analyzer against the latest
142
+ * version's README and manifest, and persist the resulting report.
143
+ *
144
+ * One token is consumed from the rate limiter before the registry fetch,
145
+ * so concurrent callers are naturally throttled. Per-package failures are
146
+ * surfaced via the `refresh:error` event rather than thrown — the promise
147
+ * resolves normally after emitting the error so a failing package does not
148
+ * abort a batch.
149
+ *
150
+ * **Cooperative cancellation**: when `options.signal` is supplied it is
151
+ * forwarded to the registry client and the LLM scan. An external abort
152
+ * propagates as `DOMException('The operation was aborted.', 'AbortError')`
153
+ * (instead of the usual `false` return) so a caller awaiting refresh can
154
+ * observe the abort promptly.
155
+ *
156
+ * @param name - Fully-qualified package name (scope included when scoped).
157
+ * @param options - Optional refresh options. `options.signal` is an
158
+ * external abort signal forwarded to the registry fetch and LLM scan.
159
+ * @returns `true` when the refresh succeeds, or `false` after emitting
160
+ * `refresh:error`. Never rejects for per-package refresh failures —
161
+ * only rejects with `AbortError` when `options.signal` is aborted.
162
+ */
163
+ refreshPackage(name: string, options?: {
164
+ readonly signal?: AbortSignal;
165
+ }): Promise<boolean>;
166
+ private scanWithLlm;
167
+ /**
168
+ * Refresh every package whose cached metadata has passed its TTL.
169
+ *
170
+ * Packages are processed sequentially (one after another) so the rate
171
+ * limiter is respected and the registry is not hammered in bursts. Each
172
+ * package's outcome is surfaced via the `refresh:complete` / `refresh:error`
173
+ * events emitted by {@link RefreshScheduler.refreshPackage}.
174
+ *
175
+ * **Cooperative cancellation**: when `options.signal` is supplied, the top
176
+ * of each loop iteration checks `signal.aborted` and throws
177
+ * `DOMException('The operation was aborted.', 'AbortError')` so a
178
+ * mid-batch abort settles promptly instead of continuing to iterate.
179
+ *
180
+ * @param options - Optional refresh options. `options.signal` is an
181
+ * external abort signal forwarded to each per-package refresh.
182
+ * @returns `true` when every stale package refresh succeeds, otherwise
183
+ * `false` after all stale packages have been attempted. Rejects with
184
+ * `AbortError` when `options.signal` is aborted.
185
+ */
186
+ refreshAll(options?: {
187
+ readonly signal?: AbortSignal;
188
+ }): Promise<boolean>;
189
+ /**
190
+ * Refresh every package on the user's watchlist.
191
+ *
192
+ * Watched packages are processed sequentially so the rate limiter is
193
+ * respected. Each package's outcome is surfaced via the
194
+ * `refresh:complete` / `refresh:error` events emitted by
195
+ * {@link RefreshScheduler.refreshPackage}.
196
+ *
197
+ * @returns Resolves once all watched packages have been attempted.
198
+ */
199
+ private refreshWatchlist;
200
+ }
201
+ //# sourceMappingURL=refresh-scheduler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refresh-scheduler.d.ts","sourceRoot":"","sources":["../../src/scheduler/refresh-scheduler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,iDAAiD;IACjD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,8CAA8C;IAC9C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,0DAA0D;IAC1D,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,OAAO,CAAC,EAAE,aAAa,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,gDAAgD;IAChD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,iDAAiD;IACjD,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB;AAOD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,gBAAiB,SAAQ,YAAY;IAChD,sDAAsD;IACtD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoB;IAC3C,oEAAoE;IACpE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IACrC,+CAA+C;IAC/C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAc;IACtC,wDAAwD;IACxD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiB;IAC1C,8EAA8E;IAC9E,OAAO,CAAC,WAAW,CAAkD;IACrE,wEAAwE;IACxE,OAAO,CAAC,UAAU,CAA+C;IAEjE;;;;;;;;;;OAUG;gBAED,MAAM,EAAE,iBAAiB,EACzB,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,cAAc,EACxB,WAAW,CAAC,EAAE,eAAe,GAAG,CAAC,MAAM,eAAe,GAAG,SAAS,CAAC;IAcrE;;;;;;;OAOG;IACH,cAAc,CAAC,QAAQ,CAAC,EAAE,eAAe,GAAG,IAAI;IAIhD,wCAAwC;IACxC,OAAO,CAAC,cAAc;IAItB;;;;;;;;;;OAUG;IACH,KAAK,CAAC,UAAU,GAAE,MAA4B,GAAG,IAAI;IAerD;;;;;;OAMG;IACH,IAAI,IAAI,IAAI;IAOZ;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACG,cAAc,CAClB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GAC1C,OAAO,CAAC,OAAO,CAAC;YA0DL,WAAW;IAqCzB;;;;;;;;;;;;;;;;;;OAkBG;IACG,UAAU,CACd,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GAC1C,OAAO,CAAC,OAAO,CAAC;IAiBnB;;;;;;;;;OASG;YACW,gBAAgB;CAM/B"}
@@ -0,0 +1,295 @@
1
+ /**
2
+ * Auto-refresh scheduler with rate-limited registry polling.
3
+ *
4
+ * {@link RefreshScheduler} orchestrates periodic re-fetching of npm package
5
+ * metadata and re-running the static analyzer for packages on the user's
6
+ * watchlist or with stale cache entries. It is a pure scheduling +
7
+ * orchestration layer: it contains no GUI integration and no CLI command
8
+ * logic. All registry calls are gated through the injected {@link TokenBucket}
9
+ * so the scheduler respects the configured rate limit, and progress is
10
+ * surfaced via three {@link EventEmitter} events:
11
+ *
12
+ * - `refresh:start` — emitted before each package refresh begins.
13
+ * - `refresh:complete`— emitted after a package refresh succeeds, carrying
14
+ * the freshly produced {@link StaticScanReport}.
15
+ * - `refresh:error` — emitted when a package refresh fails. The scheduler
16
+ * does **not** throw on per-package failures; it emits the error and
17
+ * continues with the next package.
18
+ *
19
+ * @module scheduler/refresh-scheduler
20
+ */
21
+ import { EventEmitter } from 'node:events';
22
+ /**
23
+ * Default refresh interval: 1 hour in milliseconds.
24
+ */
25
+ const DEFAULT_INTERVAL_MS = 3_600_000;
26
+ /**
27
+ * Auto-refresh scheduler that periodically polls the npm registry for
28
+ * updated package metadata, re-caches it, and re-runs the static analyzer.
29
+ *
30
+ * The scheduler is constructed with four collaborators:
31
+ *
32
+ * - {@link NpmRegistryClient} — used to fetch fresh packuments.
33
+ * - {@link CacheManager} — used to read the watchlist/stale set and persist
34
+ * refreshed metadata + security reports.
35
+ * - {@link TokenBucket} — gates every registry call so the configured rate
36
+ * limit is respected.
37
+ * - {@link StaticAnalyzer} — re-evaluates the refreshed package's README and
38
+ * latest version manifest.
39
+ *
40
+ * Call {@link RefreshScheduler.start} to begin the refresh loop and
41
+ * {@link RefreshScheduler.stop} to tear it down.
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * const scheduler = new RefreshScheduler(client, cache, limiter, analyzer);
46
+ * scheduler.on('refresh:complete', ({ packageName, report }) => {
47
+ * console.log(`${packageName}: score=${report.score}`);
48
+ * });
49
+ * scheduler.start(); // refresh hourly
50
+ * // ... later
51
+ * scheduler.stop();
52
+ * ```
53
+ */
54
+ export class RefreshScheduler extends EventEmitter {
55
+ /** Registry client used to fetch fresh packuments. */
56
+ client;
57
+ /** Cache manager used to read the watchlist and persist results. */
58
+ cache;
59
+ /** Rate limiter gating every registry call. */
60
+ limiter;
61
+ /** Static analyzer re-run on each refreshed package. */
62
+ analyzer;
63
+ /** Optional semantic analyzer for refreshed packages (mutable at runtime). */
64
+ llmProvider;
65
+ /** Handle to the recurring refresh interval, or `null` when stopped. */
66
+ intervalId = null;
67
+ /**
68
+ * @param client - Registry client for fetching package metadata.
69
+ * @param cache - Cache manager for reading the watchlist and persisting
70
+ * refreshed metadata + reports.
71
+ * @param limiter - Token bucket rate limiter; one token is consumed per
72
+ * registry fetch.
73
+ * @param analyzer - Static analyzer re-run on each refreshed package.
74
+ * @param llmProvider - Optional semantic analyzer for refreshed packages, or
75
+ * a getter that returns the current provider. A getter is preferred when the
76
+ * provider can be changed at runtime.
77
+ */
78
+ constructor(client, cache, limiter, analyzer, llmProvider) {
79
+ super();
80
+ this.client = client;
81
+ this.cache = cache;
82
+ this.limiter = limiter;
83
+ this.analyzer = analyzer;
84
+ this.llmProvider = llmProvider
85
+ ? typeof llmProvider === 'function'
86
+ ? llmProvider
87
+ : () => llmProvider
88
+ : undefined;
89
+ }
90
+ /**
91
+ * Replace the LLM provider used during refreshes.
92
+ *
93
+ * Safe to call while the scheduler is running; the next refresh cycle uses
94
+ * the new provider.
95
+ *
96
+ * @param provider - The new provider, or `undefined` to disable LLM scanning.
97
+ */
98
+ setLlmProvider(provider) {
99
+ this.llmProvider = provider ? () => provider : undefined;
100
+ }
101
+ /** Resolve the current LLM provider. */
102
+ getLlmProvider() {
103
+ return this.llmProvider?.();
104
+ }
105
+ /**
106
+ * Start the periodic refresh loop.
107
+ *
108
+ * Immediately kicks off a background {@link RefreshScheduler.refreshWatchlist}
109
+ * call (without awaiting it), then schedules the same call to repeat every
110
+ * `intervalMs` milliseconds. Safe to call multiple times; calling while
111
+ * already running clears the previous interval before starting a new one.
112
+ *
113
+ * @param intervalMs - Milliseconds between refresh cycles. Defaults to
114
+ * {@link DEFAULT_INTERVAL_MS} (1 hour).
115
+ */
116
+ start(intervalMs = DEFAULT_INTERVAL_MS) {
117
+ // Avoid double-starting: clear any existing interval first.
118
+ if (this.intervalId !== null) {
119
+ clearInterval(this.intervalId);
120
+ this.intervalId = null;
121
+ }
122
+ // Kick off the first cycle immediately in the background.
123
+ void this.refreshWatchlist();
124
+ this.intervalId = setInterval(() => {
125
+ void this.refreshWatchlist();
126
+ }, intervalMs);
127
+ }
128
+ /**
129
+ * Stop the periodic refresh loop.
130
+ *
131
+ * Clears the recurring interval. Any in-flight refresh that is already
132
+ * executing continues to completion; this method only prevents future
133
+ * cycles from being scheduled. Safe to call when not running.
134
+ */
135
+ stop() {
136
+ if (this.intervalId !== null) {
137
+ clearInterval(this.intervalId);
138
+ this.intervalId = null;
139
+ }
140
+ }
141
+ /**
142
+ * Refresh a single package: fetch its latest metadata from the registry,
143
+ * persist it to the cache, re-run the static analyzer against the latest
144
+ * version's README and manifest, and persist the resulting report.
145
+ *
146
+ * One token is consumed from the rate limiter before the registry fetch,
147
+ * so concurrent callers are naturally throttled. Per-package failures are
148
+ * surfaced via the `refresh:error` event rather than thrown — the promise
149
+ * resolves normally after emitting the error so a failing package does not
150
+ * abort a batch.
151
+ *
152
+ * **Cooperative cancellation**: when `options.signal` is supplied it is
153
+ * forwarded to the registry client and the LLM scan. An external abort
154
+ * propagates as `DOMException('The operation was aborted.', 'AbortError')`
155
+ * (instead of the usual `false` return) so a caller awaiting refresh can
156
+ * observe the abort promptly.
157
+ *
158
+ * @param name - Fully-qualified package name (scope included when scoped).
159
+ * @param options - Optional refresh options. `options.signal` is an
160
+ * external abort signal forwarded to the registry fetch and LLM scan.
161
+ * @returns `true` when the refresh succeeds, or `false` after emitting
162
+ * `refresh:error`. Never rejects for per-package refresh failures —
163
+ * only rejects with `AbortError` when `options.signal` is aborted.
164
+ */
165
+ async refreshPackage(name, options) {
166
+ this.emit('refresh:start', { packageName: name });
167
+ try {
168
+ // Gate the registry call through the rate limiter.
169
+ await this.limiter.consume(1);
170
+ const meta = await this.client.getPackageMetadata(name, {
171
+ signal: options?.signal,
172
+ });
173
+ // Persist the fresh packument before doing anything else so the
174
+ // cache is updated even if analysis fails downstream.
175
+ await this.cache.setPackage(meta);
176
+ // Derive a package.json-like object from the latest version manifest
177
+ // for the static analyzer. The analyzer reads `name`, `version`,
178
+ // `scripts`, `browser`, and `publishConfig.registry`; the manifest
179
+ // carries `name`/`version`/`scripts` directly, and any extra fields
180
+ // present on the registry payload (e.g. `browser`, `publishConfig`)
181
+ // are passed through via the spread.
182
+ const latestVersion = meta['dist-tags'].latest;
183
+ const manifest = meta.versions[latestVersion];
184
+ const packageJson = manifest
185
+ ? { ...manifest }
186
+ : undefined;
187
+ const readme = meta.readme ?? '';
188
+ const report = this.analyzer.analyze(readme, packageJson);
189
+ await this.cache.setSecurityReport(report);
190
+ const llmProvider = this.getLlmProvider();
191
+ const llmScan = llmProvider
192
+ ? await this.scanWithLlm(meta, latestVersion, options?.signal)
193
+ : undefined;
194
+ this.emit('refresh:complete', { packageName: name, report, llmScan });
195
+ return true;
196
+ }
197
+ catch (error) {
198
+ // An external abort must propagate as AbortError instead of being
199
+ // swallowed as a per-package failure — the cooperative-cancellation
200
+ // contract requires the caller to observe the abort promptly.
201
+ if (options?.signal?.aborted) {
202
+ throw new DOMException('The operation was aborted.', 'AbortError');
203
+ }
204
+ // Per-package failures are reported, not thrown, so a batch refresh
205
+ // continues with the remaining packages.
206
+ this.emit('refresh:error', { packageName: name, error });
207
+ return false;
208
+ }
209
+ }
210
+ async scanWithLlm(meta, version, signal) {
211
+ const provider = this.getLlmProvider();
212
+ if (!provider) {
213
+ return { enabled: false, reason: 'LLM provider is not configured.' };
214
+ }
215
+ try {
216
+ const manifest = meta.versions[version];
217
+ const report = await provider.scan({
218
+ packageName: meta.name,
219
+ version,
220
+ description: meta.description ?? '',
221
+ readme: meta.readme ?? '',
222
+ packageJson: manifest ? { ...manifest } : undefined,
223
+ signal,
224
+ });
225
+ await this.cache.setLlmScanReport(meta.name, version, report);
226
+ return report;
227
+ }
228
+ catch (error) {
229
+ // An external abort must propagate as AbortError so the caller
230
+ // settles promptly (cooperative cancellation).
231
+ if (signal?.aborted) {
232
+ throw new DOMException('The operation was aborted.', 'AbortError');
233
+ }
234
+ const report = {
235
+ enabled: false,
236
+ reason: error instanceof Error ? error.message : String(error),
237
+ scannedAt: new Date().toISOString(),
238
+ };
239
+ await this.cache.setLlmScanReport(meta.name, version, report);
240
+ return report;
241
+ }
242
+ }
243
+ /**
244
+ * Refresh every package whose cached metadata has passed its TTL.
245
+ *
246
+ * Packages are processed sequentially (one after another) so the rate
247
+ * limiter is respected and the registry is not hammered in bursts. Each
248
+ * package's outcome is surfaced via the `refresh:complete` / `refresh:error`
249
+ * events emitted by {@link RefreshScheduler.refreshPackage}.
250
+ *
251
+ * **Cooperative cancellation**: when `options.signal` is supplied, the top
252
+ * of each loop iteration checks `signal.aborted` and throws
253
+ * `DOMException('The operation was aborted.', 'AbortError')` so a
254
+ * mid-batch abort settles promptly instead of continuing to iterate.
255
+ *
256
+ * @param options - Optional refresh options. `options.signal` is an
257
+ * external abort signal forwarded to each per-package refresh.
258
+ * @returns `true` when every stale package refresh succeeds, otherwise
259
+ * `false` after all stale packages have been attempted. Rejects with
260
+ * `AbortError` when `options.signal` is aborted.
261
+ */
262
+ async refreshAll(options) {
263
+ const stale = await this.cache.getStalePackages();
264
+ let allSucceeded = true;
265
+ for (const name of stale) {
266
+ // Short-circuit at the top of each iteration so an aborted batch
267
+ // settles promptly (cooperative cancellation).
268
+ if (options?.signal?.aborted) {
269
+ throw new DOMException('The operation was aborted.', 'AbortError');
270
+ }
271
+ const succeeded = await this.refreshPackage(name, {
272
+ signal: options?.signal,
273
+ });
274
+ allSucceeded = allSucceeded && succeeded;
275
+ }
276
+ return allSucceeded;
277
+ }
278
+ /**
279
+ * Refresh every package on the user's watchlist.
280
+ *
281
+ * Watched packages are processed sequentially so the rate limiter is
282
+ * respected. Each package's outcome is surfaced via the
283
+ * `refresh:complete` / `refresh:error` events emitted by
284
+ * {@link RefreshScheduler.refreshPackage}.
285
+ *
286
+ * @returns Resolves once all watched packages have been attempted.
287
+ */
288
+ async refreshWatchlist() {
289
+ const watchlist = await this.cache.getWatchlist();
290
+ for (const name of watchlist) {
291
+ await this.refreshPackage(name);
292
+ }
293
+ }
294
+ }
295
+ //# sourceMappingURL=refresh-scheduler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refresh-scheduler.js","sourceRoot":"","sources":["../../src/scheduler/refresh-scheduler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Cache read/write layer for @npm-safe/core.
3
+ *
4
+ * {@link CacheManager} sits on top of a {@link DatabaseManager} connection and
5
+ * provides typed accessors for the cached npm metadata, security scan reports,
6
+ * watchlist, and key-value settings tables. It owns the TTL policy for the
7
+ * `packages` table: every {@link CacheManager.setPackage} call stamps the row
8
+ * with an expiry timestamp (`ttl_until`) computed as `now + cacheTtlMs`, and
9
+ * {@link CacheManager.getPackage} refuses to return rows whose TTL has
10
+ * elapsed. Scheduling of refreshes is intentionally out of scope — that is
11
+ * the job of the refresh-scheduler; this module only reads and writes.
12
+ *
13
+ * All methods return `Promise`s for API consistency and future async backing
14
+ * stores, even though the underlying `better-sqlite3` calls are synchronous.
15
+ *
16
+ * @module store/cache-manager
17
+ */
18
+ import { DatabaseManager } from "./database.js";
19
+ import type { PackageMetadata } from "../registry/types.js";
20
+ import { type LlmScanReport, type StaticScanReport } from "../scanner/types.js";
21
+ /**
22
+ * Default cache time-to-live for package metadata: 1 hour in milliseconds.
23
+ */
24
+ export declare const DEFAULT_CACHE_TTL_MS = 3600000;
25
+ /**
26
+ * Options accepted by the {@link CacheManager} constructor.
27
+ */
28
+ export interface CacheManagerOptions {
29
+ /**
30
+ * Time-to-live for cached package metadata, in milliseconds. A row fetched
31
+ * via {@link CacheManager.getPackage} is considered stale once `ttl_until`
32
+ * is in the past. Sub-second TTLs (e.g. `500`) are supported and stored
33
+ * with millisecond precision. Defaults to {@link DEFAULT_CACHE_TTL_MS}
34
+ * (1 hour).
35
+ */
36
+ readonly cacheTtlMs?: number;
37
+ }
38
+ /**
39
+ * Cache read/write manager backed by a {@link DatabaseManager} connection.
40
+ *
41
+ * The manager is stateless beyond the TTL configuration and the injected
42
+ * database handle, so it is safe to construct one per request or to share a
43
+ * long-lived instance. All public methods are async-returning for API
44
+ * consistency; the underlying `better-sqlite3` calls are synchronous.
45
+ */
46
+ export declare class CacheManager {
47
+ /** Underlying better-sqlite3 connection accessor. */
48
+ private readonly db;
49
+ /** Cache TTL in milliseconds applied to newly written package rows. */
50
+ private readonly cacheTtlMs;
51
+ /**
52
+ * @param database - The {@link DatabaseManager} supplying the connection.
53
+ * @param options - Optional configuration; see {@link CacheManagerOptions}.
54
+ */
55
+ constructor(database: DatabaseManager, options?: CacheManagerOptions);
56
+ /**
57
+ * Returns cached package metadata for `name` if the row is still fresh
58
+ * (i.e. `ttl_until >= now`). Returns `null` when the row is missing or
59
+ * stale; callers are expected to re-fetch from the registry and call
60
+ * {@link CacheManager.setPackage} to refresh the cache.
61
+ *
62
+ * @param name - Fully-qualified package name (scope included when scoped).
63
+ * @returns The cached {@link PackageMetadata}, or `null` if absent/stale.
64
+ */
65
+ getPackage(name: string): Promise<PackageMetadata | null>;
66
+ /**
67
+ * Upserts a package metadata row, stamping it with a fresh TTL computed as
68
+ * `now + cacheTtlMs`. The full packument is serialized into the
69
+ * `registry_data` JSON column; the scalar columns (`latest_version`,
70
+ * `description`, `homepage`, `repository`) are denormalized for cheap
71
+ * listing queries.
72
+ *
73
+ * @param meta - The packument returned by the npm registry.
74
+ */
75
+ setPackage(meta: PackageMetadata): Promise<void>;
76
+ /**
77
+ * Returns the most recent static security report for `pkg`@`version`, or
78
+ * `null` if none is cached. The {@link SecurityLevel} is reconstructed from
79
+ * the persisted numeric score because the `security_reports` table stores
80
+ * only the score, not the enum.
81
+ *
82
+ * @param pkg - Fully-qualified package name.
83
+ * @param version - Semver version string.
84
+ * @returns The cached {@link StaticScanReport}, or `null` if absent.
85
+ */
86
+ getSecurityReport(pkg: string, version: string): Promise<StaticScanReport | null>;
87
+ /**
88
+ * Upserts a static security report into the `security_reports` table. The
89
+ * findings array is serialized to JSON in the `findings_json` column; the
90
+ * numeric score is stored in `overall_score`. The row is keyed by
91
+ * `(package_name, version, scan_type)` via the table's UNIQUE constraint.
92
+ *
93
+ * @param report - The static scan report to persist.
94
+ */
95
+ setSecurityReport(report: StaticScanReport): Promise<void>;
96
+ getLlmScanReport(pkg: string, version: string): Promise<LlmScanReport | null>;
97
+ setLlmScanReport(packageName: string, version: string, report: LlmScanReport): Promise<void>;
98
+ /**
99
+ * Returns the list of package names currently on the user's watchlist.
100
+ *
101
+ * @returns All watched package names, in insertion order.
102
+ */
103
+ getWatchlist(): Promise<string[]>;
104
+ /**
105
+ * Adds `name` to the watchlist. Idempotent: inserting a name that is
106
+ * already watched is a no-op (INSERT OR IGNORE).
107
+ *
108
+ * @param name - Fully-qualified package name to watch.
109
+ */
110
+ addToWatchlist(name: string): Promise<void>;
111
+ /**
112
+ * Removes `name` from the watchlist. No-op if the name was not watched.
113
+ *
114
+ * @param name - Fully-qualified package name to stop watching.
115
+ */
116
+ removeFromWatchlist(name: string): Promise<void>;
117
+ /**
118
+ * Returns the value stored under `key` in the settings table, or `null`
119
+ * if the key is unset.
120
+ *
121
+ * @param key - Settings key.
122
+ * @returns The stored value, or `null` if absent.
123
+ */
124
+ getSetting(key: string): Promise<string | null>;
125
+ /**
126
+ * Upserts `value` under `key` in the settings table (INSERT OR REPLACE).
127
+ *
128
+ * @param key - Settings key.
129
+ * @param value - Settings value to persist.
130
+ */
131
+ setSetting(key: string, value: string): Promise<void>;
132
+ /**
133
+ * Returns the names of all cached package rows whose TTL has elapsed
134
+ * (`ttl_until < now`). These are candidates for an incremental refresh by
135
+ * the refresh-scheduler.
136
+ *
137
+ * @returns Package names with expired cache entries.
138
+ */
139
+ getStalePackages(): Promise<string[]>;
140
+ /**
141
+ * Append an entry to the persistent check history, keeping only the most
142
+ * recent {@link MAX_CHECK_HISTORY} entries.
143
+ */
144
+ addHistoryEntry(entry: {
145
+ readonly packageName: string;
146
+ readonly level: string;
147
+ readonly score: number;
148
+ readonly timestamp: string;
149
+ }): Promise<void>;
150
+ /**
151
+ * Return the persistent check history, newest first.
152
+ */
153
+ getHistory(limit?: number): Promise<ReadonlyArray<{
154
+ readonly packageName: string;
155
+ readonly level: string;
156
+ readonly score: number;
157
+ readonly timestamp: string;
158
+ }>>;
159
+ /**
160
+ * Remove every entry from the persistent check history.
161
+ */
162
+ clearHistory(): Promise<void>;
163
+ }
164
+ /** Maximum number of check-history entries retained in the database. */
165
+ export declare const MAX_CHECK_HISTORY = 1000;
166
+ //# sourceMappingURL=cache-manager.d.ts.map