@bomb.sh/router 0.0.1 → 0.7.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 +293 -0
- package/esm/_dnt.polyfills.d.ts +11 -0
- package/esm/_dnt.polyfills.js +15 -0
- package/esm/lib/argument.d.ts +7 -0
- package/esm/lib/argument.js +66 -0
- package/esm/lib/bind.d.ts +41 -0
- package/esm/lib/bind.js +285 -0
- package/esm/lib/checkpoint.d.ts +3 -0
- package/esm/lib/checkpoint.js +5 -0
- package/esm/lib/command.d.ts +6 -0
- package/esm/lib/command.js +16 -0
- package/esm/lib/dasherize.d.ts +1 -0
- package/esm/lib/dasherize.js +7 -0
- package/esm/lib/decode.d.ts +6 -0
- package/esm/lib/decode.js +46 -0
- package/esm/lib/definition.d.ts +4 -0
- package/esm/lib/definition.js +10 -0
- package/esm/lib/dynamic.d.ts +6 -0
- package/esm/lib/dynamic.js +21 -0
- package/esm/lib/env.d.ts +34 -0
- package/esm/lib/env.js +83 -0
- package/esm/lib/extend.d.ts +2 -0
- package/esm/lib/extend.js +4 -0
- package/esm/lib/maybe.d.ts +9 -0
- package/esm/lib/maybe.js +12 -0
- package/esm/lib/multiple.d.ts +8 -0
- package/esm/lib/multiple.js +9 -0
- package/esm/lib/option.d.ts +7 -0
- package/esm/lib/option.js +31 -0
- package/esm/lib/param.d.ts +26 -0
- package/esm/lib/param.js +37 -0
- package/esm/lib/parse.d.ts +2 -0
- package/esm/lib/parse.js +338 -0
- package/esm/lib/pipeline.d.ts +182 -0
- package/esm/lib/pipeline.js +6 -0
- package/esm/lib/print.d.ts +7 -0
- package/esm/lib/print.js +156 -0
- package/esm/lib/read.d.ts +27 -0
- package/esm/lib/read.js +95 -0
- package/esm/lib/rest.d.ts +9 -0
- package/esm/lib/rest.js +1 -0
- package/esm/lib/result.d.ts +9 -0
- package/esm/lib/result.js +1 -0
- package/esm/lib/route.d.ts +9 -0
- package/esm/lib/route.js +45 -0
- package/esm/lib/toggle.d.ts +7 -0
- package/esm/lib/toggle.js +91 -0
- package/esm/lib/tokenize.d.ts +20 -0
- package/esm/lib/tokenize.js +41 -0
- package/esm/lib/tokenizer.d.ts +34 -0
- package/esm/lib/tokenizer.js +130 -0
- package/esm/lib/transform.d.ts +8 -0
- package/esm/lib/transform.js +34 -0
- package/esm/lib/types.d.ts +196 -0
- package/esm/lib/types.js +1 -0
- package/esm/lib/values.d.ts +29 -0
- package/esm/lib/values.js +83 -0
- package/esm/mod.d.ts +29 -0
- package/esm/mod.js +18 -0
- package/esm/package.json +3 -0
- package/package.json +27 -9
- package/src/_dnt.polyfills.ts +27 -0
- package/src/lib/argument.ts +100 -0
- package/src/lib/bind.ts +421 -0
- package/src/lib/checkpoint.ts +10 -0
- package/src/lib/command.ts +36 -0
- package/src/lib/dasherize.ts +7 -0
- package/src/lib/decode.ts +55 -0
- package/src/lib/definition.ts +17 -0
- package/src/lib/dynamic.ts +39 -0
- package/src/lib/env.ts +142 -0
- package/src/lib/extend.ts +12 -0
- package/src/lib/maybe.ts +22 -0
- package/src/lib/multiple.ts +23 -0
- package/src/lib/option.ts +57 -0
- package/src/lib/param.ts +96 -0
- package/src/lib/parse.ts +478 -0
- package/src/lib/pipeline.ts +549 -0
- package/src/lib/print.ts +216 -0
- package/src/lib/read.ts +133 -0
- package/src/lib/rest.ts +10 -0
- package/src/lib/result.ts +10 -0
- package/src/lib/route.ts +77 -0
- package/src/lib/toggle.ts +132 -0
- package/src/lib/tokenize.ts +74 -0
- package/src/lib/tokenizer.ts +191 -0
- package/src/lib/transform.ts +49 -0
- package/src/lib/types.ts +460 -0
- package/src/lib/values.ts +131 -0
- package/src/mod.ts +74 -0
package/esm/lib/param.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { scalar } from "./decode.js";
|
|
2
|
+
import { mark, } from "./pipeline.js";
|
|
3
|
+
export function param(start, ...elements) {
|
|
4
|
+
let zero = {
|
|
5
|
+
...start,
|
|
6
|
+
schema: unknown,
|
|
7
|
+
cardinality: "one",
|
|
8
|
+
cli: {
|
|
9
|
+
read(tokens) {
|
|
10
|
+
let claim = tokens.claimAll(() => false);
|
|
11
|
+
return {
|
|
12
|
+
result: {
|
|
13
|
+
ok: true,
|
|
14
|
+
value: { exists: false },
|
|
15
|
+
issues: [],
|
|
16
|
+
},
|
|
17
|
+
claim,
|
|
18
|
+
};
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
decode: scalar,
|
|
22
|
+
};
|
|
23
|
+
return elements.reduce((value, element) => element(value), zero);
|
|
24
|
+
}
|
|
25
|
+
export function schema(schema) {
|
|
26
|
+
return mark((param) => ({
|
|
27
|
+
...param,
|
|
28
|
+
schema,
|
|
29
|
+
}));
|
|
30
|
+
}
|
|
31
|
+
const unknown = {
|
|
32
|
+
"~standard": {
|
|
33
|
+
version: 1,
|
|
34
|
+
vendor: "@bomb.sh/router",
|
|
35
|
+
validate: (value) => ({ value }),
|
|
36
|
+
},
|
|
37
|
+
};
|
package/esm/lib/parse.js
ADDED
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
import { bindPhase } from "./bind.js";
|
|
2
|
+
import { Envs } from "./env.js";
|
|
3
|
+
import { tokenize } from "./tokenize.js";
|
|
4
|
+
import { Tokenizer } from "./tokenizer.js";
|
|
5
|
+
import { Values } from "./values.js";
|
|
6
|
+
export function parse(route, input) {
|
|
7
|
+
let tokenizer = new Tokenizer(tokenize(input.argv));
|
|
8
|
+
let help = tokenizer.claimAll(flags("-h", "--help"));
|
|
9
|
+
let version = help.rest.claimAll(flags("-v", "--version"));
|
|
10
|
+
let escape = version.rest.claimAll((t) => t.type === "separator");
|
|
11
|
+
let literals = escape.rest.claimAll((t) => t.type === "literal");
|
|
12
|
+
let method = "execute";
|
|
13
|
+
if (help.tokens.length > 0) {
|
|
14
|
+
method = "help";
|
|
15
|
+
}
|
|
16
|
+
else if (version.tokens.length > 0) {
|
|
17
|
+
method = "version";
|
|
18
|
+
}
|
|
19
|
+
let rest = {
|
|
20
|
+
tokens: literals.rest,
|
|
21
|
+
values: new Values().mount([], input.values ?? []),
|
|
22
|
+
envs: new Envs().mount([], input.envs ?? []),
|
|
23
|
+
};
|
|
24
|
+
return advance({
|
|
25
|
+
segments: [{
|
|
26
|
+
id: "/",
|
|
27
|
+
route,
|
|
28
|
+
phases: route.phases,
|
|
29
|
+
tokens: Array.from(rest.tokens),
|
|
30
|
+
path: [],
|
|
31
|
+
start: -1,
|
|
32
|
+
model: {},
|
|
33
|
+
issues: [],
|
|
34
|
+
history: [],
|
|
35
|
+
}],
|
|
36
|
+
active: 0,
|
|
37
|
+
rest,
|
|
38
|
+
models: {},
|
|
39
|
+
method,
|
|
40
|
+
literals: literals.tokens,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
function advance(state) {
|
|
44
|
+
while (true) {
|
|
45
|
+
// Starting from the deepest open segment, synchronously descend through
|
|
46
|
+
// routes visible on each segment's head phase.
|
|
47
|
+
state = search(state);
|
|
48
|
+
let index = state.active;
|
|
49
|
+
let segment = state.segments[index];
|
|
50
|
+
let [phase] = segment.phases;
|
|
51
|
+
state = {
|
|
52
|
+
...state,
|
|
53
|
+
rest: {
|
|
54
|
+
...state.rest,
|
|
55
|
+
values: state.rest.values.mount(segment.path, phase.values),
|
|
56
|
+
envs: state.rest.envs.mount(segment.path, phase.envs),
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
// Controls are free of ordinary configuration validation. A dynamic phase
|
|
60
|
+
// is the exception: its model is needed by the caller before the remaining
|
|
61
|
+
// route graph (and therefore the final control target) can be known.
|
|
62
|
+
if (state.method !== "execute" && !phase.resolver) {
|
|
63
|
+
let models = {
|
|
64
|
+
...state.models,
|
|
65
|
+
[segment.id]: segment.model,
|
|
66
|
+
};
|
|
67
|
+
if (index + 1 < state.segments.length) {
|
|
68
|
+
state = {
|
|
69
|
+
...state,
|
|
70
|
+
active: index + 1,
|
|
71
|
+
models,
|
|
72
|
+
};
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
return resolve({ ...state, models }, segment);
|
|
76
|
+
}
|
|
77
|
+
// Bind only this phase, within this segment's token view.
|
|
78
|
+
//
|
|
79
|
+
// For an open dynamic segment, binding may consume the horizon but may not
|
|
80
|
+
// look beyond it. For a committed segment, `end` is the hard boundary.
|
|
81
|
+
let binding = bindPhase({
|
|
82
|
+
phase,
|
|
83
|
+
segment: {
|
|
84
|
+
range: {
|
|
85
|
+
start: segment.start,
|
|
86
|
+
end: segment.end,
|
|
87
|
+
},
|
|
88
|
+
path: segment.path,
|
|
89
|
+
},
|
|
90
|
+
rest: state.rest,
|
|
91
|
+
});
|
|
92
|
+
segment = {
|
|
93
|
+
...segment,
|
|
94
|
+
model: {
|
|
95
|
+
...segment.model,
|
|
96
|
+
...binding.model,
|
|
97
|
+
},
|
|
98
|
+
issues: [
|
|
99
|
+
...segment.issues,
|
|
100
|
+
...binding.issues,
|
|
101
|
+
],
|
|
102
|
+
};
|
|
103
|
+
state = {
|
|
104
|
+
...state,
|
|
105
|
+
rest: binding.rest,
|
|
106
|
+
segments: replace(state.segments, index, segment),
|
|
107
|
+
};
|
|
108
|
+
if (!binding.valid) {
|
|
109
|
+
let issues = phase.resolver
|
|
110
|
+
? binding.issues
|
|
111
|
+
: [...unexpected(state, segment), ...binding.issues];
|
|
112
|
+
return unprocessableContent(state.segments[state.segments.length - 1], issues);
|
|
113
|
+
}
|
|
114
|
+
if (phase.resolver) {
|
|
115
|
+
// Binding the phase succeeded, but the requirement is still needed.
|
|
116
|
+
let suspended = state;
|
|
117
|
+
return {
|
|
118
|
+
ok: true,
|
|
119
|
+
route: segment.id,
|
|
120
|
+
model: binding.model,
|
|
121
|
+
resume(result) {
|
|
122
|
+
if (!result.ok) {
|
|
123
|
+
return unprocessableContent(segment, result.issues);
|
|
124
|
+
}
|
|
125
|
+
// AnyPhase erases these exact types with `never`; dynamic() already
|
|
126
|
+
// proved them at its public boundary.
|
|
127
|
+
let resolver = phase.resolver;
|
|
128
|
+
// The extension operates against the same aggregate route metadata,
|
|
129
|
+
// but begins with one fresh, empty phase.
|
|
130
|
+
let continuation = resolver(result.value)(seed(segment.route));
|
|
131
|
+
let phases = stitch(continuation.phases, segment.phases.slice(1));
|
|
132
|
+
let history = [...segment.history, phase];
|
|
133
|
+
let next = {
|
|
134
|
+
...segment,
|
|
135
|
+
route: {
|
|
136
|
+
...continuation,
|
|
137
|
+
phases: [...history, ...phases],
|
|
138
|
+
},
|
|
139
|
+
phases,
|
|
140
|
+
history,
|
|
141
|
+
issues: [
|
|
142
|
+
...segment.issues,
|
|
143
|
+
...(result.issues ?? []),
|
|
144
|
+
],
|
|
145
|
+
};
|
|
146
|
+
return advance({
|
|
147
|
+
...suspended,
|
|
148
|
+
segments: replace(suspended.segments, index, next),
|
|
149
|
+
});
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
// A phase without a resolver is the final phase of this segment.
|
|
154
|
+
let issues = unexpected(state, segment);
|
|
155
|
+
if (issues.length > 0) {
|
|
156
|
+
return unprocessableContent(state.segments[state.segments.length - 1], issues);
|
|
157
|
+
}
|
|
158
|
+
let id = `/${segment.path.join("/")}`;
|
|
159
|
+
let models = {
|
|
160
|
+
...state.models,
|
|
161
|
+
[id]: segment.model,
|
|
162
|
+
};
|
|
163
|
+
// There is already a synchronously matched child segment.
|
|
164
|
+
if (index + 1 < state.segments.length) {
|
|
165
|
+
state = {
|
|
166
|
+
...state,
|
|
167
|
+
active: index + 1,
|
|
168
|
+
models,
|
|
169
|
+
};
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
// The deepest selected segment is fully resolved.
|
|
173
|
+
return resolve({
|
|
174
|
+
...state,
|
|
175
|
+
models,
|
|
176
|
+
}, segment);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
function search(state) {
|
|
180
|
+
let segments = [...state.segments];
|
|
181
|
+
let rest = state.rest;
|
|
182
|
+
while (true) {
|
|
183
|
+
let index = segments.length - 1;
|
|
184
|
+
let segment = segments[index];
|
|
185
|
+
let [phase] = segment.phases;
|
|
186
|
+
let remaining = new Set(Array.from(rest.tokens, (token) => token.index));
|
|
187
|
+
let selector = segment.tokens.find((token) => {
|
|
188
|
+
return remaining.has(token.index) && token.type === "word" &&
|
|
189
|
+
phase.routes.some((route) => route.name === token.text);
|
|
190
|
+
});
|
|
191
|
+
if (!selector) {
|
|
192
|
+
return { ...state, segments, rest };
|
|
193
|
+
}
|
|
194
|
+
let child = phase.routes.find((route) => route.name === selector.text);
|
|
195
|
+
let path = [...segment.path, child.name];
|
|
196
|
+
let before = segment.tokens.filter((token) => token.index < selector.index);
|
|
197
|
+
let after = segment.tokens.filter((token) => token.index > selector.index);
|
|
198
|
+
segments[index] = {
|
|
199
|
+
...segment,
|
|
200
|
+
tokens: before,
|
|
201
|
+
end: selector.index,
|
|
202
|
+
};
|
|
203
|
+
segments.push({
|
|
204
|
+
id: `/${path.join("/")}`,
|
|
205
|
+
route: child,
|
|
206
|
+
path,
|
|
207
|
+
phases: child.phases,
|
|
208
|
+
tokens: after,
|
|
209
|
+
start: selector.index,
|
|
210
|
+
model: {},
|
|
211
|
+
issues: [],
|
|
212
|
+
history: [],
|
|
213
|
+
});
|
|
214
|
+
rest = {
|
|
215
|
+
...rest,
|
|
216
|
+
tokens: rest.tokens.claimOne((token) => token.index === selector.index)
|
|
217
|
+
.rest,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
function flags(...texts) {
|
|
222
|
+
return (token) => token.type === "flag" && texts.includes(token.text);
|
|
223
|
+
}
|
|
224
|
+
function stitch(before, after) {
|
|
225
|
+
if (after.length === 0) {
|
|
226
|
+
return before;
|
|
227
|
+
}
|
|
228
|
+
let phases = [...before];
|
|
229
|
+
let phase = phases.pop();
|
|
230
|
+
let [next, ...rest] = after;
|
|
231
|
+
phases.push({
|
|
232
|
+
...phase,
|
|
233
|
+
...next,
|
|
234
|
+
model: {
|
|
235
|
+
params: {
|
|
236
|
+
...phase.model.params,
|
|
237
|
+
...next.model.params,
|
|
238
|
+
},
|
|
239
|
+
steps: [
|
|
240
|
+
...phase.model.steps,
|
|
241
|
+
...next.model.steps,
|
|
242
|
+
],
|
|
243
|
+
},
|
|
244
|
+
routes: [
|
|
245
|
+
...phase.routes,
|
|
246
|
+
...next.routes,
|
|
247
|
+
],
|
|
248
|
+
values: [
|
|
249
|
+
...phase.values,
|
|
250
|
+
...next.values,
|
|
251
|
+
],
|
|
252
|
+
envs: [
|
|
253
|
+
...phase.envs,
|
|
254
|
+
...next.envs,
|
|
255
|
+
],
|
|
256
|
+
});
|
|
257
|
+
phases.push(...rest);
|
|
258
|
+
return phases;
|
|
259
|
+
}
|
|
260
|
+
function resolve(state, segment) {
|
|
261
|
+
let route = `/${segment.path.join("/")}`;
|
|
262
|
+
let definition = segment.route;
|
|
263
|
+
if (!definition.methods.includes(state.method)) {
|
|
264
|
+
return {
|
|
265
|
+
ok: false,
|
|
266
|
+
code: "method-not-allowed",
|
|
267
|
+
route,
|
|
268
|
+
definition,
|
|
269
|
+
path: segment.path,
|
|
270
|
+
method: state.method,
|
|
271
|
+
allowed: definition.methods,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
let intent = {
|
|
275
|
+
ok: true,
|
|
276
|
+
route,
|
|
277
|
+
definition,
|
|
278
|
+
path: segment.path,
|
|
279
|
+
literals: state.literals,
|
|
280
|
+
};
|
|
281
|
+
switch (state.method) {
|
|
282
|
+
case "help":
|
|
283
|
+
return {
|
|
284
|
+
...intent,
|
|
285
|
+
method: "help",
|
|
286
|
+
};
|
|
287
|
+
case "version":
|
|
288
|
+
return {
|
|
289
|
+
...intent,
|
|
290
|
+
method: "version",
|
|
291
|
+
};
|
|
292
|
+
case "execute":
|
|
293
|
+
return {
|
|
294
|
+
...intent,
|
|
295
|
+
method: "execute",
|
|
296
|
+
model: state.models[route],
|
|
297
|
+
models: state.models,
|
|
298
|
+
issues: state.segments.flatMap((segment) => segment.issues),
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
function seed(route) {
|
|
303
|
+
return {
|
|
304
|
+
...route,
|
|
305
|
+
phases: [{
|
|
306
|
+
model: {
|
|
307
|
+
params: {},
|
|
308
|
+
steps: [],
|
|
309
|
+
},
|
|
310
|
+
routes: [],
|
|
311
|
+
values: [],
|
|
312
|
+
envs: [],
|
|
313
|
+
}],
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
function replace(items, index, value) {
|
|
317
|
+
let result = [...items];
|
|
318
|
+
result[index] = value;
|
|
319
|
+
return result;
|
|
320
|
+
}
|
|
321
|
+
function unprocessableContent(segment, issues) {
|
|
322
|
+
return {
|
|
323
|
+
ok: false,
|
|
324
|
+
code: "unprocessable-content",
|
|
325
|
+
route: segment.id,
|
|
326
|
+
definition: segment.route,
|
|
327
|
+
path: segment.path,
|
|
328
|
+
issues: [...issues],
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
function unexpected(state, segment) {
|
|
332
|
+
let members = new Set(segment.tokens.map((token) => token.index));
|
|
333
|
+
return Array.from(state.rest.tokens)
|
|
334
|
+
.filter((token) => members.has(token.index))
|
|
335
|
+
.map((token) => ({
|
|
336
|
+
message: `unexpected ${JSON.stringify(token.text)}`,
|
|
337
|
+
}));
|
|
338
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import type { AddRoutesToLast, AnyPhase, AnyPhases, AnyRoute, Done, Method, MethodsOf, Next, Phase, Route } from "./types.js";
|
|
2
|
+
export type Element<D extends Delta> = {
|
|
3
|
+
readonly [operation]: D;
|
|
4
|
+
};
|
|
5
|
+
export type AnyElement = Element<Delta>;
|
|
6
|
+
export type Unary = (value: never) => unknown;
|
|
7
|
+
export type AnyPipelineElement = Unary;
|
|
8
|
+
export interface Transform {
|
|
9
|
+
readonly input: unknown;
|
|
10
|
+
readonly output: unknown;
|
|
11
|
+
}
|
|
12
|
+
export type ApplyTransform<F extends Transform, S> = (F & {
|
|
13
|
+
readonly input: S;
|
|
14
|
+
})["output"];
|
|
15
|
+
export interface ModelTransform extends Transform {
|
|
16
|
+
readonly input: object;
|
|
17
|
+
readonly output: object;
|
|
18
|
+
}
|
|
19
|
+
export interface ModelPatch<Fields extends object> extends ModelTransform {
|
|
20
|
+
readonly fields: Fields;
|
|
21
|
+
readonly input: object;
|
|
22
|
+
readonly output: ApplyModelPatch<this["input"], Fields>;
|
|
23
|
+
}
|
|
24
|
+
export type ApplyModelPatch<Model extends object, Fields extends object> = SimplifyModel<Omit<Model, keyof Fields> & Fields>;
|
|
25
|
+
export interface TransformElement<F extends Transform> {
|
|
26
|
+
readonly [operation]: Custom<F>;
|
|
27
|
+
<S extends F["input"]>(value: S): ApplyTransform<F, S>;
|
|
28
|
+
}
|
|
29
|
+
export interface ModelElement<F extends ModelTransform> {
|
|
30
|
+
readonly [operation]: TransformModel<F>;
|
|
31
|
+
<R extends AnyRoute>(route: R): Apply<R, TransformModel<F>>;
|
|
32
|
+
}
|
|
33
|
+
export interface IdentityElement<S> {
|
|
34
|
+
readonly [operation]: Identity<S>;
|
|
35
|
+
<T extends S>(value: T): T;
|
|
36
|
+
}
|
|
37
|
+
export interface MethodElement<Added extends Method> {
|
|
38
|
+
readonly [operation]: AddMethod<Added>;
|
|
39
|
+
<const N extends string, const M extends Method, const P extends AnyPhases>(route: Route<N, M, P>): Route<N, M | Added, P>;
|
|
40
|
+
}
|
|
41
|
+
export interface RoutesElement<Added extends readonly AnyRoute[]> {
|
|
42
|
+
readonly [operation]: AddRoutes<Added>;
|
|
43
|
+
<const N extends string, const M extends Method, const P extends AnyPhases>(route: Route<N, M, P>): Route<N, M, AddRoutesToLast<P, Added>>;
|
|
44
|
+
}
|
|
45
|
+
export type Extension<E extends readonly Unary[]> = Element<Batch<E>> & (E extends readonly AnyElement[] ? GenericExtension<E> : ConcreteExtension<E>);
|
|
46
|
+
interface GenericExtension<E extends readonly Unary[]> {
|
|
47
|
+
readonly [operation]: Batch<E>;
|
|
48
|
+
<S extends InputOfPipeline<E>>(value: S): Fold<S, E>;
|
|
49
|
+
}
|
|
50
|
+
interface ConcreteExtension<E extends readonly Unary[]> {
|
|
51
|
+
(value: [Fold<InputOfPipeline<E>, E>] extends [never] ? never : InputOfPipeline<E>): Fold<InputOfPipeline<E>, E>;
|
|
52
|
+
}
|
|
53
|
+
export interface DynamicElement<Requirement, E extends AnyElement> {
|
|
54
|
+
readonly [operation]: Dynamic<Requirement, E>;
|
|
55
|
+
<R extends AnyRoute>(route: R): Apply<R, Dynamic<Requirement, E>>;
|
|
56
|
+
}
|
|
57
|
+
export type Seed<R extends AnyRoute> = Route<R["name"], MethodsOf<R>, readonly [Phase<{}, [], never>]>;
|
|
58
|
+
export type ConjoinPhases<A extends AnyRoute, B extends AnyRoute, Requirement> = ConcatPhases<ContinueLast<A["phases"], Requirement>, B["phases"]>;
|
|
59
|
+
export type Fold<S, E extends readonly AnyPipelineElement[]> = number extends E["length"] ? Conservative<S> : E extends readonly [
|
|
60
|
+
infer Head extends AnyPipelineElement,
|
|
61
|
+
...infer Tail extends readonly AnyPipelineElement[]
|
|
62
|
+
] ? true extends IsUnion<Head> ? Conservative<S> : Head extends AnyElement ? S extends AnyRoute ? DeltaOf<Head> extends StaticDelta ? TakeStatic<E> extends readonly [
|
|
63
|
+
infer Fields extends object,
|
|
64
|
+
infer Routes extends readonly AnyRoute[],
|
|
65
|
+
infer Methods extends Method,
|
|
66
|
+
infer Rest extends readonly AnyPipelineElement[]
|
|
67
|
+
] ? Fold<WithStatic<S, Fields, Routes, Methods>, Rest> : never : Fold<Apply<S, DeltaOf<Head>>, Tail> : Fold<Apply<S, DeltaOf<Head>>, Tail> : Head extends (value: S) => infer Output ? Fold<Output, Tail> : never : S;
|
|
68
|
+
export type Materialize<S> = AnyRoute extends S ? S : S extends Route<infer N, infer M, infer Phases> ? WithExtras<Route<N, M, {
|
|
69
|
+
[K in keyof Phases]: Phases[K] extends AnyPhase ? MaterializePhase<Phases[K]> : Phases[K];
|
|
70
|
+
}>, S> : S;
|
|
71
|
+
export type Check<S, E extends readonly AnyPipelineElement[]> = number extends E["length"] ? unknown : E extends readonly AnyElement[] ? [Fold<S, E>] extends [never] ? CheckMixed<S, E> : E : CheckMixed<S, E>;
|
|
72
|
+
export declare function mark<F extends Transform>(element: (value: never) => unknown): TransformElement<F>;
|
|
73
|
+
export declare function brand<E extends AnyElement>(element: unknown): E;
|
|
74
|
+
declare const operation: unique symbol;
|
|
75
|
+
type Delta = Identity<unknown> | AddMethod<Method> | TransformModel<ModelTransform> | AddRoutes<readonly AnyRoute[]> | Batch<readonly Unary[]> | Dynamic<unknown, AnyElement> | Custom<Transform>;
|
|
76
|
+
type StaticDelta = Identity<unknown> | AddMethod<Method> | TransformModel<ModelPatch<object>> | AddRoutes<readonly AnyRoute[]>;
|
|
77
|
+
interface Identity<S> {
|
|
78
|
+
readonly type: "identity";
|
|
79
|
+
readonly input: S;
|
|
80
|
+
}
|
|
81
|
+
interface AddMethod<M extends Method> {
|
|
82
|
+
readonly type: "method";
|
|
83
|
+
readonly method: M;
|
|
84
|
+
}
|
|
85
|
+
interface TransformModel<F extends ModelTransform> {
|
|
86
|
+
readonly type: "model";
|
|
87
|
+
readonly transform: F;
|
|
88
|
+
}
|
|
89
|
+
interface AddRoutes<C extends readonly AnyRoute[]> {
|
|
90
|
+
readonly type: "routes";
|
|
91
|
+
readonly children: C;
|
|
92
|
+
}
|
|
93
|
+
interface Batch<E extends readonly Unary[]> {
|
|
94
|
+
readonly type: "batch";
|
|
95
|
+
readonly elements: E;
|
|
96
|
+
}
|
|
97
|
+
interface Dynamic<Requirement, E extends AnyElement> {
|
|
98
|
+
readonly type: "dynamic";
|
|
99
|
+
readonly requirement: Requirement;
|
|
100
|
+
readonly element: E;
|
|
101
|
+
}
|
|
102
|
+
interface Custom<F extends Transform> {
|
|
103
|
+
readonly type: "custom";
|
|
104
|
+
readonly transform: F;
|
|
105
|
+
}
|
|
106
|
+
type Apply<S, D extends Delta> = Delta extends D ? Conservative<S> : D extends Identity<infer Input> ? S extends Input ? S : never : D extends AddMethod<infer M> ? S extends AnyRoute ? Route<S["name"], MethodsOf<S> | M, S["phases"]> : never : D extends TransformModel<infer F> ? S extends AnyRoute ? WithModel<S, F> : never : D extends AddRoutes<infer C> ? S extends AnyRoute ? Route<S["name"], MethodsOf<S>, AddRoutesToLast<S["phases"], C>> : never : D extends Batch<infer E> ? Fold<S, E> : D extends Dynamic<infer Requirement, infer E> ? S extends AnyRoute ? DynamicAfter<Seed<S>, E> extends infer After extends AnyRoute ? Route<After["name"], MethodsOf<After>, ConjoinPhases<S, After, Requirement>> : never : never : D extends Custom<infer F> ? S extends F["input"] ? ApplyTransform<F, S> : never : never;
|
|
107
|
+
type DeltaOf<E extends AnyElement> = E[typeof operation];
|
|
108
|
+
type DynamicAfter<S extends AnyRoute, E extends AnyElement> = DeltaOf<E> extends Batch<infer Elements> ? number extends Elements["length"] ? S : Apply<S, DeltaOf<E>> : Apply<S, DeltaOf<E>>;
|
|
109
|
+
type IsUnion<T, Whole = T> = T extends unknown ? [Whole] extends [T] ? false : true : never;
|
|
110
|
+
type Conservative<S> = S extends AnyRoute ? AnyRoute : unknown;
|
|
111
|
+
type CheckMixed<S, E extends readonly AnyPipelineElement[]> = E extends readonly [] ? E : E extends readonly [
|
|
112
|
+
infer Head extends AnyPipelineElement,
|
|
113
|
+
...infer Tail extends readonly AnyPipelineElement[]
|
|
114
|
+
] ? [Fold<S, readonly [Head]>] extends [never] ? readonly [never, ...Tail] : readonly [Head, ...CheckTail<Fold<S, readonly [Head]>, Tail>] : never;
|
|
115
|
+
type CheckTail<S, E extends readonly AnyPipelineElement[]> = number extends E["length"] ? E : E extends readonly AnyElement[] ? E : CheckMixed<S, E>;
|
|
116
|
+
type InputOf<E extends AnyPipelineElement> = E extends AnyElement ? InputOfDelta<DeltaOf<E>> : E extends (value: infer Input) => unknown ? Input : never;
|
|
117
|
+
type InputOfPipeline<E extends readonly AnyPipelineElement[]> = E extends readonly [
|
|
118
|
+
infer Head extends AnyPipelineElement,
|
|
119
|
+
...readonly AnyPipelineElement[]
|
|
120
|
+
] ? InputOf<Head> : unknown;
|
|
121
|
+
type InputOfDelta<D extends Delta> = D extends Identity<infer Input> ? Input : D extends AddMethod<Method> | TransformModel<ModelTransform> | AddRoutes<readonly AnyRoute[]> | Dynamic<unknown, AnyElement> ? AnyRoute : D extends Batch<infer E> ? InputOfPipeline<E> : D extends Custom<infer F> ? F["input"] : never;
|
|
122
|
+
type TakeStatic<E extends readonly AnyPipelineElement[], Fields extends readonly object[] = readonly [], Routes extends readonly AnyRoute[] = readonly [], Methods extends Method = never, Count extends readonly unknown[] = readonly []> = Count["length"] extends 20 ? readonly [MergeFields<Fields>, Routes, Methods, E] : E extends readonly [
|
|
123
|
+
infer Head extends AnyElement,
|
|
124
|
+
...infer Tail extends readonly AnyPipelineElement[]
|
|
125
|
+
] ? DeltaOf<Head> extends Identity<unknown> ? TakeStatic<Tail, Fields, Routes, Methods, readonly [...Count, unknown]> : DeltaOf<Head> extends AddMethod<infer M> ? TakeStatic<Tail, Fields, Routes, Methods | M, readonly [...Count, unknown]> : DeltaOf<Head> extends TransformModel<ModelPatch<infer Added>> ? TakeStatic<Tail, readonly [...Fields, Added], Routes, Methods, readonly [...Count, unknown]> : DeltaOf<Head> extends AddRoutes<infer C> ? TakeStatic<Tail, Fields, readonly [...Routes, ...C], Methods, readonly [...Count, unknown]> : readonly [MergeFields<Fields>, Routes, Methods, E] : readonly [MergeFields<Fields>, Routes, Methods, E];
|
|
126
|
+
type MergeFields<F extends readonly object[]> = {
|
|
127
|
+
[K in FieldKeys<F>]: FieldValue<F, K>;
|
|
128
|
+
};
|
|
129
|
+
type FieldKeys<F extends readonly object[]> = F extends readonly [
|
|
130
|
+
infer Head extends object,
|
|
131
|
+
...infer Tail extends readonly object[]
|
|
132
|
+
] ? keyof Head | FieldKeys<Tail> : never;
|
|
133
|
+
type FieldValue<F extends readonly object[], K extends PropertyKey> = F extends readonly [
|
|
134
|
+
...infer Rest extends readonly object[],
|
|
135
|
+
infer Last extends object
|
|
136
|
+
] ? K extends keyof Last ? Last[K] : FieldValue<Rest, K> : never;
|
|
137
|
+
type WithModel<R extends AnyRoute, F extends ModelTransform> = ApplyModel<PhaseModel<Last<R["phases"]>>, F> extends infer Model ? [Model] extends [never] ? never : Model extends object ? Route<R["name"], MethodsOf<R>, ReplaceLastModel<R["phases"], MaterializeModel<Model>>> : never : never;
|
|
138
|
+
type WithStatic<R extends AnyRoute, Fields extends object, Routes extends readonly AnyRoute[], Methods extends Method> = Route<R["name"], MethodsOf<R> | Methods, AddStaticToLast<R["phases"], Fields, Routes>>;
|
|
139
|
+
type AddStaticToLast<P extends AnyPhases, Fields extends object, Routes extends readonly AnyRoute[]> = keyof Fields extends never ? Routes extends readonly [] ? P : AddRoutesToLast<P, Routes> : Routes extends readonly [] ? AddFieldsToLast<P, Fields> : AddRoutesToLast<AddFieldsToLast<P, Fields>, Routes>;
|
|
140
|
+
type AddFieldsToLast<P extends AnyPhases, Fields extends object> = ReplaceLastModel<P, AddFields<PhaseModel<Last<P>>, Fields>>;
|
|
141
|
+
type AddFields<Model extends object, Fields extends object> = ApplyModelPatch<Model, Fields>;
|
|
142
|
+
type ReplaceLastModel<P extends AnyPhases, Model extends object> = P extends readonly [infer Only extends AnyPhase] ? readonly [WithPhaseModel<Only, Model>] : P extends readonly [
|
|
143
|
+
infer First extends AnyPhase,
|
|
144
|
+
...infer Middle extends AnyPhase[],
|
|
145
|
+
AnyPhase
|
|
146
|
+
] ? readonly [First, ...Middle, WithPhaseModel<Last<P>, Model>] : never;
|
|
147
|
+
type WithPhaseModel<P extends AnyPhase, Model extends object> = P extends Next<infer _Current, infer Routes, infer Requirement> ? Next<Model, Routes, Requirement> : P extends Done<infer _Current, infer Routes> ? Done<Model, Routes> : never;
|
|
148
|
+
type PhaseModel<P extends AnyPhase> = P extends Next<infer Model, readonly AnyRoute[], infer _Requirement> ? Model : P extends Done<infer Model, readonly AnyRoute[]> ? Model : never;
|
|
149
|
+
type ApplyModel<Model extends object, F extends ModelTransform> = Model extends F["input"] ? ApplyTransform<F, Model> : never;
|
|
150
|
+
type MaterializeModel<Model extends object> = SimplifyModel<Model>;
|
|
151
|
+
type SimplifyModel<Model> = {
|
|
152
|
+
[K in keyof Model]: Model[K];
|
|
153
|
+
};
|
|
154
|
+
type Last<P extends AnyPhases> = P extends readonly [
|
|
155
|
+
...AnyPhase[],
|
|
156
|
+
infer Tail extends AnyPhase
|
|
157
|
+
] ? Tail : never;
|
|
158
|
+
type ContinueLast<P extends AnyPhases, Requirement> = P extends readonly [infer Only extends AnyPhase] ? readonly [WithRequirement<Only, Requirement>] : P extends readonly [
|
|
159
|
+
infer Head extends AnyPhase,
|
|
160
|
+
...infer Middle extends readonly AnyPhase[],
|
|
161
|
+
infer Tail extends AnyPhase
|
|
162
|
+
] ? readonly [
|
|
163
|
+
Head,
|
|
164
|
+
...Middle,
|
|
165
|
+
WithRequirement<Tail, Requirement>
|
|
166
|
+
] : never;
|
|
167
|
+
type ConcatPhases<A extends AnyPhases, B extends AnyPhases> = A extends readonly [
|
|
168
|
+
infer AHead extends AnyPhase,
|
|
169
|
+
...infer ATail extends AnyPhase[]
|
|
170
|
+
] ? B extends readonly [
|
|
171
|
+
infer BHead extends AnyPhase,
|
|
172
|
+
...infer BTail extends AnyPhase[]
|
|
173
|
+
] ? readonly [AHead, ...ATail, BHead, ...BTail] : never : never;
|
|
174
|
+
type WithRequirement<P extends AnyPhase, Requirement> = P extends Phase<infer Model, infer Children, never> ? Phase<Model, Children, Requirement> : never;
|
|
175
|
+
type MaterializePhase<P extends AnyPhase> = P extends Next<infer Model, infer Routes, infer Requirement> ? Next<{
|
|
176
|
+
[K in keyof Model]: Model[K];
|
|
177
|
+
}, Routes, Requirement> : P extends Done<infer Model, infer Routes> ? Done<{
|
|
178
|
+
[K in keyof Model]: Model[K];
|
|
179
|
+
}, Routes> : never;
|
|
180
|
+
type RouteKeys = keyof Route<string, Method, AnyPhases>;
|
|
181
|
+
type WithExtras<R extends AnyRoute, S> = keyof Omit<S, RouteKeys> extends never ? R : R & Omit<S, RouteKeys>;
|
|
182
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 😵💫 This file has been vibe coded 😵💫
|
|
3
|
+
*/
|
|
4
|
+
import type { Help, MethodNotAllowed, RoutePath, UnprocessableContent, Version } from "./types.js";
|
|
5
|
+
export declare function printHelp<const H extends Help<RoutePath>>(intent: H): string;
|
|
6
|
+
export declare function printVersion<const V extends Version<RoutePath>>(intent: V): string;
|
|
7
|
+
export declare function printErrors(result: MethodNotAllowed | UnprocessableContent): string;
|