@bomb.sh/router 0.7.0 → 0.7.1
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/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/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
|
-
}
|
package/src/lib/bind.ts
DELETED
|
@@ -1,421 +0,0 @@
|
|
|
1
|
-
import { Just, type Maybe } from "./maybe.js";
|
|
2
|
-
import type { AnyParam, Param } from "./param.js";
|
|
3
|
-
import type { CLIRead, Symbol } from "./read.js";
|
|
4
|
-
import type { Rest } from "./rest.js";
|
|
5
|
-
import type { Result } from "./result.js";
|
|
6
|
-
import type { Word } from "./tokenize.js";
|
|
7
|
-
import type { TokenInput, Tokenizer, TokenRange } from "./tokenizer.js";
|
|
8
|
-
import type { AnyPhase, Issue, OutputOf, Path } from "./types.js";
|
|
9
|
-
|
|
10
|
-
export interface Binding<T> {
|
|
11
|
-
readonly rest: Rest;
|
|
12
|
-
readonly result: Result<T>;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface PhaseBinding {
|
|
16
|
-
readonly rest: Rest;
|
|
17
|
-
readonly model: Record<string, unknown>;
|
|
18
|
-
readonly issues: Issue[];
|
|
19
|
-
readonly valid: boolean;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface PhaseSegment {
|
|
23
|
-
readonly range: TokenRange;
|
|
24
|
-
readonly path: Path;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function fromCLI<P extends Param<string, unknown, "one">>(options: {
|
|
28
|
-
readonly param: P;
|
|
29
|
-
readonly view: TokenInput<Symbol>;
|
|
30
|
-
readonly rest: Rest;
|
|
31
|
-
}): Maybe<Binding<OutputOf<P["schema"]>>> {
|
|
32
|
-
let { param, view, rest } = options;
|
|
33
|
-
return fromRead(param, param.cli.read(view), rest);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function fromValues<P extends AnyParam>(options: {
|
|
37
|
-
readonly param: P;
|
|
38
|
-
readonly route: Path;
|
|
39
|
-
readonly rest: Rest;
|
|
40
|
-
}): Maybe<Binding<OutputOf<P["schema"]>>> {
|
|
41
|
-
let { param, route, rest } = options;
|
|
42
|
-
let claim = rest.values.claim({
|
|
43
|
-
route,
|
|
44
|
-
address: [param.name],
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
if (!claim.result.exists) {
|
|
48
|
-
return { exists: false };
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return {
|
|
52
|
-
exists: true,
|
|
53
|
-
value: {
|
|
54
|
-
rest: {
|
|
55
|
-
...rest,
|
|
56
|
-
values: claim.rest,
|
|
57
|
-
},
|
|
58
|
-
result: validate(param, claim.result.value.value, [param.name]),
|
|
59
|
-
},
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export function fromEnv<P extends AnyParam>(options: {
|
|
64
|
-
readonly param: P;
|
|
65
|
-
readonly route: Path;
|
|
66
|
-
readonly rest: Rest;
|
|
67
|
-
}): Maybe<Binding<OutputOf<P["schema"]>>> {
|
|
68
|
-
let { param, route, rest } = options;
|
|
69
|
-
let claim = rest.envs.claim({
|
|
70
|
-
route,
|
|
71
|
-
address: [param.name],
|
|
72
|
-
key: param.env,
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
if (!claim.result.exists) {
|
|
76
|
-
return { exists: false };
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
let value = claim.result.value.value;
|
|
80
|
-
|
|
81
|
-
let candidates = param.cardinality === "one"
|
|
82
|
-
? param.decode(value)
|
|
83
|
-
: param.decode([value]);
|
|
84
|
-
|
|
85
|
-
return {
|
|
86
|
-
exists: true,
|
|
87
|
-
value: {
|
|
88
|
-
rest: {
|
|
89
|
-
...rest,
|
|
90
|
-
envs: claim.rest,
|
|
91
|
-
},
|
|
92
|
-
result: decode(param, value, candidates, [param.name]),
|
|
93
|
-
},
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export function bindPhase(options: {
|
|
98
|
-
readonly phase: AnyPhase;
|
|
99
|
-
readonly segment: PhaseSegment;
|
|
100
|
-
readonly rest: Rest;
|
|
101
|
-
}): PhaseBinding {
|
|
102
|
-
let { phase, segment } = options;
|
|
103
|
-
let rest = options.rest;
|
|
104
|
-
let params = Object.values(phase.model.params) as AnyParam[];
|
|
105
|
-
let pending = new Map(params.map((param) => [param.name, param]));
|
|
106
|
-
let results = new Map<string, Result<unknown>>();
|
|
107
|
-
|
|
108
|
-
function settle(
|
|
109
|
-
param: AnyParam,
|
|
110
|
-
binding: Binding<unknown>,
|
|
111
|
-
): void {
|
|
112
|
-
rest = binding.rest;
|
|
113
|
-
results.set(param.name, binding.result);
|
|
114
|
-
pending.delete(param.name);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function accept(
|
|
118
|
-
param: AnyParam,
|
|
119
|
-
attempt: Maybe<Binding<unknown>>,
|
|
120
|
-
): void {
|
|
121
|
-
if (!attempt.exists) {
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
settle(param, attempt.value);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
let captures = new Map<string, Capture>();
|
|
129
|
-
|
|
130
|
-
// Every pending reader proposes a claim against the same immutable view.
|
|
131
|
-
// Commit the proposal beginning earliest in argv, then recompute the view.
|
|
132
|
-
// This lets `--port 9000` outrank a positional claim on `9000` without
|
|
133
|
-
// attaching binding-policy metadata to either reader.
|
|
134
|
-
while (true) {
|
|
135
|
-
let horizon = first(rest.tokens, segment.range);
|
|
136
|
-
let offer: {
|
|
137
|
-
param: AnyParam;
|
|
138
|
-
read: CLIRead;
|
|
139
|
-
index: number;
|
|
140
|
-
} | undefined;
|
|
141
|
-
|
|
142
|
-
for (let param of pending.values()) {
|
|
143
|
-
let view = rest.tokens.view({
|
|
144
|
-
range: segment.range,
|
|
145
|
-
through: horizon?.index,
|
|
146
|
-
});
|
|
147
|
-
let read = param.cli.read(view);
|
|
148
|
-
|
|
149
|
-
if (read.result.ok && !read.result.value.exists) {
|
|
150
|
-
continue;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
let index = earliest(read.claim.tokens);
|
|
154
|
-
if (!offer || index < offer.index) {
|
|
155
|
-
offer = { param, read, index };
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
if (!offer) {
|
|
160
|
-
break;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
if (offer.param.cardinality === "many") {
|
|
164
|
-
rest = {
|
|
165
|
-
...rest,
|
|
166
|
-
tokens: offer.read.claim.rest,
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
let current = captures.get(offer.param.name) ?? {
|
|
170
|
-
param: offer.param,
|
|
171
|
-
values: [],
|
|
172
|
-
issues: [],
|
|
173
|
-
failed: false,
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
current.issues.push(...offer.read.result.issues ?? []);
|
|
177
|
-
|
|
178
|
-
if (offer.read.result.ok && offer.read.result.value.exists) {
|
|
179
|
-
current.values.push(offer.read.result.value.value);
|
|
180
|
-
} else {
|
|
181
|
-
current.failed = true;
|
|
182
|
-
}
|
|
183
|
-
captures.set(offer.param.name, current);
|
|
184
|
-
|
|
185
|
-
continue;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
accept(offer.param, fromRead(offer.param, offer.read, rest));
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// deal wil all multiple
|
|
192
|
-
for (let capture of captures.values()) {
|
|
193
|
-
if (capture.failed) {
|
|
194
|
-
settle(capture.param, {
|
|
195
|
-
rest,
|
|
196
|
-
result: { ok: false, issues: capture.issues },
|
|
197
|
-
});
|
|
198
|
-
} else {
|
|
199
|
-
let values = capture.values;
|
|
200
|
-
|
|
201
|
-
let candidates: Maybe<unknown[]>[] = values.every(
|
|
202
|
-
(value): value is string => typeof value === "string",
|
|
203
|
-
)
|
|
204
|
-
? capture.param.decode(values)
|
|
205
|
-
: [Just(values)];
|
|
206
|
-
|
|
207
|
-
let result = merge(
|
|
208
|
-
decode(capture.param, values, candidates, [capture.param.name]),
|
|
209
|
-
capture.issues,
|
|
210
|
-
);
|
|
211
|
-
settle(capture.param, { result, rest });
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// Address sources have stable visibility once the route is known. They are
|
|
216
|
-
// tried only after CLI has reached its fixed point so a provisional CLI miss
|
|
217
|
-
// cannot let a lower-priority source settle the parameter too early.
|
|
218
|
-
for (let source of [fromEnv, fromValues]) {
|
|
219
|
-
for (let param of pending.values()) {
|
|
220
|
-
accept(
|
|
221
|
-
param,
|
|
222
|
-
source({
|
|
223
|
-
param,
|
|
224
|
-
route: segment.path,
|
|
225
|
-
rest,
|
|
226
|
-
}),
|
|
227
|
-
);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// Only total absence reaches the schema as undefined. This is where required,
|
|
232
|
-
// optional, and defaulting parameters diverge.
|
|
233
|
-
for (let param of pending.values()) {
|
|
234
|
-
results.set(param.name, validate(param, undefined, [param.name]));
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
let bindings: Record<string, unknown> = {};
|
|
238
|
-
let issues: Issue[] = [];
|
|
239
|
-
let valid = true;
|
|
240
|
-
|
|
241
|
-
// Collect in declaration order, independent of the source or sweep that
|
|
242
|
-
// settled each parameter.
|
|
243
|
-
for (let param of params) {
|
|
244
|
-
let result = results.get(param.name)!;
|
|
245
|
-
issues.push(...result.issues ?? []);
|
|
246
|
-
if (result.ok) {
|
|
247
|
-
bindings[param.name] = result.value;
|
|
248
|
-
} else {
|
|
249
|
-
valid = false;
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
let model = {};
|
|
254
|
-
for (let step of phase.model.steps) {
|
|
255
|
-
let result = step(model, bindings);
|
|
256
|
-
if (!result.ok) {
|
|
257
|
-
valid = false;
|
|
258
|
-
issues.push(...result.issues);
|
|
259
|
-
break;
|
|
260
|
-
} else {
|
|
261
|
-
model = result.value;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
return { rest, model, issues, valid };
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
function fromRead<P extends Param<string, unknown, "one">>(
|
|
269
|
-
param: P,
|
|
270
|
-
read: CLIRead,
|
|
271
|
-
rest: Rest,
|
|
272
|
-
): Maybe<Binding<OutputOf<P["schema"]>>> {
|
|
273
|
-
let path = [param.name];
|
|
274
|
-
|
|
275
|
-
if (!read.result.ok) {
|
|
276
|
-
return {
|
|
277
|
-
exists: true,
|
|
278
|
-
value: {
|
|
279
|
-
rest: {
|
|
280
|
-
...rest,
|
|
281
|
-
tokens: read.claim.rest,
|
|
282
|
-
},
|
|
283
|
-
result: read.result,
|
|
284
|
-
},
|
|
285
|
-
};
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
if (!read.result.value.exists) {
|
|
289
|
-
return { exists: false };
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
let value = read.result.value.value;
|
|
293
|
-
|
|
294
|
-
let candidates = typeof value === "string"
|
|
295
|
-
? param.decode(value)
|
|
296
|
-
: [Just(value)];
|
|
297
|
-
let result = merge(
|
|
298
|
-
decode(param, value, candidates, path),
|
|
299
|
-
read.result.issues,
|
|
300
|
-
);
|
|
301
|
-
|
|
302
|
-
return {
|
|
303
|
-
exists: true,
|
|
304
|
-
value: {
|
|
305
|
-
rest: {
|
|
306
|
-
...rest,
|
|
307
|
-
tokens: read.claim.rest,
|
|
308
|
-
},
|
|
309
|
-
result,
|
|
310
|
-
},
|
|
311
|
-
};
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
function decode<P extends AnyParam>(
|
|
315
|
-
param: P,
|
|
316
|
-
value: unknown,
|
|
317
|
-
candidates: Maybe<unknown>[],
|
|
318
|
-
path: string[],
|
|
319
|
-
): Result<OutputOf<P["schema"]>> {
|
|
320
|
-
if (candidates.length === 0) {
|
|
321
|
-
return {
|
|
322
|
-
ok: false,
|
|
323
|
-
issues: [{
|
|
324
|
-
message: `unable to decode ${JSON.stringify(value)}`,
|
|
325
|
-
path,
|
|
326
|
-
}],
|
|
327
|
-
};
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
let issues: readonly Issue[] | undefined;
|
|
331
|
-
|
|
332
|
-
let values = candidates.flatMap((candidate) =>
|
|
333
|
-
candidate.exists ? [candidate.value] : []
|
|
334
|
-
);
|
|
335
|
-
|
|
336
|
-
for (let candidate of values) {
|
|
337
|
-
let result = validate(param, candidate, path);
|
|
338
|
-
if (result.ok) {
|
|
339
|
-
return result;
|
|
340
|
-
}
|
|
341
|
-
issues = issues ?? result.issues;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
return {
|
|
345
|
-
ok: false,
|
|
346
|
-
issues: issues ?? [],
|
|
347
|
-
};
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
function validate<P extends AnyParam>(
|
|
351
|
-
param: P,
|
|
352
|
-
value: unknown,
|
|
353
|
-
path: string[],
|
|
354
|
-
): Result<OutputOf<P["schema"]>> {
|
|
355
|
-
let validated = param.schema["~standard"].validate(value);
|
|
356
|
-
if (validated instanceof Promise) {
|
|
357
|
-
return {
|
|
358
|
-
ok: false,
|
|
359
|
-
issues: [{
|
|
360
|
-
message: `async schemas are not allowed`,
|
|
361
|
-
path,
|
|
362
|
-
}],
|
|
363
|
-
};
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
if (validated.issues) {
|
|
367
|
-
return {
|
|
368
|
-
ok: false,
|
|
369
|
-
issues: validated.issues.map((issue) => ({
|
|
370
|
-
...issue,
|
|
371
|
-
message: issue.message,
|
|
372
|
-
path,
|
|
373
|
-
})),
|
|
374
|
-
};
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
return {
|
|
378
|
-
ok: true,
|
|
379
|
-
issues: [],
|
|
380
|
-
value: validated.value,
|
|
381
|
-
};
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
function merge<T>(
|
|
385
|
-
result: Result<T>,
|
|
386
|
-
issues: readonly Issue[] | undefined,
|
|
387
|
-
): Result<T> {
|
|
388
|
-
if (!issues || issues.length === 0) {
|
|
389
|
-
return result;
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
return {
|
|
393
|
-
...result,
|
|
394
|
-
issues: [...issues, ...(result.issues ?? [])],
|
|
395
|
-
};
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
function first(tokens: Tokenizer<Symbol>, range: TokenRange): Word | undefined {
|
|
399
|
-
for (let token of tokens.view({ range })) {
|
|
400
|
-
if (token.type === "word") {
|
|
401
|
-
return token;
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
function earliest(tokens: readonly Symbol[]): number {
|
|
407
|
-
let first = Infinity;
|
|
408
|
-
|
|
409
|
-
for (let token of tokens) {
|
|
410
|
-
first = Math.min(first, token.index);
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
return first;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
interface Capture {
|
|
417
|
-
param: Param<string, unknown, "many">;
|
|
418
|
-
values: (string | boolean)[];
|
|
419
|
-
issues: Issue[];
|
|
420
|
-
failed: boolean;
|
|
421
|
-
}
|
package/src/lib/checkpoint.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { dynamic } from "./dynamic.js";
|
|
2
|
-
import type { DynamicElement } from "./pipeline.js";
|
|
3
|
-
import { type ValueSource, withValues } from "./values.js";
|
|
4
|
-
|
|
5
|
-
export function checkpoint(): DynamicElement<
|
|
6
|
-
ValueSource[],
|
|
7
|
-
ReturnType<typeof withValues>
|
|
8
|
-
> {
|
|
9
|
-
return dynamic((values: ValueSource[]) => withValues(values));
|
|
10
|
-
}
|
package/src/lib/command.ts
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
// deno-lint-ignore-file ban-types
|
|
2
|
-
import type { Check, Fold, Materialize, Unary } from "./pipeline.js";
|
|
3
|
-
import type { Definition, Done, Route } from "./types.js";
|
|
4
|
-
|
|
5
|
-
export type CommandZero<N extends string = string> = Route<
|
|
6
|
-
N,
|
|
7
|
-
"help" | "execute",
|
|
8
|
-
[Done<{}, []>]
|
|
9
|
-
>;
|
|
10
|
-
|
|
11
|
-
export function command<
|
|
12
|
-
const N extends string,
|
|
13
|
-
const E extends readonly Unary[],
|
|
14
|
-
>(
|
|
15
|
-
start: Definition<N>,
|
|
16
|
-
...elements: E & Check<CommandZero<N>, E>
|
|
17
|
-
): Materialize<Fold<CommandZero<N>, E>> {
|
|
18
|
-
let zero: CommandZero<N> = {
|
|
19
|
-
...start,
|
|
20
|
-
methods: ["help", "execute"],
|
|
21
|
-
phases: [{
|
|
22
|
-
model: {
|
|
23
|
-
params: {},
|
|
24
|
-
steps: [],
|
|
25
|
-
},
|
|
26
|
-
routes: [],
|
|
27
|
-
values: [],
|
|
28
|
-
envs: [],
|
|
29
|
-
}],
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
return elements.reduce<unknown>(
|
|
33
|
-
(value, element) => element(value as never),
|
|
34
|
-
zero,
|
|
35
|
-
) as Materialize<Fold<CommandZero<N>, E>>;
|
|
36
|
-
}
|
package/src/lib/dasherize.ts
DELETED
package/src/lib/decode.ts
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { Just, type Maybe, Nothing } from "./maybe.js";
|
|
2
|
-
|
|
3
|
-
export type Decoder<A, B> = (value: A) => Maybe<B>[];
|
|
4
|
-
|
|
5
|
-
export const number: Decoder<string, number> = (value) => {
|
|
6
|
-
if (!numeric.test(value)) {
|
|
7
|
-
return [Nothing()];
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
let decoded = Number(value);
|
|
11
|
-
return Number.isFinite(decoded) ? [Just(decoded)] : [Nothing()];
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export const scalar: Decoder<string, string | number> = (value) => {
|
|
15
|
-
return [...number(value), Just(value)];
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export const boolean: Decoder<string, boolean> = (value) => {
|
|
19
|
-
if (value === "true") {
|
|
20
|
-
return [Just(true)];
|
|
21
|
-
}
|
|
22
|
-
if (value === "false") {
|
|
23
|
-
return [Just(false)];
|
|
24
|
-
}
|
|
25
|
-
return [Nothing()];
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
export function multiple<A, B>(decoder: Decoder<A, B>): Decoder<A[], B[]> {
|
|
29
|
-
return (input: A[]): Maybe<B[]>[] => {
|
|
30
|
-
let decodings = input.map(decoder);
|
|
31
|
-
|
|
32
|
-
let result: Maybe<B[]>[] = [];
|
|
33
|
-
|
|
34
|
-
for (let i = 0; i < decodings.length; i++) {
|
|
35
|
-
let decoding = decodings[i];
|
|
36
|
-
for (let j = 0; j < decoding.length; j++) {
|
|
37
|
-
let row: Maybe<B[]> | undefined = result[j];
|
|
38
|
-
if (!row) {
|
|
39
|
-
row = result[j] = Just([]);
|
|
40
|
-
}
|
|
41
|
-
if (row.exists) {
|
|
42
|
-
let candidate = decoding[j];
|
|
43
|
-
if (candidate.exists) {
|
|
44
|
-
row.value[i] = candidate.value;
|
|
45
|
-
} else {
|
|
46
|
-
result[j] = Nothing();
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
return result;
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const numeric = /^-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?$/;
|
package/src/lib/definition.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { brand, type IdentityElement } from "./pipeline.js";
|
|
2
|
-
import type { Definition } from "./types.js";
|
|
3
|
-
|
|
4
|
-
export function name<N extends string>(name: N): Definition<N> {
|
|
5
|
-
return { name };
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export function description(
|
|
9
|
-
description: string,
|
|
10
|
-
): IdentityElement<Definition<string>> {
|
|
11
|
-
return brand<IdentityElement<Definition<string>>>((
|
|
12
|
-
definition: Definition<string>,
|
|
13
|
-
) => ({
|
|
14
|
-
...definition,
|
|
15
|
-
description,
|
|
16
|
-
}));
|
|
17
|
-
}
|