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/235.js +1 -1
- package/701.js +5928 -0
- package/README.md +7 -65
- package/bin/runtime-resolver.js +24 -43
- package/index.d.ts +2601 -127
- package/index.js +128 -7
- package/package.json +3 -3
- package/969.js +0 -27407
package/README.md
CHANGED
|
@@ -2,20 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/runtime-resolver)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://nodejs.org/)
|
|
6
|
+
[](https://www.typescriptlang.org/)
|
|
7
|
+
[](https://effect.website/)
|
|
5
8
|
|
|
6
|
-
Resolve semver-compatible versions of Node.js, Bun
|
|
9
|
+
Resolve semver-compatible versions of Node.js, Bun and Deno runtimes. Fetches available versions from GitHub with automatic offline fallback via a build-time cache.
|
|
7
10
|
|
|
8
11
|
## Features
|
|
9
12
|
|
|
10
|
-
- Resolve matching versions for Node.js, Bun
|
|
13
|
+
- Resolve matching versions for Node.js, Bun and Deno with a single call
|
|
11
14
|
- Filter Node.js results by release phase (current, active-lts, maintenance-lts)
|
|
12
15
|
- Control version granularity with increment levels (latest, minor, patch) for all runtimes
|
|
13
|
-
- Resolve a single version from a semver range with `resolveVersion`
|
|
14
|
-
- Control data freshness with `freshness` option (`"auto"`, `"api"`, or `"cache"`)
|
|
15
16
|
- Track data provenance with the `source` field (`"api"` or `"cache"`)
|
|
16
|
-
- Input validation with typed `InvalidInputError` for all resolvers
|
|
17
17
|
- Offline fallback using bundled version data when GitHub is unreachable
|
|
18
18
|
- CLI with structured JSON output for CI/CD pipelines
|
|
19
|
+
- Full Effect API for custom layer composition and typed error handling
|
|
19
20
|
|
|
20
21
|
## Installation
|
|
21
22
|
|
|
@@ -37,8 +38,7 @@ const bun = await resolveBun({ semverRange: ">=1.1", increments: "minor" });
|
|
|
37
38
|
const deno = await resolveDeno({ semverRange: ">=2", increments: "minor" });
|
|
38
39
|
```
|
|
39
40
|
|
|
40
|
-
Set a `GITHUB_TOKEN` or `GITHUB_PERSONAL_ACCESS_TOKEN` environment variable
|
|
41
|
-
for authenticated requests. Without one, the resolver falls back to cached data.
|
|
41
|
+
Set a `GITHUB_TOKEN` or `GITHUB_PERSONAL_ACCESS_TOKEN` environment variable for authenticated requests. Without one, the resolver falls back to cached data.
|
|
42
42
|
|
|
43
43
|
### CLI
|
|
44
44
|
|
|
@@ -52,64 +52,6 @@ Output is structured JSON with a `$schema` reference for editor auto-complete. P
|
|
|
52
52
|
npx runtime-resolver --schema
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
#### Useful `jq` recipes
|
|
56
|
-
|
|
57
|
-
```bash
|
|
58
|
-
# Get the latest resolved Node.js version
|
|
59
|
-
npx runtime-resolver --node ">=22" | jq -r '.results.node.latest'
|
|
60
|
-
|
|
61
|
-
# List all matching versions as plain lines
|
|
62
|
-
npx runtime-resolver --deno ">=2" | jq -r '.results.deno.versions[]'
|
|
63
|
-
|
|
64
|
-
# Exit non-zero when any runtime fails to resolve
|
|
65
|
-
npx runtime-resolver --node ">=22" --bun "^1" | jq -e '.ok'
|
|
66
|
-
|
|
67
|
-
# Extract only the LTS version for Node.js
|
|
68
|
-
npx runtime-resolver --node ">=20" --node-phases active-lts | jq -r '.results.node.lts'
|
|
69
|
-
|
|
70
|
-
# Build a comma-separated matrix for GitHub Actions
|
|
71
|
-
npx runtime-resolver --node ">=20" --increments minor \
|
|
72
|
-
| jq -r '[.results.node.versions[]] | join(",")'
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
### Effect API
|
|
76
|
-
|
|
77
|
-
Power users can compose resolvers with custom layers and typed error handling instead of the Promise wrappers.
|
|
78
|
-
|
|
79
|
-
```typescript
|
|
80
|
-
import { Effect, Layer } from "effect";
|
|
81
|
-
import {
|
|
82
|
-
NodeResolver,
|
|
83
|
-
NodeResolverLive,
|
|
84
|
-
GitHubClientLive,
|
|
85
|
-
GitHubTokenAuth,
|
|
86
|
-
VersionCacheLive,
|
|
87
|
-
} from "runtime-resolver";
|
|
88
|
-
|
|
89
|
-
const program = Effect.gen(function* () {
|
|
90
|
-
const resolver = yield* NodeResolver;
|
|
91
|
-
return yield* resolver.resolve({
|
|
92
|
-
semverRange: ">=22",
|
|
93
|
-
phases: ["active-lts"],
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
const layer = NodeResolverLive.pipe(
|
|
98
|
-
Layer.provide(Layer.merge(
|
|
99
|
-
GitHubClientLive.pipe(Layer.provide(GitHubTokenAuth)),
|
|
100
|
-
VersionCacheLive,
|
|
101
|
-
)),
|
|
102
|
-
);
|
|
103
|
-
|
|
104
|
-
Effect.runPromise(program.pipe(Effect.provide(layer)));
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
All services (`NodeResolver`, `BunResolver`, `DenoResolver`, `GitHubClient`,
|
|
108
|
-
`VersionCache`), live layers, auth layers (`GitHubTokenAuth`, `GitHubAppAuth`,
|
|
109
|
-
`GitHubAutoAuth`), and typed errors (`AuthenticationError`, `NetworkError`,
|
|
110
|
-
`RateLimitError`, `VersionNotFoundError`, etc.) are exported from the package
|
|
111
|
-
root.
|
|
112
|
-
|
|
113
55
|
## Documentation
|
|
114
56
|
|
|
115
57
|
For configuration, API reference, and advanced usage, see [docs](./docs/).
|
package/bin/runtime-resolver.js
CHANGED
|
@@ -2,12 +2,21 @@
|
|
|
2
2
|
import { Command, Options } from "@effect/cli";
|
|
3
3
|
import { NodeContext, NodeRuntime } from "@effect/platform-node";
|
|
4
4
|
import { readFileSync } from "node:fs";
|
|
5
|
-
import { BunResolver, Console,
|
|
5
|
+
import { NodeScheduleFetcherLive, AutoDenoCacheLive, DenoVersionFetcherLive, AutoNodeCacheLive, BunResolver, Console, Option, Layer, GitHubAppAuth, GitHubAutoAuth, BunResolverLive, BunVersionFetcherLive, DenoResolverLive, Cause, Effect, GitHubClientLive, AutoBunCacheLive, GitHubTokenAuthFromToken, NodeResolverLive, NodeVersionFetcherLive, DenoResolver, NodeResolver } from "../701.js";
|
|
6
6
|
const GitHubLayer = GitHubClientLive.pipe(Layer.provide(GitHubAutoAuth));
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
|
|
7
|
+
const makeNodeLayer = (authOverride)=>{
|
|
8
|
+
const ghLayer = authOverride ? GitHubClientLive.pipe(Layer.provide(authOverride)) : GitHubLayer;
|
|
9
|
+
const fetchersLayer = Layer.merge(NodeVersionFetcherLive.pipe(Layer.provide(ghLayer)), NodeScheduleFetcherLive.pipe(Layer.provide(ghLayer)));
|
|
10
|
+
return NodeResolverLive.pipe(Layer.provide(AutoNodeCacheLive.pipe(Layer.provide(fetchersLayer))));
|
|
11
|
+
};
|
|
12
|
+
const makeBunLayer = (authOverride)=>{
|
|
13
|
+
const ghLayer = authOverride ? GitHubClientLive.pipe(Layer.provide(authOverride)) : GitHubLayer;
|
|
14
|
+
return BunResolverLive.pipe(Layer.provide(AutoBunCacheLive.pipe(Layer.provide(BunVersionFetcherLive.pipe(Layer.provide(ghLayer))))));
|
|
15
|
+
};
|
|
16
|
+
const makeDenoLayer = (authOverride)=>{
|
|
17
|
+
const ghLayer = authOverride ? GitHubClientLive.pipe(Layer.provide(authOverride)) : GitHubLayer;
|
|
18
|
+
return DenoResolverLive.pipe(Layer.provide(AutoDenoCacheLive.pipe(Layer.provide(DenoVersionFetcherLive.pipe(Layer.provide(ghLayer))))));
|
|
19
|
+
};
|
|
11
20
|
const SCHEMA_URL = "https://raw.githubusercontent.com/spencerbeggs/runtime-resolver/main/runtime-resolver.schema.json";
|
|
12
21
|
const nodeOption = Options.text("node").pipe(Options.optional);
|
|
13
22
|
const bunOption = Options.text("bun").pipe(Options.optional);
|
|
@@ -17,7 +26,6 @@ const incrementsOption = Options.text("increments").pipe(Options.optional);
|
|
|
17
26
|
const nodeDefaultOption = Options.text("node-default").pipe(Options.optional);
|
|
18
27
|
const bunDefaultOption = Options.text("bun-default").pipe(Options.optional);
|
|
19
28
|
const denoDefaultOption = Options.text("deno-default").pipe(Options.optional);
|
|
20
|
-
const freshnessOption = Options.text("freshness").pipe(Options.optional);
|
|
21
29
|
const nodeDateOption = Options.text("node-date").pipe(Options.optional);
|
|
22
30
|
const prettyOption = Options.boolean("pretty").pipe(Options.withDefault(false));
|
|
23
31
|
const schemaOption = Options.boolean("schema").pipe(Options.withDefault(false));
|
|
@@ -36,11 +44,6 @@ const VALID_INCREMENTS = [
|
|
|
36
44
|
"minor",
|
|
37
45
|
"patch"
|
|
38
46
|
];
|
|
39
|
-
const VALID_FRESHNESS = [
|
|
40
|
-
"auto",
|
|
41
|
-
"api",
|
|
42
|
-
"cache"
|
|
43
|
-
];
|
|
44
47
|
const validatePhases = (raw)=>{
|
|
45
48
|
const phases = raw.split(",").map((s)=>s.trim());
|
|
46
49
|
for (const p of phases)if (!VALID_PHASES.includes(p)) throw new Error(`Invalid phase: "${p}". Valid values: ${VALID_PHASES.join(", ")}`);
|
|
@@ -50,10 +53,6 @@ const validateIncrements = (raw)=>{
|
|
|
50
53
|
if (!VALID_INCREMENTS.includes(raw)) throw new Error(`Invalid increments value: "${raw}". Valid values: ${VALID_INCREMENTS.join(", ")}`);
|
|
51
54
|
return raw;
|
|
52
55
|
};
|
|
53
|
-
const validateFreshness = (raw)=>{
|
|
54
|
-
if (!VALID_FRESHNESS.includes(raw)) throw new Error(`Invalid freshness value: "${raw}". Valid values: ${VALID_FRESHNESS.join(", ")}`);
|
|
55
|
-
return raw;
|
|
56
|
-
};
|
|
57
56
|
const serializeError = (error)=>{
|
|
58
57
|
if (error && "object" == typeof error && "_tag" in error && "message" in error) {
|
|
59
58
|
const { _tag, message, ...rest } = error;
|
|
@@ -92,7 +91,7 @@ const toError = (name, error)=>[
|
|
|
92
91
|
error: serializeError(error)
|
|
93
92
|
}
|
|
94
93
|
];
|
|
95
|
-
const resolveNode = (semverRange, phases, increments, defaultVersion, date,
|
|
94
|
+
const resolveNode = (semverRange, phases, increments, defaultVersion, date, authOverride)=>Effect.gen(function*() {
|
|
96
95
|
const resolver = yield* NodeResolver;
|
|
97
96
|
const result = yield* resolver.resolve({
|
|
98
97
|
semverRange,
|
|
@@ -107,14 +106,11 @@ const resolveNode = (semverRange, phases, increments, defaultVersion, date, fres
|
|
|
107
106
|
} : {},
|
|
108
107
|
...date ? {
|
|
109
108
|
date
|
|
110
|
-
} : {},
|
|
111
|
-
...freshness ? {
|
|
112
|
-
freshness
|
|
113
109
|
} : {}
|
|
114
110
|
});
|
|
115
111
|
return toSuccess("node", result);
|
|
116
|
-
}).pipe(Effect.provide(
|
|
117
|
-
const resolveBun = (semverRange, increments, defaultVersion,
|
|
112
|
+
}).pipe(Effect.provide(makeNodeLayer(authOverride)), Effect.catchAll((error)=>Effect.succeed(toError("node", error))));
|
|
113
|
+
const resolveBun = (semverRange, increments, defaultVersion, authOverride)=>Effect.gen(function*() {
|
|
118
114
|
const resolver = yield* BunResolver;
|
|
119
115
|
const result = yield* resolver.resolve({
|
|
120
116
|
semverRange,
|
|
@@ -123,14 +119,11 @@ const resolveBun = (semverRange, increments, defaultVersion, freshness, authOver
|
|
|
123
119
|
} : {},
|
|
124
120
|
...defaultVersion ? {
|
|
125
121
|
defaultVersion
|
|
126
|
-
} : {},
|
|
127
|
-
...freshness ? {
|
|
128
|
-
freshness
|
|
129
122
|
} : {}
|
|
130
123
|
});
|
|
131
124
|
return toSuccess("bun", result);
|
|
132
|
-
}).pipe(Effect.provide(
|
|
133
|
-
const resolveDeno = (semverRange, increments, defaultVersion,
|
|
125
|
+
}).pipe(Effect.provide(makeBunLayer(authOverride)), Effect.catchAll((error)=>Effect.succeed(toError("bun", error))));
|
|
126
|
+
const resolveDeno = (semverRange, increments, defaultVersion, authOverride)=>Effect.gen(function*() {
|
|
134
127
|
const resolver = yield* DenoResolver;
|
|
135
128
|
const result = yield* resolver.resolve({
|
|
136
129
|
semverRange,
|
|
@@ -139,13 +132,10 @@ const resolveDeno = (semverRange, increments, defaultVersion, freshness, authOve
|
|
|
139
132
|
} : {},
|
|
140
133
|
...defaultVersion ? {
|
|
141
134
|
defaultVersion
|
|
142
|
-
} : {},
|
|
143
|
-
...freshness ? {
|
|
144
|
-
freshness
|
|
145
135
|
} : {}
|
|
146
136
|
});
|
|
147
137
|
return toSuccess("deno", result);
|
|
148
|
-
}).pipe(Effect.provide(
|
|
138
|
+
}).pipe(Effect.provide(makeDenoLayer(authOverride)), Effect.catchAll((error)=>Effect.succeed(toError("deno", error))));
|
|
149
139
|
const formatResponse = (ok, results, pretty)=>{
|
|
150
140
|
const response = {
|
|
151
141
|
$schema: SCHEMA_URL,
|
|
@@ -162,7 +152,6 @@ const resolveHandler = (args)=>Effect.gen(function*() {
|
|
|
162
152
|
args.deno,
|
|
163
153
|
args.nodePhases,
|
|
164
154
|
args.increments,
|
|
165
|
-
args.freshness,
|
|
166
155
|
args.nodeDefault,
|
|
167
156
|
args.bunDefault,
|
|
168
157
|
args.denoDefault,
|
|
@@ -227,13 +216,6 @@ const resolveHandler = (args)=>Effect.gen(function*() {
|
|
|
227
216
|
yield* Console.error(e.message);
|
|
228
217
|
return;
|
|
229
218
|
}
|
|
230
|
-
let validatedFreshness;
|
|
231
|
-
if (Option.isSome(args.freshness)) try {
|
|
232
|
-
validatedFreshness = validateFreshness(args.freshness.value);
|
|
233
|
-
} catch (e) {
|
|
234
|
-
yield* Console.error(e.message);
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
219
|
let nodeDate;
|
|
238
220
|
if (Option.isSome(args.nodeDate)) {
|
|
239
221
|
nodeDate = new Date(args.nodeDate.value);
|
|
@@ -242,15 +224,15 @@ const resolveHandler = (args)=>Effect.gen(function*() {
|
|
|
242
224
|
const tasks = [];
|
|
243
225
|
if (hasNode) {
|
|
244
226
|
const nodeDefaultVersion = Option.isSome(args.nodeDefault) ? args.nodeDefault.value : void 0;
|
|
245
|
-
tasks.push(resolveNode(args.node.value, validatedPhases, validatedIncrements, nodeDefaultVersion, nodeDate,
|
|
227
|
+
tasks.push(resolveNode(args.node.value, validatedPhases, validatedIncrements, nodeDefaultVersion, nodeDate, authLayerOverride));
|
|
246
228
|
}
|
|
247
229
|
if (hasBun) {
|
|
248
230
|
const bunDefaultVersion = Option.isSome(args.bunDefault) ? args.bunDefault.value : void 0;
|
|
249
|
-
tasks.push(resolveBun(args.bun.value, validatedIncrements, bunDefaultVersion,
|
|
231
|
+
tasks.push(resolveBun(args.bun.value, validatedIncrements, bunDefaultVersion, authLayerOverride));
|
|
250
232
|
}
|
|
251
233
|
if (hasDeno) {
|
|
252
234
|
const denoDefaultVersion = Option.isSome(args.denoDefault) ? args.denoDefault.value : void 0;
|
|
253
|
-
tasks.push(resolveDeno(args.deno.value, validatedIncrements, denoDefaultVersion,
|
|
235
|
+
tasks.push(resolveDeno(args.deno.value, validatedIncrements, denoDefaultVersion, authLayerOverride));
|
|
254
236
|
}
|
|
255
237
|
const entries = yield* Effect.all(tasks, {
|
|
256
238
|
concurrency: "unbounded"
|
|
@@ -265,7 +247,6 @@ const rootCommand = Command.make("runtime-resolver", {
|
|
|
265
247
|
deno: denoOption,
|
|
266
248
|
nodePhases: nodePhasesOption,
|
|
267
249
|
increments: incrementsOption,
|
|
268
|
-
freshness: freshnessOption,
|
|
269
250
|
nodeDefault: nodeDefaultOption,
|
|
270
251
|
bunDefault: bunDefaultOption,
|
|
271
252
|
denoDefault: denoDefaultOption,
|
|
@@ -279,7 +260,7 @@ const rootCommand = Command.make("runtime-resolver", {
|
|
|
279
260
|
}, resolveHandler);
|
|
280
261
|
const cli = Command.run(rootCommand, {
|
|
281
262
|
name: "runtime-resolver",
|
|
282
|
-
version: "0.
|
|
263
|
+
version: "0.2.0"
|
|
283
264
|
});
|
|
284
265
|
const main = Effect.suspend(()=>cli(process.argv)).pipe(Effect.provide(NodeContext.layer), Effect.catchAllCause((cause)=>{
|
|
285
266
|
const defects = Cause.defects(cause);
|