runtime-resolver 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.d.ts CHANGED
@@ -1,10 +1,47 @@
1
1
  import { Context } from 'effect';
2
- import type { Effect } from 'effect';
2
+ import { DateTime } from 'effect';
3
+ import { Effect } from 'effect';
4
+ import type { EmptyCacheError } from 'semver-effect';
3
5
  import { Equals } from 'effect/Types';
6
+ import type { InvalidRangeError } from 'semver-effect';
7
+ import type { InvalidVersionError } from 'semver-effect';
4
8
  import { Layer } from 'effect';
9
+ import { Option } from 'effect';
10
+ import { Ref } from 'effect';
5
11
  import { Schema } from 'effect';
12
+ import { SemVer } from 'semver-effect';
13
+ import type { UnsatisfiedRangeError } from 'semver-effect';
14
+ import { VersionCache } from 'semver-effect';
6
15
  import { YieldableError } from 'effect/Cause';
7
16
 
17
+ /**
18
+ * Raised when a GitHub authentication attempt fails.
19
+ *
20
+ * This error is produced by the {@link GitHubTokenAuth} and
21
+ * {@link GitHubAppAuth} layers when the supplied credentials are rejected by
22
+ * the GitHub API or when required configuration values (e.g. a token or app
23
+ * private key) are missing from the environment.
24
+ *
25
+ * @see {@link GitHubTokenAuth}
26
+ * @see {@link GitHubAppAuth}
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * import { Effect } from "effect";
31
+ * import type { AuthenticationError } from "./AuthenticationError.js";
32
+ * import { resolveNode } from "../resolvers/node.js";
33
+ *
34
+ * const program = resolveNode(">=20").pipe(
35
+ * Effect.catchTag("AuthenticationError", (err: AuthenticationError) =>
36
+ * Effect.logError(
37
+ * `Auth failed via ${err.method}: ${err.message}`
38
+ * ).pipe(Effect.andThen(Effect.fail(err)))
39
+ * )
40
+ * );
41
+ * ```
42
+ *
43
+ * @public
44
+ */
8
45
  export declare class AuthenticationError extends AuthenticationErrorBase<{
9
46
  readonly method: "token" | "app";
10
47
  readonly message: string;
@@ -18,81 +55,478 @@ export declare class AuthenticationError extends AuthenticationErrorBase<{
18
55
  * `Data.TaggedError(...)`, TypeScript emits an un-nameable `_base` symbol in
19
56
  * the declaration file. Splitting the base into a named export gives the
20
57
  * bundler a stable reference.
58
+ *
59
+ * @privateRemarks
60
+ * This base constant must remain a named export so that api-extractor can
61
+ * resolve the extends clause of {@link AuthenticationError} to a stable
62
+ * declaration. Without it the bundled `.d.ts` would contain an anonymous
63
+ * `_base` symbol that cannot be referenced by downstream consumers.
21
64
  */
22
65
  export declare const AuthenticationErrorBase: new <A extends Record<string, any> = {}>(args: Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => YieldableError & {
23
66
  readonly _tag: "AuthenticationError";
24
67
  } & Readonly<A>;
25
68
 
26
69
  /**
27
- * Service interface for resolving Bun runtime versions.
70
+ * Provides the {@link BunReleaseCache} service using the "Auto" cache strategy.
71
+ *
72
+ * Attempts to fetch live release data from the GitHub API via
73
+ * {@link BunVersionFetcher}. If the fetch fails for any reason — authentication
74
+ * errors, network errors, parse errors, or rate-limit errors — it silently falls
75
+ * back to the bundled default release data so the layer always succeeds.
76
+ *
77
+ * Use this strategy when you want the freshest data when available but can
78
+ * tolerate slightly stale bundled data in degraded environments.
79
+ *
80
+ * The three Bun cache strategy layers are:
81
+ * - `AutoBunCacheLive` — tries API first, falls back to bundled defaults on any error
82
+ * - {@link FreshBunCacheLive} — requires live API data, fails with `FreshnessError` if unavailable
83
+ * - {@link OfflineBunCacheLive} — uses only bundled defaults, no network I/O
84
+ *
85
+ * @see {@link BunReleaseCache}
86
+ * @see {@link FreshBunCacheLive}
87
+ * @see {@link OfflineBunCacheLive}
88
+ * @public
89
+ */
90
+ export declare const AutoBunCacheLive: Layer.Layer<BunReleaseCache, never, BunVersionFetcher>;
91
+
92
+ /**
93
+ * Provides the {@link DenoReleaseCache} service using the "Auto" cache strategy.
94
+ *
95
+ * Attempts to fetch live release data from the GitHub API via
96
+ * {@link DenoVersionFetcher}. If the fetch fails for any reason — authentication
97
+ * errors, network errors, parse errors, or rate-limit errors — it silently falls
98
+ * back to the bundled default release data so the layer always succeeds.
99
+ *
100
+ * Use this strategy when you want the freshest data when available but can
101
+ * tolerate slightly stale bundled data in degraded environments.
102
+ *
103
+ * The three Deno cache strategy layers are:
104
+ * - `AutoDenoCacheLive` — tries API first, falls back to bundled defaults on any error
105
+ * - {@link FreshDenoCacheLive} — requires live API data, fails with `FreshnessError` if unavailable
106
+ * - {@link OfflineDenoCacheLive} — uses only bundled defaults, no network I/O
107
+ *
108
+ * @see {@link DenoReleaseCache}
109
+ * @see {@link FreshDenoCacheLive}
110
+ * @see {@link OfflineDenoCacheLive}
111
+ * @public
112
+ */
113
+ export declare const AutoDenoCacheLive: Layer.Layer<DenoReleaseCache, never, DenoVersionFetcher>;
114
+
115
+ /**
116
+ * Provides the {@link NodeReleaseCache} service using the "Auto" cache strategy.
117
+ *
118
+ * Attempts to fetch live release data from `nodejs.org/dist/index.json` via
119
+ * {@link NodeVersionFetcher} and the release schedule from the `nodejs/Release`
120
+ * repository via {@link NodeScheduleFetcher}. If either fetch fails — due to
121
+ * network errors or parse errors — the layer silently falls back to bundled
122
+ * default data (both version list and schedule) so it always succeeds.
123
+ *
124
+ * Node.js requires both a version fetcher and a schedule fetcher because LTS and
125
+ * maintenance phase information is tracked separately from the version index.
126
+ *
127
+ * The three Node cache strategy layers are:
128
+ * - `AutoNodeCacheLive` — tries API first, falls back to bundled defaults on any error
129
+ * - {@link FreshNodeCacheLive} — requires live API data, fails with `FreshnessError` if unavailable
130
+ * - {@link OfflineNodeCacheLive} — uses only bundled defaults, no network I/O
131
+ *
132
+ * @see {@link NodeReleaseCache}
133
+ * @see {@link FreshNodeCacheLive}
134
+ * @see {@link OfflineNodeCacheLive}
135
+ * @public
136
+ */
137
+ export declare const AutoNodeCacheLive: Layer.Layer<NodeReleaseCache, never, NodeVersionFetcher | NodeScheduleFetcher>;
138
+
139
+ /**
140
+ * A Bun release entry with a parsed {@link SemVer.SemVer} version and an
141
+ * Effect {@link DateTime.DateTime} publication date.
142
+ *
143
+ * `BunRelease` extends `Data.TaggedClass` so instances support structural
144
+ * equality comparison and can be used safely inside Effect data structures.
145
+ * Construct instances via the {@link BunRelease.fromInput} factory rather than
146
+ * calling `new BunRelease(...)` directly, as the factory validates the version
147
+ * string through `SemVer.fromString`.
148
+ *
149
+ * @see {@link BunReleaseCache}
150
+ * @see {@link BunResolver}
151
+ * @see {@link RuntimeReleaseInput}
152
+ *
153
+ * @example
154
+ * ```typescript
155
+ * import { Effect } from "effect";
156
+ * import type { BunRelease } from "./bun-release.js";
157
+ * import { BunRelease as BunReleaseClass } from "./bun-release.js";
158
+ *
159
+ * const program = BunReleaseClass.fromInput({ version: "1.1.0", date: "2024-03-01" }).pipe(
160
+ * Effect.map((release: BunRelease) => {
161
+ * console.log(release.version.major); // 1
162
+ * console.log(release.version.minor); // 1
163
+ * console.log(release._tag); // "BunRelease"
164
+ * }),
165
+ * );
166
+ *
167
+ * Effect.runPromise(program);
168
+ * ```
169
+ *
170
+ * @public
171
+ */
172
+ export declare class BunRelease extends BunReleaseBase<{
173
+ readonly version: SemVer.SemVer;
174
+ readonly date: DateTime.DateTime;
175
+ }> {
176
+ /**
177
+ * Create a {@link BunRelease} from lean string inputs.
178
+ *
179
+ * Parses `input.version` via `SemVer.fromString` and constructs the
180
+ * publication date with `DateTime.unsafeMake`. Falls back to the current
181
+ * timestamp when `input.date` is an empty string (rare in production data).
182
+ *
183
+ * @param input - A {@link RuntimeReleaseInput} object with raw string fields.
184
+ * @returns An `Effect` that succeeds with a `BunRelease` or fails with an
185
+ * `InvalidVersionError` when the version string cannot be parsed.
186
+ */
187
+ static fromInput(input: RuntimeReleaseInput): Effect.Effect<BunRelease, InvalidVersionError>;
188
+ }
189
+
190
+ /**
191
+ * @internal
192
+ *
193
+ * Exported for declaration bundling (api-extractor). When `export *`
194
+ * re-exports a class whose `extends` expression is an inline call like
195
+ * `Data.TaggedClass(...)`, TypeScript emits an un-nameable `_base` symbol in
196
+ * the declaration file. Splitting the base into a named export gives the
197
+ * bundler a stable reference.
198
+ *
199
+ * @privateRemarks
200
+ * This base constant must remain a named export so that api-extractor can
201
+ * resolve the extends clause of {@link BunRelease} to a stable declaration.
202
+ * Without it the bundled `.d.ts` would contain an anonymous `_base` symbol
203
+ * that cannot be referenced by downstream consumers.
204
+ */
205
+ export declare const BunReleaseBase: new <A extends Record<string, any> = {}>(args: Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => Readonly<A> & {
206
+ readonly _tag: "BunRelease";
207
+ };
208
+
209
+ /**
210
+ * Specialisation of {@link RuntimeCache} for Bun release objects.
211
+ *
212
+ * @see {@link BunRelease}
213
+ * @see {@link RuntimeCache}
214
+ * @see {@link AutoBunCacheLive}
215
+ * @see {@link FreshBunCacheLive}
216
+ * @see {@link OfflineBunCacheLive}
217
+ *
218
+ * @public
219
+ */
220
+ export declare type BunReleaseCache = RuntimeCache<BunRelease>;
221
+
222
+ /**
223
+ * Service tag and companion object for {@link BunReleaseCache}.
224
+ *
225
+ * Acts as both the TypeScript service interface (via the type alias above) and
226
+ * the Effect dependency tag used for dependency injection. Yield this tag
227
+ * inside `Effect.gen` to access the cache implementation provided by one of
228
+ * the cache layers.
229
+ *
230
+ * @example
231
+ * ```typescript
232
+ * import type { BunRelease } from "runtime-resolver";
233
+ * import { BunReleaseCache, OfflineBunCacheLive } from "runtime-resolver";
234
+ * import { Effect } from "effect";
235
+ *
236
+ * const program = Effect.gen(function* () {
237
+ * const cache = yield* BunReleaseCache;
238
+ * const releases = yield* cache.releases();
239
+ * console.log(releases.length);
240
+ * });
241
+ *
242
+ * Effect.runPromise(program.pipe(Effect.provide(OfflineBunCacheLive)));
243
+ * ```
244
+ *
245
+ * @see {@link AutoBunCacheLive}
246
+ * @see {@link FreshBunCacheLive}
247
+ * @see {@link OfflineBunCacheLive}
248
+ *
249
+ * @public
250
+ */
251
+ export declare const BunReleaseCache: Context.Tag<BunReleaseCache, BunReleaseCache>;
252
+
253
+ /**
254
+ * Low-level layer that constructs the {@link BunReleaseCache} service backed by
255
+ * a `SemVerVersionCache` instance from `semver-effect`.
256
+ *
257
+ * This layer wires up the cache storage but does not populate it with any
258
+ * release data. Callers are responsible for loading releases after construction.
259
+ * In normal usage you should prefer the higher-level cache strategy layers
260
+ * instead of using this layer directly:
261
+ * - {@link AutoBunCacheLive} — API with fallback to bundled defaults
262
+ * - {@link FreshBunCacheLive} — API only, fails if unavailable
263
+ * - {@link OfflineBunCacheLive} — bundled defaults only
264
+ *
265
+ * @see {@link BunReleaseCache}
266
+ * @see {@link createRuntimeCache}
267
+ * @internal
268
+ */
269
+ export declare const BunReleaseCacheLive: Layer.Layer<BunReleaseCache, never, VersionCache>;
270
+
271
+ /**
272
+ * Service interface for resolving Bun runtime versions against the cached
273
+ * release index.
274
+ *
275
+ * @see {@link BunResolverLive}
276
+ * @see {@link resolveBun}
277
+ * @see {@link BunResolverOptions}
278
+ * @see {@link ResolvedVersions}
279
+ *
280
+ * @public
28
281
  */
29
282
  export declare interface BunResolver {
283
+ /**
284
+ * Resolves Bun versions according to `options` and returns a
285
+ * {@link ResolvedVersions} object containing the matching version list,
286
+ * the latest version string, and the data source indicator.
287
+ *
288
+ * Fails with {@link BunResolverError} when no version can be resolved and
289
+ * no `defaultVersion` was provided.
290
+ *
291
+ * @param options - Optional resolution constraints.
292
+ *
293
+ * @see {@link BunResolverOptions}
294
+ */
30
295
  readonly resolve: (options?: BunResolverOptions) => Effect.Effect<ResolvedVersions, BunResolverError>;
31
- readonly resolveVersion: (versionOrRange: string) => Effect.Effect<string, BunResolverError>;
32
296
  }
33
297
 
34
298
  /**
35
- * BunResolver tag for dependency injection.
299
+ * Service tag and companion object for {@link BunResolver}.
36
300
  *
37
- * @internal Uses GenericTag to avoid generating un-nameable `_base` types
38
- * that break the declaration bundler (api-extractor) when re-exported via
39
- * `export *`.
301
+ * Acts as both the TypeScript service interface and the Effect dependency tag
302
+ * used for dependency injection (companion object pattern). Yield this tag
303
+ * inside `Effect.gen` to obtain the resolver implementation provided by
304
+ * {@link BunResolverLive}.
305
+ *
306
+ * For a one-shot Promise-based API see {@link resolveBun}.
307
+ *
308
+ * @example
309
+ * ```typescript
310
+ * import type { ResolvedVersions } from "runtime-resolver";
311
+ * import { BunResolver, BunResolverLive } from "runtime-resolver";
312
+ * import { Effect } from "effect";
313
+ *
314
+ * const program = Effect.gen(function* () {
315
+ * const resolver = yield* BunResolver;
316
+ * const result = yield* resolver.resolve({ semverRange: "^1.0.0", increments: "minor" });
317
+ * console.log(result.latest);
318
+ * console.log(result.versions);
319
+ * });
320
+ *
321
+ * Effect.runPromise(program.pipe(Effect.provide(BunResolverLive)));
322
+ * ```
323
+ *
324
+ * @see {@link BunResolverLive}
325
+ * @see {@link resolveBun}
326
+ *
327
+ * @public
40
328
  */
41
329
  export declare const BunResolver: Context.Tag<BunResolver, BunResolver>;
42
330
 
43
- export declare type BunResolverError = AuthenticationError | NetworkError | ParseError | RateLimitError | VersionNotFoundError | InvalidInputError | FreshnessError | CacheError;
331
+ /**
332
+ * Union of all typed errors that {@link BunResolver.resolve} can fail with.
333
+ *
334
+ * @see {@link VersionNotFoundError}
335
+ *
336
+ * @public
337
+ */
338
+ export declare type BunResolverError = VersionNotFoundError;
44
339
 
45
- export declare const BunResolverLive: Layer.Layer<BunResolver, never, GitHubClient | VersionCache>;
340
+ /**
341
+ * Provides the {@link BunResolver} service backed by a {@link BunReleaseCache}.
342
+ *
343
+ * This layer composes with any of the three Bun cache strategy layers to form
344
+ * a complete resolver stack. It implements semver range filtering and increment
345
+ * grouping (latest per major, latest per minor, or all patch versions) using
346
+ * the releases already loaded into the cache.
347
+ *
348
+ * @example
349
+ * ```ts
350
+ * import { BunResolverLive, AutoBunCacheLive, BunVersionFetcherLive, GitHubClientLive, GitHubAutoAuth } from "runtime-resolver";
351
+ * import type { ResolvedVersions } from "runtime-resolver";
352
+ * import { BunResolver } from "runtime-resolver";
353
+ * import { Effect, Layer } from "effect";
354
+ *
355
+ * const GitHubLayer = GitHubClientLive.pipe(Layer.provide(GitHubAutoAuth));
356
+ * const CacheLayer = AutoBunCacheLive.pipe(Layer.provide(BunVersionFetcherLive.pipe(Layer.provide(GitHubLayer))));
357
+ * const ResolverLayer = BunResolverLive.pipe(Layer.provide(CacheLayer));
358
+ *
359
+ * const program = Effect.gen(function* () {
360
+ * const resolver = yield* BunResolver;
361
+ * const result = yield* resolver.resolve({ semverRange: "^1.0.0" });
362
+ * console.log(result.latest);
363
+ * });
364
+ *
365
+ * Effect.runPromise(program.pipe(Effect.provide(ResolverLayer)));
366
+ * ```
367
+ *
368
+ * @see {@link BunResolver}
369
+ * @see {@link BunReleaseCache}
370
+ * @see {@link AutoBunCacheLive}
371
+ * @see {@link FreshBunCacheLive}
372
+ * @see {@link OfflineBunCacheLive}
373
+ * @public
374
+ */
375
+ export declare const BunResolverLive: Layer.Layer<BunResolver, never, BunReleaseCache>;
46
376
 
377
+ /**
378
+ * Options accepted by {@link BunResolver.resolve}.
379
+ *
380
+ * All fields are optional; omitting them causes the resolver to fall back to
381
+ * its built-in defaults (latest stable release, `"latest"` increment
382
+ * granularity).
383
+ *
384
+ * @see {@link BunResolver}
385
+ * @see {@link Increments}
386
+ * @see {@link ResolvedVersions}
387
+ *
388
+ * @public
389
+ */
47
390
  export declare interface BunResolverOptions {
391
+ /**
392
+ * A semver range string used to constrain which Bun releases are
393
+ * considered (e.g. `"^1.0.0"` or `">=1.1.0 <2.0.0"`).
394
+ *
395
+ * When omitted all cached releases are eligible.
396
+ */
48
397
  readonly semverRange?: string;
398
+ /**
399
+ * A fallback version string returned when no release satisfies
400
+ * `semverRange`. Must be a valid semver version (e.g. `"1.0.0"`).
401
+ *
402
+ * When omitted and no match is found the effect fails with
403
+ * {@link VersionNotFoundError}.
404
+ */
49
405
  readonly defaultVersion?: string;
406
+ /**
407
+ * Controls the granularity of the version list included in
408
+ * {@link ResolvedVersions.versions}.
409
+ *
410
+ * - `"latest"` — only the single highest matching version.
411
+ * - `"minor"` — the latest patch for every minor line.
412
+ * - `"patch"` — every individual patch release.
413
+ *
414
+ * Defaults to `"latest"` when omitted.
415
+ *
416
+ * @see {@link Increments}
417
+ */
50
418
  readonly increments?: Increments;
51
- readonly freshness?: Freshness;
52
419
  }
53
420
 
54
- export declare type CachedData = CachedNodeData | CachedTagData;
55
-
56
- export declare const CachedNodeData: Schema.Struct<{
57
- versions: Schema.Array$<Schema.Struct<{
58
- version: typeof Schema.String;
59
- date: typeof Schema.String;
60
- files: Schema.Array$<typeof Schema.String>;
61
- npm: Schema.optional<typeof Schema.String>;
62
- v8: Schema.optional<typeof Schema.String>;
63
- uv: Schema.optional<typeof Schema.String>;
64
- zlib: Schema.optional<typeof Schema.String>;
65
- openssl: Schema.optional<typeof Schema.String>;
66
- modules: Schema.optional<typeof Schema.String>;
67
- lts: Schema.Union<[Schema.Literal<[false]>, typeof Schema.String]>;
68
- security: typeof Schema.Boolean;
69
- }>>;
70
- schedule: Schema.Record$<typeof Schema.String, Schema.Struct<{
71
- start: typeof Schema.String;
72
- lts: Schema.optional<typeof Schema.String>;
73
- maintenance: Schema.optional<typeof Schema.String>;
74
- end: typeof Schema.String;
75
- codename: Schema.optional<typeof Schema.String>;
76
- }>>;
77
- }>;
421
+ /**
422
+ * Service interface for fetching Bun release data from the GitHub API.
423
+ *
424
+ * Retrieves both the parsed semver version list and the raw release input
425
+ * records needed to populate a {@link BunReleaseCache}. The live
426
+ * implementation queries the `oven-sh/bun` GitHub repository via
427
+ * {@link GitHubClient}.
428
+ *
429
+ * @see {@link BunVersionFetcherLive}
430
+ * @see {@link BunReleaseCache}
431
+ * @see {@link AuthenticationError}
432
+ * @see {@link NetworkError}
433
+ * @see {@link ParseError}
434
+ * @see {@link RateLimitError}
435
+ *
436
+ * @public
437
+ */
438
+ export declare interface BunVersionFetcher {
439
+ /**
440
+ * Fetches all available Bun releases from the GitHub API and returns both
441
+ * the parsed semver version objects and the raw release input records.
442
+ *
443
+ * Fails with:
444
+ * - {@link AuthenticationError} — missing or invalid GitHub credentials.
445
+ * - {@link NetworkError} — the upstream request could not be completed.
446
+ * - {@link ParseError} — the response payload could not be decoded.
447
+ * - {@link RateLimitError} — the GitHub API rate limit was exceeded.
448
+ */
449
+ readonly fetch: () => Effect.Effect<{
450
+ readonly versions: ReadonlyArray<SemVer.SemVer>;
451
+ readonly inputs: ReadonlyArray<RuntimeReleaseInput>;
452
+ }, AuthenticationError | NetworkError | ParseError | RateLimitError>;
453
+ }
78
454
 
79
- export declare type CachedNodeData = typeof CachedNodeData.Type;
80
-
81
- export declare const CachedTagData: Schema.Struct<{
82
- tags: Schema.Array$<Schema.Struct<{
83
- name: typeof Schema.String;
84
- zipball_url: typeof Schema.String;
85
- tarball_url: typeof Schema.String;
86
- commit: Schema.Struct<{
87
- sha: typeof Schema.String;
88
- url: typeof Schema.String;
89
- }>;
90
- node_id: typeof Schema.String;
91
- }>>;
92
- }>;
455
+ /**
456
+ * Service tag and companion object for {@link BunVersionFetcher}.
457
+ *
458
+ * Acts as both the TypeScript service interface and the Effect dependency tag
459
+ * used for dependency injection (companion object pattern). Yield this tag
460
+ * inside `Effect.gen` to access the fetcher implementation provided by
461
+ * {@link BunVersionFetcherLive}.
462
+ *
463
+ * @example
464
+ * ```typescript
465
+ * import type { RuntimeReleaseInput } from "runtime-resolver";
466
+ * import { BunVersionFetcher, BunVersionFetcherLive, GitHubAutoAuth } from "runtime-resolver";
467
+ * import { Effect } from "effect";
468
+ *
469
+ * const program = Effect.gen(function* () {
470
+ * const fetcher = yield* BunVersionFetcher;
471
+ * const { versions, inputs } = yield* fetcher.fetch();
472
+ * console.log(`Fetched ${versions.length} Bun releases`);
473
+ * });
474
+ *
475
+ * Effect.runPromise(
476
+ * program.pipe(Effect.provide(BunVersionFetcherLive), Effect.provide(GitHubAutoAuth)),
477
+ * );
478
+ * ```
479
+ *
480
+ * @see {@link BunVersionFetcherLive}
481
+ *
482
+ * @public
483
+ */
484
+ export declare const BunVersionFetcher: Context.Tag<BunVersionFetcher, BunVersionFetcher>;
93
485
 
94
- export declare type CachedTagData = typeof CachedTagData.Type;
486
+ /**
487
+ * Provides the {@link BunVersionFetcher} service using the live GitHub API.
488
+ *
489
+ * Fetches Bun releases from the `oven-sh/bun` GitHub repository using the
490
+ * provided {@link GitHubClient}. Draft releases and pre-releases are excluded.
491
+ * Tag names are normalized by stripping the `bun-` prefix and any leading `v`
492
+ * or `V` before parsing as semver.
493
+ *
494
+ * This layer is a required dependency of {@link AutoBunCacheLive} and
495
+ * {@link FreshBunCacheLive}. It is not needed by {@link OfflineBunCacheLive}.
496
+ *
497
+ * @see {@link BunVersionFetcher}
498
+ * @see {@link GitHubClient}
499
+ * @see {@link AutoBunCacheLive}
500
+ * @see {@link FreshBunCacheLive}
501
+ * @public
502
+ */
503
+ export declare const BunVersionFetcherLive: Layer.Layer<BunVersionFetcher, never, GitHubClient>;
95
504
 
505
+ /**
506
+ * Raised when a cache read or write operation fails.
507
+ *
508
+ * This error surfaces when the local on-disk version cache cannot be accessed
509
+ * or updated. The `operation` field distinguishes between failures that occur
510
+ * while loading cached data (`"read"`) and failures that occur while
511
+ * persisting new data (`"write"`).
512
+ *
513
+ * @example
514
+ * ```typescript
515
+ * import { Effect } from "effect";
516
+ * import type { CacheError } from "./CacheError.js";
517
+ * import { resolveNode } from "../resolvers/node.js";
518
+ *
519
+ * const program = resolveNode(">=20").pipe(
520
+ * Effect.catchTag("CacheError", (err: CacheError) =>
521
+ * Effect.logWarning(
522
+ * `Cache ${err.operation} failed: ${err.message}`
523
+ * ).pipe(Effect.andThen(Effect.fail(err)))
524
+ * )
525
+ * );
526
+ * ```
527
+ *
528
+ * @public
529
+ */
96
530
  export declare class CacheError extends CacheErrorBase<{
97
531
  readonly operation: "read" | "write";
98
532
  readonly message: string;
@@ -102,41 +536,494 @@ export declare class CacheError extends CacheErrorBase<{
102
536
  /**
103
537
  * @internal
104
538
  * Exported for declaration bundling — see AuthenticationError.ts for details.
539
+ *
540
+ * @privateRemarks
541
+ * This base constant must remain a named export so that api-extractor can
542
+ * resolve the extends clause of {@link CacheError} to a stable declaration.
543
+ * Without it the bundled `.d.ts` would contain an anonymous `_base` symbol
544
+ * that cannot be referenced by downstream consumers.
105
545
  */
106
546
  export declare const CacheErrorBase: new <A extends Record<string, any> = {}>(args: Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => YieldableError & {
107
547
  readonly _tag: "CacheError";
108
548
  } & Readonly<A>;
109
549
 
110
550
  /**
111
- * Service interface for resolving Deno runtime versions.
551
+ * Factory that creates a {@link RuntimeCache} implementation backed by
552
+ * `semver-effect`'s `VersionCache`.
553
+ *
554
+ * The returned `Effect` must be executed within an Effect context that provides
555
+ * a `SemVerVersionCache` service (i.e., it requires `SemVerVersionCache` in its
556
+ * environment). The cache maintains an internal lookup map from version string
557
+ * to the full release object so that resolved semver versions can be
558
+ * round-tripped back to their typed release records.
559
+ *
560
+ * This factory is used internally by {@link BunReleaseCacheLive},
561
+ * {@link DenoReleaseCacheLive}, and {@link NodeReleaseCacheLive} to build their
562
+ * respective cache service implementations. Prefer those higher-level layers
563
+ * over calling this factory directly.
564
+ *
565
+ * Note: `load` is not concurrency-safe. Callers must not invoke it concurrently.
566
+ * In practice this constraint is upheld because `load` is only called from layer
567
+ * setup and `NodeReleaseCacheLive.loadFromInputs`, both of which are serialized
568
+ * by the Effect runtime.
569
+ *
570
+ * @typeParam R - The runtime release type stored in the cache. Must extend
571
+ * {@link RuntimeRelease}.
572
+ *
573
+ * @see {@link RuntimeCache}
574
+ * @see {@link BunReleaseCacheLive}
575
+ * @see {@link DenoReleaseCacheLive}
576
+ * @see {@link NodeReleaseCacheLive}
577
+ * @public
578
+ */
579
+ export declare const createRuntimeCache: <R extends RuntimeRelease>() => Effect.Effect<RuntimeCache<R>, never, VersionCache>;
580
+
581
+ /**
582
+ * A Deno release entry with a parsed {@link SemVer.SemVer} version and an
583
+ * Effect {@link DateTime.DateTime} publication date.
584
+ *
585
+ * `DenoRelease` extends `Data.TaggedClass` so instances support structural
586
+ * equality comparison and can be used safely inside Effect data structures.
587
+ * Construct instances via the {@link DenoRelease.fromInput} factory rather than
588
+ * calling `new DenoRelease(...)` directly, as the factory validates the version
589
+ * string through `SemVer.fromString`.
590
+ *
591
+ * @see {@link DenoReleaseCache}
592
+ * @see {@link DenoResolver}
593
+ * @see {@link RuntimeReleaseInput}
594
+ *
595
+ * @example
596
+ * ```typescript
597
+ * import { Effect } from "effect";
598
+ * import type { DenoRelease } from "./deno-release.js";
599
+ * import { DenoRelease as DenoReleaseClass } from "./deno-release.js";
600
+ *
601
+ * const program = DenoReleaseClass.fromInput({ version: "1.44.0", date: "2024-05-23" }).pipe(
602
+ * Effect.map((release: DenoRelease) => {
603
+ * console.log(release.version.major); // 1
604
+ * console.log(release.version.minor); // 44
605
+ * console.log(release._tag); // "DenoRelease"
606
+ * }),
607
+ * );
608
+ *
609
+ * Effect.runPromise(program);
610
+ * ```
611
+ *
612
+ * @public
613
+ */
614
+ export declare class DenoRelease extends DenoReleaseBase<{
615
+ readonly version: SemVer.SemVer;
616
+ readonly date: DateTime.DateTime;
617
+ }> {
618
+ /**
619
+ * Create a {@link DenoRelease} from lean string inputs.
620
+ *
621
+ * Parses `input.version` via `SemVer.fromString` and constructs the
622
+ * publication date with `DateTime.unsafeMake`. Falls back to the current
623
+ * timestamp when `input.date` is an empty string (rare in production data).
624
+ *
625
+ * @param input - A {@link RuntimeReleaseInput} object with raw string fields.
626
+ * @returns An `Effect` that succeeds with a `DenoRelease` or fails with an
627
+ * `InvalidVersionError` when the version string cannot be parsed.
628
+ */
629
+ static fromInput(input: RuntimeReleaseInput): Effect.Effect<DenoRelease, InvalidVersionError>;
630
+ }
631
+
632
+ /**
633
+ * @internal
634
+ *
635
+ * Exported for declaration bundling (api-extractor). When `export *`
636
+ * re-exports a class whose `extends` expression is an inline call like
637
+ * `Data.TaggedClass(...)`, TypeScript emits an un-nameable `_base` symbol in
638
+ * the declaration file. Splitting the base into a named export gives the
639
+ * bundler a stable reference.
640
+ *
641
+ * @privateRemarks
642
+ * This base constant must remain a named export so that api-extractor can
643
+ * resolve the extends clause of {@link DenoRelease} to a stable declaration.
644
+ * Without it the bundled `.d.ts` would contain an anonymous `_base` symbol
645
+ * that cannot be referenced by downstream consumers.
646
+ */
647
+ export declare const DenoReleaseBase: new <A extends Record<string, any> = {}>(args: Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => Readonly<A> & {
648
+ readonly _tag: "DenoRelease";
649
+ };
650
+
651
+ /**
652
+ * Specialisation of {@link RuntimeCache} for Deno release objects.
653
+ *
654
+ * @see {@link DenoRelease}
655
+ * @see {@link RuntimeCache}
656
+ * @see {@link AutoDenoCacheLive}
657
+ * @see {@link FreshDenoCacheLive}
658
+ * @see {@link OfflineDenoCacheLive}
659
+ *
660
+ * @public
661
+ */
662
+ export declare type DenoReleaseCache = RuntimeCache<DenoRelease>;
663
+
664
+ /**
665
+ * Service tag and companion object for {@link DenoReleaseCache}.
666
+ *
667
+ * Acts as both the TypeScript service interface (via the type alias above) and
668
+ * the Effect dependency tag used for dependency injection. Yield this tag
669
+ * inside `Effect.gen` to access the cache implementation provided by one of
670
+ * the cache layers.
671
+ *
672
+ * @example
673
+ * ```typescript
674
+ * import type { DenoRelease } from "runtime-resolver";
675
+ * import { DenoReleaseCache, OfflineDenoCacheLive } from "runtime-resolver";
676
+ * import { Effect } from "effect";
677
+ *
678
+ * const program = Effect.gen(function* () {
679
+ * const cache = yield* DenoReleaseCache;
680
+ * const latest = yield* cache.latest();
681
+ * console.log(latest.version.toString());
682
+ * });
683
+ *
684
+ * Effect.runPromise(program.pipe(Effect.provide(OfflineDenoCacheLive)));
685
+ * ```
686
+ *
687
+ * @see {@link AutoDenoCacheLive}
688
+ * @see {@link FreshDenoCacheLive}
689
+ * @see {@link OfflineDenoCacheLive}
690
+ *
691
+ * @public
692
+ */
693
+ export declare const DenoReleaseCache: Context.Tag<DenoReleaseCache, DenoReleaseCache>;
694
+
695
+ /**
696
+ * Low-level layer that constructs the {@link DenoReleaseCache} service backed by
697
+ * a `SemVerVersionCache` instance from `semver-effect`.
698
+ *
699
+ * This layer wires up the cache storage but does not populate it with any
700
+ * release data. Callers are responsible for loading releases after construction.
701
+ * In normal usage you should prefer the higher-level cache strategy layers
702
+ * instead of using this layer directly:
703
+ * - {@link AutoDenoCacheLive} — API with fallback to bundled defaults
704
+ * - {@link FreshDenoCacheLive} — API only, fails if unavailable
705
+ * - {@link OfflineDenoCacheLive} — bundled defaults only
706
+ *
707
+ * @see {@link DenoReleaseCache}
708
+ * @see {@link createRuntimeCache}
709
+ * @internal
710
+ */
711
+ export declare const DenoReleaseCacheLive: Layer.Layer<DenoReleaseCache, never, VersionCache>;
712
+
713
+ /**
714
+ * Service interface for resolving Deno runtime versions against the cached
715
+ * release index.
716
+ *
717
+ * @see {@link DenoResolverLive}
718
+ * @see {@link resolveDeno}
719
+ * @see {@link DenoResolverOptions}
720
+ * @see {@link ResolvedVersions}
721
+ *
722
+ * @public
112
723
  */
113
724
  export declare interface DenoResolver {
725
+ /**
726
+ * Resolves Deno versions according to `options` and returns a
727
+ * {@link ResolvedVersions} object containing the matching version list,
728
+ * the latest version string, and the data source indicator.
729
+ *
730
+ * Fails with {@link DenoResolverError} when no version can be resolved and
731
+ * no `defaultVersion` was provided.
732
+ *
733
+ * @param options - Optional resolution constraints.
734
+ *
735
+ * @see {@link DenoResolverOptions}
736
+ */
114
737
  readonly resolve: (options?: DenoResolverOptions) => Effect.Effect<ResolvedVersions, DenoResolverError>;
115
- readonly resolveVersion: (versionOrRange: string) => Effect.Effect<string, DenoResolverError>;
116
738
  }
117
739
 
118
740
  /**
119
- * @internal Uses GenericTag — see BunResolver.ts for rationale.
741
+ * Service tag and companion object for {@link DenoResolver}.
742
+ *
743
+ * Acts as both the TypeScript service interface and the Effect dependency tag
744
+ * used for dependency injection (companion object pattern). Yield this tag
745
+ * inside `Effect.gen` to obtain the resolver implementation provided by
746
+ * {@link DenoResolverLive}.
747
+ *
748
+ * For a one-shot Promise-based API see {@link resolveDeno}.
749
+ *
750
+ * @example
751
+ * ```typescript
752
+ * import type { ResolvedVersions } from "runtime-resolver";
753
+ * import { DenoResolver, DenoResolverLive } from "runtime-resolver";
754
+ * import { Effect } from "effect";
755
+ *
756
+ * const program = Effect.gen(function* () {
757
+ * const resolver = yield* DenoResolver;
758
+ * const result = yield* resolver.resolve({ semverRange: "^2.0.0", increments: "minor" });
759
+ * console.log(result.latest);
760
+ * console.log(result.versions);
761
+ * });
762
+ *
763
+ * Effect.runPromise(program.pipe(Effect.provide(DenoResolverLive)));
764
+ * ```
765
+ *
766
+ * @see {@link DenoResolverLive}
767
+ * @see {@link resolveDeno}
768
+ *
769
+ * @public
120
770
  */
121
771
  export declare const DenoResolver: Context.Tag<DenoResolver, DenoResolver>;
122
772
 
123
- export declare type DenoResolverError = AuthenticationError | NetworkError | ParseError | RateLimitError | VersionNotFoundError | InvalidInputError | FreshnessError | CacheError;
773
+ /**
774
+ * Union of all typed errors that {@link DenoResolver.resolve} can fail with.
775
+ *
776
+ * @see {@link VersionNotFoundError}
777
+ *
778
+ * @public
779
+ */
780
+ export declare type DenoResolverError = VersionNotFoundError;
124
781
 
125
- export declare const DenoResolverLive: Layer.Layer<DenoResolver, never, GitHubClient | VersionCache>;
782
+ /**
783
+ * Provides the {@link DenoResolver} service backed by a {@link DenoReleaseCache}.
784
+ *
785
+ * This layer composes with any of the three Deno cache strategy layers to form
786
+ * a complete resolver stack. It implements semver range filtering and increment
787
+ * grouping (latest per major, latest per minor, or all patch versions) using
788
+ * the releases already loaded into the cache.
789
+ *
790
+ * @example
791
+ * ```ts
792
+ * import { DenoResolverLive, AutoDenoCacheLive, DenoVersionFetcherLive, GitHubClientLive, GitHubAutoAuth } from "runtime-resolver";
793
+ * import { DenoResolver } from "runtime-resolver";
794
+ * import { Effect, Layer } from "effect";
795
+ *
796
+ * const GitHubLayer = GitHubClientLive.pipe(Layer.provide(GitHubAutoAuth));
797
+ * const CacheLayer = AutoDenoCacheLive.pipe(Layer.provide(DenoVersionFetcherLive.pipe(Layer.provide(GitHubLayer))));
798
+ * const ResolverLayer = DenoResolverLive.pipe(Layer.provide(CacheLayer));
799
+ *
800
+ * const program = Effect.gen(function* () {
801
+ * const resolver = yield* DenoResolver;
802
+ * const result = yield* resolver.resolve({ semverRange: "^2.0.0" });
803
+ * console.log(result.latest);
804
+ * });
805
+ *
806
+ * Effect.runPromise(program.pipe(Effect.provide(ResolverLayer)));
807
+ * ```
808
+ *
809
+ * @see {@link DenoResolver}
810
+ * @see {@link DenoReleaseCache}
811
+ * @see {@link AutoDenoCacheLive}
812
+ * @see {@link FreshDenoCacheLive}
813
+ * @see {@link OfflineDenoCacheLive}
814
+ * @public
815
+ */
816
+ export declare const DenoResolverLive: Layer.Layer<DenoResolver, never, DenoReleaseCache>;
126
817
 
818
+ /**
819
+ * Options accepted by {@link DenoResolver.resolve}.
820
+ *
821
+ * All fields are optional; omitting them causes the resolver to fall back to
822
+ * its built-in defaults (latest stable release, `"latest"` increment
823
+ * granularity).
824
+ *
825
+ * @see {@link DenoResolver}
826
+ * @see {@link Increments}
827
+ * @see {@link ResolvedVersions}
828
+ *
829
+ * @public
830
+ */
127
831
  export declare interface DenoResolverOptions {
832
+ /**
833
+ * A semver range string used to constrain which Deno releases are
834
+ * considered (e.g. `"^2.0.0"` or `">=1.40.0 <2.0.0"`).
835
+ *
836
+ * When omitted all cached releases are eligible.
837
+ */
128
838
  readonly semverRange?: string;
839
+ /**
840
+ * A fallback version string returned when no release satisfies
841
+ * `semverRange`. Must be a valid semver version (e.g. `"2.0.0"`).
842
+ *
843
+ * When omitted and no match is found the effect fails with
844
+ * {@link VersionNotFoundError}.
845
+ */
129
846
  readonly defaultVersion?: string;
847
+ /**
848
+ * Controls the granularity of the version list included in
849
+ * {@link ResolvedVersions.versions}.
850
+ *
851
+ * - `"latest"` — only the single highest matching version.
852
+ * - `"minor"` — the latest patch for every minor line.
853
+ * - `"patch"` — every individual patch release.
854
+ *
855
+ * Defaults to `"latest"` when omitted.
856
+ *
857
+ * @see {@link Increments}
858
+ */
130
859
  readonly increments?: Increments;
131
- readonly freshness?: Freshness;
132
860
  }
133
861
 
134
- export declare const Freshness: Schema.Literal<["auto", "api", "cache"]>;
862
+ /**
863
+ * Service interface for fetching Deno release data from the GitHub API.
864
+ *
865
+ * Retrieves both the parsed semver version list and the raw release input
866
+ * records needed to populate a {@link DenoReleaseCache}. The live
867
+ * implementation queries the `denoland/deno` GitHub repository via
868
+ * {@link GitHubClient}.
869
+ *
870
+ * @see {@link DenoVersionFetcherLive}
871
+ * @see {@link DenoReleaseCache}
872
+ * @see {@link AuthenticationError}
873
+ * @see {@link NetworkError}
874
+ * @see {@link ParseError}
875
+ * @see {@link RateLimitError}
876
+ *
877
+ * @public
878
+ */
879
+ export declare interface DenoVersionFetcher {
880
+ /**
881
+ * Fetches all available Deno releases from the GitHub API and returns both
882
+ * the parsed semver version objects and the raw release input records.
883
+ *
884
+ * Fails with:
885
+ * - {@link AuthenticationError} — missing or invalid GitHub credentials.
886
+ * - {@link NetworkError} — the upstream request could not be completed.
887
+ * - {@link ParseError} — the response payload could not be decoded.
888
+ * - {@link RateLimitError} — the GitHub API rate limit was exceeded.
889
+ */
890
+ readonly fetch: () => Effect.Effect<{
891
+ readonly versions: ReadonlyArray<SemVer.SemVer>;
892
+ readonly inputs: ReadonlyArray<RuntimeReleaseInput>;
893
+ }, AuthenticationError | NetworkError | ParseError | RateLimitError>;
894
+ }
135
895
 
136
- export declare type Freshness = typeof Freshness.Type;
896
+ /**
897
+ * Service tag and companion object for {@link DenoVersionFetcher}.
898
+ *
899
+ * Acts as both the TypeScript service interface and the Effect dependency tag
900
+ * used for dependency injection (companion object pattern). Yield this tag
901
+ * inside `Effect.gen` to access the fetcher implementation provided by
902
+ * {@link DenoVersionFetcherLive}.
903
+ *
904
+ * @example
905
+ * ```typescript
906
+ * import type { RuntimeReleaseInput } from "runtime-resolver";
907
+ * import { DenoVersionFetcher, DenoVersionFetcherLive, GitHubAutoAuth } from "runtime-resolver";
908
+ * import { Effect } from "effect";
909
+ *
910
+ * const program = Effect.gen(function* () {
911
+ * const fetcher = yield* DenoVersionFetcher;
912
+ * const { versions, inputs } = yield* fetcher.fetch();
913
+ * console.log(`Fetched ${versions.length} Deno releases`);
914
+ * });
915
+ *
916
+ * Effect.runPromise(
917
+ * program.pipe(Effect.provide(DenoVersionFetcherLive), Effect.provide(GitHubAutoAuth)),
918
+ * );
919
+ * ```
920
+ *
921
+ * @see {@link DenoVersionFetcherLive}
922
+ *
923
+ * @public
924
+ */
925
+ export declare const DenoVersionFetcher: Context.Tag<DenoVersionFetcher, DenoVersionFetcher>;
926
+
927
+ /**
928
+ * Provides the {@link DenoVersionFetcher} service using the live GitHub API.
929
+ *
930
+ * Fetches Deno releases from the `denoland/deno` GitHub repository using the
931
+ * provided {@link GitHubClient}. Draft releases and pre-releases are excluded.
932
+ * Tag names are normalized by stripping any leading `v` before parsing as
933
+ * semver.
934
+ *
935
+ * This layer is a required dependency of {@link AutoDenoCacheLive} and
936
+ * {@link FreshDenoCacheLive}. It is not needed by {@link OfflineDenoCacheLive}.
937
+ *
938
+ * @see {@link DenoVersionFetcher}
939
+ * @see {@link GitHubClient}
940
+ * @see {@link AutoDenoCacheLive}
941
+ * @see {@link FreshDenoCacheLive}
942
+ * @public
943
+ */
944
+ export declare const DenoVersionFetcherLive: Layer.Layer<DenoVersionFetcher, never, GitHubClient>;
945
+
946
+ /**
947
+ * Provides the {@link BunReleaseCache} service using the "Fresh" cache strategy.
948
+ *
949
+ * Requires live release data from the GitHub API via {@link BunVersionFetcher}.
950
+ * If the fetch fails for any reason — authentication errors, network errors,
951
+ * parse errors, or rate-limit errors — the layer fails with a `FreshnessError`
952
+ * rather than falling back to stale data.
953
+ *
954
+ * Use this strategy in contexts where working with outdated version data would
955
+ * be incorrect or misleading, such as CI pipelines or tooling that must reflect
956
+ * the true current state of Bun releases.
957
+ *
958
+ * The three Bun cache strategy layers are:
959
+ * - {@link AutoBunCacheLive} — tries API first, falls back to bundled defaults on any error
960
+ * - `FreshBunCacheLive` — requires live API data, fails with `FreshnessError` if unavailable
961
+ * - {@link OfflineBunCacheLive} — uses only bundled defaults, no network I/O
962
+ *
963
+ * @see {@link BunReleaseCache}
964
+ * @see {@link AutoBunCacheLive}
965
+ * @see {@link OfflineBunCacheLive}
966
+ * @public
967
+ */
968
+ export declare const FreshBunCacheLive: Layer.Layer<BunReleaseCache, FreshnessError, BunVersionFetcher>;
137
969
 
970
+ /**
971
+ * Provides the {@link DenoReleaseCache} service using the "Fresh" cache strategy.
972
+ *
973
+ * Requires live release data from the GitHub API via {@link DenoVersionFetcher}.
974
+ * If the fetch fails for any reason — authentication errors, network errors,
975
+ * parse errors, or rate-limit errors — the layer fails with a `FreshnessError`
976
+ * rather than falling back to stale data.
977
+ *
978
+ * Use this strategy in contexts where working with outdated version data would
979
+ * be incorrect or misleading, such as CI pipelines or tooling that must reflect
980
+ * the true current state of Deno releases.
981
+ *
982
+ * The three Deno cache strategy layers are:
983
+ * - {@link AutoDenoCacheLive} — tries API first, falls back to bundled defaults on any error
984
+ * - `FreshDenoCacheLive` — requires live API data, fails with `FreshnessError` if unavailable
985
+ * - {@link OfflineDenoCacheLive} — uses only bundled defaults, no network I/O
986
+ *
987
+ * @see {@link DenoReleaseCache}
988
+ * @see {@link AutoDenoCacheLive}
989
+ * @see {@link OfflineDenoCacheLive}
990
+ * @public
991
+ */
992
+ export declare const FreshDenoCacheLive: Layer.Layer<DenoReleaseCache, FreshnessError, DenoVersionFetcher>;
993
+
994
+ /**
995
+ * Raised when a cache freshness check cannot be completed.
996
+ *
997
+ * Each runtime cache layer ({@link FreshBunCacheLive}, {@link FreshDenoCacheLive},
998
+ * {@link FreshNodeCacheLive}) validates whether locally stored version data is
999
+ * still current before returning it. This error is produced when that
1000
+ * validation fails — for example, when the upstream API is unreachable during
1001
+ * an `"api"` freshness check, or when the cached timestamp is corrupt during
1002
+ * an `"auto"` or `"cache"` check.
1003
+ *
1004
+ * @see {@link FreshBunCacheLive}
1005
+ * @see {@link FreshDenoCacheLive}
1006
+ * @see {@link FreshNodeCacheLive}
1007
+ *
1008
+ * @example
1009
+ * ```typescript
1010
+ * import { Effect } from "effect";
1011
+ * import type { FreshnessError } from "./FreshnessError.js";
1012
+ * import { resolveNode } from "../resolvers/node.js";
1013
+ *
1014
+ * const program = resolveNode(">=20").pipe(
1015
+ * Effect.catchTag("FreshnessError", (err: FreshnessError) =>
1016
+ * Effect.logWarning(
1017
+ * `Freshness check (${err.strategy}) failed: ${err.message}`
1018
+ * ).pipe(Effect.andThen(Effect.fail(err)))
1019
+ * )
1020
+ * );
1021
+ * ```
1022
+ *
1023
+ * @public
1024
+ */
138
1025
  export declare class FreshnessError extends FreshnessErrorBase<{
139
- readonly strategy: Freshness;
1026
+ readonly strategy: "auto" | "api" | "cache";
140
1027
  readonly message: string;
141
1028
  }> {
142
1029
  }
@@ -144,39 +1031,250 @@ export declare class FreshnessError extends FreshnessErrorBase<{
144
1031
  /**
145
1032
  * @internal
146
1033
  * Exported for declaration bundling — see AuthenticationError.ts for details.
1034
+ *
1035
+ * @privateRemarks
1036
+ * This base constant must remain a named export so that api-extractor can
1037
+ * resolve the extends clause of {@link FreshnessError} to a stable declaration.
1038
+ * Without it the bundled `.d.ts` would contain an anonymous `_base` symbol
1039
+ * that cannot be referenced by downstream consumers.
147
1040
  */
148
1041
  export declare const FreshnessErrorBase: new <A extends Record<string, any> = {}>(args: Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => YieldableError & {
149
1042
  readonly _tag: "FreshnessError";
150
1043
  } & Readonly<A>;
151
1044
 
1045
+ /**
1046
+ * Provides the {@link NodeReleaseCache} service using the "Fresh" cache strategy.
1047
+ *
1048
+ * Requires live release data from `nodejs.org/dist/index.json` via
1049
+ * {@link NodeVersionFetcher} and the release schedule from the `nodejs/Release`
1050
+ * repository via {@link NodeScheduleFetcher}. If either fetch fails — due to
1051
+ * network errors or parse errors — the layer fails with a `FreshnessError`
1052
+ * rather than falling back to stale data.
1053
+ *
1054
+ * Use this strategy in contexts where working with outdated version data would
1055
+ * be incorrect or misleading, such as CI pipelines or tooling that must reflect
1056
+ * the true current state of Node.js releases and LTS schedule.
1057
+ *
1058
+ * The three Node cache strategy layers are:
1059
+ * - {@link AutoNodeCacheLive} — tries API first, falls back to bundled defaults on any error
1060
+ * - `FreshNodeCacheLive` — requires live API data, fails with `FreshnessError` if unavailable
1061
+ * - {@link OfflineNodeCacheLive} — uses only bundled defaults, no network I/O
1062
+ *
1063
+ * @see {@link NodeReleaseCache}
1064
+ * @see {@link AutoNodeCacheLive}
1065
+ * @see {@link OfflineNodeCacheLive}
1066
+ * @public
1067
+ */
1068
+ export declare const FreshNodeCacheLive: Layer.Layer<NodeReleaseCache, FreshnessError, NodeVersionFetcher | NodeScheduleFetcher>;
1069
+
1070
+ /**
1071
+ * Provides an {@link OctokitInstance} authenticated as a GitHub App installation.
1072
+ *
1073
+ * Accepts a {@link GitHubAppAuthConfig} and uses `@octokit/auth-app` to obtain
1074
+ * an installation access token. If `installationId` is omitted, the first
1075
+ * installation returned by the GitHub API is used automatically (suitable for
1076
+ * apps with a single installation). If authentication fails for any reason the
1077
+ * layer fails with an `AuthenticationError`.
1078
+ *
1079
+ * Use this auth method when your integration runs as a GitHub App rather than
1080
+ * as an individual user or a CI token.
1081
+ *
1082
+ * @param config - App credentials and optional installation ID.
1083
+ *
1084
+ * @see {@link GitHubAppAuthConfig}
1085
+ * @see {@link OctokitInstance}
1086
+ * @see {@link GitHubAutoAuth}
1087
+ * @see {@link GitHubTokenAuth}
1088
+ * @public
1089
+ */
152
1090
  export declare const GitHubAppAuth: (config: GitHubAppAuthConfig) => Layer.Layer<OctokitInstance, AuthenticationError, never>;
153
1091
 
1092
+ /**
1093
+ * Configuration required to authenticate as a GitHub App installation.
1094
+ *
1095
+ * @see {@link GitHubAppAuth}
1096
+ * @public
1097
+ */
154
1098
  export declare interface GitHubAppAuthConfig {
155
1099
  readonly appId: string;
156
1100
  readonly privateKey: string;
157
1101
  readonly installationId?: number;
158
1102
  }
159
1103
 
1104
+ /**
1105
+ * Provides an {@link OctokitInstance} by auto-detecting the available
1106
+ * authentication credentials from environment variables at layer construction
1107
+ * time.
1108
+ *
1109
+ * Credentials are evaluated in the following priority order:
1110
+ * 1. **GitHub App** — when both `GITHUB_APP_ID` and `GITHUB_APP_PRIVATE_KEY`
1111
+ * are set, authenticates as a GitHub App installation via {@link GitHubAppAuth}.
1112
+ * `GITHUB_APP_INSTALLATION_ID` is optional; if absent the first installation
1113
+ * is used automatically.
1114
+ * 2. **Personal Access Token** — when `GITHUB_PERSONAL_ACCESS_TOKEN` is set,
1115
+ * authenticates with that token.
1116
+ * 3. **`GITHUB_TOKEN`** — when `GITHUB_TOKEN` is set (e.g., the default token
1117
+ * injected in GitHub Actions), authenticates with that token.
1118
+ * 4. **Unauthenticated** — falls back to an unauthenticated Octokit instance
1119
+ * when no credentials are found (lower rate limits apply).
1120
+ *
1121
+ * If both App and token credentials are present, App auth takes precedence and
1122
+ * a warning is logged. This layer fails with `AuthenticationError` only when
1123
+ * App credentials are present but the App authentication itself fails.
1124
+ *
1125
+ * Use this layer as the default auth strategy in most applications. Switch to
1126
+ * {@link GitHubTokenAuth} or {@link GitHubAppAuth} when you need explicit control
1127
+ * over which credential source is used.
1128
+ *
1129
+ * @example
1130
+ * ```ts
1131
+ * import { GitHubClientLive, GitHubAutoAuth } from "runtime-resolver";
1132
+ * import { Layer } from "effect";
1133
+ *
1134
+ * const GitHubLayer = GitHubClientLive.pipe(Layer.provide(GitHubAutoAuth));
1135
+ * ```
1136
+ *
1137
+ * @see {@link OctokitInstance}
1138
+ * @see {@link GitHubTokenAuth}
1139
+ * @see {@link GitHubAppAuth}
1140
+ * @public
1141
+ */
160
1142
  export declare const GitHubAutoAuth: Layer.Layer<OctokitInstance, AuthenticationError>;
161
1143
 
162
1144
  /**
163
- * Service interface for GitHub API operations.
1145
+ * Service interface for GitHub REST API operations used by the version
1146
+ * fetcher services.
1147
+ *
1148
+ * Abstracts over pagination and authentication so that higher-level services
1149
+ * such as {@link BunVersionFetcher}, {@link DenoVersionFetcher}, and
1150
+ * {@link NodeScheduleFetcher} remain independent of the underlying HTTP
1151
+ * transport. The live implementation is provided by {@link GitHubClientLive}
1152
+ * and requires an {@link OctokitInstance} in its dependency graph.
1153
+ *
1154
+ * @see {@link GitHubClientLive}
1155
+ * @see {@link OctokitInstance}
1156
+ * @see {@link ListOptions}
1157
+ *
1158
+ * @public
164
1159
  */
165
1160
  export declare interface GitHubClient {
1161
+ /**
1162
+ * Lists all git tags for the given repository, automatically paginating
1163
+ * through results according to `options`.
1164
+ *
1165
+ * Fails with:
1166
+ * - {@link AuthenticationError} — missing or invalid GitHub credentials.
1167
+ * - {@link NetworkError} — the upstream request could not be completed.
1168
+ * - {@link RateLimitError} — the GitHub API rate limit was exceeded.
1169
+ * - {@link ParseError} — a response page could not be decoded.
1170
+ *
1171
+ * @param owner - GitHub organisation or user name (e.g. `"oven-sh"`).
1172
+ * @param repo - Repository name (e.g. `"bun"`).
1173
+ * @param options - Optional pagination controls.
1174
+ */
166
1175
  readonly listTags: (owner: string, repo: string, options?: ListOptions) => Effect.Effect<ReadonlyArray<GitHubTag>, AuthenticationError | NetworkError | RateLimitError | ParseError>;
1176
+ /**
1177
+ * Lists all GitHub Releases for the given repository, automatically
1178
+ * paginating through results according to `options`.
1179
+ *
1180
+ * Fails with:
1181
+ * - {@link AuthenticationError} — missing or invalid GitHub credentials.
1182
+ * - {@link NetworkError} — the upstream request could not be completed.
1183
+ * - {@link RateLimitError} — the GitHub API rate limit was exceeded.
1184
+ * - {@link ParseError} — a response page could not be decoded.
1185
+ *
1186
+ * @param owner - GitHub organisation or user name (e.g. `"denoland"`).
1187
+ * @param repo - Repository name (e.g. `"deno"`).
1188
+ * @param options - Optional pagination controls.
1189
+ */
167
1190
  readonly listReleases: (owner: string, repo: string, options?: ListOptions) => Effect.Effect<ReadonlyArray<GitHubRelease>, AuthenticationError | NetworkError | RateLimitError | ParseError>;
1191
+ /**
1192
+ * Fetches arbitrary JSON from `url` and decodes it with the provided
1193
+ * schema, returning the typed result.
1194
+ *
1195
+ * Useful for endpoints outside the standard Octokit REST methods (e.g.
1196
+ * raw GitHub content URLs).
1197
+ *
1198
+ * Fails with:
1199
+ * - {@link NetworkError} — the upstream request could not be completed.
1200
+ * - {@link ParseError} — the response body could not be decoded by `schema`.
1201
+ *
1202
+ * @typeParam A - The decoded output type produced by `schema`.
1203
+ * @param url - Fully qualified URL to fetch.
1204
+ * @param schema - An object with a `decode` method compatible with
1205
+ * Effect Schema's `decodeUnknown`.
1206
+ */
168
1207
  readonly getJson: <A>(url: string, schema: {
169
1208
  readonly decode: (input: unknown) => Effect.Effect<A, ParseError>;
170
1209
  }) => Effect.Effect<A, NetworkError | ParseError>;
171
1210
  }
172
1211
 
173
1212
  /**
174
- * @internal Uses GenericTag — see BunResolver.ts for rationale.
1213
+ * Service tag and companion object for {@link GitHubClient}.
1214
+ *
1215
+ * Acts as both the TypeScript service interface and the Effect dependency tag
1216
+ * used for dependency injection (companion object pattern). Yield this tag
1217
+ * inside `Effect.gen` to access the client implementation provided by
1218
+ * {@link GitHubClientLive}.
1219
+ *
1220
+ * @example
1221
+ * ```typescript
1222
+ * import type { GitHubTag } from "runtime-resolver";
1223
+ * import { GitHubClient, GitHubClientLive, GitHubAutoAuth } from "runtime-resolver";
1224
+ * import { Effect } from "effect";
1225
+ *
1226
+ * const program = Effect.gen(function* () {
1227
+ * const client = yield* GitHubClient;
1228
+ * const tags = yield* client.listTags("oven-sh", "bun", { perPage: 100 });
1229
+ * console.log(`Found ${tags.length} tags`);
1230
+ * });
1231
+ *
1232
+ * Effect.runPromise(
1233
+ * program.pipe(Effect.provide(GitHubClientLive), Effect.provide(GitHubAutoAuth)),
1234
+ * );
1235
+ * ```
1236
+ *
1237
+ * @see {@link GitHubClientLive}
1238
+ * @see {@link OctokitInstance}
1239
+ *
1240
+ * @public
175
1241
  */
176
1242
  export declare const GitHubClient: Context.Tag<GitHubClient, GitHubClient>;
177
1243
 
1244
+ /**
1245
+ * Provides the {@link GitHubClient} service backed by an {@link OctokitInstance}.
1246
+ *
1247
+ * This layer wraps an Octokit instance and exposes a typed, Effect-native API
1248
+ * for listing GitHub releases, listing tags, and fetching arbitrary JSON from
1249
+ * URLs. HTTP errors are mapped to typed domain errors (`NetworkError`,
1250
+ * `RateLimitError`, `AuthenticationError`) so they can be handled precisely in
1251
+ * calling code.
1252
+ *
1253
+ * Pair this layer with one of the auth layers to form a complete GitHub
1254
+ * integration stack:
1255
+ * - {@link GitHubAutoAuth} — auto-detects auth from environment variables
1256
+ * - {@link GitHubTokenAuth} — authenticates using a personal access token
1257
+ * - {@link GitHubAppAuth} — authenticates as a GitHub App installation
1258
+ *
1259
+ * @see {@link GitHubClient}
1260
+ * @see {@link OctokitInstance}
1261
+ * @see {@link GitHubAutoAuth}
1262
+ * @public
1263
+ */
178
1264
  export declare const GitHubClientLive: Layer.Layer<GitHubClient, never, OctokitInstance>;
179
1265
 
1266
+ /**
1267
+ * A single release entry as returned by the GitHub REST API
1268
+ * (`GET /repos/{owner}/{repo}/releases`).
1269
+ *
1270
+ * Consumed by {@link GitHubClient} when fetching Deno releases that carry
1271
+ * publication timestamps.
1272
+ *
1273
+ * @see {@link GitHubClient}
1274
+ * @see {@link GitHubReleaseList}
1275
+ *
1276
+ * @public
1277
+ */
180
1278
  export declare const GitHubRelease: Schema.Struct<{
181
1279
  tag_name: typeof Schema.String;
182
1280
  name: Schema.NullOr<typeof Schema.String>;
@@ -185,8 +1283,26 @@ export declare const GitHubRelease: Schema.Struct<{
185
1283
  published_at: Schema.NullOr<typeof Schema.String>;
186
1284
  }>;
187
1285
 
1286
+ /**
1287
+ * A single release entry as returned by the GitHub REST API.
1288
+ *
1289
+ * @see {@link GitHubClient}
1290
+ *
1291
+ * @public
1292
+ */
188
1293
  export declare type GitHubRelease = typeof GitHubRelease.Type;
189
1294
 
1295
+ /**
1296
+ * A single tag entry as returned by the GitHub REST API
1297
+ * (`GET /repos/{owner}/{repo}/tags`).
1298
+ *
1299
+ * Consumed by {@link GitHubClient} when fetching Bun and Deno release tags.
1300
+ *
1301
+ * @see {@link GitHubClient}
1302
+ * @see {@link GitHubTagList}
1303
+ *
1304
+ * @public
1305
+ */
190
1306
  export declare const GitHubTag: Schema.Struct<{
191
1307
  name: typeof Schema.String;
192
1308
  zipball_url: typeof Schema.String;
@@ -198,87 +1314,972 @@ export declare const GitHubTag: Schema.Struct<{
198
1314
  node_id: typeof Schema.String;
199
1315
  }>;
200
1316
 
1317
+ /**
1318
+ * A single tag entry as returned by the GitHub REST API.
1319
+ *
1320
+ * @see {@link GitHubClient}
1321
+ *
1322
+ * @public
1323
+ */
201
1324
  export declare type GitHubTag = typeof GitHubTag.Type;
202
1325
 
203
- export declare const GitHubTokenAuth: Layer.Layer<OctokitInstance>;
1326
+ /**
1327
+ * Provides an {@link OctokitInstance} authenticated with a GitHub personal
1328
+ * access token read from the environment at layer construction time.
1329
+ *
1330
+ * The token is resolved by checking, in order:
1331
+ * 1. `GITHUB_PERSONAL_ACCESS_TOKEN` environment variable
1332
+ * 2. `GITHUB_TOKEN` environment variable
1333
+ *
1334
+ * If neither variable is set the layer constructs an unauthenticated Octokit
1335
+ * instance (lower rate limits apply). This layer never fails.
1336
+ *
1337
+ * Use {@link GitHubTokenAuthFromToken} when you have the token value available
1338
+ * programmatically at construction time.
1339
+ *
1340
+ * @see {@link OctokitInstance}
1341
+ * @see {@link GitHubTokenAuthFromToken}
1342
+ * @see {@link GitHubAutoAuth}
1343
+ * @see {@link GitHubAppAuth}
1344
+ * @public
1345
+ */
1346
+ export declare const GitHubTokenAuth: Layer.Layer<OctokitInstance>;
1347
+
1348
+ /**
1349
+ * Provides an {@link OctokitInstance} authenticated with the given personal
1350
+ * access token string.
1351
+ *
1352
+ * Use this variant when the token is available as a value in your program
1353
+ * rather than read from an environment variable. The resulting layer never
1354
+ * fails.
1355
+ *
1356
+ * @param token - A GitHub personal access token string.
1357
+ *
1358
+ * @see {@link OctokitInstance}
1359
+ * @see {@link GitHubTokenAuth}
1360
+ * @see {@link GitHubAutoAuth}
1361
+ * @see {@link GitHubAppAuth}
1362
+ * @public
1363
+ */
1364
+ export declare const GitHubTokenAuthFromToken: (token: string) => Layer.Layer<OctokitInstance, never, never>;
1365
+
1366
+ /**
1367
+ * Controls the granularity at which matching versions are grouped when a
1368
+ * resolver returns multiple results.
1369
+ *
1370
+ * - `"latest"` — return only the single highest matching version.
1371
+ * - `"minor"` — return the latest patch for every minor line.
1372
+ * - `"patch"` — return every individual patch release.
1373
+ *
1374
+ * @public
1375
+ */
1376
+ export declare const Increments: Schema.Literal<["latest", "minor", "patch"]>;
1377
+
1378
+ /**
1379
+ * Controls the granularity at which matching versions are grouped.
1380
+ *
1381
+ * @public
1382
+ */
1383
+ export declare type Increments = typeof Increments.Type;
1384
+
1385
+ /**
1386
+ * Raised when a caller-supplied value fails validation.
1387
+ *
1388
+ * This error is produced at the boundary where user-provided inputs (such as
1389
+ * version constraint strings or runtime identifiers) are decoded against their
1390
+ * expected schemas. The `field` identifies which input was invalid, `value`
1391
+ * carries the raw string that was rejected, and `message` explains the
1392
+ * specific violation.
1393
+ *
1394
+ * @example
1395
+ * ```typescript
1396
+ * import { Effect } from "effect";
1397
+ * import type { InvalidInputError } from "./InvalidInputError.js";
1398
+ * import { resolveNode } from "../resolvers/node.js";
1399
+ *
1400
+ * const program = resolveNode("not-a-semver-range").pipe(
1401
+ * Effect.catchTag("InvalidInputError", (err: InvalidInputError) =>
1402
+ * Effect.logError(
1403
+ * `Invalid value for "${err.field}": ${err.value} — ${err.message}`
1404
+ * ).pipe(Effect.andThen(Effect.fail(err)))
1405
+ * )
1406
+ * );
1407
+ * ```
1408
+ *
1409
+ * @public
1410
+ */
1411
+ export declare class InvalidInputError extends InvalidInputErrorBase<{
1412
+ readonly field: string;
1413
+ readonly value: string;
1414
+ readonly message: string;
1415
+ }> {
1416
+ }
1417
+
1418
+ /**
1419
+ * @internal
1420
+ * Exported for declaration bundling — see AuthenticationError.ts for details.
1421
+ *
1422
+ * @privateRemarks
1423
+ * This base constant must remain a named export so that api-extractor can
1424
+ * resolve the extends clause of {@link InvalidInputError} to a stable
1425
+ * declaration. Without it the bundled `.d.ts` would contain an anonymous
1426
+ * `_base` symbol that cannot be referenced by downstream consumers.
1427
+ */
1428
+ export declare const InvalidInputErrorBase: new <A extends Record<string, any> = {}>(args: Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => YieldableError & {
1429
+ readonly _tag: "InvalidInputError";
1430
+ } & Readonly<A>;
1431
+
1432
+ /**
1433
+ * Pagination options shared by all list operations on {@link GitHubClient}.
1434
+ *
1435
+ * @see {@link GitHubClient}
1436
+ *
1437
+ * @public
1438
+ */
1439
+ export declare interface ListOptions {
1440
+ /**
1441
+ * Number of items to request per page. Defaults to the GitHub API maximum
1442
+ * of `100` when omitted.
1443
+ */
1444
+ readonly perPage?: number;
1445
+ /**
1446
+ * Maximum number of pages to fetch. When omitted all pages are fetched
1447
+ * until the API returns an empty result set.
1448
+ */
1449
+ readonly pages?: number;
1450
+ }
1451
+
1452
+ /**
1453
+ * Raised when an HTTP request to an upstream API fails.
1454
+ *
1455
+ * This error is produced when a fetch to a runtime release API (e.g. the
1456
+ * Node.js release index, the Deno release feed, or the Bun GitHub releases
1457
+ * endpoint) either does not receive a response or receives a non-successful
1458
+ * HTTP status code. When a status code is available it is included in the
1459
+ * `status` field; for connection-level failures (timeouts, DNS errors) that
1460
+ * field is absent.
1461
+ *
1462
+ * @example
1463
+ * ```typescript
1464
+ * import { Effect } from "effect";
1465
+ * import type { NetworkError } from "./NetworkError.js";
1466
+ * import { resolveNode } from "../resolvers/node.js";
1467
+ *
1468
+ * const program = resolveNode(">=20").pipe(
1469
+ * Effect.catchTag("NetworkError", (err: NetworkError) =>
1470
+ * Effect.logError(
1471
+ * `Request to ${err.url} failed${err.status != null ? ` with status ${err.status}` : ""}: ${err.message}`
1472
+ * ).pipe(Effect.andThen(Effect.fail(err)))
1473
+ * )
1474
+ * );
1475
+ * ```
1476
+ *
1477
+ * @public
1478
+ */
1479
+ export declare class NetworkError extends NetworkErrorBase<{
1480
+ readonly url: string;
1481
+ readonly status?: number;
1482
+ readonly message: string;
1483
+ }> {
1484
+ }
1485
+
1486
+ /**
1487
+ * @internal
1488
+ * Exported for declaration bundling — see AuthenticationError.ts for details.
1489
+ *
1490
+ * @privateRemarks
1491
+ * This base constant must remain a named export so that api-extractor can
1492
+ * resolve the extends clause of {@link NetworkError} to a stable declaration.
1493
+ * Without it the bundled `.d.ts` would contain an anonymous `_base` symbol
1494
+ * that cannot be referenced by downstream consumers.
1495
+ */
1496
+ export declare const NetworkErrorBase: new <A extends Record<string, any> = {}>(args: Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => YieldableError & {
1497
+ readonly _tag: "NetworkError";
1498
+ } & Readonly<A>;
1499
+
1500
+ /**
1501
+ * Lifecycle phase of a Node.js major release line, derived from the official
1502
+ * Node.js release schedule.
1503
+ *
1504
+ * - `"current"` — actively receiving features and bug fixes.
1505
+ * - `"active-lts"` — Long-Term Support; stable, receiving only bug and
1506
+ * security fixes.
1507
+ * - `"maintenance-lts"` — critical security fixes only.
1508
+ * - `"end-of-life"` — no longer maintained.
1509
+ *
1510
+ * @see {@link NodeRelease.phase}
1511
+ *
1512
+ * @public
1513
+ */
1514
+ export declare const NodePhase: Schema.Literal<["current", "active-lts", "maintenance-lts", "end-of-life"]>;
1515
+
1516
+ /**
1517
+ * Lifecycle phase of a Node.js major release line.
1518
+ *
1519
+ * @see {@link NodeRelease.phase}
1520
+ *
1521
+ * @public
1522
+ */
1523
+ export declare type NodePhase = typeof NodePhase.Type;
1524
+
1525
+ /**
1526
+ * A Node.js release entry with parsed version, bundled npm version,
1527
+ * publication date, and a shared reference to the Node.js release schedule.
1528
+ *
1529
+ * `NodeRelease` is a plain class rather than a `Data.TaggedClass` because it
1530
+ * holds a `Ref<NodeSchedule>` — mutable Effect references break structural
1531
+ * equality semantics, making `Data.TaggedClass` unsuitable here. The
1532
+ * `_tag` field is set manually to preserve tagged-union compatibility.
1533
+ *
1534
+ * Use {@link NodeRelease.fromInput} to construct instances; call
1535
+ * {@link NodeRelease.phase} or {@link NodeRelease.lts} to query the lifecycle
1536
+ * state of the release at a given point in time.
1537
+ *
1538
+ * @see {@link NodeReleaseCache}
1539
+ * @see {@link NodeResolver}
1540
+ * @see {@link NodeSchedule}
1541
+ *
1542
+ * @example
1543
+ * ```typescript
1544
+ * import { Effect, Ref } from "effect";
1545
+ * import type { NodePhase } from "./common.js";
1546
+ * import type { NodeRelease } from "./node-release.js";
1547
+ * import { NodeRelease as NodeReleaseClass } from "./node-release.js";
1548
+ * import { NodeSchedule } from "./node-schedule.js";
1549
+ *
1550
+ * const program = Effect.gen(function* () {
1551
+ * const schedule = NodeSchedule.fromData({
1552
+ * v20: { start: "2023-04-18", lts: "2023-10-24", end: "2026-04-30" },
1553
+ * });
1554
+ * const scheduleRef = yield* Ref.make(schedule);
1555
+ *
1556
+ * const release: NodeRelease = yield* NodeReleaseClass.fromInput(
1557
+ * { version: "v20.0.0", npm: "9.6.4", date: "2023-04-18" },
1558
+ * scheduleRef,
1559
+ * );
1560
+ *
1561
+ * const phase: NodePhase | null = yield* release.phase();
1562
+ * const isLts: boolean = yield* release.lts();
1563
+ * console.log(phase); // e.g. "active-lts"
1564
+ * console.log(isLts); // true
1565
+ * });
1566
+ *
1567
+ * Effect.runPromise(program);
1568
+ * ```
1569
+ *
1570
+ * @public
1571
+ */
1572
+ export declare class NodeRelease {
1573
+ readonly version: SemVer.SemVer;
1574
+ readonly npm: SemVer.SemVer;
1575
+ readonly date: DateTime.DateTime;
1576
+ readonly scheduleRef: Ref.Ref<NodeSchedule>;
1577
+ readonly _tag: "NodeRelease";
1578
+ constructor(version: SemVer.SemVer, npm: SemVer.SemVer, date: DateTime.DateTime, scheduleRef: Ref.Ref<NodeSchedule>);
1579
+ /**
1580
+ * Determine this release's lifecycle phase at the given point in time.
1581
+ *
1582
+ * Reads the current value of the shared `scheduleRef` and delegates to
1583
+ * {@link NodeSchedule.phaseFor}. Defaults to the current wall-clock time
1584
+ * when `now` is omitted.
1585
+ *
1586
+ * @param now - Optional reference time; defaults to `new Date()`.
1587
+ * @returns An `Effect` that resolves to a {@link NodePhase} string, or
1588
+ * `null` when the major version is not present in the schedule or has
1589
+ * not yet been released.
1590
+ */
1591
+ phase(now?: DateTime.DateTime): Effect.Effect<NodePhase | null>;
1592
+ /**
1593
+ * Whether this release is currently in Long-Term Support.
1594
+ *
1595
+ * Returns `true` for both `"active-lts"` and `"maintenance-lts"` phases.
1596
+ * Defaults to the current wall-clock time when `now` is omitted.
1597
+ *
1598
+ * @param now - Optional reference time; defaults to `new Date()`.
1599
+ */
1600
+ lts(now?: DateTime.DateTime): Effect.Effect<boolean>;
1601
+ /**
1602
+ * Create a {@link NodeRelease} from lean string inputs.
1603
+ *
1604
+ * Parses `input.version` and `input.npm` via `SemVer.fromString` and
1605
+ * constructs the publication date with `DateTime.unsafeMake`.
1606
+ *
1607
+ * @param input - A {@link NodeReleaseInput} object with raw string fields.
1608
+ * @param scheduleRef - A shared `Ref<NodeSchedule>` injected by the cache layer.
1609
+ * @returns An `Effect` that succeeds with a `NodeRelease` or fails with an
1610
+ * `InvalidVersionError` when either version string cannot be parsed.
1611
+ */
1612
+ static fromInput(input: NodeReleaseInput, scheduleRef: Ref.Ref<NodeSchedule>): Effect.Effect<NodeRelease, InvalidVersionError>;
1613
+ }
1614
+
1615
+ /**
1616
+ * Node.js-specific cache service that extends {@link RuntimeCache} with
1617
+ * additional operations for lifecycle schedule data and LTS / current
1618
+ * release filtering.
1619
+ *
1620
+ * Unlike the Bun and Deno equivalents this interface is not a plain type alias
1621
+ * — it adds four Node-specific methods on top of the generic cache contract.
1622
+ *
1623
+ * @see {@link NodeRelease}
1624
+ * @see {@link NodeScheduleData}
1625
+ * @see {@link RuntimeCache}
1626
+ * @see {@link AutoNodeCacheLive}
1627
+ * @see {@link FreshNodeCacheLive}
1628
+ * @see {@link OfflineNodeCacheLive}
1629
+ *
1630
+ * @example
1631
+ * ```typescript
1632
+ * import type { NodeRelease } from "runtime-resolver";
1633
+ * import { NodeReleaseCache, OfflineNodeCacheLive } from "runtime-resolver";
1634
+ * import { Effect } from "effect";
1635
+ *
1636
+ * const program = Effect.gen(function* () {
1637
+ * const cache = yield* NodeReleaseCache;
1638
+ * const lts = yield* cache.ltsReleases();
1639
+ * for (const release of lts) {
1640
+ * console.log(release.version.toString(), release.phase);
1641
+ * }
1642
+ * });
1643
+ *
1644
+ * Effect.runPromise(program.pipe(Effect.provide(OfflineNodeCacheLive)));
1645
+ * ```
1646
+ *
1647
+ * @public
1648
+ */
1649
+ export declare interface NodeReleaseCache extends RuntimeCache<NodeRelease> {
1650
+ /**
1651
+ * Merges Node.js release-schedule data into the cache, updating lifecycle
1652
+ * phase information (LTS dates, end-of-life dates, etc.) for every major
1653
+ * release line.
1654
+ *
1655
+ * @param data - Parsed schedule payload, typically fetched via
1656
+ * {@link NodeScheduleFetcher}.
1657
+ *
1658
+ * @see {@link NodeScheduleData}
1659
+ */
1660
+ readonly updateSchedule: (data: NodeScheduleData) => Effect.Effect<void>;
1661
+ /**
1662
+ * Populates the cache from raw {@link NodeReleaseInput} records (as
1663
+ * returned by the Node.js release index JSON) rather than from already-
1664
+ * decoded {@link NodeRelease} objects.
1665
+ *
1666
+ * Prefer this over `load` when consuming data directly from the Node.js
1667
+ * distribution server.
1668
+ *
1669
+ * @param inputs - An array of un-decoded release index entries.
1670
+ *
1671
+ * @see {@link NodeReleaseInput}
1672
+ */
1673
+ readonly loadFromInputs: (inputs: ReadonlyArray<NodeReleaseInput>) => Effect.Effect<void>;
1674
+ /**
1675
+ * Returns all cached releases whose lifecycle phase is `"active-lts"` or
1676
+ * `"maintenance-lts"` as of the given point in time.
1677
+ *
1678
+ * When `now` is omitted the current wall-clock time is used.
1679
+ *
1680
+ * @param now - Optional reference timestamp for phase evaluation.
1681
+ *
1682
+ * @see {@link NodeRelease}
1683
+ */
1684
+ readonly ltsReleases: (now?: DateTime.DateTime) => Effect.Effect<ReadonlyArray<NodeRelease>>;
1685
+ /**
1686
+ * Returns all cached releases whose lifecycle phase is `"current"` as of
1687
+ * the given point in time.
1688
+ *
1689
+ * When `now` is omitted the current wall-clock time is used.
1690
+ *
1691
+ * @param now - Optional reference timestamp for phase evaluation.
1692
+ *
1693
+ * @see {@link NodeRelease}
1694
+ */
1695
+ readonly currentReleases: (now?: DateTime.DateTime) => Effect.Effect<ReadonlyArray<NodeRelease>>;
1696
+ }
1697
+
1698
+ /**
1699
+ * Service tag and companion object for {@link NodeReleaseCache}.
1700
+ *
1701
+ * Acts as both the TypeScript service interface and the Effect dependency tag
1702
+ * used for dependency injection (companion object pattern). Yield this tag
1703
+ * inside `Effect.gen` to access the cache implementation supplied by one of
1704
+ * the Node cache layers.
1705
+ *
1706
+ * @see {@link AutoNodeCacheLive}
1707
+ * @see {@link FreshNodeCacheLive}
1708
+ * @see {@link OfflineNodeCacheLive}
1709
+ *
1710
+ * @public
1711
+ */
1712
+ export declare const NodeReleaseCache: Context.Tag<NodeReleaseCache, NodeReleaseCache>;
1713
+
1714
+ /**
1715
+ * Low-level layer that constructs the {@link NodeReleaseCache} service backed by
1716
+ * a `SemVerVersionCache` instance from `semver-effect`.
1717
+ *
1718
+ * This layer is more complex than the Bun and Deno equivalents because it manages
1719
+ * additional LTS schedule state via an internal `Ref`. The schedule is stored
1720
+ * separately from the version list so that calling `updateSchedule` can
1721
+ * transparently rebuild all release objects with the latest phase information
1722
+ * without reloading data from the network.
1723
+ *
1724
+ * This layer wires up the cache storage but does not populate it with any
1725
+ * release data. Callers are responsible for calling `updateSchedule` and
1726
+ * `loadFromInputs` after construction. In normal usage you should prefer the
1727
+ * higher-level cache strategy layers instead of using this layer directly:
1728
+ * - {@link AutoNodeCacheLive} — API with fallback to bundled defaults
1729
+ * - {@link FreshNodeCacheLive} — API only, fails if unavailable
1730
+ * - {@link OfflineNodeCacheLive} — bundled defaults only
1731
+ *
1732
+ * @see {@link NodeReleaseCache}
1733
+ * @see {@link createRuntimeCache}
1734
+ * @internal
1735
+ */
1736
+ export declare const NodeReleaseCacheLive: Layer.Layer<NodeReleaseCache, never, VersionCache>;
1737
+
1738
+ /**
1739
+ * Lean input schema used to construct {@link NodeRelease} instances from
1740
+ * bundled default data files and the Node.js dist API response.
1741
+ *
1742
+ * The `version` and `npm` fields are raw version strings validated by
1743
+ * `SemVer.fromString`; `date` is a date string accepted by
1744
+ * `DateTime.unsafeMake`.
1745
+ *
1746
+ * @see {@link NodeRelease.fromInput}
1747
+ * @see {@link NodeVersionFetcher}
1748
+ *
1749
+ * @public
1750
+ */
1751
+ export declare const NodeReleaseInput: Schema.Struct<{
1752
+ version: typeof Schema.String;
1753
+ npm: typeof Schema.String;
1754
+ date: typeof Schema.String;
1755
+ }>;
1756
+
1757
+ /**
1758
+ * Lean input type used to construct {@link NodeRelease} instances.
1759
+ *
1760
+ * @see {@link NodeRelease.fromInput}
1761
+ *
1762
+ * @public
1763
+ */
1764
+ export declare type NodeReleaseInput = typeof NodeReleaseInput.Type;
1765
+
1766
+ /**
1767
+ * Service interface for resolving Node.js runtime versions against the cached
1768
+ * release index.
1769
+ *
1770
+ * Unlike {@link BunResolver} and {@link DenoResolver}, this service is
1771
+ * lifecycle-aware: results can be filtered by {@link NodePhase} and evaluated
1772
+ * against a specific reference date.
1773
+ *
1774
+ * @see {@link NodeResolverLive}
1775
+ * @see {@link resolveNode}
1776
+ * @see {@link NodeResolverOptions}
1777
+ * @see {@link ResolvedVersions}
1778
+ *
1779
+ * @public
1780
+ */
1781
+ export declare interface NodeResolver {
1782
+ /**
1783
+ * Resolves Node.js versions according to `options` and returns a
1784
+ * {@link ResolvedVersions} object containing the matching version list,
1785
+ * the latest version string, optional LTS version string, and the data
1786
+ * source indicator.
1787
+ *
1788
+ * Fails with {@link NodeResolverError} when no version can be resolved and
1789
+ * no `defaultVersion` was provided.
1790
+ *
1791
+ * @param options - Optional resolution constraints.
1792
+ *
1793
+ * @see {@link NodeResolverOptions}
1794
+ */
1795
+ readonly resolve: (options?: NodeResolverOptions) => Effect.Effect<ResolvedVersions, NodeResolverError>;
1796
+ }
1797
+
1798
+ /**
1799
+ * Service tag and companion object for {@link NodeResolver}.
1800
+ *
1801
+ * Acts as both the TypeScript service interface and the Effect dependency tag
1802
+ * used for dependency injection (companion object pattern). Yield this tag
1803
+ * inside `Effect.gen` to obtain the resolver implementation provided by
1804
+ * {@link NodeResolverLive}.
1805
+ *
1806
+ * For a one-shot Promise-based API see {@link resolveNode}.
1807
+ *
1808
+ * @example
1809
+ * ```typescript
1810
+ * import type { ResolvedVersions } from "runtime-resolver";
1811
+ * import { NodeResolver, NodeResolverLive } from "runtime-resolver";
1812
+ * import { Effect } from "effect";
1813
+ *
1814
+ * const program = Effect.gen(function* () {
1815
+ * const resolver = yield* NodeResolver;
1816
+ * const result = yield* resolver.resolve({
1817
+ * semverRange: "^20.0.0",
1818
+ * phases: ["active-lts"],
1819
+ * increments: "minor",
1820
+ * });
1821
+ * console.log(result.latest);
1822
+ * console.log(result.lts);
1823
+ * });
1824
+ *
1825
+ * Effect.runPromise(program.pipe(Effect.provide(NodeResolverLive)));
1826
+ * ```
1827
+ *
1828
+ * @see {@link NodeResolverLive}
1829
+ * @see {@link resolveNode}
1830
+ *
1831
+ * @public
1832
+ */
1833
+ export declare const NodeResolver: Context.Tag<NodeResolver, NodeResolver>;
204
1834
 
205
- export declare const GitHubTokenAuthFromToken: (token: string) => Layer.Layer<OctokitInstance, never, never>;
1835
+ /**
1836
+ * Union of all typed errors that {@link NodeResolver.resolve} can fail with.
1837
+ *
1838
+ * @see {@link VersionNotFoundError}
1839
+ *
1840
+ * @public
1841
+ */
1842
+ export declare type NodeResolverError = VersionNotFoundError;
206
1843
 
207
- export declare const Increments: Schema.Literal<["latest", "minor", "patch"]>;
1844
+ /**
1845
+ * Provides the {@link NodeResolver} service backed by a {@link NodeReleaseCache}.
1846
+ *
1847
+ * This layer composes with any of the three Node cache strategy layers to form
1848
+ * a complete resolver stack. In addition to semver range filtering and increment
1849
+ * grouping, it supports filtering by Node.js release phase (e.g., `"current"`,
1850
+ * `"active-lts"`, `"maintenance-lts"`, `"end-of-life"`) and automatically
1851
+ * determines the current LTS version from the loaded schedule data.
1852
+ *
1853
+ * @example
1854
+ * ```ts
1855
+ * import {
1856
+ * NodeResolverLive, AutoNodeCacheLive,
1857
+ * NodeVersionFetcherLive, NodeScheduleFetcherLive,
1858
+ * GitHubClientLive, GitHubAutoAuth,
1859
+ * } from "runtime-resolver";
1860
+ * import { NodeResolver } from "runtime-resolver";
1861
+ * import { Effect, Layer } from "effect";
1862
+ *
1863
+ * const GitHubLayer = GitHubClientLive.pipe(Layer.provide(GitHubAutoAuth));
1864
+ * const FetchersLayer = Layer.merge(
1865
+ * NodeVersionFetcherLive.pipe(Layer.provide(GitHubLayer)),
1866
+ * NodeScheduleFetcherLive.pipe(Layer.provide(GitHubLayer)),
1867
+ * );
1868
+ * const CacheLayer = AutoNodeCacheLive.pipe(Layer.provide(FetchersLayer));
1869
+ * const ResolverLayer = NodeResolverLive.pipe(Layer.provide(CacheLayer));
1870
+ *
1871
+ * const program = Effect.gen(function* () {
1872
+ * const resolver = yield* NodeResolver;
1873
+ * // Resolve only active-LTS and maintenance-LTS releases in the ^20 range
1874
+ * const result = yield* resolver.resolve({
1875
+ * semverRange: "^20.0.0",
1876
+ * phases: ["active-lts", "maintenance-lts"],
1877
+ * });
1878
+ * console.log(result.lts);
1879
+ * });
1880
+ *
1881
+ * Effect.runPromise(program.pipe(Effect.provide(ResolverLayer)));
1882
+ * ```
1883
+ *
1884
+ * @see {@link NodeResolver}
1885
+ * @see {@link NodeReleaseCache}
1886
+ * @see {@link AutoNodeCacheLive}
1887
+ * @see {@link FreshNodeCacheLive}
1888
+ * @see {@link OfflineNodeCacheLive}
1889
+ * @public
1890
+ */
1891
+ export declare const NodeResolverLive: Layer.Layer<NodeResolver, never, NodeReleaseCache>;
208
1892
 
209
- export declare type Increments = typeof Increments.Type;
1893
+ /**
1894
+ * Options accepted by {@link NodeResolver.resolve}.
1895
+ *
1896
+ * All fields are optional; omitting them causes the resolver to fall back to
1897
+ * its built-in defaults (all lifecycle phases, latest stable release,
1898
+ * `"latest"` increment granularity, current wall-clock date).
1899
+ *
1900
+ * @see {@link NodeResolver}
1901
+ * @see {@link NodePhase}
1902
+ * @see {@link Increments}
1903
+ * @see {@link ResolvedVersions}
1904
+ *
1905
+ * @public
1906
+ */
1907
+ export declare interface NodeResolverOptions {
1908
+ /**
1909
+ * A semver range string used to constrain which Node.js releases are
1910
+ * considered (e.g. `"^20.0.0"` or `">=18.0.0 <22.0.0"`).
1911
+ *
1912
+ * When omitted all cached releases are eligible.
1913
+ */
1914
+ readonly semverRange?: string;
1915
+ /**
1916
+ * A fallback version string returned when no release satisfies
1917
+ * `semverRange`. Must be a valid semver version (e.g. `"20.0.0"`).
1918
+ *
1919
+ * When omitted and no match is found the effect fails with
1920
+ * {@link VersionNotFoundError}.
1921
+ */
1922
+ readonly defaultVersion?: string;
1923
+ /**
1924
+ * Restricts resolution to releases whose lifecycle phase is included in
1925
+ * this list. For example, passing `["active-lts"]` excludes `"current"`
1926
+ * and `"maintenance-lts"` releases from consideration.
1927
+ *
1928
+ * When omitted all phases are eligible.
1929
+ *
1930
+ * @see {@link NodePhase}
1931
+ */
1932
+ readonly phases?: ReadonlyArray<NodePhase>;
1933
+ /**
1934
+ * Controls the granularity of the version list included in
1935
+ * {@link ResolvedVersions.versions}.
1936
+ *
1937
+ * - `"latest"` — only the single highest matching version.
1938
+ * - `"minor"` — the latest patch for every minor line.
1939
+ * - `"patch"` — every individual patch release.
1940
+ *
1941
+ * Defaults to `"latest"` when omitted.
1942
+ *
1943
+ * @see {@link Increments}
1944
+ */
1945
+ readonly increments?: Increments;
1946
+ /**
1947
+ * Reference date used when evaluating Node.js lifecycle phases. Releases
1948
+ * whose LTS or end-of-life boundaries fall relative to this date are
1949
+ * classified accordingly.
1950
+ *
1951
+ * When omitted the current wall-clock date is used.
1952
+ */
1953
+ readonly date?: Date;
1954
+ }
210
1955
 
211
- export declare class InvalidInputError extends InvalidInputErrorBase<{
212
- readonly field: string;
213
- readonly value: string;
214
- readonly message: string;
1956
+ /**
1957
+ * Immutable snapshot of the Node.js release schedule.
1958
+ *
1959
+ * Holds a `ReadonlyArray<NodeScheduleEntry>` parsed from the raw
1960
+ * {@link NodeScheduleData} fetched by {@link NodeScheduleFetcher}. A single
1961
+ * `NodeSchedule` instance lives inside a `Ref<NodeSchedule>` that is shared
1962
+ * across all {@link NodeRelease} instances so that schedule updates propagate
1963
+ * without rebuilding the release cache.
1964
+ *
1965
+ * `NodeSchedule` extends `Data.TaggedClass` and therefore supports structural
1966
+ * equality — two schedules built from identical data compare as equal.
1967
+ *
1968
+ * @see {@link NodeRelease}
1969
+ * @see {@link NodeScheduleFetcher}
1970
+ *
1971
+ * @example
1972
+ * ```typescript
1973
+ * import { Effect } from "effect";
1974
+ * import type { NodePhase } from "./common.js";
1975
+ * import type { NodeScheduleData } from "./node-schedule.js";
1976
+ * import { NodeSchedule } from "./node-schedule.js";
1977
+ *
1978
+ * const data: NodeScheduleData = {
1979
+ * v20: {
1980
+ * start: "2023-04-18",
1981
+ * lts: "2023-10-24",
1982
+ * maintenance: "2024-10-22",
1983
+ * end: "2026-04-30",
1984
+ * codename: "Iron",
1985
+ * },
1986
+ * };
1987
+ *
1988
+ * const schedule = NodeSchedule.fromData(data);
1989
+ *
1990
+ * const program = schedule.phaseFor(20, new Date("2024-01-01") as any).pipe(
1991
+ * Effect.map((phase: NodePhase | null) => {
1992
+ * console.log(phase); // "active-lts"
1993
+ * }),
1994
+ * );
1995
+ *
1996
+ * Effect.runPromise(program);
1997
+ * ```
1998
+ *
1999
+ * @public
2000
+ */
2001
+ export declare class NodeSchedule extends NodeScheduleBase<{
2002
+ readonly entries: ReadonlyArray<NodeScheduleEntry>;
215
2003
  }> {
2004
+ /**
2005
+ * Parse raw {@link NodeScheduleData} into a `NodeSchedule` instance.
2006
+ *
2007
+ * Iterates over every key of the form `"vNN"`, converts ISO date strings
2008
+ * to `DateTime.DateTime`, and normalises missing codenames to `""`.
2009
+ *
2010
+ * @param data - The raw JSON object fetched from the Node.js release repo.
2011
+ * @returns A fully constructed, immutable `NodeSchedule`.
2012
+ */
2013
+ static fromData(data: NodeScheduleData): NodeSchedule;
2014
+ /**
2015
+ * Determine the current {@link NodePhase} for a given major version number
2016
+ * at the specified point in time.
2017
+ *
2018
+ * Returns `null` when the major is not present in the schedule or when
2019
+ * `now` is before the release's start date.
2020
+ *
2021
+ * @param major - The Node.js major version number (e.g. `20`).
2022
+ * @param now - The reference `DateTime` against which phases are evaluated.
2023
+ */
2024
+ phaseFor(major: number, now: DateTime.DateTime): Effect.Effect<NodePhase | null>;
2025
+ /**
2026
+ * Look up the {@link NodeScheduleEntry} for a given major version number.
2027
+ *
2028
+ * Returns `Option.none()` when the major is not present in the schedule.
2029
+ *
2030
+ * @param major - The Node.js major version number (e.g. `20`).
2031
+ */
2032
+ entryFor(major: number): Option.Option<NodeScheduleEntry>;
216
2033
  }
217
2034
 
218
2035
  /**
219
2036
  * @internal
220
- * Exported for declaration bundling — see AuthenticationError.ts for details.
2037
+ *
2038
+ * Exported for declaration bundling (api-extractor). When `export *`
2039
+ * re-exports a class whose `extends` expression is an inline call like
2040
+ * `Data.TaggedClass(...)`, TypeScript emits an un-nameable `_base` symbol in
2041
+ * the declaration file. Splitting the base into a named export gives the
2042
+ * bundler a stable reference.
2043
+ *
2044
+ * @privateRemarks
2045
+ * This base constant must remain a named export so that api-extractor can
2046
+ * resolve the extends clause of {@link NodeSchedule} to a stable declaration.
2047
+ * Without it the bundled `.d.ts` would contain an anonymous `_base` symbol
2048
+ * that cannot be referenced by downstream consumers.
221
2049
  */
222
- export declare const InvalidInputErrorBase: new <A extends Record<string, any> = {}>(args: Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => YieldableError & {
223
- readonly _tag: "InvalidInputError";
224
- } & Readonly<A>;
2050
+ export declare const NodeScheduleBase: new <A extends Record<string, any> = {}>(args: Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => Readonly<A> & {
2051
+ readonly _tag: "NodeSchedule";
2052
+ };
225
2053
 
226
- export declare interface ListOptions {
227
- readonly perPage?: number;
228
- readonly pages?: number;
229
- }
2054
+ /**
2055
+ * Raw schedule format as fetched from the Node.js Release GitHub repository
2056
+ * (`nodejs/Release`).
2057
+ *
2058
+ * This is the intermediate type between the {@link NodeScheduleFetcher} layer
2059
+ * and the {@link NodeSchedule} class. Date values are plain ISO strings; the
2060
+ * `NodeSchedule.fromData` factory converts them to `DateTime`.
2061
+ *
2062
+ * @see {@link NodeSchedule.fromData}
2063
+ * @see {@link NodeScheduleFetcher}
2064
+ *
2065
+ * @public
2066
+ */
2067
+ export declare type NodeScheduleData = Record<string, {
2068
+ readonly start: string;
2069
+ readonly lts?: string;
2070
+ readonly maintenance?: string;
2071
+ readonly end: string;
2072
+ readonly codename?: string;
2073
+ }>;
230
2074
 
231
- export declare class NetworkError extends NetworkErrorBase<{
232
- readonly url: string;
233
- readonly status?: number;
234
- readonly message: string;
235
- }> {
2075
+ /**
2076
+ * A single entry in the Node.js release schedule, representing one major
2077
+ * release line's lifecycle dates and optional LTS codename.
2078
+ *
2079
+ * All date fields have already been converted from ISO strings to Effect
2080
+ * `DateTime.DateTime` values by {@link NodeSchedule.fromData}.
2081
+ *
2082
+ * @see {@link NodeSchedule}
2083
+ * @see {@link NodeSchedule.phaseFor}
2084
+ *
2085
+ * @public
2086
+ */
2087
+ export declare interface NodeScheduleEntry {
2088
+ readonly major: number;
2089
+ readonly start: DateTime.DateTime;
2090
+ readonly lts: DateTime.DateTime | null;
2091
+ readonly maintenance: DateTime.DateTime | null;
2092
+ readonly end: DateTime.DateTime;
2093
+ readonly codename: string;
236
2094
  }
237
2095
 
238
2096
  /**
239
- * @internal
240
- * Exported for declaration bundling — see AuthenticationError.ts for details.
2097
+ * Service interface for fetching the official Node.js release schedule.
2098
+ *
2099
+ * The schedule maps each major release line to its LTS start date and
2100
+ * end-of-life date, and is used by {@link NodeReleaseCache} to assign
2101
+ * lifecycle phases to individual releases. The live implementation fetches
2102
+ * from the `nodejs/Release` GitHub repository via a plain HTTP request.
2103
+ *
2104
+ * @see {@link NodeScheduleFetcherLive}
2105
+ * @see {@link NodeReleaseCache}
2106
+ * @see {@link NodeScheduleData}
2107
+ * @see {@link NetworkError}
2108
+ * @see {@link ParseError}
2109
+ *
2110
+ * @public
241
2111
  */
242
- export declare const NetworkErrorBase: new <A extends Record<string, any> = {}>(args: Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => YieldableError & {
243
- readonly _tag: "NetworkError";
244
- } & Readonly<A>;
2112
+ export declare interface NodeScheduleFetcher {
2113
+ /**
2114
+ * Fetches and decodes the Node.js release schedule JSON, returning a
2115
+ * typed {@link NodeScheduleData} map keyed by major release line.
2116
+ *
2117
+ * Fails with:
2118
+ * - {@link NetworkError} — the upstream request could not be completed.
2119
+ * - {@link ParseError} — the response payload could not be decoded.
2120
+ */
2121
+ readonly fetch: () => Effect.Effect<NodeScheduleData, NetworkError | ParseError>;
2122
+ }
245
2123
 
246
- export declare const NodePhase: Schema.Literal<["current", "active-lts", "maintenance-lts", "end-of-life"]>;
2124
+ /**
2125
+ * Service tag and companion object for {@link NodeScheduleFetcher}.
2126
+ *
2127
+ * Acts as both the TypeScript service interface and the Effect dependency tag
2128
+ * used for dependency injection (companion object pattern). Yield this tag
2129
+ * inside `Effect.gen` to access the fetcher implementation provided by
2130
+ * {@link NodeScheduleFetcherLive}.
2131
+ *
2132
+ * @example
2133
+ * ```typescript
2134
+ * import type { NodeScheduleData } from "runtime-resolver";
2135
+ * import { NodeScheduleFetcher, NodeScheduleFetcherLive } from "runtime-resolver";
2136
+ * import { Effect } from "effect";
2137
+ *
2138
+ * const program = Effect.gen(function* () {
2139
+ * const fetcher = yield* NodeScheduleFetcher;
2140
+ * const schedule = yield* fetcher.fetch();
2141
+ * console.log(Object.keys(schedule).length, "major release lines");
2142
+ * });
2143
+ *
2144
+ * Effect.runPromise(program.pipe(Effect.provide(NodeScheduleFetcherLive)));
2145
+ * ```
2146
+ *
2147
+ * @see {@link NodeScheduleFetcherLive}
2148
+ *
2149
+ * @public
2150
+ */
2151
+ export declare const NodeScheduleFetcher: Context.Tag<NodeScheduleFetcher, NodeScheduleFetcher>;
247
2152
 
248
- export declare type NodePhase = typeof NodePhase.Type;
2153
+ /**
2154
+ * Provides the {@link NodeScheduleFetcher} service using the live Node.js release schedule.
2155
+ *
2156
+ * Fetches the Node.js release schedule from the raw GitHub content URL for
2157
+ * `nodejs/Release` (`schedule.json` on the `main` branch) using the HTTP client
2158
+ * on the provided {@link GitHubClient} service. The schedule data maps major
2159
+ * version codenames to their start, LTS, and end-of-life dates and is used by
2160
+ * {@link NodeReleaseCache} to compute per-release lifecycle phases.
2161
+ *
2162
+ * This layer is a required dependency of {@link AutoNodeCacheLive} and
2163
+ * {@link FreshNodeCacheLive}. It is not needed by {@link OfflineNodeCacheLive}.
2164
+ *
2165
+ * @see {@link NodeScheduleFetcher}
2166
+ * @see {@link GitHubClient}
2167
+ * @see {@link AutoNodeCacheLive}
2168
+ * @see {@link FreshNodeCacheLive}
2169
+ * @public
2170
+ */
2171
+ export declare const NodeScheduleFetcherLive: Layer.Layer<NodeScheduleFetcher, never, GitHubClient>;
249
2172
 
250
2173
  /**
251
- * Service interface for resolving Node.js runtime versions.
2174
+ * Service interface for fetching Node.js release data from the official
2175
+ * Node.js distribution index.
2176
+ *
2177
+ * Retrieves both the parsed semver version list and the raw
2178
+ * {@link NodeReleaseInput} records needed to populate a
2179
+ * {@link NodeReleaseCache}. The live implementation fetches from
2180
+ * `https://nodejs.org/dist/index.json` via a plain HTTP request — no GitHub
2181
+ * credentials are required.
2182
+ *
2183
+ * @see {@link NodeVersionFetcherLive}
2184
+ * @see {@link NodeReleaseCache}
2185
+ * @see {@link NetworkError}
2186
+ * @see {@link ParseError}
2187
+ *
2188
+ * @public
252
2189
  */
253
- export declare interface NodeResolver {
254
- readonly resolve: (options?: NodeResolverOptions) => Effect.Effect<ResolvedVersions, NodeResolverError>;
255
- readonly resolveVersion: (versionOrRange: string) => Effect.Effect<string, NodeResolverError>;
2190
+ export declare interface NodeVersionFetcher {
2191
+ /**
2192
+ * Fetches all available Node.js releases from the distribution index and
2193
+ * returns both the parsed semver version objects and the raw release input
2194
+ * records.
2195
+ *
2196
+ * Fails with:
2197
+ * - {@link NetworkError} — the upstream request could not be completed.
2198
+ * - {@link ParseError} — the response payload could not be decoded.
2199
+ */
2200
+ readonly fetch: () => Effect.Effect<{
2201
+ readonly versions: ReadonlyArray<SemVer.SemVer>;
2202
+ readonly inputs: ReadonlyArray<NodeReleaseInput>;
2203
+ }, NetworkError | ParseError>;
256
2204
  }
257
2205
 
258
2206
  /**
259
- * @internal Uses GenericTag — see BunResolver.ts for rationale.
2207
+ * Service tag and companion object for {@link NodeVersionFetcher}.
2208
+ *
2209
+ * Acts as both the TypeScript service interface and the Effect dependency tag
2210
+ * used for dependency injection (companion object pattern). Yield this tag
2211
+ * inside `Effect.gen` to access the fetcher implementation provided by
2212
+ * {@link NodeVersionFetcherLive}.
2213
+ *
2214
+ * @example
2215
+ * ```typescript
2216
+ * import type { NodeReleaseInput } from "runtime-resolver";
2217
+ * import { NodeVersionFetcher, NodeVersionFetcherLive } from "runtime-resolver";
2218
+ * import { Effect } from "effect";
2219
+ *
2220
+ * const program = Effect.gen(function* () {
2221
+ * const fetcher = yield* NodeVersionFetcher;
2222
+ * const { versions, inputs } = yield* fetcher.fetch();
2223
+ * console.log(`Fetched ${versions.length} Node.js releases`);
2224
+ * });
2225
+ *
2226
+ * Effect.runPromise(program.pipe(Effect.provide(NodeVersionFetcherLive)));
2227
+ * ```
2228
+ *
2229
+ * @see {@link NodeVersionFetcherLive}
2230
+ *
2231
+ * @public
260
2232
  */
261
- export declare const NodeResolver: Context.Tag<NodeResolver, NodeResolver>;
262
-
263
- export declare type NodeResolverError = AuthenticationError | NetworkError | ParseError | RateLimitError | VersionNotFoundError | InvalidInputError | FreshnessError | CacheError;
2233
+ export declare const NodeVersionFetcher: Context.Tag<NodeVersionFetcher, NodeVersionFetcher>;
264
2234
 
265
- export declare const NodeResolverLive: Layer.Layer<NodeResolver, never, GitHubClient | VersionCache>;
266
-
267
- export declare interface NodeResolverOptions {
268
- readonly semverRange?: string;
269
- readonly defaultVersion?: string;
270
- readonly phases?: ReadonlyArray<NodePhase>;
271
- readonly increments?: Increments;
272
- readonly date?: Date;
273
- readonly freshness?: Freshness;
274
- }
2235
+ /**
2236
+ * Provides the {@link NodeVersionFetcher} service using the live Node.js dist index.
2237
+ *
2238
+ * Fetches the full Node.js version list from `https://nodejs.org/dist/index.json`
2239
+ * using the HTTP client on the provided {@link GitHubClient} service. Version
2240
+ * strings are normalized by stripping the leading `v` before parsing as semver.
2241
+ * Entries with non-string `npm` fields (present in very old releases prior to
2242
+ * v0.6.3) are substituted with the sentinel version `"0.0.0"`.
2243
+ *
2244
+ * This layer is a required dependency of {@link AutoNodeCacheLive} and
2245
+ * {@link FreshNodeCacheLive}. It is not needed by {@link OfflineNodeCacheLive}.
2246
+ *
2247
+ * @see {@link NodeVersionFetcher}
2248
+ * @see {@link GitHubClient}
2249
+ * @see {@link AutoNodeCacheLive}
2250
+ * @see {@link FreshNodeCacheLive}
2251
+ * @public
2252
+ */
2253
+ export declare const NodeVersionFetcherLive: Layer.Layer<NodeVersionFetcher, never, GitHubClient>;
275
2254
 
276
2255
  /**
277
- * Service interface for Octokit REST API client.
2256
+ * Thin wrapper around the subset of Octokit's REST client used by
2257
+ * {@link GitHubClient}.
2258
+ *
2259
+ * Exposing only the repository methods actually consumed by this library
2260
+ * keeps the surface area narrow and makes the interface straightforward to
2261
+ * stub in tests without pulling in the full Octokit type.
2262
+ *
2263
+ * An `OctokitInstance` is provided to the dependency graph by one of the
2264
+ * three authentication layers: {@link GitHubTokenAuth}, {@link GitHubAppAuth},
2265
+ * or {@link GitHubAutoAuth}.
2266
+ *
2267
+ * @see {@link GitHubClientLive}
2268
+ * @see {@link GitHubTokenAuth}
2269
+ * @see {@link GitHubAppAuth}
2270
+ * @see {@link GitHubAutoAuth}
2271
+ *
2272
+ * @public
278
2273
  */
279
2274
  export declare interface OctokitInstance {
280
2275
  readonly rest: {
281
2276
  readonly repos: {
2277
+ /**
2278
+ * Lists git tags for a repository, corresponding to
2279
+ * `GET /repos/{owner}/{repo}/tags`.
2280
+ *
2281
+ * @param params - Owner, repo, and optional pagination parameters.
2282
+ */
282
2283
  readonly listTags: (params: {
283
2284
  owner: string;
284
2285
  repo: string;
@@ -287,6 +2288,12 @@ export declare interface OctokitInstance {
287
2288
  }) => Promise<{
288
2289
  data: Array<unknown>;
289
2290
  }>;
2291
+ /**
2292
+ * Lists GitHub Releases for a repository, corresponding to
2293
+ * `GET /repos/{owner}/{repo}/releases`.
2294
+ *
2295
+ * @param params - Owner, repo, and optional pagination parameters.
2296
+ */
290
2297
  readonly listReleases: (params: {
291
2298
  owner: string;
292
2299
  repo: string;
@@ -300,10 +2307,139 @@ export declare interface OctokitInstance {
300
2307
  }
301
2308
 
302
2309
  /**
303
- * @internal Uses GenericTag — see BunResolver.ts for rationale.
2310
+ * Service tag and companion object for {@link OctokitInstance}.
2311
+ *
2312
+ * Acts as both the TypeScript service interface and the Effect dependency tag
2313
+ * used for dependency injection (companion object pattern). This tag is
2314
+ * consumed internally by {@link GitHubClientLive} and is satisfied by
2315
+ * whichever authentication layer is provided — {@link GitHubTokenAuth},
2316
+ * {@link GitHubAppAuth}, or {@link GitHubAutoAuth}.
2317
+ *
2318
+ * Most application code should depend on {@link GitHubClient} rather than
2319
+ * this tag directly.
2320
+ *
2321
+ * @example
2322
+ * ```typescript
2323
+ * import { OctokitInstance, GitHubClientLive, GitHubAutoAuth } from "runtime-resolver";
2324
+ * import { Effect } from "effect";
2325
+ *
2326
+ * const program = Effect.gen(function* () {
2327
+ * const octokit = yield* OctokitInstance;
2328
+ * const response = await octokit.rest.repos.listTags({
2329
+ * owner: "oven-sh",
2330
+ * repo: "bun",
2331
+ * per_page: 10,
2332
+ * });
2333
+ * console.log(response.data.length);
2334
+ * });
2335
+ *
2336
+ * Effect.runPromise(program.pipe(Effect.provide(GitHubAutoAuth)));
2337
+ * ```
2338
+ *
2339
+ * @see {@link GitHubClientLive}
2340
+ * @see {@link GitHubTokenAuth}
2341
+ * @see {@link GitHubAppAuth}
2342
+ * @see {@link GitHubAutoAuth}
2343
+ *
2344
+ * @public
304
2345
  */
305
2346
  export declare const OctokitInstance: Context.Tag<OctokitInstance, OctokitInstance>;
306
2347
 
2348
+ /**
2349
+ * Provides the {@link BunReleaseCache} service using the "Offline" cache strategy.
2350
+ *
2351
+ * Uses only the bundled default Bun release data that was baked in at build time.
2352
+ * Makes no network requests and has no runtime dependencies. This layer always
2353
+ * succeeds immediately.
2354
+ *
2355
+ * Use this strategy in environments where network access is unavailable or
2356
+ * undesirable (e.g., air-gapped systems, hermetic test environments, or any
2357
+ * context where version data from the bundle is sufficient).
2358
+ *
2359
+ * The three Bun cache strategy layers are:
2360
+ * - {@link AutoBunCacheLive} — tries API first, falls back to bundled defaults on any error
2361
+ * - {@link FreshBunCacheLive} — requires live API data, fails with `FreshnessError` if unavailable
2362
+ * - `OfflineBunCacheLive` — uses only bundled defaults, no network I/O
2363
+ *
2364
+ * @see {@link BunReleaseCache}
2365
+ * @see {@link AutoBunCacheLive}
2366
+ * @see {@link FreshBunCacheLive}
2367
+ * @public
2368
+ */
2369
+ export declare const OfflineBunCacheLive: Layer.Layer<BunReleaseCache>;
2370
+
2371
+ /**
2372
+ * Provides the {@link DenoReleaseCache} service using the "Offline" cache strategy.
2373
+ *
2374
+ * Uses only the bundled default Deno release data that was baked in at build time.
2375
+ * Makes no network requests and has no runtime dependencies. This layer always
2376
+ * succeeds immediately.
2377
+ *
2378
+ * Use this strategy in environments where network access is unavailable or
2379
+ * undesirable (e.g., air-gapped systems, hermetic test environments, or any
2380
+ * context where version data from the bundle is sufficient).
2381
+ *
2382
+ * The three Deno cache strategy layers are:
2383
+ * - {@link AutoDenoCacheLive} — tries API first, falls back to bundled defaults on any error
2384
+ * - {@link FreshDenoCacheLive} — requires live API data, fails with `FreshnessError` if unavailable
2385
+ * - `OfflineDenoCacheLive` — uses only bundled defaults, no network I/O
2386
+ *
2387
+ * @see {@link DenoReleaseCache}
2388
+ * @see {@link AutoDenoCacheLive}
2389
+ * @see {@link FreshDenoCacheLive}
2390
+ * @public
2391
+ */
2392
+ export declare const OfflineDenoCacheLive: Layer.Layer<DenoReleaseCache>;
2393
+
2394
+ /**
2395
+ * Provides the {@link NodeReleaseCache} service using the "Offline" cache strategy.
2396
+ *
2397
+ * Uses only the bundled default Node.js release data and schedule data that were
2398
+ * baked in at build time. Makes no network requests and has no runtime
2399
+ * dependencies. This layer always succeeds immediately.
2400
+ *
2401
+ * Use this strategy in environments where network access is unavailable or
2402
+ * undesirable (e.g., air-gapped systems, hermetic test environments, or any
2403
+ * context where version data from the bundle is sufficient).
2404
+ *
2405
+ * The three Node cache strategy layers are:
2406
+ * - {@link AutoNodeCacheLive} — tries API first, falls back to bundled defaults on any error
2407
+ * - {@link FreshNodeCacheLive} — requires live API data, fails with `FreshnessError` if unavailable
2408
+ * - `OfflineNodeCacheLive` — uses only bundled defaults, no network I/O
2409
+ *
2410
+ * @see {@link NodeReleaseCache}
2411
+ * @see {@link AutoNodeCacheLive}
2412
+ * @see {@link FreshNodeCacheLive}
2413
+ * @public
2414
+ */
2415
+ export declare const OfflineNodeCacheLive: Layer.Layer<NodeReleaseCache>;
2416
+
2417
+ /**
2418
+ * Raised when the body of an upstream API response cannot be decoded.
2419
+ *
2420
+ * This error is produced after a successful HTTP response is received but
2421
+ * its payload does not match the expected shape — for example, when a release
2422
+ * index returns malformed JSON or when a required field is missing from the
2423
+ * response structure. The `source` field identifies the URL or data source
2424
+ * that produced the unparseable content.
2425
+ *
2426
+ * @example
2427
+ * ```typescript
2428
+ * import { Effect } from "effect";
2429
+ * import type { ParseError } from "./ParseError.js";
2430
+ * import { resolveNode } from "../resolvers/node.js";
2431
+ *
2432
+ * const program = resolveNode(">=20").pipe(
2433
+ * Effect.catchTag("ParseError", (err: ParseError) =>
2434
+ * Effect.logError(
2435
+ * `Failed to parse response from ${err.source}: ${err.message}`
2436
+ * ).pipe(Effect.andThen(Effect.fail(err)))
2437
+ * )
2438
+ * );
2439
+ * ```
2440
+ *
2441
+ * @public
2442
+ */
307
2443
  export declare class ParseError extends ParseErrorBase<{
308
2444
  readonly source: string;
309
2445
  readonly message: string;
@@ -313,11 +2449,49 @@ export declare class ParseError extends ParseErrorBase<{
313
2449
  /**
314
2450
  * @internal
315
2451
  * Exported for declaration bundling — see AuthenticationError.ts for details.
2452
+ *
2453
+ * @privateRemarks
2454
+ * This base constant must remain a named export so that api-extractor can
2455
+ * resolve the extends clause of {@link ParseError} to a stable declaration.
2456
+ * Without it the bundled `.d.ts` would contain an anonymous `_base` symbol
2457
+ * that cannot be referenced by downstream consumers.
316
2458
  */
317
2459
  export declare const ParseErrorBase: new <A extends Record<string, any> = {}>(args: Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => YieldableError & {
318
2460
  readonly _tag: "ParseError";
319
2461
  } & Readonly<A>;
320
2462
 
2463
+ /**
2464
+ * Raised when a GitHub API rate limit is exceeded.
2465
+ *
2466
+ * The `limit` and `remaining` fields reflect the values returned in the
2467
+ * `X-RateLimit-Limit` and `X-RateLimit-Remaining` response headers. When the
2468
+ * API indicates how long to wait before the quota resets, that duration (in
2469
+ * seconds) is available via `retryAfter`.
2470
+ *
2471
+ * Use {@link retryOnRateLimit} to automatically retry an effect with
2472
+ * exponential backoff whenever this error is encountered.
2473
+ *
2474
+ * @see {@link retryOnRateLimit}
2475
+ *
2476
+ * @example
2477
+ * ```typescript
2478
+ * import { Effect } from "effect";
2479
+ * import type { RateLimitError } from "./RateLimitError.js";
2480
+ * import { resolveNode } from "../resolvers/node.js";
2481
+ *
2482
+ * const program = resolveNode(">=20").pipe(
2483
+ * Effect.catchTag("RateLimitError", (err: RateLimitError) =>
2484
+ * Effect.logWarning(
2485
+ * `Rate limit hit (${err.remaining}/${err.limit} remaining)` +
2486
+ * (err.retryAfter != null ? `, retry after ${err.retryAfter}s` : "") +
2487
+ * `: ${err.message}`
2488
+ * ).pipe(Effect.andThen(Effect.fail(err)))
2489
+ * )
2490
+ * );
2491
+ * ```
2492
+ *
2493
+ * @public
2494
+ */
321
2495
  export declare class RateLimitError extends RateLimitErrorBase<{
322
2496
  readonly retryAfter?: number;
323
2497
  readonly limit: number;
@@ -329,6 +2503,12 @@ export declare class RateLimitError extends RateLimitErrorBase<{
329
2503
  /**
330
2504
  * @internal
331
2505
  * Exported for declaration bundling — see AuthenticationError.ts for details.
2506
+ *
2507
+ * @privateRemarks
2508
+ * This base constant must remain a named export so that api-extractor can
2509
+ * resolve the extends clause of {@link RateLimitError} to a stable declaration.
2510
+ * Without it the bundled `.d.ts` would contain an anonymous `_base` symbol
2511
+ * that cannot be referenced by downstream consumers.
332
2512
  */
333
2513
  export declare const RateLimitErrorBase: new <A extends Record<string, any> = {}>(args: Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => YieldableError & {
334
2514
  readonly _tag: "RateLimitError";
@@ -337,8 +2517,29 @@ export declare const RateLimitErrorBase: new <A extends Record<string, any> = {}
337
2517
  /**
338
2518
  * Resolve Bun versions matching the given options.
339
2519
  *
340
- * Uses GITHUB_PERSONAL_ACCESS_TOKEN or GITHUB_TOKEN for authentication.
341
- * Falls back to build-time cache when the network is unavailable.
2520
+ * Fetches release data from the Bun GitHub repository, then filters and ranks
2521
+ * versions according to `options`. Authentication is read from
2522
+ * `GITHUB_PERSONAL_ACCESS_TOKEN` or `GITHUB_TOKEN` environment variables.
2523
+ * Falls back to a build-time offline cache when the network is unavailable.
2524
+ *
2525
+ * @param options - Filtering and ranking options. See {@link BunResolverOptions}.
2526
+ * @returns A promise that resolves to a {@link ResolvedVersions} object
2527
+ * containing the matched version list and the highest matching version as
2528
+ * `latest`.
2529
+ *
2530
+ * @example
2531
+ * ```ts
2532
+ * import type { ResolvedVersions } from "runtime-resolver";
2533
+ * import { resolveBun } from "runtime-resolver";
2534
+ *
2535
+ * const result: ResolvedVersions = await resolveBun({ semverRange: "^1.0.0" });
2536
+ * console.log(result.latest);
2537
+ * ```
2538
+ *
2539
+ * @see {@link BunResolver} for the Effect-based API
2540
+ * @see {@link BunResolverOptions} for available options
2541
+ *
2542
+ * @public
342
2543
  */
343
2544
  export declare const resolveBun: (options?: BunResolverOptions | undefined) => Promise<{
344
2545
  readonly source: "api" | "cache";
@@ -351,8 +2552,29 @@ export declare const resolveBun: (options?: BunResolverOptions | undefined) => P
351
2552
  /**
352
2553
  * Resolve Deno versions matching the given options.
353
2554
  *
354
- * Uses GITHUB_PERSONAL_ACCESS_TOKEN or GITHUB_TOKEN for authentication.
355
- * Falls back to build-time cache when the network is unavailable.
2555
+ * Fetches release data from the Deno GitHub repository, then filters and ranks
2556
+ * versions according to `options`. Authentication is read from
2557
+ * `GITHUB_PERSONAL_ACCESS_TOKEN` or `GITHUB_TOKEN` environment variables.
2558
+ * Falls back to a build-time offline cache when the network is unavailable.
2559
+ *
2560
+ * @param options - Filtering and ranking options. See {@link DenoResolverOptions}.
2561
+ * @returns A promise that resolves to a {@link ResolvedVersions} object
2562
+ * containing the matched version list and the highest matching version as
2563
+ * `latest`.
2564
+ *
2565
+ * @example
2566
+ * ```ts
2567
+ * import type { ResolvedVersions } from "runtime-resolver";
2568
+ * import { resolveDeno } from "runtime-resolver";
2569
+ *
2570
+ * const result: ResolvedVersions = await resolveDeno({ semverRange: "^2.0.0" });
2571
+ * console.log(result.latest);
2572
+ * ```
2573
+ *
2574
+ * @see {@link DenoResolver} for the Effect-based API
2575
+ * @see {@link DenoResolverOptions} for available options
2576
+ *
2577
+ * @public
356
2578
  */
357
2579
  export declare const resolveDeno: (options?: DenoResolverOptions | undefined) => Promise<{
358
2580
  readonly source: "api" | "cache";
@@ -362,6 +2584,35 @@ export declare const resolveDeno: (options?: DenoResolverOptions | undefined) =>
362
2584
  readonly default?: string | undefined;
363
2585
  }>;
364
2586
 
2587
+ /**
2588
+ * The primary return type produced by every runtime resolver.
2589
+ *
2590
+ * Contains the full list of matching versions, the latest version string, and
2591
+ * optional `lts` / `default` version strings depending on the runtime.
2592
+ * The `source` field indicates whether the data came from a live API call or
2593
+ * a cached snapshot.
2594
+ *
2595
+ * @see {@link resolveNode}
2596
+ * @see {@link resolveBun}
2597
+ * @see {@link resolveDeno}
2598
+ *
2599
+ * @example
2600
+ * ```typescript
2601
+ * import { Effect } from "effect";
2602
+ * import type { ResolvedVersions } from "./common.js";
2603
+ * import { resolveNode } from "../resolvers/node.js";
2604
+ *
2605
+ * const program: Effect.Effect<ResolvedVersions> = resolveNode(">=20");
2606
+ *
2607
+ * Effect.runPromise(program).then((result: ResolvedVersions) => {
2608
+ * console.log(result.latest); // e.g. "22.3.0"
2609
+ * console.log(result.source); // "api" | "cache"
2610
+ * console.log(result.versions); // ["22.3.0", "22.2.0", ...]
2611
+ * });
2612
+ * ```
2613
+ *
2614
+ * @public
2615
+ */
365
2616
  export declare const ResolvedVersions: Schema.Struct<{
366
2617
  source: Schema.Literal<["api", "cache"]>;
367
2618
  versions: Schema.Array$<typeof Schema.String>;
@@ -370,13 +2621,48 @@ export declare const ResolvedVersions: Schema.Struct<{
370
2621
  default: Schema.optional<typeof Schema.String>;
371
2622
  }>;
372
2623
 
2624
+ /**
2625
+ * The primary return type produced by every runtime resolver.
2626
+ *
2627
+ * @see {@link resolveNode}
2628
+ * @see {@link resolveBun}
2629
+ * @see {@link resolveDeno}
2630
+ *
2631
+ * @public
2632
+ */
373
2633
  export declare type ResolvedVersions = typeof ResolvedVersions.Type;
374
2634
 
375
2635
  /**
376
2636
  * Resolve Node.js versions matching the given options.
377
2637
  *
378
- * Uses GITHUB_PERSONAL_ACCESS_TOKEN or GITHUB_TOKEN for authentication.
379
- * Falls back to build-time cache when the network is unavailable.
2638
+ * Fetches release data from the Node.js GitHub repository and the official
2639
+ * release schedule API, then filters and ranks versions according to
2640
+ * `options`. Authentication is read from `GITHUB_PERSONAL_ACCESS_TOKEN` or
2641
+ * `GITHUB_TOKEN` environment variables. Falls back to a build-time offline
2642
+ * cache when the network is unavailable.
2643
+ *
2644
+ * @param options - Filtering and ranking options. See {@link NodeResolverOptions}.
2645
+ * @returns A promise that resolves to a {@link ResolvedVersions} object
2646
+ * containing the matched version list, the highest matching version as
2647
+ * `latest`, and optional `lts` / `default` strings.
2648
+ *
2649
+ * @example
2650
+ * ```ts
2651
+ * import type { ResolvedVersions } from "runtime-resolver";
2652
+ * import { resolveNode } from "runtime-resolver";
2653
+ *
2654
+ * const result: ResolvedVersions = await resolveNode({
2655
+ * semverRange: ">=20.0.0",
2656
+ * phases: ["active-lts"],
2657
+ * });
2658
+ * console.log(result.latest); // e.g. "22.11.0"
2659
+ * console.log(result.versions); // all matching versions
2660
+ * ```
2661
+ *
2662
+ * @see {@link NodeResolver} for the Effect-based API
2663
+ * @see {@link NodeResolverOptions} for available options
2664
+ *
2665
+ * @public
380
2666
  */
381
2667
  export declare const resolveNode: (options?: NodeResolverOptions | undefined) => Promise<{
382
2668
  readonly source: "api" | "cache";
@@ -386,32 +2672,214 @@ export declare const resolveNode: (options?: NodeResolverOptions | undefined) =>
386
2672
  readonly default?: string | undefined;
387
2673
  }>;
388
2674
 
2675
+ /**
2676
+ * Discriminates which JavaScript runtime a resolver targets.
2677
+ *
2678
+ * @see {@link resolveNode}
2679
+ * @see {@link resolveBun}
2680
+ * @see {@link resolveDeno}
2681
+ *
2682
+ * @public
2683
+ */
389
2684
  export declare const Runtime: Schema.Literal<["node", "bun", "deno"]>;
390
2685
 
2686
+ /**
2687
+ * Discriminates which JavaScript runtime a resolver targets.
2688
+ *
2689
+ * @see {@link resolveNode}
2690
+ * @see {@link resolveBun}
2691
+ * @see {@link resolveDeno}
2692
+ *
2693
+ * @public
2694
+ */
391
2695
  export declare type Runtime = typeof Runtime.Type;
392
2696
 
393
- export declare const Source: Schema.Literal<["api", "cache"]>;
394
-
395
- export declare type Source = typeof Source.Type;
2697
+ /**
2698
+ * Generic in-memory cache service for a typed runtime release collection,
2699
+ * built on top of semver-effect's version cache primitives.
2700
+ *
2701
+ * Concrete instantiations are {@link BunReleaseCache}, {@link DenoReleaseCache},
2702
+ * and {@link NodeReleaseCache}. The canonical factory function for obtaining a
2703
+ * `RuntimeCache` implementation is {@link createRuntimeCache}.
2704
+ *
2705
+ * @typeParam R - A {@link RuntimeRelease} subtype specific to the runtime
2706
+ * (e.g. `BunRelease`, `DenoRelease`, `NodeRelease`).
2707
+ *
2708
+ * @example
2709
+ * ```typescript
2710
+ * import type { BunRelease } from "runtime-resolver";
2711
+ * import { BunReleaseCache, OfflineBunCacheLive } from "runtime-resolver";
2712
+ * import { Effect } from "effect";
2713
+ *
2714
+ * const program = Effect.gen(function* () {
2715
+ * const cache = yield* BunReleaseCache;
2716
+ * const latest = yield* cache.latest();
2717
+ * console.log(latest.version.toString());
2718
+ * });
2719
+ *
2720
+ * Effect.runPromise(program.pipe(Effect.provide(OfflineBunCacheLive)));
2721
+ * ```
2722
+ *
2723
+ * @see {@link BunReleaseCache}
2724
+ * @see {@link DenoReleaseCache}
2725
+ * @see {@link NodeReleaseCache}
2726
+ * @see {@link createRuntimeCache}
2727
+ *
2728
+ * @public
2729
+ */
2730
+ export declare interface RuntimeCache<R extends RuntimeRelease> {
2731
+ /**
2732
+ * Populates the cache with the given array of typed release objects,
2733
+ * replacing any previously loaded entries.
2734
+ *
2735
+ * @param releases - The full set of releases to store.
2736
+ */
2737
+ readonly load: (releases: ReadonlyArray<R>) => Effect.Effect<void>;
2738
+ /**
2739
+ * Finds the single best-matching release for the given semver range string,
2740
+ * preferring the highest satisfying version.
2741
+ *
2742
+ * Fails with {@link InvalidRangeError} when `range` is not a valid semver
2743
+ * range expression, or with {@link UnsatisfiedRangeError} when no cached
2744
+ * release satisfies the range.
2745
+ *
2746
+ * @param range - A semver range string (e.g. `">=1.0.0 <2.0.0"`).
2747
+ */
2748
+ readonly resolve: (range: string) => Effect.Effect<R, InvalidRangeError | UnsatisfiedRangeError>;
2749
+ /**
2750
+ * Returns the complete list of all releases currently held in the cache,
2751
+ * in the order they were loaded.
2752
+ */
2753
+ readonly releases: () => Effect.Effect<ReadonlyArray<R>>;
2754
+ /**
2755
+ * Returns all cached releases whose version satisfies the given semver
2756
+ * range string.
2757
+ *
2758
+ * Fails with {@link InvalidRangeError} when `range` is not a valid semver
2759
+ * range expression.
2760
+ *
2761
+ * @param range - A semver range string (e.g. `"^20.0.0"`).
2762
+ */
2763
+ readonly filter: (range: string) => Effect.Effect<ReadonlyArray<R>, InvalidRangeError>;
2764
+ /**
2765
+ * Returns the release with the highest version among all entries in the
2766
+ * cache.
2767
+ *
2768
+ * Fails with {@link EmptyCacheError} when the cache contains no releases.
2769
+ */
2770
+ readonly latest: () => Effect.Effect<R, EmptyCacheError>;
2771
+ /**
2772
+ * Returns one release per major version line, each being the highest
2773
+ * available version within that major line.
2774
+ *
2775
+ * Returns an empty array when the cache is empty.
2776
+ */
2777
+ readonly latestByMajor: () => Effect.Effect<ReadonlyArray<R>>;
2778
+ /**
2779
+ * Returns one release per minor version line, each being the highest
2780
+ * available patch version within that minor line.
2781
+ *
2782
+ * Returns an empty array when the cache is empty.
2783
+ */
2784
+ readonly latestByMinor: () => Effect.Effect<ReadonlyArray<R>>;
2785
+ }
396
2786
 
397
2787
  /**
398
- * Service interface for version data caching.
2788
+ * Structural base constraint shared by {@link BunRelease}, {@link DenoRelease},
2789
+ * and {@link NodeRelease}.
2790
+ *
2791
+ * Enables the generic `RuntimeCache<R extends RuntimeRelease>` service to
2792
+ * operate over any runtime release type without coupling to a concrete class.
2793
+ *
2794
+ * @see {@link RuntimeCache}
2795
+ * @see {@link BunRelease}
2796
+ * @see {@link DenoRelease}
2797
+ * @see {@link NodeRelease}
2798
+ *
2799
+ * @public
399
2800
  */
400
- export declare interface VersionCache {
401
- readonly get: (runtime: Runtime) => Effect.Effect<{
402
- data: CachedData;
403
- source: Source;
404
- }, CacheError>;
405
- readonly set: (runtime: Runtime, data: CachedData) => Effect.Effect<void, CacheError>;
2801
+ export declare interface RuntimeRelease {
2802
+ readonly _tag: string;
2803
+ readonly version: SemVer.SemVer;
2804
+ readonly date: DateTime.DateTime;
406
2805
  }
407
2806
 
408
2807
  /**
409
- * @internal Uses GenericTag — see BunResolver.ts for rationale.
2808
+ * Lean input schema used to construct {@link BunRelease} and
2809
+ * {@link DenoRelease} instances from bundled default data files.
2810
+ *
2811
+ * Both Bun and Deno source their offline snapshots from JSON/TS data files
2812
+ * whose entries conform to this shape. The `version` and `date` fields are
2813
+ * plain strings that are subsequently parsed by `SemVer.fromString` and
2814
+ * `DateTime.unsafeMake` respectively.
2815
+ *
2816
+ * @see {@link BunRelease.fromInput}
2817
+ * @see {@link DenoRelease.fromInput}
2818
+ *
2819
+ * @public
2820
+ */
2821
+ export declare const RuntimeReleaseInput: Schema.Struct<{
2822
+ version: typeof Schema.String;
2823
+ date: typeof Schema.String;
2824
+ }>;
2825
+
2826
+ /**
2827
+ * Lean input type used to construct {@link BunRelease} and {@link DenoRelease}
2828
+ * instances from bundled default data files.
2829
+ *
2830
+ * @see {@link BunRelease.fromInput}
2831
+ * @see {@link DenoRelease.fromInput}
2832
+ *
2833
+ * @public
2834
+ */
2835
+ export declare type RuntimeReleaseInput = typeof RuntimeReleaseInput.Type;
2836
+
2837
+ /**
2838
+ * Indicates whether a {@link ResolvedVersions} result was served from
2839
+ * a live API call or a local cache.
2840
+ *
2841
+ * @public
410
2842
  */
411
- export declare const VersionCache: Context.Tag<VersionCache, VersionCache>;
2843
+ export declare const Source: Schema.Literal<["api", "cache"]>;
412
2844
 
413
- export declare const VersionCacheLive: Layer.Layer<VersionCache>;
2845
+ /**
2846
+ * Indicates whether a {@link ResolvedVersions} result was served from
2847
+ * a live API call or a local cache.
2848
+ *
2849
+ * @public
2850
+ */
2851
+ export declare type Source = typeof Source.Type;
414
2852
 
2853
+ /**
2854
+ * Raised when no published version satisfies the requested constraint.
2855
+ *
2856
+ * This error is produced by {@link NodeResolver}, {@link BunResolver}, and
2857
+ * {@link DenoResolver} after the full list of available versions has been
2858
+ * fetched and filtered but no entry matches the caller-supplied semver
2859
+ * constraint. The `runtime` field identifies which runtime was queried and
2860
+ * `constraint` echoes the range string that was provided.
2861
+ *
2862
+ * @see {@link NodeResolver}
2863
+ * @see {@link BunResolver}
2864
+ * @see {@link DenoResolver}
2865
+ *
2866
+ * @example
2867
+ * ```typescript
2868
+ * import { Effect } from "effect";
2869
+ * import type { VersionNotFoundError } from "./VersionNotFoundError.js";
2870
+ * import { resolveNode } from "../resolvers/node.js";
2871
+ *
2872
+ * const program = resolveNode(">=999").pipe(
2873
+ * Effect.catchTag("VersionNotFoundError", (err: VersionNotFoundError) =>
2874
+ * Effect.logError(
2875
+ * `No ${err.runtime} version matched "${err.constraint}": ${err.message}`
2876
+ * ).pipe(Effect.andThen(Effect.fail(err)))
2877
+ * )
2878
+ * );
2879
+ * ```
2880
+ *
2881
+ * @public
2882
+ */
415
2883
  export declare class VersionNotFoundError extends VersionNotFoundErrorBase<{
416
2884
  readonly runtime: Runtime;
417
2885
  readonly constraint: string;
@@ -422,6 +2890,12 @@ export declare class VersionNotFoundError extends VersionNotFoundErrorBase<{
422
2890
  /**
423
2891
  * @internal
424
2892
  * Exported for declaration bundling — see AuthenticationError.ts for details.
2893
+ *
2894
+ * @privateRemarks
2895
+ * This base constant must remain a named export so that api-extractor can
2896
+ * resolve the extends clause of {@link VersionNotFoundError} to a stable
2897
+ * declaration. Without it the bundled `.d.ts` would contain an anonymous
2898
+ * `_base` symbol that cannot be referenced by downstream consumers.
425
2899
  */
426
2900
  export declare const VersionNotFoundErrorBase: new <A extends Record<string, any> = {}>(args: Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => YieldableError & {
427
2901
  readonly _tag: "VersionNotFoundError";