runtime-resolver 0.3.12 → 0.3.14
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/bin/runtime-resolver.d.ts +1 -0
- package/bin/runtime-resolver.js +29 -261
- package/cli/commands/resolve.js +239 -0
- package/cli/schemas/json-schema.js +12 -0
- package/cli/schemas/response.js +86 -0
- package/data/bun-defaults.js +850 -0
- package/data/deno-defaults.js +1526 -0
- package/data/node-defaults.js +4400 -0
- package/errors/AuthenticationError.js +35 -0
- package/errors/CacheError.js +32 -0
- package/errors/FreshnessError.js +38 -0
- package/errors/InvalidInputError.js +33 -0
- package/errors/NetworkError.js +34 -0
- package/errors/ParseError.js +33 -0
- package/errors/RateLimitError.js +39 -0
- package/errors/VersionNotFoundError.js +37 -0
- package/index.d.ts +2577 -2608
- package/index.js +158 -132
- package/layers/AutoBunCacheLive.js +47 -0
- package/layers/AutoDenoCacheLive.js +47 -0
- package/layers/AutoNodeCacheLive.js +58 -0
- package/layers/BunReleaseCacheLive.js +25 -0
- package/layers/BunResolverLive.js +85 -0
- package/layers/BunVersionFetcherLive.js +55 -0
- package/layers/DenoReleaseCacheLive.js +25 -0
- package/layers/DenoResolverLive.js +84 -0
- package/layers/DenoVersionFetcherLive.js +51 -0
- package/layers/FreshBunCacheLive.js +60 -0
- package/layers/FreshDenoCacheLive.js +60 -0
- package/layers/FreshNodeCacheLive.js +55 -0
- package/layers/GitHubAppAuth.js +58 -0
- package/layers/GitHubAutoAuth.js +70 -0
- package/layers/GitHubClientLive.js +130 -0
- package/layers/GitHubTokenAuth.js +50 -0
- package/layers/NodeReleaseCacheLive.js +77 -0
- package/layers/NodeResolverLive.js +107 -0
- package/layers/NodeScheduleFetcherLive.js +37 -0
- package/layers/NodeVersionFetcherLive.js +55 -0
- package/layers/OfflineBunCacheLive.js +40 -0
- package/layers/OfflineDenoCacheLive.js +40 -0
- package/layers/OfflineNodeCacheLive.js +39 -0
- package/layers/RuntimeCacheLive.js +78 -0
- package/lib/retry.js +27 -0
- package/lib/semver.js +21 -0
- package/package.json +57 -68
- package/schemas/bun-release.js +61 -0
- package/schemas/deno-release.js +61 -0
- package/schemas/github.js +74 -0
- package/schemas/node-release.js +130 -0
- package/schemas/node-schedule.js +110 -0
- package/schemas/node.js +68 -0
- package/services/BunReleaseCache.js +33 -0
- package/services/BunResolver.js +36 -0
- package/services/BunVersionFetcher.js +41 -0
- package/services/DenoReleaseCache.js +33 -0
- package/services/DenoResolver.js +36 -0
- package/services/DenoVersionFetcher.js +41 -0
- package/services/GitHubClient.js +40 -0
- package/services/NodeReleaseCache.js +41 -0
- package/services/NodeResolver.js +44 -0
- package/services/NodeScheduleFetcher.js +38 -0
- package/services/NodeVersionFetcher.js +39 -0
- package/services/OctokitInstance.js +44 -0
- package/tsdoc-metadata.json +11 -11
- package/612.js +0 -23
- package/711.js +0 -7554
- package/json-schema.js +0 -95
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Data, DateTime, Effect } from "effect";
|
|
2
|
+
import { SemVer } from "semver-effect";
|
|
3
|
+
|
|
4
|
+
//#region src/schemas/bun-release.ts
|
|
5
|
+
/**
|
|
6
|
+
* A Bun release entry with a parsed {@link SemVer} version and an
|
|
7
|
+
* Effect {@link DateTime.DateTime} publication date.
|
|
8
|
+
*
|
|
9
|
+
* `BunRelease` extends `Data.TaggedClass` so instances support structural
|
|
10
|
+
* equality comparison and can be used safely inside Effect data structures.
|
|
11
|
+
* Construct instances via the {@link BunRelease.fromInput} factory rather than
|
|
12
|
+
* calling `new BunRelease(...)` directly, as the factory validates the version
|
|
13
|
+
* string through `SemVer.parse`.
|
|
14
|
+
*
|
|
15
|
+
* @see {@link BunReleaseCache}
|
|
16
|
+
* @see {@link BunResolver}
|
|
17
|
+
* @see {@link RuntimeReleaseInput}
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* import { Effect } from "effect";
|
|
22
|
+
* import type { BunRelease } from "./bun-release.js";
|
|
23
|
+
* import { BunRelease as BunReleaseClass } from "./bun-release.js";
|
|
24
|
+
*
|
|
25
|
+
* const program = BunReleaseClass.fromInput({ version: "1.1.0", date: "2024-03-01" }).pipe(
|
|
26
|
+
* Effect.map((release: BunRelease) => {
|
|
27
|
+
* console.log(release.version.major); // 1
|
|
28
|
+
* console.log(release.version.minor); // 1
|
|
29
|
+
* console.log(release._tag); // "BunRelease"
|
|
30
|
+
* }),
|
|
31
|
+
* );
|
|
32
|
+
*
|
|
33
|
+
* Effect.runPromise(program);
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @public
|
|
37
|
+
*/
|
|
38
|
+
var BunRelease = class BunRelease extends Data.TaggedClass("BunRelease") {
|
|
39
|
+
/**
|
|
40
|
+
* Create a {@link BunRelease} from lean string inputs.
|
|
41
|
+
*
|
|
42
|
+
* Parses `input.version` via `SemVer.parse` and constructs the
|
|
43
|
+
* publication date with `DateTime.unsafeMake`. Falls back to the current
|
|
44
|
+
* timestamp when `input.date` is an empty string (rare in production data).
|
|
45
|
+
*
|
|
46
|
+
* @param input - A {@link RuntimeReleaseInput} object with raw string fields.
|
|
47
|
+
* @returns An `Effect` that succeeds with a `BunRelease` or fails with an
|
|
48
|
+
* `InvalidVersionError` when the version string cannot be parsed.
|
|
49
|
+
*/
|
|
50
|
+
static fromInput(input) {
|
|
51
|
+
return Effect.gen(function* () {
|
|
52
|
+
return new BunRelease({
|
|
53
|
+
version: yield* SemVer.parse(input.version),
|
|
54
|
+
date: input.date ? DateTime.unsafeMake(input.date) : DateTime.unsafeMake(/* @__PURE__ */ new Date())
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
//#endregion
|
|
61
|
+
export { BunRelease };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Data, DateTime, Effect } from "effect";
|
|
2
|
+
import { SemVer } from "semver-effect";
|
|
3
|
+
|
|
4
|
+
//#region src/schemas/deno-release.ts
|
|
5
|
+
/**
|
|
6
|
+
* A Deno release entry with a parsed {@link SemVer} version and an
|
|
7
|
+
* Effect {@link DateTime.DateTime} publication date.
|
|
8
|
+
*
|
|
9
|
+
* `DenoRelease` extends `Data.TaggedClass` so instances support structural
|
|
10
|
+
* equality comparison and can be used safely inside Effect data structures.
|
|
11
|
+
* Construct instances via the {@link DenoRelease.fromInput} factory rather than
|
|
12
|
+
* calling `new DenoRelease(...)` directly, as the factory validates the version
|
|
13
|
+
* string through `SemVer.parse`.
|
|
14
|
+
*
|
|
15
|
+
* @see {@link DenoReleaseCache}
|
|
16
|
+
* @see {@link DenoResolver}
|
|
17
|
+
* @see {@link RuntimeReleaseInput}
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* import { Effect } from "effect";
|
|
22
|
+
* import type { DenoRelease } from "./deno-release.js";
|
|
23
|
+
* import { DenoRelease as DenoReleaseClass } from "./deno-release.js";
|
|
24
|
+
*
|
|
25
|
+
* const program = DenoReleaseClass.fromInput({ version: "1.44.0", date: "2024-05-23" }).pipe(
|
|
26
|
+
* Effect.map((release: DenoRelease) => {
|
|
27
|
+
* console.log(release.version.major); // 1
|
|
28
|
+
* console.log(release.version.minor); // 44
|
|
29
|
+
* console.log(release._tag); // "DenoRelease"
|
|
30
|
+
* }),
|
|
31
|
+
* );
|
|
32
|
+
*
|
|
33
|
+
* Effect.runPromise(program);
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @public
|
|
37
|
+
*/
|
|
38
|
+
var DenoRelease = class DenoRelease extends Data.TaggedClass("DenoRelease") {
|
|
39
|
+
/**
|
|
40
|
+
* Create a {@link DenoRelease} from lean string inputs.
|
|
41
|
+
*
|
|
42
|
+
* Parses `input.version` via `SemVer.parse` and constructs the
|
|
43
|
+
* publication date with `DateTime.unsafeMake`. Falls back to the current
|
|
44
|
+
* timestamp when `input.date` is an empty string (rare in production data).
|
|
45
|
+
*
|
|
46
|
+
* @param input - A {@link RuntimeReleaseInput} object with raw string fields.
|
|
47
|
+
* @returns An `Effect` that succeeds with a `DenoRelease` or fails with an
|
|
48
|
+
* `InvalidVersionError` when the version string cannot be parsed.
|
|
49
|
+
*/
|
|
50
|
+
static fromInput(input) {
|
|
51
|
+
return Effect.gen(function* () {
|
|
52
|
+
return new DenoRelease({
|
|
53
|
+
version: yield* SemVer.parse(input.version),
|
|
54
|
+
date: input.date ? DateTime.unsafeMake(input.date) : DateTime.unsafeMake(/* @__PURE__ */ new Date())
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
//#endregion
|
|
61
|
+
export { DenoRelease };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
|
|
3
|
+
//#region src/schemas/github.ts
|
|
4
|
+
/**
|
|
5
|
+
* The `commit` sub-object returned for each entry in the GitHub tags API.
|
|
6
|
+
*
|
|
7
|
+
* Used as a nested field within {@link GitHubTag}.
|
|
8
|
+
*
|
|
9
|
+
* @see {@link GitHubClient}
|
|
10
|
+
*
|
|
11
|
+
* @public
|
|
12
|
+
*/
|
|
13
|
+
const GitHubTagCommit = Schema.Struct({
|
|
14
|
+
sha: Schema.String,
|
|
15
|
+
url: Schema.String
|
|
16
|
+
});
|
|
17
|
+
/**
|
|
18
|
+
* A single tag entry as returned by the GitHub REST API
|
|
19
|
+
* (`GET /repos/{owner}/{repo}/tags`).
|
|
20
|
+
*
|
|
21
|
+
* Consumed by {@link GitHubClient} when fetching Bun and Deno release tags.
|
|
22
|
+
*
|
|
23
|
+
* @see {@link GitHubClient}
|
|
24
|
+
* @see {@link GitHubTagList}
|
|
25
|
+
*
|
|
26
|
+
* @public
|
|
27
|
+
*/
|
|
28
|
+
const GitHubTag = Schema.Struct({
|
|
29
|
+
name: Schema.String,
|
|
30
|
+
zipball_url: Schema.String,
|
|
31
|
+
tarball_url: Schema.String,
|
|
32
|
+
commit: GitHubTagCommit,
|
|
33
|
+
node_id: Schema.String
|
|
34
|
+
});
|
|
35
|
+
/**
|
|
36
|
+
* The decoded response body from `GET /repos/{owner}/{repo}/tags`.
|
|
37
|
+
*
|
|
38
|
+
* @see {@link GitHubClient}
|
|
39
|
+
* @see {@link GitHubTag}
|
|
40
|
+
*
|
|
41
|
+
* @public
|
|
42
|
+
*/
|
|
43
|
+
const GitHubTagList = Schema.Array(GitHubTag);
|
|
44
|
+
/**
|
|
45
|
+
* A single release entry as returned by the GitHub REST API
|
|
46
|
+
* (`GET /repos/{owner}/{repo}/releases`).
|
|
47
|
+
*
|
|
48
|
+
* Consumed by {@link GitHubClient} when fetching Deno releases that carry
|
|
49
|
+
* publication timestamps.
|
|
50
|
+
*
|
|
51
|
+
* @see {@link GitHubClient}
|
|
52
|
+
* @see {@link GitHubReleaseList}
|
|
53
|
+
*
|
|
54
|
+
* @public
|
|
55
|
+
*/
|
|
56
|
+
const GitHubRelease = Schema.Struct({
|
|
57
|
+
tag_name: Schema.String,
|
|
58
|
+
name: Schema.NullOr(Schema.String),
|
|
59
|
+
draft: Schema.Boolean,
|
|
60
|
+
prerelease: Schema.Boolean,
|
|
61
|
+
published_at: Schema.NullOr(Schema.String)
|
|
62
|
+
});
|
|
63
|
+
/**
|
|
64
|
+
* The decoded response body from `GET /repos/{owner}/{repo}/releases`.
|
|
65
|
+
*
|
|
66
|
+
* @see {@link GitHubClient}
|
|
67
|
+
* @see {@link GitHubRelease}
|
|
68
|
+
*
|
|
69
|
+
* @public
|
|
70
|
+
*/
|
|
71
|
+
const GitHubReleaseList = Schema.Array(GitHubRelease);
|
|
72
|
+
|
|
73
|
+
//#endregion
|
|
74
|
+
export { GitHubReleaseList, GitHubTagList };
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { DateTime, Effect, Ref, Schema } from "effect";
|
|
2
|
+
import { SemVer } from "semver-effect";
|
|
3
|
+
|
|
4
|
+
//#region src/schemas/node-release.ts
|
|
5
|
+
/**
|
|
6
|
+
* Lean input schema used to construct {@link NodeRelease} instances from
|
|
7
|
+
* bundled default data files and the Node.js dist API response.
|
|
8
|
+
*
|
|
9
|
+
* The `version` and `npm` fields are raw version strings validated by
|
|
10
|
+
* `SemVer.parse`; `date` is a date string accepted by
|
|
11
|
+
* `DateTime.unsafeMake`.
|
|
12
|
+
*
|
|
13
|
+
* @see {@link NodeRelease.fromInput}
|
|
14
|
+
* @see {@link NodeVersionFetcher}
|
|
15
|
+
*
|
|
16
|
+
* @public
|
|
17
|
+
*/
|
|
18
|
+
const NodeReleaseInput = Schema.Struct({
|
|
19
|
+
version: Schema.String,
|
|
20
|
+
npm: Schema.String,
|
|
21
|
+
date: Schema.String
|
|
22
|
+
});
|
|
23
|
+
/**
|
|
24
|
+
* A Node.js release entry with parsed version, bundled npm version,
|
|
25
|
+
* publication date, and a shared reference to the Node.js release schedule.
|
|
26
|
+
*
|
|
27
|
+
* `NodeRelease` is a plain class rather than a `Data.TaggedClass` because it
|
|
28
|
+
* holds a `Ref<NodeSchedule>` — mutable Effect references break structural
|
|
29
|
+
* equality semantics, making `Data.TaggedClass` unsuitable here. The
|
|
30
|
+
* `_tag` field is set manually to preserve tagged-union compatibility.
|
|
31
|
+
*
|
|
32
|
+
* Use {@link NodeRelease.fromInput} to construct instances; call
|
|
33
|
+
* {@link NodeRelease.phase} or {@link NodeRelease.lts} to query the lifecycle
|
|
34
|
+
* state of the release at a given point in time.
|
|
35
|
+
*
|
|
36
|
+
* @see {@link NodeReleaseCache}
|
|
37
|
+
* @see {@link NodeResolver}
|
|
38
|
+
* @see {@link NodeSchedule}
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* import { Effect, Ref } from "effect";
|
|
43
|
+
* import type { NodePhase } from "./common.js";
|
|
44
|
+
* import type { NodeRelease } from "./node-release.js";
|
|
45
|
+
* import { NodeRelease as NodeReleaseClass } from "./node-release.js";
|
|
46
|
+
* import { NodeSchedule } from "./node-schedule.js";
|
|
47
|
+
*
|
|
48
|
+
* const program = Effect.gen(function* () {
|
|
49
|
+
* const schedule = NodeSchedule.fromData({
|
|
50
|
+
* v20: { start: "2023-04-18", lts: "2023-10-24", end: "2026-04-30" },
|
|
51
|
+
* });
|
|
52
|
+
* const scheduleRef = yield* Ref.make(schedule);
|
|
53
|
+
*
|
|
54
|
+
* const release: NodeRelease = yield* NodeReleaseClass.fromInput(
|
|
55
|
+
* { version: "v20.0.0", npm: "9.6.4", date: "2023-04-18" },
|
|
56
|
+
* scheduleRef,
|
|
57
|
+
* );
|
|
58
|
+
*
|
|
59
|
+
* const phase: NodePhase | null = yield* release.phase();
|
|
60
|
+
* const isLts: boolean = yield* release.lts();
|
|
61
|
+
* console.log(phase); // e.g. "active-lts"
|
|
62
|
+
* console.log(isLts); // true
|
|
63
|
+
* });
|
|
64
|
+
*
|
|
65
|
+
* Effect.runPromise(program);
|
|
66
|
+
* ```
|
|
67
|
+
*
|
|
68
|
+
* @public
|
|
69
|
+
*/
|
|
70
|
+
var NodeRelease = class NodeRelease {
|
|
71
|
+
version;
|
|
72
|
+
npm;
|
|
73
|
+
date;
|
|
74
|
+
scheduleRef;
|
|
75
|
+
_tag = "NodeRelease";
|
|
76
|
+
constructor(version, npm, date, scheduleRef) {
|
|
77
|
+
this.version = version;
|
|
78
|
+
this.npm = npm;
|
|
79
|
+
this.date = date;
|
|
80
|
+
this.scheduleRef = scheduleRef;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Determine this release's lifecycle phase at the given point in time.
|
|
84
|
+
*
|
|
85
|
+
* Reads the current value of the shared `scheduleRef` and delegates to
|
|
86
|
+
* {@link NodeSchedule.phaseFor}. Defaults to the current wall-clock time
|
|
87
|
+
* when `now` is omitted.
|
|
88
|
+
*
|
|
89
|
+
* @param now - Optional reference time; defaults to `new Date()`.
|
|
90
|
+
* @returns An `Effect` that resolves to a {@link NodePhase} string, or
|
|
91
|
+
* `null` when the major version is not present in the schedule or has
|
|
92
|
+
* not yet been released.
|
|
93
|
+
*/
|
|
94
|
+
phase(now) {
|
|
95
|
+
const effectiveNow = now ?? DateTime.unsafeMake(/* @__PURE__ */ new Date());
|
|
96
|
+
return Effect.gen(this, function* () {
|
|
97
|
+
return yield* (yield* Ref.get(this.scheduleRef)).phaseFor(this.version.major, effectiveNow);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Whether this release is currently in Long-Term Support.
|
|
102
|
+
*
|
|
103
|
+
* Returns `true` for both `"active-lts"` and `"maintenance-lts"` phases.
|
|
104
|
+
* Defaults to the current wall-clock time when `now` is omitted.
|
|
105
|
+
*
|
|
106
|
+
* @param now - Optional reference time; defaults to `new Date()`.
|
|
107
|
+
*/
|
|
108
|
+
lts(now) {
|
|
109
|
+
return this.phase(now).pipe(Effect.map((p) => p === "active-lts" || p === "maintenance-lts"));
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Create a {@link NodeRelease} from lean string inputs.
|
|
113
|
+
*
|
|
114
|
+
* Parses `input.version` and `input.npm` via `SemVer.parse` and
|
|
115
|
+
* constructs the publication date with `DateTime.unsafeMake`.
|
|
116
|
+
*
|
|
117
|
+
* @param input - A {@link NodeReleaseInput} object with raw string fields.
|
|
118
|
+
* @param scheduleRef - A shared `Ref<NodeSchedule>` injected by the cache layer.
|
|
119
|
+
* @returns An `Effect` that succeeds with a `NodeRelease` or fails with an
|
|
120
|
+
* `InvalidVersionError` when either version string cannot be parsed.
|
|
121
|
+
*/
|
|
122
|
+
static fromInput(input, scheduleRef) {
|
|
123
|
+
return Effect.gen(function* () {
|
|
124
|
+
return new NodeRelease(yield* SemVer.parse(input.version), yield* SemVer.parse(input.npm), DateTime.unsafeMake(input.date), scheduleRef);
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
//#endregion
|
|
130
|
+
export { NodeRelease };
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { Data, DateTime, Effect, Option } from "effect";
|
|
2
|
+
|
|
3
|
+
//#region src/schemas/node-schedule.ts
|
|
4
|
+
/**
|
|
5
|
+
* Immutable snapshot of the Node.js release schedule.
|
|
6
|
+
*
|
|
7
|
+
* Holds a `ReadonlyArray<NodeScheduleEntry>` parsed from the raw
|
|
8
|
+
* {@link NodeScheduleData} fetched by {@link NodeScheduleFetcher}. A single
|
|
9
|
+
* `NodeSchedule` instance lives inside a `Ref<NodeSchedule>` that is shared
|
|
10
|
+
* across all {@link NodeRelease} instances so that schedule updates propagate
|
|
11
|
+
* without rebuilding the release cache.
|
|
12
|
+
*
|
|
13
|
+
* `NodeSchedule` extends `Data.TaggedClass` and therefore supports structural
|
|
14
|
+
* equality — two schedules built from identical data compare as equal.
|
|
15
|
+
*
|
|
16
|
+
* @see {@link NodeRelease}
|
|
17
|
+
* @see {@link NodeScheduleFetcher}
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* import { Effect } from "effect";
|
|
22
|
+
* import type { NodePhase } from "./common.js";
|
|
23
|
+
* import type { NodeScheduleData } from "./node-schedule.js";
|
|
24
|
+
* import { NodeSchedule } from "./node-schedule.js";
|
|
25
|
+
*
|
|
26
|
+
* const data: NodeScheduleData = {
|
|
27
|
+
* v20: {
|
|
28
|
+
* start: "2023-04-18",
|
|
29
|
+
* lts: "2023-10-24",
|
|
30
|
+
* maintenance: "2024-10-22",
|
|
31
|
+
* end: "2026-04-30",
|
|
32
|
+
* codename: "Iron",
|
|
33
|
+
* },
|
|
34
|
+
* };
|
|
35
|
+
*
|
|
36
|
+
* const schedule = NodeSchedule.fromData(data);
|
|
37
|
+
*
|
|
38
|
+
* const program = schedule.phaseFor(20, new Date("2024-01-01") as any).pipe(
|
|
39
|
+
* Effect.map((phase: NodePhase | null) => {
|
|
40
|
+
* console.log(phase); // "active-lts"
|
|
41
|
+
* }),
|
|
42
|
+
* );
|
|
43
|
+
*
|
|
44
|
+
* Effect.runPromise(program);
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* @public
|
|
48
|
+
*/
|
|
49
|
+
var NodeSchedule = class NodeSchedule extends Data.TaggedClass("NodeSchedule") {
|
|
50
|
+
/**
|
|
51
|
+
* Parse raw {@link NodeScheduleData} into a `NodeSchedule` instance.
|
|
52
|
+
*
|
|
53
|
+
* Iterates over every key of the form `"vNN"`, converts ISO date strings
|
|
54
|
+
* to `DateTime.DateTime`, and normalises missing codenames to `""`.
|
|
55
|
+
*
|
|
56
|
+
* @param data - The raw JSON object fetched from the Node.js release repo.
|
|
57
|
+
* @returns A fully constructed, immutable `NodeSchedule`.
|
|
58
|
+
*/
|
|
59
|
+
static fromData(data) {
|
|
60
|
+
const entries = [];
|
|
61
|
+
for (const [key, value] of Object.entries(data)) {
|
|
62
|
+
const major = Number.parseInt(key.replace("v", ""), 10);
|
|
63
|
+
if (Number.isNaN(major)) continue;
|
|
64
|
+
entries.push({
|
|
65
|
+
major,
|
|
66
|
+
start: DateTime.unsafeMake(value.start),
|
|
67
|
+
lts: value.lts ? DateTime.unsafeMake(value.lts) : null,
|
|
68
|
+
maintenance: value.maintenance ? DateTime.unsafeMake(value.maintenance) : null,
|
|
69
|
+
end: DateTime.unsafeMake(value.end),
|
|
70
|
+
codename: value.codename ?? ""
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
return new NodeSchedule({ entries });
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Determine the current {@link NodePhase} for a given major version number
|
|
77
|
+
* at the specified point in time.
|
|
78
|
+
*
|
|
79
|
+
* Returns `null` when the major is not present in the schedule or when
|
|
80
|
+
* `now` is before the release's start date.
|
|
81
|
+
*
|
|
82
|
+
* @param major - The Node.js major version number (e.g. `20`).
|
|
83
|
+
* @param now - The reference `DateTime` against which phases are evaluated.
|
|
84
|
+
*/
|
|
85
|
+
phaseFor(major, now) {
|
|
86
|
+
return Effect.sync(() => {
|
|
87
|
+
const entry = this.entries.find((e) => e.major === major);
|
|
88
|
+
if (!entry) return null;
|
|
89
|
+
if (DateTime.lessThan(now, entry.start)) return null;
|
|
90
|
+
if (DateTime.greaterThanOrEqualTo(now, entry.end)) return "end-of-life";
|
|
91
|
+
if (entry.maintenance && DateTime.greaterThanOrEqualTo(now, entry.maintenance)) return "maintenance-lts";
|
|
92
|
+
if (entry.lts && DateTime.greaterThanOrEqualTo(now, entry.lts)) return "active-lts";
|
|
93
|
+
return "current";
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Look up the {@link NodeScheduleEntry} for a given major version number.
|
|
98
|
+
*
|
|
99
|
+
* Returns `Option.none()` when the major is not present in the schedule.
|
|
100
|
+
*
|
|
101
|
+
* @param major - The Node.js major version number (e.g. `20`).
|
|
102
|
+
*/
|
|
103
|
+
entryFor(major) {
|
|
104
|
+
const entry = this.entries.find((e) => e.major === major);
|
|
105
|
+
return entry ? Option.some(entry) : Option.none();
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
//#endregion
|
|
110
|
+
export { NodeSchedule };
|
package/schemas/node.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
|
|
3
|
+
//#region src/schemas/node.ts
|
|
4
|
+
/**
|
|
5
|
+
* A single entry in the Node.js distribution index
|
|
6
|
+
* (`https://nodejs.org/dist/index.json`).
|
|
7
|
+
*
|
|
8
|
+
* Decoded by {@link NodeVersionFetcherLive} when fetching the full list of
|
|
9
|
+
* available Node.js releases from the official distribution server.
|
|
10
|
+
*
|
|
11
|
+
* @internal
|
|
12
|
+
*/
|
|
13
|
+
const NodeDistVersion = Schema.Struct({
|
|
14
|
+
version: Schema.String,
|
|
15
|
+
date: Schema.String,
|
|
16
|
+
files: Schema.Array(Schema.String),
|
|
17
|
+
npm: Schema.optional(Schema.String),
|
|
18
|
+
v8: Schema.optional(Schema.String),
|
|
19
|
+
uv: Schema.optional(Schema.String),
|
|
20
|
+
zlib: Schema.optional(Schema.String),
|
|
21
|
+
openssl: Schema.optional(Schema.String),
|
|
22
|
+
modules: Schema.optional(Schema.String),
|
|
23
|
+
lts: Schema.Union(Schema.Literal(false), Schema.String),
|
|
24
|
+
security: Schema.Boolean
|
|
25
|
+
});
|
|
26
|
+
/**
|
|
27
|
+
* The full decoded body of `https://nodejs.org/dist/index.json`.
|
|
28
|
+
*
|
|
29
|
+
* Decoded by {@link NodeVersionFetcherLive} before being mapped into
|
|
30
|
+
* {@link NodeRelease} instances.
|
|
31
|
+
*
|
|
32
|
+
* @internal
|
|
33
|
+
*/
|
|
34
|
+
const NodeDistIndex = Schema.Array(NodeDistVersion);
|
|
35
|
+
/**
|
|
36
|
+
* A single entry in the Node.js release schedule JSON fetched by
|
|
37
|
+
* {@link NodeScheduleFetcherLive}.
|
|
38
|
+
*
|
|
39
|
+
* All date-like fields are optional because odd-numbered major lines
|
|
40
|
+
* (e.g. v21) never enter LTS.
|
|
41
|
+
*
|
|
42
|
+
* @internal
|
|
43
|
+
*/
|
|
44
|
+
const ReleaseScheduleEntry = Schema.Struct({
|
|
45
|
+
start: Schema.String,
|
|
46
|
+
lts: Schema.optional(Schema.String),
|
|
47
|
+
maintenance: Schema.optional(Schema.String),
|
|
48
|
+
end: Schema.String,
|
|
49
|
+
codename: Schema.optional(Schema.String)
|
|
50
|
+
});
|
|
51
|
+
/**
|
|
52
|
+
* The full decoded body of the Node.js release schedule JSON fetched by
|
|
53
|
+
* {@link NodeScheduleFetcherLive}.
|
|
54
|
+
*
|
|
55
|
+
* Keyed by major version string (e.g. `"v20"`); values are
|
|
56
|
+
* {@link ReleaseScheduleEntry} objects. After decoding, this record is handed
|
|
57
|
+
* to {@link NodeSchedule.fromData} to produce an immutable
|
|
58
|
+
* {@link NodeSchedule}.
|
|
59
|
+
*
|
|
60
|
+
* @internal
|
|
61
|
+
*/
|
|
62
|
+
const NodeReleaseSchedule = Schema.Record({
|
|
63
|
+
key: Schema.String,
|
|
64
|
+
value: ReleaseScheduleEntry
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
//#endregion
|
|
68
|
+
export { NodeDistIndex, NodeReleaseSchedule };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Context } from "effect";
|
|
2
|
+
|
|
3
|
+
//#region src/services/BunReleaseCache.ts
|
|
4
|
+
/**
|
|
5
|
+
* Specialisation of {@link RuntimeCache} for Bun release objects.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import type { BunRelease } from "runtime-resolver";
|
|
10
|
+
* import { BunReleaseCache, OfflineBunCacheLive } from "runtime-resolver";
|
|
11
|
+
* import { Effect } from "effect";
|
|
12
|
+
*
|
|
13
|
+
* const program = Effect.gen(function* () {
|
|
14
|
+
* const cache = yield* BunReleaseCache;
|
|
15
|
+
* const releases = yield* cache.releases();
|
|
16
|
+
* console.log(releases.length);
|
|
17
|
+
* });
|
|
18
|
+
*
|
|
19
|
+
* Effect.runPromise(program.pipe(Effect.provide(OfflineBunCacheLive)));
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* @see {@link BunRelease}
|
|
23
|
+
* @see {@link RuntimeCache}
|
|
24
|
+
* @see {@link AutoBunCacheLive}
|
|
25
|
+
* @see {@link FreshBunCacheLive}
|
|
26
|
+
* @see {@link OfflineBunCacheLive}
|
|
27
|
+
*
|
|
28
|
+
* @public
|
|
29
|
+
*/
|
|
30
|
+
var BunReleaseCache = class extends Context.Tag("runtime-resolver/BunReleaseCache")() {};
|
|
31
|
+
|
|
32
|
+
//#endregion
|
|
33
|
+
export { BunReleaseCache };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Context } from "effect";
|
|
2
|
+
|
|
3
|
+
//#region src/services/BunResolver.ts
|
|
4
|
+
/**
|
|
5
|
+
* Service for resolving Bun runtime versions against the cached
|
|
6
|
+
* release index.
|
|
7
|
+
*
|
|
8
|
+
* For a one-shot Promise-based API see {@link resolveBun}.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import type { ResolvedVersions } from "runtime-resolver";
|
|
13
|
+
* import { BunResolver, BunResolverLive } from "runtime-resolver";
|
|
14
|
+
* import { Effect } from "effect";
|
|
15
|
+
*
|
|
16
|
+
* const program = Effect.gen(function* () {
|
|
17
|
+
* const resolver = yield* BunResolver;
|
|
18
|
+
* const result = yield* resolver.resolve({ semverRange: "^1.0.0", increments: "minor" });
|
|
19
|
+
* console.log(result.latest);
|
|
20
|
+
* console.log(result.versions);
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* Effect.runPromise(program.pipe(Effect.provide(BunResolverLive)));
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* @see {@link BunResolverLive}
|
|
27
|
+
* @see {@link resolveBun}
|
|
28
|
+
* @see {@link BunResolverOptions}
|
|
29
|
+
* @see {@link ResolvedVersions}
|
|
30
|
+
*
|
|
31
|
+
* @public
|
|
32
|
+
*/
|
|
33
|
+
var BunResolver = class extends Context.Tag("runtime-resolver/BunResolver")() {};
|
|
34
|
+
|
|
35
|
+
//#endregion
|
|
36
|
+
export { BunResolver };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Context } from "effect";
|
|
2
|
+
|
|
3
|
+
//#region src/services/BunVersionFetcher.ts
|
|
4
|
+
/**
|
|
5
|
+
* Service for fetching Bun release data from the GitHub API.
|
|
6
|
+
*
|
|
7
|
+
* Retrieves both the parsed semver version list and the raw release input
|
|
8
|
+
* records needed to populate a {@link BunReleaseCache}. The live
|
|
9
|
+
* implementation queries the `oven-sh/bun` GitHub repository via
|
|
10
|
+
* {@link GitHubClient}.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* import type { RuntimeReleaseInput } from "runtime-resolver";
|
|
15
|
+
* import { BunVersionFetcher, BunVersionFetcherLive, GitHubAutoAuth } from "runtime-resolver";
|
|
16
|
+
* import { Effect } from "effect";
|
|
17
|
+
*
|
|
18
|
+
* const program = Effect.gen(function* () {
|
|
19
|
+
* const fetcher = yield* BunVersionFetcher;
|
|
20
|
+
* const { versions, inputs } = yield* fetcher.fetch();
|
|
21
|
+
* console.log(`Fetched ${versions.length} Bun releases`);
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* Effect.runPromise(
|
|
25
|
+
* program.pipe(Effect.provide(BunVersionFetcherLive), Effect.provide(GitHubAutoAuth)),
|
|
26
|
+
* );
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @see {@link BunVersionFetcherLive}
|
|
30
|
+
* @see {@link BunReleaseCache}
|
|
31
|
+
* @see {@link AuthenticationError}
|
|
32
|
+
* @see {@link NetworkError}
|
|
33
|
+
* @see {@link ParseError}
|
|
34
|
+
* @see {@link RateLimitError}
|
|
35
|
+
*
|
|
36
|
+
* @public
|
|
37
|
+
*/
|
|
38
|
+
var BunVersionFetcher = class extends Context.Tag("runtime-resolver/BunVersionFetcher")() {};
|
|
39
|
+
|
|
40
|
+
//#endregion
|
|
41
|
+
export { BunVersionFetcher };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Context } from "effect";
|
|
2
|
+
|
|
3
|
+
//#region src/services/DenoReleaseCache.ts
|
|
4
|
+
/**
|
|
5
|
+
* Specialisation of {@link RuntimeCache} for Deno release objects.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import type { DenoRelease } from "runtime-resolver";
|
|
10
|
+
* import { DenoReleaseCache, OfflineDenoCacheLive } from "runtime-resolver";
|
|
11
|
+
* import { Effect } from "effect";
|
|
12
|
+
*
|
|
13
|
+
* const program = Effect.gen(function* () {
|
|
14
|
+
* const cache = yield* DenoReleaseCache;
|
|
15
|
+
* const latest = yield* cache.latest();
|
|
16
|
+
* console.log(latest.version.toString());
|
|
17
|
+
* });
|
|
18
|
+
*
|
|
19
|
+
* Effect.runPromise(program.pipe(Effect.provide(OfflineDenoCacheLive)));
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* @see {@link DenoRelease}
|
|
23
|
+
* @see {@link RuntimeCache}
|
|
24
|
+
* @see {@link AutoDenoCacheLive}
|
|
25
|
+
* @see {@link FreshDenoCacheLive}
|
|
26
|
+
* @see {@link OfflineDenoCacheLive}
|
|
27
|
+
*
|
|
28
|
+
* @public
|
|
29
|
+
*/
|
|
30
|
+
var DenoReleaseCache = class extends Context.Tag("runtime-resolver/DenoReleaseCache")() {};
|
|
31
|
+
|
|
32
|
+
//#endregion
|
|
33
|
+
export { DenoReleaseCache };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Context } from "effect";
|
|
2
|
+
|
|
3
|
+
//#region src/services/DenoResolver.ts
|
|
4
|
+
/**
|
|
5
|
+
* Service for resolving Deno runtime versions against the cached
|
|
6
|
+
* release index.
|
|
7
|
+
*
|
|
8
|
+
* For a one-shot Promise-based API see {@link resolveDeno}.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import type { ResolvedVersions } from "runtime-resolver";
|
|
13
|
+
* import { DenoResolver, DenoResolverLive } from "runtime-resolver";
|
|
14
|
+
* import { Effect } from "effect";
|
|
15
|
+
*
|
|
16
|
+
* const program = Effect.gen(function* () {
|
|
17
|
+
* const resolver = yield* DenoResolver;
|
|
18
|
+
* const result = yield* resolver.resolve({ semverRange: "^2.0.0", increments: "minor" });
|
|
19
|
+
* console.log(result.latest);
|
|
20
|
+
* console.log(result.versions);
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* Effect.runPromise(program.pipe(Effect.provide(DenoResolverLive)));
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* @see {@link DenoResolverLive}
|
|
27
|
+
* @see {@link resolveDeno}
|
|
28
|
+
* @see {@link DenoResolverOptions}
|
|
29
|
+
* @see {@link ResolvedVersions}
|
|
30
|
+
*
|
|
31
|
+
* @public
|
|
32
|
+
*/
|
|
33
|
+
var DenoResolver = class extends Context.Tag("runtime-resolver/DenoResolver")() {};
|
|
34
|
+
|
|
35
|
+
//#endregion
|
|
36
|
+
export { DenoResolver };
|