@alpacakit/channels 0.1.0-beta.19

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.
Files changed (72) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +772 -0
  3. package/dist/binding-compiler.d.ts +29 -0
  4. package/dist/binding-compiler.d.ts.map +1 -0
  5. package/dist/binding-compiler.js +40 -0
  6. package/dist/cli-coercion.d.ts +5 -0
  7. package/dist/cli-coercion.d.ts.map +1 -0
  8. package/dist/cli-coercion.js +51 -0
  9. package/dist/cli-compiler.d.ts +46 -0
  10. package/dist/cli-compiler.d.ts.map +1 -0
  11. package/dist/cli-compiler.js +213 -0
  12. package/dist/cli-executor.d.ts +25 -0
  13. package/dist/cli-executor.d.ts.map +1 -0
  14. package/dist/cli-executor.js +44 -0
  15. package/dist/cli-help-projection.d.ts +13 -0
  16. package/dist/cli-help-projection.d.ts.map +1 -0
  17. package/dist/cli-help-projection.js +159 -0
  18. package/dist/cli-model.d.ts +240 -0
  19. package/dist/cli-model.d.ts.map +1 -0
  20. package/dist/cli-model.js +89 -0
  21. package/dist/cli-presentation.d.ts +23 -0
  22. package/dist/cli-presentation.d.ts.map +1 -0
  23. package/dist/cli-presentation.js +265 -0
  24. package/dist/cli-schema.d.ts +4 -0
  25. package/dist/cli-schema.d.ts.map +1 -0
  26. package/dist/cli-schema.js +7 -0
  27. package/dist/cli-tree.d.ts +4 -0
  28. package/dist/cli-tree.d.ts.map +1 -0
  29. package/dist/cli-tree.js +421 -0
  30. package/dist/cli-values.d.ts +24 -0
  31. package/dist/cli-values.d.ts.map +1 -0
  32. package/dist/cli-values.js +40 -0
  33. package/dist/cli.d.ts +6 -0
  34. package/dist/cli.d.ts.map +1 -0
  35. package/dist/cli.js +5 -0
  36. package/dist/codecs.d.ts +25 -0
  37. package/dist/codecs.d.ts.map +1 -0
  38. package/dist/codecs.js +74 -0
  39. package/dist/contract.d.ts +12 -0
  40. package/dist/contract.d.ts.map +1 -0
  41. package/dist/contract.js +28 -0
  42. package/dist/factory.d.ts +16 -0
  43. package/dist/factory.d.ts.map +1 -0
  44. package/dist/factory.js +188 -0
  45. package/dist/index.d.ts +9 -0
  46. package/dist/index.d.ts.map +1 -0
  47. package/dist/index.js +8 -0
  48. package/dist/internal.d.ts +3 -0
  49. package/dist/internal.d.ts.map +1 -0
  50. package/dist/internal.js +3 -0
  51. package/dist/introspection.d.ts +40 -0
  52. package/dist/introspection.d.ts.map +1 -0
  53. package/dist/introspection.js +56 -0
  54. package/dist/mcp.d.ts +26 -0
  55. package/dist/mcp.d.ts.map +1 -0
  56. package/dist/mcp.js +84 -0
  57. package/dist/model.d.ts +166 -0
  58. package/dist/model.d.ts.map +1 -0
  59. package/dist/model.js +22 -0
  60. package/dist/runtime.d.ts +47 -0
  61. package/dist/runtime.d.ts.map +1 -0
  62. package/dist/runtime.js +140 -0
  63. package/dist/schema.d.ts +14 -0
  64. package/dist/schema.d.ts.map +1 -0
  65. package/dist/schema.js +70 -0
  66. package/dist/tree-internal.d.ts +14 -0
  67. package/dist/tree-internal.d.ts.map +1 -0
  68. package/dist/tree-internal.js +20 -0
  69. package/dist/values.d.ts +32 -0
  70. package/dist/values.d.ts.map +1 -0
  71. package/dist/values.js +30 -0
  72. package/package.json +47 -0
@@ -0,0 +1,421 @@
1
+ import { coerceCliArgvValue } from "./cli-coercion.js";
2
+ import { CLI_OPTION_ACTION_KIND, compileCliBinding, } from "./cli-compiler.js";
3
+ import { createCliHelpSurface, validateCliCommandHelp, validateCliHelpExamples, } from "./cli-help-projection.js";
4
+ import { CLI_ROUTER_RESULT_KIND, } from "./cli-model.js";
5
+ import { isCliEntryBinding, isCliSegment } from "./cli-schema.js";
6
+ import { CLI_OPTION_VALUE_MODE, CLI_POSITIONAL_PLAN_KIND, } from "./cli-values.js";
7
+ import { decodeEntryInput } from "./runtime.js";
8
+ import { EntryTreeDefinitionError, findChannelBinding, formatTreePath, validateEntryTreeStructure, visitEntryTree, } from "./schema.js";
9
+ import { CHANNEL, ENTRY_DECODE_RESULT_KIND } from "./values.js";
10
+ const CLI_ARGUMENT_SYNTAX = {
11
+ inlineValueSeparator: "=",
12
+ optionPrefix: "-",
13
+ positionalSeparator: "--",
14
+ repeatedValueSeparator: ",",
15
+ };
16
+ const CLI_ARGUMENT_TOKEN_KIND = {
17
+ option: "option",
18
+ positionalSeparator: "positional_separator",
19
+ value: "value",
20
+ };
21
+ const CLI_PARSE_RESULT_KIND = {
22
+ parsed: "parsed",
23
+ raw: "raw",
24
+ };
25
+ const CLI_SEGMENT_NAME_KIND = {
26
+ alias: "alias",
27
+ token: "token",
28
+ };
29
+ const CLI_TREE_VALIDATION_ISSUE = {
30
+ subcommandHintWithoutChildren: "CLI subcommand hint requires at least one child command",
31
+ };
32
+ const EMPTY_CLI_GROUP_HELP = {
33
+ subcommandHint: null,
34
+ examples: [],
35
+ };
36
+ export function createCliRouter(tree) {
37
+ const root = compileCliTree(tree);
38
+ return {
39
+ help: root.help,
40
+ endpoints: listCliEndpoints(root),
41
+ resolve: (argv) => resolveCli(root, argv),
42
+ };
43
+ }
44
+ function listCliEndpoints(root) {
45
+ const endpoints = [];
46
+ const visited = new Set();
47
+ const pending = [root];
48
+ while (pending.length > 0) {
49
+ const node = pending.pop();
50
+ if (!node || visited.has(node)) {
51
+ continue;
52
+ }
53
+ visited.add(node);
54
+ if (node.endpoint !== null) {
55
+ endpoints.push(node.endpoint.endpoint);
56
+ }
57
+ pending.push(...node.childByToken.values());
58
+ }
59
+ return endpoints;
60
+ }
61
+ function compileCliTree(tree) {
62
+ const issues = [...validateEntryTreeStructure(tree)];
63
+ const nodeRecords = new Map();
64
+ visitEntryTree(tree, {
65
+ visitNode(node, path) {
66
+ nodeRecords.set(node, {
67
+ path,
68
+ segment: cliSegmentFor(node, path, node === tree.root, issues),
69
+ endpoint: cliEndpointFor(node, path, issues),
70
+ });
71
+ },
72
+ visitEndpoint() { },
73
+ });
74
+ const publicNodes = new Map();
75
+ const pending = [];
76
+ visitEntryTree(tree, {
77
+ visitNode(node) {
78
+ pending.push(node);
79
+ },
80
+ visitEndpoint() { },
81
+ });
82
+ for (let index = pending.length - 1; index >= 0; index -= 1) {
83
+ const node = pending[index];
84
+ if (!node) {
85
+ continue;
86
+ }
87
+ const record = nodeRecords.get(node);
88
+ if (!record) {
89
+ continue;
90
+ }
91
+ const children = node.children.flatMap((child) => {
92
+ const compiled = publicNodes.get(child);
93
+ return compiled ? [compiled] : [];
94
+ });
95
+ if (record.segment.help.subcommandHint !== null && children.length === 0) {
96
+ issues.push(`${formatTreePath(record.path)}: ${CLI_TREE_VALIDATION_ISSUE.subcommandHintWithoutChildren}`);
97
+ }
98
+ if (record.endpoint === null &&
99
+ children.length === 0 &&
100
+ node !== tree.root) {
101
+ continue;
102
+ }
103
+ validateCliChildren(record.path, children, issues);
104
+ if (record.endpoint !== null &&
105
+ record.endpoint.positionals.length > 0 &&
106
+ children.length > 0) {
107
+ issues.push(`${formatTreePath(record.path)}: executable CLI node cannot have both positionals and children`);
108
+ }
109
+ const childByToken = new Map();
110
+ for (const child of children) {
111
+ childByToken.set(child.segment.token, child);
112
+ for (const alias of child.segment.aliases) {
113
+ childByToken.set(alias, child);
114
+ }
115
+ }
116
+ const compiledNode = {
117
+ path: record.path,
118
+ segment: record.segment,
119
+ endpoint: record.endpoint,
120
+ childByToken,
121
+ help: createCliHelpSurface({
122
+ node,
123
+ segment: record.segment,
124
+ endpoint: record.endpoint,
125
+ childHelp: children.map((child) => child.help),
126
+ root: node === tree.root,
127
+ }),
128
+ };
129
+ publicNodes.set(node, compiledNode);
130
+ }
131
+ if (issues.length > 0) {
132
+ throw new EntryTreeDefinitionError(issues);
133
+ }
134
+ const root = publicNodes.get(tree.root);
135
+ if (!root) {
136
+ throw new EntryTreeDefinitionError([
137
+ "entry tree has no public CLI endpoint",
138
+ ]);
139
+ }
140
+ return root;
141
+ }
142
+ function cliSegmentFor(node, path, root, issues) {
143
+ const override = node.segments.find((row) => row.channel === CHANNEL.cli);
144
+ let segment;
145
+ if (!override) {
146
+ segment = { token: node.name, aliases: [], help: EMPTY_CLI_GROUP_HELP };
147
+ }
148
+ else if (!isCliSegment(override.segment)) {
149
+ issues.push(`${formatTreePath(path)}: invalid CLI segment override`);
150
+ segment = { token: node.name, aliases: [], help: EMPTY_CLI_GROUP_HELP };
151
+ }
152
+ else {
153
+ segment = override.segment;
154
+ }
155
+ validateCliHelpExamples(segment.help.examples, formatTreePath(path), issues);
156
+ if (!root) {
157
+ validateCliCommandToken(segment.token, CLI_SEGMENT_NAME_KIND.token, path, issues);
158
+ for (const alias of segment.aliases) {
159
+ validateCliCommandToken(alias, CLI_SEGMENT_NAME_KIND.alias, path, issues);
160
+ }
161
+ }
162
+ return segment;
163
+ }
164
+ function validateCliCommandToken(token, kind, path, issues) {
165
+ if (token.length === 0) {
166
+ issues.push(`${formatTreePath(path)}: CLI segment ${kind} must not be empty`);
167
+ return;
168
+ }
169
+ if (classifyCliArgumentToken(token) !== CLI_ARGUMENT_TOKEN_KIND.value) {
170
+ issues.push(`${formatTreePath(path)}: CLI segment ${kind} ${JSON.stringify(token)} is unreachable`);
171
+ }
172
+ }
173
+ function cliEndpointFor(node, path, issues) {
174
+ const endpoints = node.endpoints.flatMap((endpoint) => findChannelBinding(endpoint, CHANNEL.cli) ? [endpoint] : []);
175
+ if (endpoints.length > 1) {
176
+ issues.push(`${formatTreePath(path)}: more than one public CLI endpoint`);
177
+ }
178
+ const endpoint = endpoints[0];
179
+ if (!endpoint) {
180
+ return null;
181
+ }
182
+ const channelBinding = findChannelBinding(endpoint, CHANNEL.cli);
183
+ if (!channelBinding || !isCliEntryBinding(channelBinding.binding)) {
184
+ issues.push(`${formatTreePath(path)}: invalid CLI endpoint binding`);
185
+ return null;
186
+ }
187
+ const location = `${formatTreePath(path)}#${endpoint.entry.name}`;
188
+ const compiled = compileCliBinding(endpoint, channelBinding.binding, location, issues);
189
+ validateCliCommandHelp(compiled, location, issues);
190
+ return compiled;
191
+ }
192
+ function validateCliChildren(path, children, issues) {
193
+ const tokens = new Map();
194
+ for (const child of children) {
195
+ for (const token of [child.segment.token, ...child.segment.aliases]) {
196
+ const owner = tokens.get(token);
197
+ if (owner !== undefined) {
198
+ issues.push(`${formatTreePath(path)}: CLI sibling token/alias collision ${JSON.stringify(token)} between ${owner} and ${child.help.name}`);
199
+ }
200
+ else {
201
+ tokens.set(token, child.help.name);
202
+ }
203
+ }
204
+ }
205
+ }
206
+ function resolveCli(root, argv) {
207
+ let node = root;
208
+ let index = 0;
209
+ while (index < argv.length) {
210
+ const token = argv[index];
211
+ if (token === undefined) {
212
+ break;
213
+ }
214
+ if (classifyCliArgumentToken(token) !== CLI_ARGUMENT_TOKEN_KIND.value) {
215
+ break;
216
+ }
217
+ const child = node.childByToken.get(token);
218
+ if (!child) {
219
+ break;
220
+ }
221
+ node = child;
222
+ index += 1;
223
+ }
224
+ const unmatchedToken = argv[index];
225
+ if (node.endpoint !== null &&
226
+ node.childByToken.size > 0 &&
227
+ unmatchedToken !== undefined &&
228
+ classifyCliArgumentToken(unmatchedToken) === CLI_ARGUMENT_TOKEN_KIND.value) {
229
+ return {
230
+ kind: CLI_ROUTER_RESULT_KIND.unknownCommand,
231
+ path: node.path,
232
+ token: unmatchedToken,
233
+ };
234
+ }
235
+ if (node.endpoint === null) {
236
+ return unmatchedToken === undefined
237
+ ? { kind: CLI_ROUTER_RESULT_KIND.incompleteCommand, path: node.path }
238
+ : {
239
+ kind: CLI_ROUTER_RESULT_KIND.unknownCommand,
240
+ path: node.path,
241
+ token: unmatchedToken,
242
+ };
243
+ }
244
+ const compiledEndpoint = node.endpoint;
245
+ const rawResult = parseCliArguments(compiledEndpoint, argv.slice(index));
246
+ switch (rawResult.kind) {
247
+ case CLI_ROUTER_RESULT_KIND.invalidInput:
248
+ case CLI_ROUTER_RESULT_KIND.invalidParameter:
249
+ return { ...rawResult, path: node.path };
250
+ case CLI_PARSE_RESULT_KIND.raw:
251
+ break;
252
+ }
253
+ const endpoint = compiledEndpoint.endpoint;
254
+ const decoded = decodeEntryInput({
255
+ entry: endpoint.entry,
256
+ parameterNames: endpoint.entry.parameters.map((parameter) => parameter.name),
257
+ raw: rawResult.raw,
258
+ coerce: (parameter, rawValue) => coerceCliArgvValue(parameter.codec.shape, rawValue, cliCoercionFor(compiledEndpoint, parameter.name)),
259
+ });
260
+ switch (decoded.kind) {
261
+ case ENTRY_DECODE_RESULT_KIND.resolved:
262
+ return {
263
+ kind: CLI_ROUTER_RESULT_KIND.resolved,
264
+ path: node.path,
265
+ endpoint,
266
+ projection: decoded.projection,
267
+ };
268
+ case ENTRY_DECODE_RESULT_KIND.invalidInput:
269
+ return {
270
+ kind: CLI_ROUTER_RESULT_KIND.invalidInput,
271
+ path: node.path,
272
+ issues: decoded.issues,
273
+ };
274
+ case ENTRY_DECODE_RESULT_KIND.invalidParameter:
275
+ return {
276
+ kind: CLI_ROUTER_RESULT_KIND.invalidParameter,
277
+ path: node.path,
278
+ parameterName: decoded.parameterName,
279
+ issues: decoded.issues,
280
+ };
281
+ case ENTRY_DECODE_RESULT_KIND.internalError:
282
+ return {
283
+ kind: CLI_ROUTER_RESULT_KIND.internalError,
284
+ path: node.path,
285
+ error: decoded.error,
286
+ };
287
+ }
288
+ }
289
+ function cliCoercionFor(endpoint, parameterName) {
290
+ return ([...endpoint.options, ...endpoint.positionals].find((binding) => binding.parameter.name === parameterName)?.coercion ?? null);
291
+ }
292
+ function parseCliArguments(endpoint, argv) {
293
+ const raw = new Map();
294
+ const positionalValues = [];
295
+ let positionalOnly = false;
296
+ for (let index = 0; index < argv.length; index += 1) {
297
+ const token = argv[index];
298
+ if (token === undefined) {
299
+ continue;
300
+ }
301
+ if (!positionalOnly) {
302
+ switch (classifyCliArgumentToken(token)) {
303
+ case CLI_ARGUMENT_TOKEN_KIND.positionalSeparator:
304
+ positionalOnly = true;
305
+ continue;
306
+ case CLI_ARGUMENT_TOKEN_KIND.option: {
307
+ const parsed = parseCliOption({ endpoint, argv, index, raw });
308
+ switch (parsed.kind) {
309
+ case CLI_ROUTER_RESULT_KIND.invalidInput:
310
+ case CLI_ROUTER_RESULT_KIND.invalidParameter:
311
+ return parsed;
312
+ case CLI_PARSE_RESULT_KIND.parsed:
313
+ if (parsed.consumedNext) {
314
+ index += 1;
315
+ }
316
+ }
317
+ continue;
318
+ }
319
+ case CLI_ARGUMENT_TOKEN_KIND.value:
320
+ break;
321
+ }
322
+ }
323
+ positionalValues.push(token);
324
+ }
325
+ return bindCliPositionals(endpoint, positionalValues, raw);
326
+ }
327
+ function parseCliOption(input) {
328
+ const token = input.argv[input.index] ?? "";
329
+ const [flag, inlineValue] = splitOptionToken(token);
330
+ const action = input.endpoint.optionByToken.get(flag);
331
+ if (!action) {
332
+ return invalidCliInput(`Unknown option ${flag}`);
333
+ }
334
+ if (action.kind === CLI_OPTION_ACTION_KIND.setBoolean) {
335
+ return setCliBooleanValue(input.raw, action, flag, inlineValue);
336
+ }
337
+ const option = action.option;
338
+ const value = inlineValue ?? input.argv[input.index + 1];
339
+ if (value === undefined) {
340
+ return invalidCliParameter(option.parameter.name, `${flag} requires a value`);
341
+ }
342
+ storeOptionValue(input.raw, option, value);
343
+ return {
344
+ kind: CLI_PARSE_RESULT_KIND.parsed,
345
+ consumedNext: inlineValue === undefined,
346
+ };
347
+ }
348
+ function setCliBooleanValue(raw, action, flag, inlineValue) {
349
+ if (inlineValue !== undefined) {
350
+ return invalidCliParameter(action.parameterName, `${flag} does not take a value`);
351
+ }
352
+ raw.set(action.parameterName, action.value);
353
+ return { kind: CLI_PARSE_RESULT_KIND.parsed, consumedNext: false };
354
+ }
355
+ function bindCliPositionals(endpoint, positionalValues, raw) {
356
+ let positionalIndex = 0;
357
+ for (const positional of endpoint.positionals) {
358
+ if (positional.kind === CLI_POSITIONAL_PLAN_KIND.rest) {
359
+ const values = positionalValues.slice(positionalIndex);
360
+ if (values.length > 0) {
361
+ raw.set(positional.parameter.name, values);
362
+ }
363
+ positionalIndex = positionalValues.length;
364
+ }
365
+ else {
366
+ const value = positionalValues[positionalIndex];
367
+ if (value !== undefined) {
368
+ raw.set(positional.parameter.name, value);
369
+ positionalIndex += 1;
370
+ }
371
+ }
372
+ }
373
+ if (positionalIndex < positionalValues.length) {
374
+ return invalidCliInput(`Unexpected argument ${positionalValues[positionalIndex]}`);
375
+ }
376
+ return { kind: CLI_PARSE_RESULT_KIND.raw, raw: Object.fromEntries(raw) };
377
+ }
378
+ function storeOptionValue(raw, option, value) {
379
+ switch (option.valueMode) {
380
+ case CLI_OPTION_VALUE_MODE.replace:
381
+ raw.set(option.parameter.name, value);
382
+ return;
383
+ case CLI_OPTION_VALUE_MODE.repeat:
384
+ appendOptionValues(raw, option.parameter.name, [value]);
385
+ return;
386
+ case CLI_OPTION_VALUE_MODE.commaOrRepeat:
387
+ appendOptionValues(raw, option.parameter.name, value.split(CLI_ARGUMENT_SYNTAX.repeatedValueSeparator));
388
+ return;
389
+ }
390
+ }
391
+ function appendOptionValues(raw, parameterName, values) {
392
+ const current = raw.get(parameterName);
393
+ raw.set(parameterName, Array.isArray(current) ? [...current, ...values] : values);
394
+ }
395
+ function splitOptionToken(token) {
396
+ const equalsIndex = token.indexOf(CLI_ARGUMENT_SYNTAX.inlineValueSeparator);
397
+ return equalsIndex < 0
398
+ ? [token, undefined]
399
+ : [token.slice(0, equalsIndex), token.slice(equalsIndex + 1)];
400
+ }
401
+ function classifyCliArgumentToken(token) {
402
+ if (token === CLI_ARGUMENT_SYNTAX.positionalSeparator) {
403
+ return CLI_ARGUMENT_TOKEN_KIND.positionalSeparator;
404
+ }
405
+ return token.startsWith(CLI_ARGUMENT_SYNTAX.optionPrefix)
406
+ ? CLI_ARGUMENT_TOKEN_KIND.option
407
+ : CLI_ARGUMENT_TOKEN_KIND.value;
408
+ }
409
+ function invalidCliInput(issue) {
410
+ return {
411
+ kind: CLI_ROUTER_RESULT_KIND.invalidInput,
412
+ issues: [issue],
413
+ };
414
+ }
415
+ function invalidCliParameter(parameterName, issue) {
416
+ return {
417
+ kind: CLI_ROUTER_RESULT_KIND.invalidParameter,
418
+ parameterName,
419
+ issues: [issue],
420
+ };
421
+ }
@@ -0,0 +1,24 @@
1
+ export declare const CLI_BOOLEAN_MODE: {
2
+ readonly flag: "flag";
3
+ readonly value: "value";
4
+ };
5
+ export declare const CLI_HELP_USAGE_KIND: {
6
+ readonly authored: "authored";
7
+ readonly derived: "derived";
8
+ };
9
+ export declare const CLI_OPTION_KIND: {
10
+ readonly booleanFlag: "boolean_flag";
11
+ readonly value: "value";
12
+ };
13
+ export declare const CLI_OPTION_VALUE_MODE: {
14
+ readonly replace: "replace";
15
+ readonly repeat: "repeat";
16
+ readonly commaOrRepeat: "comma_or_repeat";
17
+ };
18
+ export declare const CLI_POSITIONAL_PLAN_KIND: {
19
+ readonly single: "single";
20
+ readonly rest: "rest";
21
+ };
22
+ export declare function defaultCliOptionFlag(parameterName: string): `--${string}`;
23
+ export declare function defaultCliValueName(parameterName: string): string;
24
+ //# sourceMappingURL=cli-values.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-values.d.ts","sourceRoot":"","sources":["../src/cli-values.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,gBAAgB;;;CAGnB,CAAC;AAEX,eAAO,MAAM,mBAAmB;;;CAGtB,CAAC;AAEX,eAAO,MAAM,eAAe;;;CAGlB,CAAC;AAEX,eAAO,MAAM,qBAAqB;;;;CAIxB,CAAC;AAEX,eAAO,MAAM,wBAAwB;;;CAG3B,CAAC;AAEX,wBAAgB,oBAAoB,CAAC,aAAa,EAAE,MAAM,GAAG,KAAK,MAAM,EAAE,CAEzE;AAED,wBAAgB,mBAAmB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAIjE"}
@@ -0,0 +1,40 @@
1
+ const CLI_PARAMETER_SYNTAX = {
2
+ longOptionPrefix: "--",
3
+ parameterWordSeparator: "-",
4
+ valueWordSeparator: "_",
5
+ };
6
+ export const CLI_BOOLEAN_MODE = {
7
+ flag: "flag",
8
+ value: "value",
9
+ };
10
+ export const CLI_HELP_USAGE_KIND = {
11
+ authored: "authored",
12
+ derived: "derived",
13
+ };
14
+ export const CLI_OPTION_KIND = {
15
+ booleanFlag: "boolean_flag",
16
+ value: "value",
17
+ };
18
+ export const CLI_OPTION_VALUE_MODE = {
19
+ replace: "replace",
20
+ repeat: "repeat",
21
+ commaOrRepeat: "comma_or_repeat",
22
+ };
23
+ export const CLI_POSITIONAL_PLAN_KIND = {
24
+ single: "single",
25
+ rest: "rest",
26
+ };
27
+ export function defaultCliOptionFlag(parameterName) {
28
+ return `${CLI_PARAMETER_SYNTAX.longOptionPrefix}${toKebabCase(parameterName)}`;
29
+ }
30
+ export function defaultCliValueName(parameterName) {
31
+ return toKebabCase(parameterName)
32
+ .replace(/-/g, CLI_PARAMETER_SYNTAX.valueWordSeparator)
33
+ .toUpperCase();
34
+ }
35
+ function toKebabCase(name) {
36
+ return name
37
+ .replace(/[A-Z]/g, (letter) => `${CLI_PARAMETER_SYNTAX.parameterWordSeparator}${letter.toLowerCase()}`)
38
+ .replace(/_/g, CLI_PARAMETER_SYNTAX.parameterWordSeparator)
39
+ .replace(/^-/, "");
40
+ }
package/dist/cli.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ export * from "./cli-executor.js";
2
+ export * from "./cli-model.js";
3
+ export * from "./cli-presentation.js";
4
+ export { createCliRouter } from "./cli-tree.js";
5
+ export { CLI_BOOLEAN_MODE, CLI_HELP_USAGE_KIND, CLI_OPTION_KIND, CLI_OPTION_VALUE_MODE, } from "./cli-values.js";
6
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,uBAAuB,CAAC;AACtC,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EACnB,eAAe,EACf,qBAAqB,GACtB,MAAM,iBAAiB,CAAC"}
package/dist/cli.js ADDED
@@ -0,0 +1,5 @@
1
+ export * from "./cli-executor.js";
2
+ export * from "./cli-model.js";
3
+ export * from "./cli-presentation.js";
4
+ export { createCliRouter } from "./cli-tree.js";
5
+ export { CLI_BOOLEAN_MODE, CLI_HELP_USAGE_KIND, CLI_OPTION_KIND, CLI_OPTION_VALUE_MODE, } from "./cli-values.js";
@@ -0,0 +1,25 @@
1
+ import { z } from "zod";
2
+ import type { ValueCodec, ValueForShape, ValueShape } from "./model.js";
3
+ declare function defineValueCodec<const Shape extends ValueShape>(input: {
4
+ readonly schema: z.ZodType<ValueForShape<Shape>>;
5
+ readonly shape: Shape;
6
+ }): ValueCodec<ValueForShape<Shape>>;
7
+ declare function stringCodec(): ValueCodec<string>;
8
+ declare function booleanCodec(): ValueCodec<boolean>;
9
+ declare function positiveIntegerCodec(input?: {
10
+ readonly max: number | null;
11
+ }): ValueCodec<number>;
12
+ declare function stringEnumCodec<const Values extends readonly [string, ...string[]]>(values: Values): ValueCodec<Values[number]>;
13
+ declare function stringEnumListCodec<const Values extends readonly [string, ...string[]]>(values: Values): ValueCodec<readonly Values[number][]>;
14
+ declare function stringListCodec(): ValueCodec<readonly string[]>;
15
+ export declare const codecs: {
16
+ define: typeof defineValueCodec;
17
+ string: typeof stringCodec;
18
+ stringList: typeof stringListCodec;
19
+ boolean: typeof booleanCodec;
20
+ positiveInteger: typeof positiveIntegerCodec;
21
+ stringEnum: typeof stringEnumCodec;
22
+ stringEnumList: typeof stringEnumListCodec;
23
+ };
24
+ export {};
25
+ //# sourceMappingURL=codecs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codecs.d.ts","sourceRoot":"","sources":["../src/codecs.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAGxE,iBAAS,gBAAgB,CAAC,KAAK,CAAC,KAAK,SAAS,UAAU,EAAE,KAAK,EAAE;IAC/D,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;IACjD,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;CACvB,GAAG,UAAU,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAQnC;AAED,iBAAS,WAAW,IAAI,UAAU,CAAC,MAAM,CAAC,CAKzC;AAED,iBAAS,YAAY,IAAI,UAAU,CAAC,OAAO,CAAC,CAK3C;AAED,iBAAS,oBAAoB,CAC3B,KAAK,GAAE;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;CAAkB,GACrD,UAAU,CAAC,MAAM,CAAC,CAQpB;AAED,iBAAS,eAAe,CAAC,KAAK,CAAC,MAAM,SAAS,SAAS,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,EAC1E,MAAM,EAAE,MAAM,GACb,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAQ5B;AAED,iBAAS,mBAAmB,CAC1B,KAAK,CAAC,MAAM,SAAS,SAAS,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,EACnD,MAAM,EAAE,MAAM,GAAG,UAAU,CAAC,SAAS,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAYvD;AAED,iBAAS,eAAe,IAAI,UAAU,CAAC,SAAS,MAAM,EAAE,CAAC,CASxD;AAED,eAAO,MAAM,MAAM;;;;;;;;CAQlB,CAAC"}
package/dist/codecs.js ADDED
@@ -0,0 +1,74 @@
1
+ import { createSchemaRuntimeCodec, readonlyArraySchema } from "@alpacakit/core";
2
+ import { z } from "zod";
3
+ import { VALUE_SHAPE_KIND } from "./values.js";
4
+ function defineValueCodec(input) {
5
+ const codec = createSchemaRuntimeCodec(input.schema);
6
+ return {
7
+ schema: input.schema,
8
+ shape: input.shape,
9
+ decode: codec.decode,
10
+ encode: codec.encode,
11
+ };
12
+ }
13
+ function stringCodec() {
14
+ return defineValueCodec({
15
+ schema: z.string(),
16
+ shape: { kind: VALUE_SHAPE_KIND.string },
17
+ });
18
+ }
19
+ function booleanCodec() {
20
+ return defineValueCodec({
21
+ schema: z.boolean(),
22
+ shape: { kind: VALUE_SHAPE_KIND.boolean },
23
+ });
24
+ }
25
+ function positiveIntegerCodec(input = { max: null }) {
26
+ const base = z.number().int().positive();
27
+ return defineValueCodec({
28
+ schema: (input.max === null
29
+ ? base
30
+ : base.max(input.max)),
31
+ shape: { kind: VALUE_SHAPE_KIND.positiveInteger },
32
+ });
33
+ }
34
+ function stringEnumCodec(values) {
35
+ return defineValueCodec({
36
+ schema: z.enum(values),
37
+ shape: {
38
+ kind: VALUE_SHAPE_KIND.stringEnum,
39
+ values,
40
+ },
41
+ });
42
+ }
43
+ function stringEnumListCodec(values) {
44
+ const element = stringEnumCodec(values);
45
+ return defineValueCodec({
46
+ schema: readonlyArraySchema(element.schema),
47
+ shape: {
48
+ kind: VALUE_SHAPE_KIND.array,
49
+ element: {
50
+ kind: VALUE_SHAPE_KIND.stringEnum,
51
+ values,
52
+ },
53
+ },
54
+ });
55
+ }
56
+ function stringListCodec() {
57
+ const element = stringCodec();
58
+ return defineValueCodec({
59
+ schema: readonlyArraySchema(element.schema),
60
+ shape: {
61
+ kind: VALUE_SHAPE_KIND.array,
62
+ element: element.shape,
63
+ },
64
+ });
65
+ }
66
+ export const codecs = {
67
+ define: defineValueCodec,
68
+ string: stringCodec,
69
+ stringList: stringListCodec,
70
+ boolean: booleanCodec,
71
+ positiveInteger: positiveIntegerCodec,
72
+ stringEnum: stringEnumCodec,
73
+ stringEnumList: stringEnumListCodec,
74
+ };
@@ -0,0 +1,12 @@
1
+ import { type JsonValue } from "@alpacakit/core";
2
+ import type { z } from "zod";
3
+ import type { EntryDefinition, EntryDefinitionWithOutput, OutputContract, ParameterSet } from "./model.js";
4
+ export declare function createOutputContract<Value extends JsonValue>(schema: z.ZodType<Value>): OutputContract<Value>;
5
+ export declare class EntryOutputContractError extends Error {
6
+ readonly entryName: string;
7
+ readonly issues: readonly string[];
8
+ constructor(entryName: string, issues: readonly string[]);
9
+ }
10
+ export declare function requireEntryOutput<const Name extends string, const Parameters extends ParameterSet, Projection, Output extends JsonValue>(entry: EntryDefinition<Name, Parameters, Projection, Output>): OutputContract<Output>;
11
+ export declare function verifyEntryOutput<const Name extends string, const Parameters extends ParameterSet, Projection, Output extends JsonValue>(entry: EntryDefinitionWithOutput<Name, Parameters, Projection, Output>, value: NoInfer<Output>): Output;
12
+ //# sourceMappingURL=contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,SAAS,EACf,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EACV,eAAe,EACf,yBAAyB,EACzB,cAAc,EACd,YAAY,EACb,MAAM,YAAY,CAAC;AAEpB,wBAAgB,oBAAoB,CAAC,KAAK,SAAS,SAAS,EAC1D,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,GACvB,cAAc,CAAC,KAAK,CAAC,CAGvB;AAED,qBAAa,wBAAyB,SAAQ,KAAK;IAE/C,QAAQ,CAAC,SAAS,EAAE,MAAM;IAC1B,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE;gBADzB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,SAAS,MAAM,EAAE;CAKrC;AAED,wBAAgB,kBAAkB,CAChC,KAAK,CAAC,IAAI,SAAS,MAAM,EACzB,KAAK,CAAC,UAAU,SAAS,YAAY,EACrC,UAAU,EACV,MAAM,SAAS,SAAS,EAExB,KAAK,EAAE,eAAe,CAAC,IAAI,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,CAAC,GAC3D,cAAc,CAAC,MAAM,CAAC,CAKxB;AAED,wBAAgB,iBAAiB,CAC/B,KAAK,CAAC,IAAI,SAAS,MAAM,EACzB,KAAK,CAAC,UAAU,SAAS,YAAY,EACrC,UAAU,EACV,MAAM,SAAS,SAAS,EAExB,KAAK,EAAE,yBAAyB,CAAC,IAAI,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,CAAC,EACtE,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,GACrB,MAAM,CAMR"}
@@ -0,0 +1,28 @@
1
+ import { CODEC_DECODE_RESULT_KIND, createSchemaRuntimeCodec, } from "@alpacakit/core";
2
+ export function createOutputContract(schema) {
3
+ const { decode } = createSchemaRuntimeCodec(schema);
4
+ return { schema, decode };
5
+ }
6
+ export class EntryOutputContractError extends Error {
7
+ entryName;
8
+ issues;
9
+ constructor(entryName, issues) {
10
+ super(`Invalid output for entry ${entryName}:\n${issues.join("\n")}`);
11
+ this.entryName = entryName;
12
+ this.issues = issues;
13
+ this.name = EntryOutputContractError.name;
14
+ }
15
+ }
16
+ export function requireEntryOutput(entry) {
17
+ if (entry.output === null) {
18
+ throw new Error(`Entry ${entry.name} does not declare an output contract`);
19
+ }
20
+ return entry.output;
21
+ }
22
+ export function verifyEntryOutput(entry, value) {
23
+ const decoded = requireEntryOutput(entry).decode(value);
24
+ if (decoded.kind === CODEC_DECODE_RESULT_KIND.invalid) {
25
+ throw new EntryOutputContractError(entry.name, decoded.issues);
26
+ }
27
+ return decoded.value;
28
+ }
@@ -0,0 +1,16 @@
1
+ import type { JsonValue } from "@alpacakit/core";
2
+ import type { ChannelSegmentOverride, CommandDocumentationInput, EntryChannelBinding, EntryDefinition, EntryDefinitionWithOutput, EntryTreeDefinition, EntryTreeEndpoint, EntryTreeNode, ParameterSet } from "./model.js";
3
+ import type { Channel } from "./values.js";
4
+ export declare function defineEntryTree(root: EntryTreeNode): EntryTreeDefinition;
5
+ export declare function defineEntryNode(input: {
6
+ readonly name: string;
7
+ readonly description: string;
8
+ readonly documentation?: CommandDocumentationInput;
9
+ readonly segments?: readonly ChannelSegmentOverride[];
10
+ readonly endpoints?: readonly EntryTreeEndpoint[];
11
+ readonly children?: readonly EntryTreeNode[];
12
+ }): EntryTreeNode;
13
+ export declare function mergeEntryTreeNodes(nodes: readonly EntryTreeNode[]): readonly EntryTreeNode[];
14
+ export declare function defineEntryEndpoint<const Name extends string, const Parameters extends ParameterSet, Projection, Output extends JsonValue>(entry: EntryDefinitionWithOutput<Name, Parameters, Projection, Output>, ...channels: readonly EntryChannelBinding<Channel, NoInfer<Parameters[number]["name"]>>[]): EntryTreeEndpoint<EntryDefinitionWithOutput<Name, Parameters, Projection, Output>>;
15
+ export declare function defineEntryEndpoint<const Name extends string, const Parameters extends ParameterSet, Projection>(entry: EntryDefinition<Name, Parameters, Projection>, ...channels: readonly EntryChannelBinding<Channel, NoInfer<Parameters[number]["name"]>>[]): EntryTreeEndpoint<EntryDefinition<Name, Parameters, Projection>>;
16
+ //# sourceMappingURL=factory.d.ts.map