@bomb.sh/router 0.7.0 → 0.8.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/README.md +50 -24
- package/esm/lib/checkpoint.js +26 -1
- package/esm/lib/command.d.ts +3 -2
- package/esm/lib/command.js +2 -1
- package/esm/lib/dynamic.d.ts +3 -3
- package/esm/lib/dynamic.js +20 -3
- package/esm/lib/extend.d.ts +3 -2
- package/esm/lib/extend.js +3 -1
- package/esm/lib/parse.js +6 -5
- package/esm/lib/pipeline.d.ts +1 -1
- package/esm/lib/route.d.ts +16 -3
- package/esm/lib/route.js +5 -2
- package/esm/lib/types.d.ts +4 -4
- package/esm/mod.d.ts +2 -1
- package/esm/mod.js +2 -1
- package/package.json +1 -1
- package/src/_dnt.polyfills.ts +0 -27
- package/src/lib/argument.ts +0 -100
- package/src/lib/bind.ts +0 -421
- package/src/lib/checkpoint.ts +0 -10
- package/src/lib/command.ts +0 -36
- package/src/lib/dasherize.ts +0 -7
- package/src/lib/decode.ts +0 -55
- package/src/lib/definition.ts +0 -17
- package/src/lib/dynamic.ts +0 -39
- package/src/lib/env.ts +0 -142
- package/src/lib/extend.ts +0 -12
- package/src/lib/maybe.ts +0 -22
- package/src/lib/multiple.ts +0 -23
- package/src/lib/option.ts +0 -57
- package/src/lib/param.ts +0 -96
- package/src/lib/parse.ts +0 -478
- package/src/lib/pipeline.ts +0 -549
- package/src/lib/print.ts +0 -216
- package/src/lib/read.ts +0 -133
- package/src/lib/rest.ts +0 -10
- package/src/lib/result.ts +0 -10
- package/src/lib/route.ts +0 -77
- package/src/lib/toggle.ts +0 -132
- package/src/lib/tokenize.ts +0 -74
- package/src/lib/tokenizer.ts +0 -191
- package/src/lib/transform.ts +0 -49
- package/src/lib/types.ts +0 -460
- package/src/lib/values.ts +0 -131
- package/src/mod.ts +0 -74
package/README.md
CHANGED
|
@@ -40,7 +40,6 @@ import {
|
|
|
40
40
|
name,
|
|
41
41
|
option,
|
|
42
42
|
route,
|
|
43
|
-
routes,
|
|
44
43
|
schema,
|
|
45
44
|
toggle,
|
|
46
45
|
version,
|
|
@@ -52,19 +51,15 @@ export const app = command(
|
|
|
52
51
|
description("Run and manage local service simulators."),
|
|
53
52
|
version("1.0.0"),
|
|
54
53
|
toggle(name("verbose")),
|
|
55
|
-
|
|
54
|
+
command(
|
|
55
|
+
name("serve"),
|
|
56
|
+
option(name("port"), schema(z.number().default(4000))),
|
|
57
|
+
),
|
|
58
|
+
route(
|
|
59
|
+
name("database"),
|
|
56
60
|
command(
|
|
57
|
-
name("
|
|
58
|
-
|
|
59
|
-
),
|
|
60
|
-
route(
|
|
61
|
-
name("database"),
|
|
62
|
-
routes(
|
|
63
|
-
command(
|
|
64
|
-
name("clean"),
|
|
65
|
-
toggle(name("dryRun")),
|
|
66
|
-
),
|
|
67
|
-
),
|
|
61
|
+
name("clean"),
|
|
62
|
+
toggle(name("dryRun")),
|
|
68
63
|
),
|
|
69
64
|
),
|
|
70
65
|
);
|
|
@@ -79,6 +74,10 @@ HELP /database
|
|
|
79
74
|
HELP /database/clean EXECUTE /database/clean
|
|
80
75
|
```
|
|
81
76
|
|
|
77
|
+
Nest `route()` and `command()` directly inside another route, command, or
|
|
78
|
+
`extend()`. Each child is mounted at that point in the pipeline. Use `extend()`
|
|
79
|
+
to package reusable groups of children and other elements.
|
|
80
|
+
|
|
82
81
|
The root name identifies the executable; it is not repeated in route IDs.
|
|
83
82
|
`simulacrum serve` therefore selects `/serve`, not `/simulacrum/serve`.
|
|
84
83
|
|
|
@@ -195,6 +194,32 @@ Dynamic phases serve two common cases:
|
|
|
195
194
|
route-extension mechanism. Both keep I/O in the application while preserving the
|
|
196
195
|
exact type of what parsing can produce next.
|
|
197
196
|
|
|
197
|
+
`dynamic(schema, extension)` requires a Standard Schema for the value supplied
|
|
198
|
+
to `resume()`. The schema's input type determines what `resume()` accepts; its
|
|
199
|
+
validated output is passed to the extension, including any schema transforms.
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
import { command, dynamic, extend, name, parse } from "@bomb.sh/router";
|
|
203
|
+
import * as z from "zod";
|
|
204
|
+
|
|
205
|
+
const app = command(
|
|
206
|
+
name("plugins"),
|
|
207
|
+
dynamic(
|
|
208
|
+
z.array(z.string()),
|
|
209
|
+
(plugins) => extend(...plugins.map((plugin) => command(name(plugin)))),
|
|
210
|
+
),
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
const step = parse(app, { argv: ["serve"] });
|
|
214
|
+
if (step.ok) {
|
|
215
|
+
const result = step.resume(["serve"]); // EXECUTE /serve
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Invalid input produces `unprocessable-content` with the schema's issues and
|
|
220
|
+
paths, without applying the extension. Validation must be synchronous; async
|
|
221
|
+
schemas also produce `unprocessable-content`.
|
|
222
|
+
|
|
198
223
|
### Help and version cross checkpoints
|
|
199
224
|
|
|
200
225
|
`--help` and `--version` request methods; they do not settle an intent or bypass
|
|
@@ -216,12 +241,12 @@ app --config app.json auth0 --help
|
|
|
216
241
|
|
|
217
242
|
Help and version are not escape hatches around configuration loading. Do not
|
|
218
243
|
inspect `argv` to skip a checkpoint. A phase may be required by `HELP`,
|
|
219
|
-
`VERSION`, or `EXECUTE`, so its driver work must be safe for all three:
|
|
220
|
-
loading
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
244
|
+
`VERSION`, or `EXECUTE`, so its driver work must be safe for all three: handle
|
|
245
|
+
loading failures in the application, avoid command side effects, and defer
|
|
246
|
+
execution until an `EXECUTE` intent. If discovery fails, report that failure
|
|
247
|
+
rather than printing incomplete help for an unresolved route graph. Routes
|
|
248
|
+
without dynamic phases still resolve directly; the rule is to stop only at an
|
|
249
|
+
intent or failure, never merely because the arguments look informational.
|
|
225
250
|
|
|
226
251
|
```ts
|
|
227
252
|
import process from "node:process";
|
|
@@ -234,7 +259,6 @@ import {
|
|
|
234
259
|
printErrors,
|
|
235
260
|
printHelp,
|
|
236
261
|
printVersion,
|
|
237
|
-
type Result,
|
|
238
262
|
schema,
|
|
239
263
|
type ValueSource,
|
|
240
264
|
version,
|
|
@@ -280,13 +304,15 @@ switch (result.method) {
|
|
|
280
304
|
break;
|
|
281
305
|
}
|
|
282
306
|
|
|
283
|
-
declare function load(path: string): Promise<
|
|
307
|
+
declare function load(path: string): Promise<ValueSource[]>;
|
|
284
308
|
```
|
|
285
309
|
|
|
286
310
|
The parser remains synchronous and performs no I/O. The caller loads the file
|
|
287
|
-
and resumes with a
|
|
288
|
-
|
|
289
|
-
the
|
|
311
|
+
and resumes directly with a value-source array. `checkpoint()` validates the
|
|
312
|
+
source names and the presence of their values; later parameter schemas validate
|
|
313
|
+
the contents. The application handles I/O failures before resuming. Unconsumed
|
|
314
|
+
CLI input survives the pause, so a later `--port 5000` can override the value
|
|
315
|
+
loaded from the file.
|
|
290
316
|
|
|
291
317
|
The same phase mechanism can add options or routes from runtime data. Parsing
|
|
292
318
|
then continues against the expanded route graph, and the continuation type
|
package/esm/lib/checkpoint.js
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
import { dynamic } from "./dynamic.js";
|
|
2
2
|
import { withValues } from "./values.js";
|
|
3
3
|
export function checkpoint() {
|
|
4
|
-
return dynamic((values) => withValues(values));
|
|
4
|
+
return dynamic(sources, (values) => withValues(values));
|
|
5
5
|
}
|
|
6
|
+
const sources = {
|
|
7
|
+
"~standard": {
|
|
8
|
+
version: 1,
|
|
9
|
+
vendor: "@bomb.sh/router",
|
|
10
|
+
validate(value) {
|
|
11
|
+
if (!Array.isArray(value)) {
|
|
12
|
+
return { issues: [{ message: "expected an array of value sources" }] };
|
|
13
|
+
}
|
|
14
|
+
let issues = [];
|
|
15
|
+
for (let [index, source] of value.entries()) {
|
|
16
|
+
if (source === null || typeof source !== "object") {
|
|
17
|
+
issues.push({ message: "expected a value source", path: [index] });
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
if (typeof source.name !== "string") {
|
|
21
|
+
issues.push({ message: "expected a string", path: [index, "name"] });
|
|
22
|
+
}
|
|
23
|
+
if (!("value" in source)) {
|
|
24
|
+
issues.push({ message: "expected a value", path: [index, "value"] });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return issues.length > 0 ? { issues } : { value: value };
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
};
|
package/esm/lib/command.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Fold, Materialize } from "./pipeline.js";
|
|
2
|
+
import { type Input, type Normalize, type Validate } from "./route.js";
|
|
2
3
|
import type { Definition, Done, Route } from "./types.js";
|
|
3
4
|
export type CommandZero<N extends string = string> = Route<N, "help" | "execute", [
|
|
4
5
|
Done<{}, []>
|
|
5
6
|
]>;
|
|
6
|
-
export declare function command<const N extends string, const E extends readonly
|
|
7
|
+
export declare function command<const N extends string, const E extends readonly Input[]>(start: Definition<N>, ...elements: E & Validate<CommandZero<N>, E>): Materialize<Fold<CommandZero<N>, Normalize<E>>>;
|
package/esm/lib/command.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { normalize, } from "./route.js";
|
|
1
2
|
export function command(start, ...elements) {
|
|
2
3
|
let zero = {
|
|
3
4
|
...start,
|
|
@@ -12,5 +13,5 @@ export function command(start, ...elements) {
|
|
|
12
13
|
envs: [],
|
|
13
14
|
}],
|
|
14
15
|
};
|
|
15
|
-
return elements.reduce((value, element) => element(value), zero);
|
|
16
|
+
return normalize(elements).reduce((value, element) => element(value), zero);
|
|
16
17
|
}
|
package/esm/lib/dynamic.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { AnyRoute } from "./types.js";
|
|
2
|
-
import { type AnyElement, type DynamicElement } from "./pipeline.js";
|
|
1
|
+
import type { AnyRoute, Schema } from "./types.js";
|
|
2
|
+
import { type AnyElement, type DynamicElement, type Element } from "./pipeline.js";
|
|
3
3
|
export type { ConjoinPhases, Seed } from "./pipeline.js";
|
|
4
4
|
export type PhaseOf<R extends AnyRoute> = R["phases"][0];
|
|
5
5
|
export type PhasesOf<R extends AnyRoute> = R["phases"];
|
|
6
|
-
export declare function dynamic<
|
|
6
|
+
export declare function dynamic<Input, Output, E extends Element>(schema: Schema<Input, Output>, extension: (requires: Output) => E): DynamicElement<Input, Extract<E, AnyElement>>;
|
package/esm/lib/dynamic.js
CHANGED
|
@@ -1,11 +1,28 @@
|
|
|
1
|
-
import { brand } from "./pipeline.js";
|
|
2
|
-
|
|
1
|
+
import { brand, } from "./pipeline.js";
|
|
2
|
+
// Constrain only the brand while inferring E. The full operation would
|
|
3
|
+
// contextually widen extend() tuples and erase their continuation types.
|
|
4
|
+
export function dynamic(schema, extension) {
|
|
3
5
|
return brand((route) => {
|
|
4
6
|
let phases = [...route.phases];
|
|
5
7
|
let phase = phases.pop();
|
|
6
8
|
phases.push({
|
|
7
9
|
...phase,
|
|
8
|
-
resolver
|
|
10
|
+
resolver(requirement) {
|
|
11
|
+
let result = schema["~standard"].validate(requirement);
|
|
12
|
+
if (result instanceof Promise) {
|
|
13
|
+
return {
|
|
14
|
+
ok: false,
|
|
15
|
+
issues: [{ message: "async schemas are not allowed" }],
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
if (result.issues) {
|
|
19
|
+
return { ok: false, issues: result.issues };
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
ok: true,
|
|
23
|
+
value: extension(result.value),
|
|
24
|
+
};
|
|
25
|
+
},
|
|
9
26
|
});
|
|
10
27
|
phases.push({
|
|
11
28
|
model: {
|
package/esm/lib/extend.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
|
|
1
|
+
import { type Input, type Normalize } from "./route.js";
|
|
2
|
+
import { type Extension } from "./pipeline.js";
|
|
3
|
+
export declare function extend<const E extends readonly Input[]>(...elements: E): Extension<Normalize<E>>;
|
package/esm/lib/extend.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { normalize } from "./route.js";
|
|
1
2
|
import { brand } from "./pipeline.js";
|
|
2
3
|
export function extend(...elements) {
|
|
3
|
-
|
|
4
|
+
let normalized = normalize(elements);
|
|
5
|
+
return brand((start) => normalized.reduce((value, element) => element(value), start));
|
|
4
6
|
}
|
package/esm/lib/parse.js
CHANGED
|
@@ -118,16 +118,17 @@ function advance(state) {
|
|
|
118
118
|
ok: true,
|
|
119
119
|
route: segment.id,
|
|
120
120
|
model: binding.model,
|
|
121
|
-
resume(
|
|
122
|
-
if (!result.ok) {
|
|
123
|
-
return unprocessableContent(segment, result.issues);
|
|
124
|
-
}
|
|
121
|
+
resume(value) {
|
|
125
122
|
// AnyPhase erases these exact types with `never`; dynamic() already
|
|
126
123
|
// proved them at its public boundary.
|
|
127
124
|
let resolver = phase.resolver;
|
|
125
|
+
let result = resolver(value);
|
|
126
|
+
if (!result.ok) {
|
|
127
|
+
return unprocessableContent(segment, result.issues);
|
|
128
|
+
}
|
|
128
129
|
// The extension operates against the same aggregate route metadata,
|
|
129
130
|
// but begins with one fresh, empty phase.
|
|
130
|
-
let continuation =
|
|
131
|
+
let continuation = result.value(seed(segment.route));
|
|
131
132
|
let phases = stitch(continuation.phases, segment.phases.slice(1));
|
|
132
133
|
let history = [...segment.history, phase];
|
|
133
134
|
let next = {
|
package/esm/lib/pipeline.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AddRoutesToLast, AnyPhase, AnyPhases, AnyRoute, Done, Method, MethodsOf, Next, Phase, Route } from "./types.js";
|
|
2
|
-
export type Element<D
|
|
2
|
+
export type Element<D = unknown> = {
|
|
3
3
|
readonly [operation]: D;
|
|
4
4
|
};
|
|
5
5
|
export type AnyElement = Element<Delta>;
|
package/esm/lib/route.d.ts
CHANGED
|
@@ -1,9 +1,22 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type Fold, type Materialize, type MethodElement, type RoutesElement, type Unary } from "./pipeline.js";
|
|
2
2
|
import type { AnyRoute, Definition, Done, Route } from "./types.js";
|
|
3
3
|
export type RouteZero<N extends string = string> = Route<N, "help", [
|
|
4
4
|
Done<{}, []>
|
|
5
5
|
]>;
|
|
6
|
-
export declare function route<const N extends string, const E extends readonly
|
|
6
|
+
export declare function route<const N extends string, const E extends readonly Input[]>(start: Definition<N>, ...elements: E & Validate<RouteZero<N>, E>): Materialize<Fold<RouteZero<N>, Normalize<E>>>;
|
|
7
7
|
export declare function version(semver: string): MethodElement<"version">;
|
|
8
8
|
export declare function executable(): MethodElement<"execute">;
|
|
9
|
-
export
|
|
9
|
+
export type Input = Unary | AnyRoute;
|
|
10
|
+
export type Normalize<E extends readonly Input[]> = {
|
|
11
|
+
[K in keyof E]: Normalized<E[K]>;
|
|
12
|
+
};
|
|
13
|
+
export type Validate<S, E extends readonly Input[]> = [
|
|
14
|
+
Fold<S, Normalize<E>>
|
|
15
|
+
] extends [never] ? Invalid<S, E> : unknown;
|
|
16
|
+
export declare function normalize<const E extends readonly Input[]>(elements: E): Normalize<E>;
|
|
17
|
+
type Normalized<E extends Input> = E extends Unary ? E : E extends AnyRoute ? RoutesElement<readonly [E]> : never;
|
|
18
|
+
type Invalid<S, E extends readonly Input[]> = E extends readonly [
|
|
19
|
+
infer Head extends Input,
|
|
20
|
+
...infer Tail extends readonly Input[]
|
|
21
|
+
] ? Fold<S, readonly [Normalized<Head>]> extends infer Next ? [Next] extends [never] ? readonly [never, ...Tail] : readonly [Head, ...Invalid<Next, Tail>] : never : E;
|
|
22
|
+
export {};
|
package/esm/lib/route.js
CHANGED
|
@@ -14,7 +14,7 @@ export function route(start, ...elements) {
|
|
|
14
14
|
envs: [],
|
|
15
15
|
}],
|
|
16
16
|
};
|
|
17
|
-
return elements.reduce((value, element) => element(value), zero);
|
|
17
|
+
return normalize(elements).reduce((value, element) => element(value), zero);
|
|
18
18
|
}
|
|
19
19
|
export function version(semver) {
|
|
20
20
|
return brand((route) => ({
|
|
@@ -29,7 +29,10 @@ export function executable() {
|
|
|
29
29
|
methods: [...route.methods, "execute"],
|
|
30
30
|
}));
|
|
31
31
|
}
|
|
32
|
-
export function
|
|
32
|
+
export function normalize(elements) {
|
|
33
|
+
return elements.map((element) => typeof element === "function" ? element : routes(element));
|
|
34
|
+
}
|
|
35
|
+
function routes(...children) {
|
|
33
36
|
return brand((route) => {
|
|
34
37
|
let phases = [...route.phases];
|
|
35
38
|
let phase = phases.pop();
|
package/esm/lib/types.d.ts
CHANGED
|
@@ -27,7 +27,7 @@ export type Next<Model extends object, Routes extends readonly AnyRoute[], T> =
|
|
|
27
27
|
readonly routes: Routes;
|
|
28
28
|
readonly values: readonly ValueSource[];
|
|
29
29
|
readonly envs: readonly EnvSource[];
|
|
30
|
-
readonly resolver: (requirement: T) => (input: AnyRoute) => AnyRoute
|
|
30
|
+
readonly resolver: (requirement: T) => Result<(input: AnyRoute) => AnyRoute>;
|
|
31
31
|
};
|
|
32
32
|
export type Done<Model extends object, Routes extends readonly AnyRoute[]> = {
|
|
33
33
|
readonly [phaseModel]?: Model;
|
|
@@ -44,7 +44,7 @@ export interface ParseIncrement<R extends AnyRoute, P extends RoutePath = "/", M
|
|
|
44
44
|
readonly ok: true;
|
|
45
45
|
readonly route: P;
|
|
46
46
|
readonly model: IncrementModelOf<R>;
|
|
47
|
-
resume(
|
|
47
|
+
resume(value: RequirementOf<R>): Outcome<ParseAt<ContinuationOf<R>, P, Models>>;
|
|
48
48
|
}
|
|
49
49
|
export type ContinuationOf<R extends AnyRoute> = Route<R["name"], MethodsOf<R>, SettleNext<R["phases"]>>;
|
|
50
50
|
export type ModelOf<R extends AnyRoute, P extends RoutePath = "/"> = ModelOfRoute<RouteAt<R, P>>;
|
|
@@ -65,7 +65,7 @@ export interface AnyPhase {
|
|
|
65
65
|
readonly routes: readonly AnyRoute[];
|
|
66
66
|
readonly values: readonly ValueSource[];
|
|
67
67
|
readonly envs: readonly EnvSource[];
|
|
68
|
-
readonly resolver?: (requirement: never) => (route: never) => AnyRoute
|
|
68
|
+
readonly resolver?: (requirement: never) => Result<(route: never) => AnyRoute>;
|
|
69
69
|
}
|
|
70
70
|
export type AnyPhases = readonly [AnyPhase, ...AnyPhase[]];
|
|
71
71
|
export type Method = "help" | "version" | "execute";
|
|
@@ -154,7 +154,7 @@ type NextModelIn<P extends readonly AnyPhase[]> = P extends readonly [
|
|
|
154
154
|
...infer Tail extends readonly AnyPhase[]
|
|
155
155
|
] ? Head extends Next<infer Model, readonly AnyRoute[], infer _Requirement> ? Model : NextModelIn<Tail> : never;
|
|
156
156
|
type RequirementIn<P extends AnyPhase> = P extends {
|
|
157
|
-
readonly resolver: (requirement: infer Requirement) => (route: AnyRoute) => AnyRoute
|
|
157
|
+
readonly resolver: (requirement: infer Requirement) => Result<(route: AnyRoute) => AnyRoute>;
|
|
158
158
|
} ? Requirement : never;
|
|
159
159
|
type RequirementInPhases<P extends readonly AnyPhase[]> = P extends readonly [
|
|
160
160
|
infer Head extends AnyPhase,
|
package/esm/mod.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export { extend } from "./lib/extend.js";
|
|
|
4
4
|
export { command } from "./lib/command.js";
|
|
5
5
|
export type { CommandZero } from "./lib/command.js";
|
|
6
6
|
export { checkpoint } from "./lib/checkpoint.js";
|
|
7
|
+
export { dynamic } from "./lib/dynamic.js";
|
|
7
8
|
export { description, name } from "./lib/definition.js";
|
|
8
9
|
export { env, withEnvs } from "./lib/env.js";
|
|
9
10
|
export type { Environment, EnvSource } from "./lib/env.js";
|
|
@@ -18,7 +19,7 @@ export { printErrors, printHelp, printVersion } from "./lib/print.js";
|
|
|
18
19
|
export type { Rest } from "./lib/rest.js";
|
|
19
20
|
export { cli } from "./lib/read.js";
|
|
20
21
|
export type { CLIOptions, CLIRead, ReadCLI, Symbol as CLISymbol, } from "./lib/read.js";
|
|
21
|
-
export { executable, route,
|
|
22
|
+
export { executable, route, version } from "./lib/route.js";
|
|
22
23
|
export type { RouteZero } from "./lib/route.js";
|
|
23
24
|
export { toggle } from "./lib/toggle.js";
|
|
24
25
|
export { transform } from "./lib/transform.js";
|
package/esm/mod.js
CHANGED
|
@@ -3,6 +3,7 @@ export { argument } from "./lib/argument.js";
|
|
|
3
3
|
export { extend } from "./lib/extend.js";
|
|
4
4
|
export { command } from "./lib/command.js";
|
|
5
5
|
export { checkpoint } from "./lib/checkpoint.js";
|
|
6
|
+
export { dynamic } from "./lib/dynamic.js";
|
|
6
7
|
export { description, name } from "./lib/definition.js";
|
|
7
8
|
export { env, withEnvs } from "./lib/env.js";
|
|
8
9
|
export { option } from "./lib/option.js";
|
|
@@ -12,7 +13,7 @@ export { parse } from "./lib/parse.js";
|
|
|
12
13
|
export { mark } from "./lib/pipeline.js";
|
|
13
14
|
export { printErrors, printHelp, printVersion } from "./lib/print.js";
|
|
14
15
|
export { cli } from "./lib/read.js";
|
|
15
|
-
export { executable, route,
|
|
16
|
+
export { executable, route, version } from "./lib/route.js";
|
|
16
17
|
export { toggle } from "./lib/toggle.js";
|
|
17
18
|
export { transform } from "./lib/transform.js";
|
|
18
19
|
export { withValues } from "./lib/values.js";
|
package/package.json
CHANGED
package/src/_dnt.polyfills.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
// https://github.com/tc39/proposal-accessible-object-hasownproperty/blob/main/polyfill.js
|
|
2
|
-
if (!Object.hasOwn) {
|
|
3
|
-
Object.defineProperty(Object, "hasOwn", {
|
|
4
|
-
value: function (object: any, property: any) {
|
|
5
|
-
if (object == null) {
|
|
6
|
-
throw new TypeError("Cannot convert undefined or null to object");
|
|
7
|
-
}
|
|
8
|
-
return Object.prototype.hasOwnProperty.call(Object(object), property);
|
|
9
|
-
},
|
|
10
|
-
configurable: true,
|
|
11
|
-
enumerable: false,
|
|
12
|
-
writable: true,
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
declare global {
|
|
17
|
-
interface Object {
|
|
18
|
-
/**
|
|
19
|
-
* Determines whether an object has a property with the specified name.
|
|
20
|
-
* @param o An object.
|
|
21
|
-
* @param v A property name.
|
|
22
|
-
*/
|
|
23
|
-
hasOwn(o: object, v: PropertyKey): boolean;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export {};
|
package/src/lib/argument.ts
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import { type AnyParam, type Param, param, type ParamModel } from "./param.js";
|
|
2
|
-
import {
|
|
3
|
-
brand,
|
|
4
|
-
type Check,
|
|
5
|
-
type Fold,
|
|
6
|
-
type ModelElement,
|
|
7
|
-
type Unary,
|
|
8
|
-
} from "./pipeline.js";
|
|
9
|
-
import type { CLIRead, ReadCLI } from "./read.js";
|
|
10
|
-
import type { Word } from "./tokenize.js";
|
|
11
|
-
import type { AnyRoute, Definition } from "./types.js";
|
|
12
|
-
|
|
13
|
-
export function argument<
|
|
14
|
-
const N extends string,
|
|
15
|
-
const E extends readonly Unary[],
|
|
16
|
-
>(
|
|
17
|
-
named: Definition<N>,
|
|
18
|
-
...elements: E & Check<Zero<N>, E>
|
|
19
|
-
): ElementOf<N, Fold<Zero<N>, E>> {
|
|
20
|
-
const added = elements.reduce<unknown>(
|
|
21
|
-
(value, element) => element(value as never),
|
|
22
|
-
param(named, positional),
|
|
23
|
-
) as AnyParam;
|
|
24
|
-
|
|
25
|
-
return brand<ElementOf<N, Fold<Zero<N>, E>>>(
|
|
26
|
-
(route: AnyRoute) => {
|
|
27
|
-
let phases = [...route.phases];
|
|
28
|
-
let phase = phases.pop()!;
|
|
29
|
-
phases.push({
|
|
30
|
-
...phase,
|
|
31
|
-
model: {
|
|
32
|
-
params: {
|
|
33
|
-
...phase.model.params,
|
|
34
|
-
[added.name]: added,
|
|
35
|
-
},
|
|
36
|
-
steps: phase.model.steps.concat((current, bindings) => ({
|
|
37
|
-
ok: true,
|
|
38
|
-
value: {
|
|
39
|
-
...current,
|
|
40
|
-
[added.name]: bindings[added.name],
|
|
41
|
-
},
|
|
42
|
-
})),
|
|
43
|
-
},
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
return {
|
|
47
|
-
...route,
|
|
48
|
-
phases,
|
|
49
|
-
};
|
|
50
|
-
},
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
type Zero<N extends string> = Param<N, unknown, "one">;
|
|
55
|
-
|
|
56
|
-
type ElementOf<N extends string, P> = P extends
|
|
57
|
-
Param<N, infer Model, infer _Cardinality> ? ModelElement<ParamModel<N, Model>>
|
|
58
|
-
: never;
|
|
59
|
-
|
|
60
|
-
function positional<P extends AnyParam>(param: P): P {
|
|
61
|
-
return {
|
|
62
|
-
...param,
|
|
63
|
-
cli: {
|
|
64
|
-
read,
|
|
65
|
-
syntax: {
|
|
66
|
-
type: "argument",
|
|
67
|
-
label: `<${param.name.toUpperCase()}>`,
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const read: ReadCLI = (tokens): CLIRead => {
|
|
74
|
-
let claim = tokens.claimOne((token): token is Word => token.type === "word");
|
|
75
|
-
let [word] = claim.tokens;
|
|
76
|
-
|
|
77
|
-
return word
|
|
78
|
-
? {
|
|
79
|
-
claim,
|
|
80
|
-
result: {
|
|
81
|
-
ok: true,
|
|
82
|
-
value: { exists: true, value: word.text },
|
|
83
|
-
issues: [],
|
|
84
|
-
},
|
|
85
|
-
}
|
|
86
|
-
: nothing(tokens);
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
function nothing(tokens: Parameters<ReadCLI>[0]): CLIRead {
|
|
90
|
-
let claim = tokens.claimAll(() => false);
|
|
91
|
-
|
|
92
|
-
return {
|
|
93
|
-
claim,
|
|
94
|
-
result: {
|
|
95
|
-
ok: true,
|
|
96
|
-
value: { exists: false },
|
|
97
|
-
issues: [],
|
|
98
|
-
},
|
|
99
|
-
};
|
|
100
|
-
}
|