runtime-resolver 0.1.0 → 0.3.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/711.js +5932 -0
- package/README.md +7 -65
- package/bin/runtime-resolver.js +26 -46
- package/index.d.ts +2480 -237
- package/index.js +127 -7
- package/{235.js → json-schema.js} +8 -2
- package/package.json +10 -8
- package/rslib-runtime.js +37 -0
- package/tsdoc-metadata.json +1 -1
- 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
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command, Options } from "@effect/cli";
|
|
3
3
|
import { NodeContext, NodeRuntime } from "@effect/platform-node";
|
|
4
|
+
import { Cause, Console, Effect, Layer, Option } from "effect";
|
|
4
5
|
import { readFileSync } from "node:fs";
|
|
5
|
-
import {
|
|
6
|
+
import { NodeScheduleFetcherLive, AutoDenoCacheLive, DenoVersionFetcherLive, AutoNodeCacheLive, BunResolver, NodeResolver, NodeVersionFetcherLive, NodeResolverLive, GitHubAutoAuth, BunResolverLive, BunVersionFetcherLive, GitHubClientLive, DenoResolverLive, GitHubTokenAuthFromToken, GitHubAppAuth, AutoBunCacheLive, DenoResolver } from "../711.js";
|
|
6
7
|
const GitHubLayer = GitHubClientLive.pipe(Layer.provide(GitHubAutoAuth));
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
|
|
8
|
+
const makeNodeLayer = (authOverride)=>{
|
|
9
|
+
const ghLayer = authOverride ? GitHubClientLive.pipe(Layer.provide(authOverride)) : GitHubLayer;
|
|
10
|
+
const fetchersLayer = Layer.merge(NodeVersionFetcherLive.pipe(Layer.provide(ghLayer)), NodeScheduleFetcherLive.pipe(Layer.provide(ghLayer)));
|
|
11
|
+
return NodeResolverLive.pipe(Layer.provide(AutoNodeCacheLive.pipe(Layer.provide(fetchersLayer))));
|
|
12
|
+
};
|
|
13
|
+
const makeBunLayer = (authOverride)=>{
|
|
14
|
+
const ghLayer = authOverride ? GitHubClientLive.pipe(Layer.provide(authOverride)) : GitHubLayer;
|
|
15
|
+
return BunResolverLive.pipe(Layer.provide(AutoBunCacheLive.pipe(Layer.provide(BunVersionFetcherLive.pipe(Layer.provide(ghLayer))))));
|
|
16
|
+
};
|
|
17
|
+
const makeDenoLayer = (authOverride)=>{
|
|
18
|
+
const ghLayer = authOverride ? GitHubClientLive.pipe(Layer.provide(authOverride)) : GitHubLayer;
|
|
19
|
+
return DenoResolverLive.pipe(Layer.provide(AutoDenoCacheLive.pipe(Layer.provide(DenoVersionFetcherLive.pipe(Layer.provide(ghLayer))))));
|
|
20
|
+
};
|
|
11
21
|
const SCHEMA_URL = "https://raw.githubusercontent.com/spencerbeggs/runtime-resolver/main/runtime-resolver.schema.json";
|
|
12
22
|
const nodeOption = Options.text("node").pipe(Options.optional);
|
|
13
23
|
const bunOption = Options.text("bun").pipe(Options.optional);
|
|
@@ -17,7 +27,6 @@ const incrementsOption = Options.text("increments").pipe(Options.optional);
|
|
|
17
27
|
const nodeDefaultOption = Options.text("node-default").pipe(Options.optional);
|
|
18
28
|
const bunDefaultOption = Options.text("bun-default").pipe(Options.optional);
|
|
19
29
|
const denoDefaultOption = Options.text("deno-default").pipe(Options.optional);
|
|
20
|
-
const freshnessOption = Options.text("freshness").pipe(Options.optional);
|
|
21
30
|
const nodeDateOption = Options.text("node-date").pipe(Options.optional);
|
|
22
31
|
const prettyOption = Options.boolean("pretty").pipe(Options.withDefault(false));
|
|
23
32
|
const schemaOption = Options.boolean("schema").pipe(Options.withDefault(false));
|
|
@@ -36,11 +45,6 @@ const VALID_INCREMENTS = [
|
|
|
36
45
|
"minor",
|
|
37
46
|
"patch"
|
|
38
47
|
];
|
|
39
|
-
const VALID_FRESHNESS = [
|
|
40
|
-
"auto",
|
|
41
|
-
"api",
|
|
42
|
-
"cache"
|
|
43
|
-
];
|
|
44
48
|
const validatePhases = (raw)=>{
|
|
45
49
|
const phases = raw.split(",").map((s)=>s.trim());
|
|
46
50
|
for (const p of phases)if (!VALID_PHASES.includes(p)) throw new Error(`Invalid phase: "${p}". Valid values: ${VALID_PHASES.join(", ")}`);
|
|
@@ -50,10 +54,6 @@ const validateIncrements = (raw)=>{
|
|
|
50
54
|
if (!VALID_INCREMENTS.includes(raw)) throw new Error(`Invalid increments value: "${raw}". Valid values: ${VALID_INCREMENTS.join(", ")}`);
|
|
51
55
|
return raw;
|
|
52
56
|
};
|
|
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
57
|
const serializeError = (error)=>{
|
|
58
58
|
if (error && "object" == typeof error && "_tag" in error && "message" in error) {
|
|
59
59
|
const { _tag, message, ...rest } = error;
|
|
@@ -92,7 +92,7 @@ const toError = (name, error)=>[
|
|
|
92
92
|
error: serializeError(error)
|
|
93
93
|
}
|
|
94
94
|
];
|
|
95
|
-
const resolveNode = (semverRange, phases, increments, defaultVersion, date,
|
|
95
|
+
const resolveNode = (semverRange, phases, increments, defaultVersion, date, authOverride)=>Effect.gen(function*() {
|
|
96
96
|
const resolver = yield* NodeResolver;
|
|
97
97
|
const result = yield* resolver.resolve({
|
|
98
98
|
semverRange,
|
|
@@ -107,14 +107,11 @@ const resolveNode = (semverRange, phases, increments, defaultVersion, date, fres
|
|
|
107
107
|
} : {},
|
|
108
108
|
...date ? {
|
|
109
109
|
date
|
|
110
|
-
} : {},
|
|
111
|
-
...freshness ? {
|
|
112
|
-
freshness
|
|
113
110
|
} : {}
|
|
114
111
|
});
|
|
115
112
|
return toSuccess("node", result);
|
|
116
|
-
}).pipe(Effect.provide(
|
|
117
|
-
const resolveBun = (semverRange, increments, defaultVersion,
|
|
113
|
+
}).pipe(Effect.provide(makeNodeLayer(authOverride)), Effect.catchAll((error)=>Effect.succeed(toError("node", error))));
|
|
114
|
+
const resolveBun = (semverRange, increments, defaultVersion, authOverride)=>Effect.gen(function*() {
|
|
118
115
|
const resolver = yield* BunResolver;
|
|
119
116
|
const result = yield* resolver.resolve({
|
|
120
117
|
semverRange,
|
|
@@ -123,14 +120,11 @@ const resolveBun = (semverRange, increments, defaultVersion, freshness, authOver
|
|
|
123
120
|
} : {},
|
|
124
121
|
...defaultVersion ? {
|
|
125
122
|
defaultVersion
|
|
126
|
-
} : {},
|
|
127
|
-
...freshness ? {
|
|
128
|
-
freshness
|
|
129
123
|
} : {}
|
|
130
124
|
});
|
|
131
125
|
return toSuccess("bun", result);
|
|
132
|
-
}).pipe(Effect.provide(
|
|
133
|
-
const resolveDeno = (semverRange, increments, defaultVersion,
|
|
126
|
+
}).pipe(Effect.provide(makeBunLayer(authOverride)), Effect.catchAll((error)=>Effect.succeed(toError("bun", error))));
|
|
127
|
+
const resolveDeno = (semverRange, increments, defaultVersion, authOverride)=>Effect.gen(function*() {
|
|
134
128
|
const resolver = yield* DenoResolver;
|
|
135
129
|
const result = yield* resolver.resolve({
|
|
136
130
|
semverRange,
|
|
@@ -139,13 +133,10 @@ const resolveDeno = (semverRange, increments, defaultVersion, freshness, authOve
|
|
|
139
133
|
} : {},
|
|
140
134
|
...defaultVersion ? {
|
|
141
135
|
defaultVersion
|
|
142
|
-
} : {},
|
|
143
|
-
...freshness ? {
|
|
144
|
-
freshness
|
|
145
136
|
} : {}
|
|
146
137
|
});
|
|
147
138
|
return toSuccess("deno", result);
|
|
148
|
-
}).pipe(Effect.provide(
|
|
139
|
+
}).pipe(Effect.provide(makeDenoLayer(authOverride)), Effect.catchAll((error)=>Effect.succeed(toError("deno", error))));
|
|
149
140
|
const formatResponse = (ok, results, pretty)=>{
|
|
150
141
|
const response = {
|
|
151
142
|
$schema: SCHEMA_URL,
|
|
@@ -162,7 +153,6 @@ const resolveHandler = (args)=>Effect.gen(function*() {
|
|
|
162
153
|
args.deno,
|
|
163
154
|
args.nodePhases,
|
|
164
155
|
args.increments,
|
|
165
|
-
args.freshness,
|
|
166
156
|
args.nodeDefault,
|
|
167
157
|
args.bunDefault,
|
|
168
158
|
args.denoDefault,
|
|
@@ -174,9 +164,7 @@ const resolveHandler = (args)=>Effect.gen(function*() {
|
|
|
174
164
|
];
|
|
175
165
|
const hasResolveFlags = resolveFlags.some((f)=>Option.isSome(f));
|
|
176
166
|
if (hasResolveFlags) return void (yield* Console.error("Error: --schema cannot be combined with resolve flags (--node, --bun, --deno, etc.)"));
|
|
177
|
-
const { cliJsonSchema } = yield* Effect.promise(()=>import("../
|
|
178
|
-
cliJsonSchema: mod.cliJsonSchema
|
|
179
|
-
})));
|
|
167
|
+
const { cliJsonSchema } = yield* Effect.promise(()=>import("../json-schema.js").then((m)=>m.json_schema_namespaceObject));
|
|
180
168
|
yield* Console.log(JSON.stringify(cliJsonSchema, null, 2));
|
|
181
169
|
return;
|
|
182
170
|
}
|
|
@@ -227,13 +215,6 @@ const resolveHandler = (args)=>Effect.gen(function*() {
|
|
|
227
215
|
yield* Console.error(e.message);
|
|
228
216
|
return;
|
|
229
217
|
}
|
|
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
218
|
let nodeDate;
|
|
238
219
|
if (Option.isSome(args.nodeDate)) {
|
|
239
220
|
nodeDate = new Date(args.nodeDate.value);
|
|
@@ -242,15 +223,15 @@ const resolveHandler = (args)=>Effect.gen(function*() {
|
|
|
242
223
|
const tasks = [];
|
|
243
224
|
if (hasNode) {
|
|
244
225
|
const nodeDefaultVersion = Option.isSome(args.nodeDefault) ? args.nodeDefault.value : void 0;
|
|
245
|
-
tasks.push(resolveNode(args.node.value, validatedPhases, validatedIncrements, nodeDefaultVersion, nodeDate,
|
|
226
|
+
tasks.push(resolveNode(args.node.value, validatedPhases, validatedIncrements, nodeDefaultVersion, nodeDate, authLayerOverride));
|
|
246
227
|
}
|
|
247
228
|
if (hasBun) {
|
|
248
229
|
const bunDefaultVersion = Option.isSome(args.bunDefault) ? args.bunDefault.value : void 0;
|
|
249
|
-
tasks.push(resolveBun(args.bun.value, validatedIncrements, bunDefaultVersion,
|
|
230
|
+
tasks.push(resolveBun(args.bun.value, validatedIncrements, bunDefaultVersion, authLayerOverride));
|
|
250
231
|
}
|
|
251
232
|
if (hasDeno) {
|
|
252
233
|
const denoDefaultVersion = Option.isSome(args.denoDefault) ? args.denoDefault.value : void 0;
|
|
253
|
-
tasks.push(resolveDeno(args.deno.value, validatedIncrements, denoDefaultVersion,
|
|
234
|
+
tasks.push(resolveDeno(args.deno.value, validatedIncrements, denoDefaultVersion, authLayerOverride));
|
|
254
235
|
}
|
|
255
236
|
const entries = yield* Effect.all(tasks, {
|
|
256
237
|
concurrency: "unbounded"
|
|
@@ -265,7 +246,6 @@ const rootCommand = Command.make("runtime-resolver", {
|
|
|
265
246
|
deno: denoOption,
|
|
266
247
|
nodePhases: nodePhasesOption,
|
|
267
248
|
increments: incrementsOption,
|
|
268
|
-
freshness: freshnessOption,
|
|
269
249
|
nodeDefault: nodeDefaultOption,
|
|
270
250
|
bunDefault: bunDefaultOption,
|
|
271
251
|
denoDefault: denoDefaultOption,
|
|
@@ -279,7 +259,7 @@ const rootCommand = Command.make("runtime-resolver", {
|
|
|
279
259
|
}, resolveHandler);
|
|
280
260
|
const cli = Command.run(rootCommand, {
|
|
281
261
|
name: "runtime-resolver",
|
|
282
|
-
version: "0.
|
|
262
|
+
version: "0.3.0"
|
|
283
263
|
});
|
|
284
264
|
const main = Effect.suspend(()=>cli(process.argv)).pipe(Effect.provide(NodeContext.layer), Effect.catchAllCause((cause)=>{
|
|
285
265
|
const defects = Cause.defects(cause);
|