@reliverse/rempts 1.7.29 → 1.7.31
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 +141 -29
- package/bin/{components/animate/animate.js → libs/animate/animate-mod.js} +1 -4
- package/bin/{components → libs}/anykey/anykey-mod.js +2 -2
- package/bin/libs/confirm/confirm-alias.d.ts +1 -0
- package/bin/libs/confirm/confirm-alias.js +2 -0
- package/bin/{components/confirm/confirm-prompt.js → libs/confirm/confirm-mod.js} +1 -1
- package/bin/{components → libs}/editor/editor-mod.js +8 -8
- package/bin/{components → libs}/figures/figures-mod.d.ts +2 -3
- package/bin/{components → libs}/figures/figures-mod.js +1 -2
- package/bin/libs/input/input-alias.d.ts +4 -0
- package/bin/libs/input/input-alias.js +4 -0
- package/bin/{components/input/input-prompt.js → libs/input/input-mod.js} +2 -2
- package/bin/libs/intro/intro-alias.d.ts +2 -0
- package/bin/libs/intro/intro-alias.js +3 -0
- package/bin/{components/intro/intro-start.js → libs/intro/intro-mod.js} +3 -3
- package/bin/libs/launcher/launcher-alias.d.ts +2 -0
- package/bin/libs/launcher/launcher-alias.js +2 -0
- package/bin/libs/launcher/launcher-mod.d.ts +114 -0
- package/bin/{components → libs}/launcher/launcher-mod.js +355 -191
- package/bin/{components → libs}/launcher/launcher-types.d.ts +1 -1
- package/bin/libs/launcher/trpc-orpc-support/completions.d.ts +4 -0
- package/bin/libs/launcher/trpc-orpc-support/completions.js +45 -0
- package/bin/libs/launcher/trpc-orpc-support/errors.d.ts +11 -0
- package/bin/libs/launcher/trpc-orpc-support/errors.js +10 -0
- package/bin/libs/launcher/trpc-orpc-support/index.d.ts +34 -0
- package/bin/libs/launcher/trpc-orpc-support/index.js +641 -0
- package/bin/libs/launcher/trpc-orpc-support/json-schema.d.ts +17 -0
- package/bin/libs/launcher/trpc-orpc-support/json-schema.js +168 -0
- package/bin/libs/launcher/trpc-orpc-support/json.d.ts +44 -0
- package/bin/libs/launcher/trpc-orpc-support/json.js +41 -0
- package/bin/libs/launcher/trpc-orpc-support/logging.d.ts +11 -0
- package/bin/libs/launcher/trpc-orpc-support/logging.js +26 -0
- package/bin/libs/launcher/trpc-orpc-support/parse-procedure.d.ts +2 -0
- package/bin/libs/launcher/trpc-orpc-support/parse-procedure.js +486 -0
- package/bin/libs/launcher/trpc-orpc-support/prompts.d.ts +18 -0
- package/bin/libs/launcher/trpc-orpc-support/prompts.js +534 -0
- package/bin/libs/launcher/trpc-orpc-support/standard-schema/contract.d.ts +53 -0
- package/bin/libs/launcher/trpc-orpc-support/standard-schema/contract.js +0 -0
- package/bin/libs/launcher/trpc-orpc-support/standard-schema/errors.d.ts +9 -0
- package/bin/libs/launcher/trpc-orpc-support/standard-schema/errors.js +47 -0
- package/bin/libs/launcher/trpc-orpc-support/standard-schema/utils.d.ts +3 -0
- package/bin/libs/launcher/trpc-orpc-support/standard-schema/utils.js +6 -0
- package/bin/libs/launcher/trpc-orpc-support/trpc-compat.d.ts +71 -0
- package/bin/libs/launcher/trpc-orpc-support/trpc-compat.js +11 -0
- package/bin/libs/launcher/trpc-orpc-support/types.d.ts +276 -0
- package/bin/libs/launcher/trpc-orpc-support/types.js +0 -0
- package/bin/libs/launcher/trpc-orpc-support/util.d.ts +9 -0
- package/bin/libs/launcher/trpc-orpc-support/util.js +9 -0
- package/bin/{components → libs}/msg-fmt/logger.js +1 -1
- package/bin/libs/multiselect/multiselect-alias.d.ts +1 -0
- package/bin/libs/multiselect/multiselect-alias.js +2 -0
- package/bin/{components/select → libs/multiselect}/multiselect-prompt.js +1 -1
- package/bin/{components → libs}/number/number-mod.js +1 -4
- package/bin/libs/outro/outro-alias.d.ts +2 -0
- package/bin/libs/outro/outro-alias.js +3 -0
- package/bin/{components/outro/outro-end.js → libs/outro/outro-mod.js} +2 -2
- package/bin/libs/select/aliases-alias.d.ts +1 -0
- package/bin/libs/select/aliases-alias.js +2 -0
- package/bin/{components → libs}/select/select-prompt.js +2 -2
- package/bin/{components → libs}/select/toggle-prompt.js +1 -1
- package/bin/libs/spinner/spinner-alias.d.ts +1 -0
- package/bin/libs/spinner/spinner-alias.js +2 -0
- package/bin/{components → libs}/spinner/spinner-mod.js +3 -1
- package/bin/{components → libs}/task/task-spin.js +1 -1
- package/bin/{utils → libs/utils}/colorize.d.ts +1 -1
- package/bin/{utils → libs/utils}/prevent.d.ts +1 -1
- package/bin/{utils → libs/utils}/prevent.js +2 -2
- package/bin/{utils → libs/utils}/prompt-end.d.ts +1 -1
- package/bin/{utils → libs/utils}/prompt-end.js +2 -2
- package/bin/{utils → libs/utils}/stream-text.d.ts +1 -1
- package/bin/{utils → libs/utils}/stream-text.js +2 -2
- package/bin/mod.d.ts +66 -41
- package/bin/mod.js +102 -66
- package/package.json +17 -2
- package/bin/components/aliases/aliases-mod.d.ts +0 -11
- package/bin/components/aliases/aliases-mod.js +0 -16
- package/bin/components/launcher/launcher-mod.d.ts +0 -52
- /package/bin/{components/animate/animate.d.ts → libs/animate/animate-mod.d.ts} +0 -0
- /package/bin/{components → libs}/anykey/anykey-mod.d.ts +0 -0
- /package/bin/{components → libs}/cancel/cancel.d.ts +0 -0
- /package/bin/{components → libs}/cancel/cancel.js +0 -0
- /package/bin/{components/confirm/confirm-prompt.d.ts → libs/confirm/confirm-mod.d.ts} +0 -0
- /package/bin/{components → libs}/date/date.d.ts +0 -0
- /package/bin/{components → libs}/date/date.js +0 -0
- /package/bin/{components → libs}/editor/editor-mod.d.ts +0 -0
- /package/bin/{components/input/input-prompt.d.ts → libs/input/input-mod.d.ts} +0 -0
- /package/bin/{components/intro/intro-start.d.ts → libs/intro/intro-mod.d.ts} +0 -0
- /package/bin/{components → libs}/launcher/launcher-types.js +0 -0
- /package/bin/{components → libs}/launcher/run-command.d.ts +0 -0
- /package/bin/{components → libs}/launcher/run-command.js +0 -0
- /package/bin/{components/log/log.d.ts → libs/log/log-alias.d.ts} +0 -0
- /package/bin/{components/log/log.js → libs/log/log-alias.js} +0 -0
- /package/bin/{components → libs}/msg-fmt/colors.d.ts +0 -0
- /package/bin/{components → libs}/msg-fmt/colors.js +0 -0
- /package/bin/{components → libs}/msg-fmt/logger.d.ts +0 -0
- /package/bin/{components → libs}/msg-fmt/mapping.d.ts +0 -0
- /package/bin/{components → libs}/msg-fmt/mapping.js +0 -0
- /package/bin/{components → libs}/msg-fmt/messages.d.ts +0 -0
- /package/bin/{components → libs}/msg-fmt/messages.js +0 -0
- /package/bin/{components → libs}/msg-fmt/terminal.d.ts +0 -0
- /package/bin/{components → libs}/msg-fmt/terminal.js +0 -0
- /package/bin/{components → libs}/msg-fmt/variants.d.ts +0 -0
- /package/bin/{components → libs}/msg-fmt/variants.js +0 -0
- /package/bin/{components/select → libs/multiselect}/multiselect-prompt.d.ts +0 -0
- /package/bin/{components → libs}/next-steps/next-steps.d.ts +0 -0
- /package/bin/{components → libs}/next-steps/next-steps.js +0 -0
- /package/bin/{components → libs}/number/number-mod.d.ts +0 -0
- /package/bin/{components/outro/outro-end.d.ts → libs/outro/outro-mod.d.ts} +0 -0
- /package/bin/{components → libs}/results/results.d.ts +0 -0
- /package/bin/{components → libs}/results/results.js +0 -0
- /package/bin/{components → libs}/select/nummultiselect-prompt.d.ts +0 -0
- /package/bin/{components → libs}/select/nummultiselect-prompt.js +0 -0
- /package/bin/{components → libs}/select/numselect-prompt.d.ts +0 -0
- /package/bin/{components → libs}/select/numselect-prompt.js +0 -0
- /package/bin/{components → libs}/select/select-prompt.d.ts +0 -0
- /package/bin/{components → libs}/select/toggle-prompt.d.ts +0 -0
- /package/bin/{components → libs}/spinner/spinner-mod.d.ts +0 -0
- /package/bin/{components → libs}/task/progress.d.ts +0 -0
- /package/bin/{components → libs}/task/progress.js +0 -0
- /package/bin/{components → libs}/task/task-spin.d.ts +0 -0
- /package/bin/{utils → libs/utils}/colorize.js +0 -0
- /package/bin/{utils → libs/utils}/errors.d.ts +0 -0
- /package/bin/{utils → libs/utils}/errors.js +0 -0
- /package/bin/{utils → libs/utils}/system.d.ts +0 -0
- /package/bin/{utils → libs/utils}/system.js +0 -0
- /package/bin/{utils → libs/utils}/validate.d.ts +0 -0
- /package/bin/{utils → libs/utils}/validate.js +0 -0
- /package/bin/{components/ascii-art/ascii-art.d.ts → libs/visual/visual-mod.d.ts} +0 -0
- /package/bin/{components/ascii-art/ascii-art.js → libs/visual/visual-mod.js} +0 -0
|
@@ -0,0 +1,641 @@
|
|
|
1
|
+
import * as trpcServer11 from "@trpc/server";
|
|
2
|
+
import {
|
|
3
|
+
Argument,
|
|
4
|
+
Command as BaseCommand,
|
|
5
|
+
InvalidArgumentError,
|
|
6
|
+
Option
|
|
7
|
+
} from "commander";
|
|
8
|
+
import { inspect } from "node:util";
|
|
9
|
+
import { addCompletions } from "./completions.js";
|
|
10
|
+
import { FailedToExitError, CliValidationError } from "./errors.js";
|
|
11
|
+
import { commandToJSON } from "./json.js";
|
|
12
|
+
import {
|
|
13
|
+
flattenedProperties,
|
|
14
|
+
incompatiblePropertyPairs,
|
|
15
|
+
getDescription,
|
|
16
|
+
getSchemaTypes,
|
|
17
|
+
getEnumChoices
|
|
18
|
+
} from "./json-schema.js";
|
|
19
|
+
import { lineByLineConsoleLogger } from "./logging.js";
|
|
20
|
+
import { parseProcedureInputs } from "./parse-procedure.js";
|
|
21
|
+
import { promptify } from "./prompts.js";
|
|
22
|
+
import { prettifyStandardSchemaError } from "./standard-schema/errors.js";
|
|
23
|
+
import { looksLikeStandardSchemaFailure } from "./standard-schema/utils.js";
|
|
24
|
+
import { isOrpcRouter } from "./trpc-compat.js";
|
|
25
|
+
import { looksLikeInstanceof } from "./util.js";
|
|
26
|
+
export * from "./types.js";
|
|
27
|
+
export { z } from "zod/v4";
|
|
28
|
+
export * as zod from "zod";
|
|
29
|
+
export * as trpcServer from "@trpc/server";
|
|
30
|
+
export class TrpcCommand extends BaseCommand {
|
|
31
|
+
/** @internal track the commands that have been run, so that we can find the `__result` of the last command */
|
|
32
|
+
__ran = [];
|
|
33
|
+
__input;
|
|
34
|
+
/** @internal stash the return value of the underlying procedure on the command so to pass to `FailedToExitError` for use in a pinch */
|
|
35
|
+
__result;
|
|
36
|
+
}
|
|
37
|
+
export const parseRouter = ({
|
|
38
|
+
router: router2,
|
|
39
|
+
...params2
|
|
40
|
+
}) => {
|
|
41
|
+
if (isOrpcRouter(router2)) return parseOrpcRouter({ router: router2, ...params2 });
|
|
42
|
+
return parseTrpcRouter({ router: router2, ...params2 });
|
|
43
|
+
};
|
|
44
|
+
const parseTrpcRouter = ({
|
|
45
|
+
router: router2,
|
|
46
|
+
...params2
|
|
47
|
+
}) => {
|
|
48
|
+
const defEntries = Object.entries(router2._def.procedures);
|
|
49
|
+
return defEntries.map(
|
|
50
|
+
([procedurePath2, procedure2]) => {
|
|
51
|
+
const meta2 = getMeta(procedure2);
|
|
52
|
+
if (meta2.jsonInput) {
|
|
53
|
+
return [
|
|
54
|
+
procedurePath2,
|
|
55
|
+
{
|
|
56
|
+
meta: meta2,
|
|
57
|
+
parsedProcedure: jsonProcedureInputs(),
|
|
58
|
+
incompatiblePairs: [],
|
|
59
|
+
procedure: procedure2
|
|
60
|
+
}
|
|
61
|
+
];
|
|
62
|
+
}
|
|
63
|
+
const procedureInputsResult = parseProcedureInputs(
|
|
64
|
+
procedure2._def.inputs,
|
|
65
|
+
params2
|
|
66
|
+
);
|
|
67
|
+
if (!procedureInputsResult.success) {
|
|
68
|
+
const procedureInputs2 = jsonProcedureInputs(
|
|
69
|
+
`procedure's schema couldn't be converted to CLI arguments: ${procedureInputsResult.error}`
|
|
70
|
+
);
|
|
71
|
+
return [
|
|
72
|
+
procedurePath2,
|
|
73
|
+
{
|
|
74
|
+
meta: meta2,
|
|
75
|
+
parsedProcedure: procedureInputs2,
|
|
76
|
+
incompatiblePairs: [],
|
|
77
|
+
procedure: procedure2
|
|
78
|
+
}
|
|
79
|
+
];
|
|
80
|
+
}
|
|
81
|
+
const procedureInputs = procedureInputsResult.value;
|
|
82
|
+
const incompatiblePairs2 = incompatiblePropertyPairs(
|
|
83
|
+
procedureInputs.optionsJsonSchema
|
|
84
|
+
);
|
|
85
|
+
return [
|
|
86
|
+
procedurePath2,
|
|
87
|
+
{
|
|
88
|
+
meta: getMeta(procedure2),
|
|
89
|
+
parsedProcedure: procedureInputs,
|
|
90
|
+
incompatiblePairs: incompatiblePairs2,
|
|
91
|
+
procedure: procedure2
|
|
92
|
+
}
|
|
93
|
+
];
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
};
|
|
97
|
+
const parseOrpcRouter = (params) => {
|
|
98
|
+
const entries = [];
|
|
99
|
+
const { traverseContractProcedures, isProcedure } = eval(
|
|
100
|
+
`require('@orpc/server')`
|
|
101
|
+
);
|
|
102
|
+
const router = params.router;
|
|
103
|
+
const lazyRoutes = traverseContractProcedures(
|
|
104
|
+
{ path: [], router },
|
|
105
|
+
({ contract, path }) => {
|
|
106
|
+
let procedure2 = params.router;
|
|
107
|
+
for (const p of path) procedure2 = procedure2[p];
|
|
108
|
+
if (!isProcedure(procedure2)) return;
|
|
109
|
+
const procedureInputsResult = parseProcedureInputs(
|
|
110
|
+
[contract["~orpc"].inputSchema],
|
|
111
|
+
{
|
|
112
|
+
"@valibot/to-json-schema": params["@valibot/to-json-schema"],
|
|
113
|
+
effect: params.effect
|
|
114
|
+
}
|
|
115
|
+
);
|
|
116
|
+
const procedurePath2 = path.join(".");
|
|
117
|
+
const procedureish = {
|
|
118
|
+
_def: { meta: contract["~orpc"].meta }
|
|
119
|
+
};
|
|
120
|
+
const meta2 = getMeta(procedureish);
|
|
121
|
+
if (meta2.jsonInput) {
|
|
122
|
+
entries.push([
|
|
123
|
+
procedurePath2,
|
|
124
|
+
{
|
|
125
|
+
meta: meta2,
|
|
126
|
+
parsedProcedure: jsonProcedureInputs(),
|
|
127
|
+
incompatiblePairs: [],
|
|
128
|
+
procedure: procedure2
|
|
129
|
+
}
|
|
130
|
+
]);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (!procedureInputsResult.success) {
|
|
134
|
+
const parsedProcedure3 = jsonProcedureInputs(
|
|
135
|
+
`procedure's schema couldn't be converted to CLI arguments: ${procedureInputsResult.error}`
|
|
136
|
+
);
|
|
137
|
+
entries.push([
|
|
138
|
+
procedurePath2,
|
|
139
|
+
{
|
|
140
|
+
meta: meta2,
|
|
141
|
+
parsedProcedure: parsedProcedure3,
|
|
142
|
+
incompatiblePairs: [],
|
|
143
|
+
procedure: procedure2
|
|
144
|
+
}
|
|
145
|
+
]);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const parsedProcedure2 = procedureInputsResult.value;
|
|
149
|
+
const incompatiblePairs2 = incompatiblePropertyPairs(
|
|
150
|
+
parsedProcedure2.optionsJsonSchema
|
|
151
|
+
);
|
|
152
|
+
entries.push([
|
|
153
|
+
procedurePath2,
|
|
154
|
+
{ procedure: procedure2, meta: meta2, incompatiblePairs: incompatiblePairs2, parsedProcedure: parsedProcedure2 }
|
|
155
|
+
]);
|
|
156
|
+
}
|
|
157
|
+
);
|
|
158
|
+
if (lazyRoutes.length) {
|
|
159
|
+
const suggestion = `Please use \`import {unlazyRouter} from '@orpc/server'\` to unlazy the router before passing it to @reliverse/rempts`;
|
|
160
|
+
const routes = lazyRoutes.map(({ path }) => path.join(".")).join(", ");
|
|
161
|
+
throw new Error(
|
|
162
|
+
`Lazy routers are not supported. ${suggestion}. Lazy routes detected: ${routes}`
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
return entries;
|
|
166
|
+
};
|
|
167
|
+
const jsonProcedureInputs = (reason) => {
|
|
168
|
+
let description = "Input formatted as JSON";
|
|
169
|
+
if (reason) description += ` (${reason})`;
|
|
170
|
+
return {
|
|
171
|
+
positionalParameters: [],
|
|
172
|
+
optionsJsonSchema: {
|
|
173
|
+
type: "object",
|
|
174
|
+
properties: {
|
|
175
|
+
input: { type: "json", description }
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
getPojoInput: (parsedCliParams) => {
|
|
179
|
+
if (parsedCliParams.options.input == null)
|
|
180
|
+
return parsedCliParams.options.input;
|
|
181
|
+
return JSON.parse(parsedCliParams.options.input);
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
};
|
|
185
|
+
export function createRpcCli({
|
|
186
|
+
router,
|
|
187
|
+
...params
|
|
188
|
+
}) {
|
|
189
|
+
const procedureEntries = parseRouter({ router, ...params });
|
|
190
|
+
function buildProgram(runParams) {
|
|
191
|
+
const logger = { ...lineByLineConsoleLogger, ...runParams?.logger };
|
|
192
|
+
const program = new TrpcCommand(params.name);
|
|
193
|
+
if (params.version) program.version(params.version);
|
|
194
|
+
if (params.description) program.description(params.description);
|
|
195
|
+
if (params.usage)
|
|
196
|
+
[params.usage].flat().forEach((usage) => program.usage(usage));
|
|
197
|
+
program.showHelpAfterError();
|
|
198
|
+
program.showSuggestionAfterError();
|
|
199
|
+
const commandTree = {
|
|
200
|
+
"": program
|
|
201
|
+
// Root level
|
|
202
|
+
};
|
|
203
|
+
const defaultCommands = {};
|
|
204
|
+
const _process = runParams?.process || process;
|
|
205
|
+
const configureCommand = (command, procedurePath, { meta, parsedProcedure, incompatiblePairs, procedure }) => {
|
|
206
|
+
const optionJsonSchemaProperties = flattenedProperties(
|
|
207
|
+
parsedProcedure.optionsJsonSchema
|
|
208
|
+
);
|
|
209
|
+
command.exitOverride((ec) => {
|
|
210
|
+
_process.exit(ec.exitCode);
|
|
211
|
+
throw new FailedToExitError(`Command ${command.name()} exitOverride`, {
|
|
212
|
+
exitCode: ec.exitCode,
|
|
213
|
+
cause: ec
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
command.configureOutput({
|
|
217
|
+
writeOut: (str) => {
|
|
218
|
+
logger.info?.(str);
|
|
219
|
+
},
|
|
220
|
+
writeErr: (str) => {
|
|
221
|
+
logger.error?.(str);
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
command.showHelpAfterError();
|
|
225
|
+
if (meta.usage) command.usage([meta.usage].flat().join("\n"));
|
|
226
|
+
if (meta.examples)
|
|
227
|
+
command.addHelpText(
|
|
228
|
+
"after",
|
|
229
|
+
`
|
|
230
|
+
Examples:
|
|
231
|
+
${[meta.examples].flat().join("\n")}`
|
|
232
|
+
);
|
|
233
|
+
meta?.aliases?.command?.forEach((alias) => {
|
|
234
|
+
command.alias(alias);
|
|
235
|
+
});
|
|
236
|
+
command.description(meta?.description || "");
|
|
237
|
+
parsedProcedure.positionalParameters.forEach((param) => {
|
|
238
|
+
const descriptionParts = [
|
|
239
|
+
param.type === "string" ? "" : param.type,
|
|
240
|
+
// "string" is the default assumption, don't bother showing it
|
|
241
|
+
param.description,
|
|
242
|
+
param.required ? "(required)" : ""
|
|
243
|
+
];
|
|
244
|
+
const argument = new Argument(
|
|
245
|
+
param.name,
|
|
246
|
+
descriptionParts.filter(Boolean).join(" ")
|
|
247
|
+
);
|
|
248
|
+
if (param.type === "number") {
|
|
249
|
+
argument.argParser((value) => {
|
|
250
|
+
const number = numberParser(value, { fallback: null });
|
|
251
|
+
if (number == null)
|
|
252
|
+
throw new InvalidArgumentError(`Invalid number: ${value}`);
|
|
253
|
+
return value;
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
argument.required = param.required;
|
|
257
|
+
argument.variadic = param.array;
|
|
258
|
+
command.addArgument(argument);
|
|
259
|
+
});
|
|
260
|
+
const unusedOptionAliases = {
|
|
261
|
+
...meta.aliases?.options
|
|
262
|
+
};
|
|
263
|
+
const addOptionForProperty = ([propertyKey, propertyValue]) => {
|
|
264
|
+
const description = getDescription(propertyValue);
|
|
265
|
+
const longOption = `--${kebabCase(propertyKey)}`;
|
|
266
|
+
let flags = longOption;
|
|
267
|
+
const alias = meta.aliases?.options?.[propertyKey];
|
|
268
|
+
if (alias) {
|
|
269
|
+
let prefix = "-";
|
|
270
|
+
if (alias.startsWith("-")) prefix = "";
|
|
271
|
+
else if (alias.length > 1) prefix = "--";
|
|
272
|
+
flags = `${prefix}${alias}, ${flags}`;
|
|
273
|
+
delete unusedOptionAliases[propertyKey];
|
|
274
|
+
}
|
|
275
|
+
const defaultValue = "default" in propertyValue ? { exists: true, value: propertyValue.default } : { exists: false };
|
|
276
|
+
const rootTypes = getSchemaTypes(propertyValue).sort();
|
|
277
|
+
const getValueParser = (types) => {
|
|
278
|
+
types = types.map((t) => t === "integer" ? "number" : t);
|
|
279
|
+
if (types.length === 2 && types[0] === "boolean" && types[1] === "number") {
|
|
280
|
+
return {
|
|
281
|
+
type: "boolean|number",
|
|
282
|
+
parser: (value) => booleanParser(value, { fallback: null }) ?? numberParser(value)
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
if (types.length === 1 && types[0] === "boolean") {
|
|
286
|
+
return {
|
|
287
|
+
type: "boolean",
|
|
288
|
+
parser: (value) => booleanParser(value)
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
if (types.length === 1 && types[0] === "number") {
|
|
292
|
+
return {
|
|
293
|
+
type: "number",
|
|
294
|
+
parser: (value) => numberParser(value)
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
if (types.length === 1 && types[0] === "string") {
|
|
298
|
+
return { type: "string", parser: null };
|
|
299
|
+
}
|
|
300
|
+
return {
|
|
301
|
+
type: "json",
|
|
302
|
+
parser: (value) => {
|
|
303
|
+
let parsed;
|
|
304
|
+
try {
|
|
305
|
+
parsed = JSON.parse(value);
|
|
306
|
+
} catch {
|
|
307
|
+
throw new InvalidArgumentError("Malformed JSON.");
|
|
308
|
+
}
|
|
309
|
+
const jsonSchemaType = Array.isArray(parsed) ? "array" : parsed === null ? "null" : typeof parsed;
|
|
310
|
+
if (!types.includes(jsonSchemaType)) {
|
|
311
|
+
throw new InvalidArgumentError(
|
|
312
|
+
`Got ${jsonSchemaType} but expected ${types.join(" or ")}`
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
return parsed;
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
};
|
|
319
|
+
const propertyType = rootTypes[0];
|
|
320
|
+
const isValueRequired = "required" in parsedProcedure.optionsJsonSchema && parsedProcedure.optionsJsonSchema.required?.includes(propertyKey);
|
|
321
|
+
const isCliOptionRequired = isValueRequired && propertyType !== "boolean" && !defaultValue.exists;
|
|
322
|
+
function negate() {
|
|
323
|
+
const negation = new Option(
|
|
324
|
+
longOption.replace("--", "--no-"),
|
|
325
|
+
`Negate \`${longOption}\` option.`.trim()
|
|
326
|
+
);
|
|
327
|
+
command.addOption(negation);
|
|
328
|
+
}
|
|
329
|
+
const bracketise = (name) => isCliOptionRequired ? `<${name}>` : `[${name}]`;
|
|
330
|
+
if (rootTypes.length === 2 && rootTypes[0] === "boolean" && rootTypes[1] === "string") {
|
|
331
|
+
const option2 = new Option(`${flags} [value]`, description);
|
|
332
|
+
option2.default(defaultValue.exists ? defaultValue.value : false);
|
|
333
|
+
command.addOption(option2);
|
|
334
|
+
negate();
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
if (rootTypes.length === 2 && rootTypes[0] === "boolean" && rootTypes[1] === "number") {
|
|
338
|
+
const option2 = new Option(`${flags} [value]`, description);
|
|
339
|
+
option2.argParser(getValueParser(rootTypes).parser);
|
|
340
|
+
option2.default(defaultValue.exists ? defaultValue.value : false);
|
|
341
|
+
command.addOption(option2);
|
|
342
|
+
negate();
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
if (rootTypes.length === 2 && rootTypes[0] === "number" && rootTypes[1] === "string") {
|
|
346
|
+
const option2 = new Option(
|
|
347
|
+
`${flags} ${bracketise("value")}`,
|
|
348
|
+
description
|
|
349
|
+
);
|
|
350
|
+
option2.argParser((value) => {
|
|
351
|
+
const number = numberParser(value, { fallback: null });
|
|
352
|
+
return number ?? value;
|
|
353
|
+
});
|
|
354
|
+
if (defaultValue.exists) option2.default(defaultValue.value);
|
|
355
|
+
command.addOption(option2);
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
if (rootTypes.length !== 1) {
|
|
359
|
+
const option2 = new Option(
|
|
360
|
+
`${flags} ${bracketise("json")}`,
|
|
361
|
+
`${description} (value will be parsed as JSON)`
|
|
362
|
+
);
|
|
363
|
+
option2.argParser(getValueParser(rootTypes).parser);
|
|
364
|
+
command.addOption(option2);
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
if (propertyType === "boolean" && isValueRequired) {
|
|
368
|
+
const option2 = new Option(flags, description);
|
|
369
|
+
option2.default(defaultValue.exists ? defaultValue.value : false);
|
|
370
|
+
command.addOption(option2);
|
|
371
|
+
negate();
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
if (propertyType === "boolean") {
|
|
375
|
+
const option2 = new Option(`${flags} [boolean]`, description);
|
|
376
|
+
option2.argParser((value) => booleanParser(value));
|
|
377
|
+
if (defaultValue.exists) option2.default(defaultValue.value);
|
|
378
|
+
command.addOption(option2);
|
|
379
|
+
negate();
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
let option;
|
|
383
|
+
if (propertyType === "string") {
|
|
384
|
+
option = new Option(`${flags} ${bracketise("string")}`, description);
|
|
385
|
+
} else if (propertyType === "boolean") {
|
|
386
|
+
option = new Option(flags, description);
|
|
387
|
+
} else if (propertyType === "number" || propertyType === "integer") {
|
|
388
|
+
option = new Option(`${flags} ${bracketise("number")}`, description);
|
|
389
|
+
option.argParser((value) => numberParser(value, { fallback: null }));
|
|
390
|
+
} else if (propertyType === "array") {
|
|
391
|
+
option = new Option(`${flags} [values...]`, description);
|
|
392
|
+
if (defaultValue.exists) option.default(defaultValue.value);
|
|
393
|
+
else if (isValueRequired) option.default([]);
|
|
394
|
+
const itemsProp = "items" in propertyValue ? propertyValue.items : null;
|
|
395
|
+
const itemTypes = itemsProp ? getSchemaTypes(itemsProp) : [];
|
|
396
|
+
const itemEnumTypes = itemsProp && getEnumChoices(itemsProp);
|
|
397
|
+
if (itemEnumTypes?.type === "string_enum") {
|
|
398
|
+
option.choices(itemEnumTypes.choices);
|
|
399
|
+
}
|
|
400
|
+
const itemParser = getValueParser(itemTypes);
|
|
401
|
+
if (itemParser.parser) {
|
|
402
|
+
option.argParser((value, previous) => {
|
|
403
|
+
const parsed = itemParser.parser(value);
|
|
404
|
+
if (Array.isArray(previous))
|
|
405
|
+
return [...previous, parsed];
|
|
406
|
+
return [parsed];
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
option ||= new Option(`${flags} [json]`, description);
|
|
411
|
+
if (defaultValue.exists && option.defaultValue !== defaultValue.value) {
|
|
412
|
+
option.default(defaultValue.value);
|
|
413
|
+
}
|
|
414
|
+
if (option.flags.includes("<")) {
|
|
415
|
+
option.makeOptionMandatory();
|
|
416
|
+
}
|
|
417
|
+
const enumChoices = getEnumChoices(propertyValue);
|
|
418
|
+
if (enumChoices?.type === "string_enum") {
|
|
419
|
+
option.choices(enumChoices.choices);
|
|
420
|
+
}
|
|
421
|
+
option.conflicts(
|
|
422
|
+
incompatiblePairs.flatMap((pair) => {
|
|
423
|
+
const filtered = pair.filter((p) => p !== propertyKey);
|
|
424
|
+
if (filtered.length === pair.length) return [];
|
|
425
|
+
return filtered;
|
|
426
|
+
})
|
|
427
|
+
);
|
|
428
|
+
command.addOption(option);
|
|
429
|
+
if (propertyType === "boolean") negate();
|
|
430
|
+
};
|
|
431
|
+
Object.entries(optionJsonSchemaProperties).forEach(addOptionForProperty);
|
|
432
|
+
const invalidOptionAliases = Object.entries(unusedOptionAliases).map(
|
|
433
|
+
([option, alias]) => `${option}: ${alias}`
|
|
434
|
+
);
|
|
435
|
+
if (invalidOptionAliases.length) {
|
|
436
|
+
throw new Error(
|
|
437
|
+
`Invalid option aliases: ${invalidOptionAliases.join(", ")}`
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
command.action(async (...args) => {
|
|
441
|
+
program.__ran ||= [];
|
|
442
|
+
program.__ran.push(command);
|
|
443
|
+
const options = command.opts();
|
|
444
|
+
if (args.at(-2) !== options) {
|
|
445
|
+
throw new Error(
|
|
446
|
+
"Unexpected args format, second last arg is not the options object",
|
|
447
|
+
{ cause: args }
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
if (args.at(-1) !== command) {
|
|
451
|
+
throw new Error(
|
|
452
|
+
"Unexpected args format, last arg is not the Command instance",
|
|
453
|
+
{ cause: args }
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
const positionalValues = args.slice(0, -2);
|
|
457
|
+
const input = parsedProcedure.getPojoInput({
|
|
458
|
+
positionalValues,
|
|
459
|
+
options
|
|
460
|
+
});
|
|
461
|
+
const resolvedTrpcServer = await (params.trpcServer || trpcServer11);
|
|
462
|
+
let caller;
|
|
463
|
+
const deprecatedCreateCaller = Reflect.get(
|
|
464
|
+
params,
|
|
465
|
+
"createCallerFactory"
|
|
466
|
+
);
|
|
467
|
+
if (deprecatedCreateCaller) {
|
|
468
|
+
const message = `Using deprecated \`createCallerFactory\` option. Use \`trpcServer\` instead. e.g. \`createRpcCli({router: myRouter, trpcServer: import('@trpc/server')})\``;
|
|
469
|
+
logger.error?.(message);
|
|
470
|
+
caller = deprecatedCreateCaller(router)(params.context);
|
|
471
|
+
} else if (isOrpcRouter(router)) {
|
|
472
|
+
const { call } = eval(
|
|
473
|
+
`require('@orpc/server')`
|
|
474
|
+
);
|
|
475
|
+
caller = {
|
|
476
|
+
[procedurePath]: (_input) => call(procedure, _input, { context: params.context })
|
|
477
|
+
};
|
|
478
|
+
} else {
|
|
479
|
+
const createCallerFactor = resolvedTrpcServer.initTRPC.create().createCallerFactory;
|
|
480
|
+
caller = createCallerFactor(router)(params.context);
|
|
481
|
+
}
|
|
482
|
+
const result = await (caller[procedurePath]?.(input)).catch((err) => {
|
|
483
|
+
throw transformError(err, command);
|
|
484
|
+
});
|
|
485
|
+
command.__result = result;
|
|
486
|
+
if (result != null) logger.info?.(result);
|
|
487
|
+
});
|
|
488
|
+
};
|
|
489
|
+
procedureEntries.forEach(([procedurePath2, commandConfig]) => {
|
|
490
|
+
const segments = procedurePath2.split(".");
|
|
491
|
+
let currentPath = "";
|
|
492
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
493
|
+
const segment = segments[i];
|
|
494
|
+
if (!segment) continue;
|
|
495
|
+
const parentPath2 = currentPath;
|
|
496
|
+
currentPath = currentPath ? `${currentPath}.${segment}` : segment;
|
|
497
|
+
if (!commandTree[currentPath]) {
|
|
498
|
+
const parentCommand2 = commandTree[parentPath2];
|
|
499
|
+
if (!parentCommand2) continue;
|
|
500
|
+
const newCommand = new TrpcCommand(kebabCase(segment));
|
|
501
|
+
newCommand.showHelpAfterError();
|
|
502
|
+
parentCommand2.addCommand(newCommand);
|
|
503
|
+
commandTree[currentPath] = newCommand;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
const leafName = segments.at(-1);
|
|
507
|
+
if (!leafName) return;
|
|
508
|
+
const parentPath = segments.length > 1 ? segments.slice(0, -1).join(".") : "";
|
|
509
|
+
const parentCommand = commandTree[parentPath];
|
|
510
|
+
if (!parentCommand) return;
|
|
511
|
+
const leafCommand = new TrpcCommand(kebabCase(leafName));
|
|
512
|
+
configureCommand(leafCommand, procedurePath2, commandConfig);
|
|
513
|
+
parentCommand.addCommand(leafCommand);
|
|
514
|
+
const meta2 = commandConfig.meta;
|
|
515
|
+
if (meta2.default === true) {
|
|
516
|
+
parentCommand.allowExcessArguments();
|
|
517
|
+
parentCommand.allowUnknownOption();
|
|
518
|
+
parentCommand.addHelpText("after", leafCommand.helpInformation());
|
|
519
|
+
parentCommand.action(async () => {
|
|
520
|
+
await leafCommand.parseAsync([...parentCommand.args], {
|
|
521
|
+
from: "user"
|
|
522
|
+
});
|
|
523
|
+
});
|
|
524
|
+
defaultCommands[parentPath] = {
|
|
525
|
+
procedurePath: procedurePath2,
|
|
526
|
+
config: commandConfig,
|
|
527
|
+
command: leafCommand
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
});
|
|
531
|
+
Object.entries(commandTree).forEach(([path, command2]) => {
|
|
532
|
+
if (command2.commands.length === 0) return;
|
|
533
|
+
const subcommandNames = command2.commands.map((cmd) => cmd.name());
|
|
534
|
+
const defaultCommand = defaultCommands[path]?.command.name();
|
|
535
|
+
const formattedSubcommands = subcommandNames.map((name) => name === defaultCommand ? `${name} (default)` : name).join(", ");
|
|
536
|
+
const existingDescription = command2.description() || "";
|
|
537
|
+
const descriptionParts = [
|
|
538
|
+
existingDescription,
|
|
539
|
+
`Available subcommands: ${formattedSubcommands}`
|
|
540
|
+
];
|
|
541
|
+
command2.description(descriptionParts.filter(Boolean).join("\n"));
|
|
542
|
+
});
|
|
543
|
+
return program;
|
|
544
|
+
}
|
|
545
|
+
const run = async (runParams2, program2 = buildProgram(runParams2)) => {
|
|
546
|
+
if (!looksLikeInstanceof(program2, "TrpcCommand"))
|
|
547
|
+
throw new Error("program is not a TrpcCommand instance");
|
|
548
|
+
const opts = runParams2?.argv ? { from: "user" } : void 0;
|
|
549
|
+
const argv = [...runParams2?.argv || process.argv];
|
|
550
|
+
const _process2 = runParams2?.process || process;
|
|
551
|
+
const logger2 = { ...lineByLineConsoleLogger, ...runParams2?.logger };
|
|
552
|
+
program2.exitOverride((exit) => {
|
|
553
|
+
_process2.exit(exit.exitCode);
|
|
554
|
+
throw new FailedToExitError("Root command exitOverride", {
|
|
555
|
+
exitCode: exit.exitCode,
|
|
556
|
+
cause: exit
|
|
557
|
+
});
|
|
558
|
+
});
|
|
559
|
+
program2.configureOutput({
|
|
560
|
+
writeOut: (str) => logger2.info?.(str),
|
|
561
|
+
writeErr: (str) => logger2.error?.(str)
|
|
562
|
+
});
|
|
563
|
+
if (runParams2?.completion) {
|
|
564
|
+
const completion = typeof runParams2.completion === "function" ? await runParams2.completion() : runParams2.completion;
|
|
565
|
+
addCompletions(program2, completion);
|
|
566
|
+
}
|
|
567
|
+
const formatError = runParams2?.formatError || ((err) => {
|
|
568
|
+
if (err instanceof CliValidationError) {
|
|
569
|
+
return err.message;
|
|
570
|
+
}
|
|
571
|
+
return inspect(err);
|
|
572
|
+
});
|
|
573
|
+
if (runParams2?.prompts) {
|
|
574
|
+
program2 = promptify(program2, runParams2.prompts);
|
|
575
|
+
}
|
|
576
|
+
await program2.parseAsync(argv, opts).catch((err) => {
|
|
577
|
+
if (err instanceof FailedToExitError) throw err;
|
|
578
|
+
const logMessage = looksLikeInstanceof(err, Error) ? formatError(err) || err.message : `Non-error of type ${typeof err} thrown: ${err}`;
|
|
579
|
+
logger2.error?.(logMessage);
|
|
580
|
+
_process2.exit(1);
|
|
581
|
+
throw new FailedToExitError("Program exit after failure", {
|
|
582
|
+
exitCode: 1,
|
|
583
|
+
cause: err
|
|
584
|
+
});
|
|
585
|
+
});
|
|
586
|
+
_process2.exit(0);
|
|
587
|
+
throw new FailedToExitError("Program exit after success", {
|
|
588
|
+
exitCode: 0,
|
|
589
|
+
cause: program2.__ran.at(-1)?.__result
|
|
590
|
+
});
|
|
591
|
+
};
|
|
592
|
+
return {
|
|
593
|
+
run,
|
|
594
|
+
buildProgram,
|
|
595
|
+
toJSON: (program2 = buildProgram()) => commandToJSON(program2)
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
function getMeta(procedure2) {
|
|
599
|
+
const meta2 = procedure2._def.meta;
|
|
600
|
+
return meta2?.cliMeta || meta2 || {};
|
|
601
|
+
}
|
|
602
|
+
function kebabCase(propName) {
|
|
603
|
+
return propName.replaceAll(/([A-Z])/g, "-$1").toLowerCase();
|
|
604
|
+
}
|
|
605
|
+
function transformError(err, command2) {
|
|
606
|
+
if (looksLikeInstanceof(err, Error) && err.message.includes("This is a client-only function")) {
|
|
607
|
+
return new Error(
|
|
608
|
+
"Failed to create trpc caller. If using trpc v10, either upgrade to v11 or pass in the `@trpc/server` module to `createRpcCli` explicitly"
|
|
609
|
+
);
|
|
610
|
+
}
|
|
611
|
+
if (looksLikeInstanceof(err, "TRPCError")) {
|
|
612
|
+
const cause = err.cause;
|
|
613
|
+
if (looksLikeStandardSchemaFailure(cause)) {
|
|
614
|
+
const prettyMessage = prettifyStandardSchemaError(cause);
|
|
615
|
+
return new CliValidationError(
|
|
616
|
+
`${prettyMessage}
|
|
617
|
+
${command2.helpInformation()}`
|
|
618
|
+
);
|
|
619
|
+
}
|
|
620
|
+
if (err.code === "BAD_REQUEST" && (err.cause?.constructor?.name === "TraversalError" || // arktype error
|
|
621
|
+
err.cause?.constructor?.name === "StandardSchemaV1Error")) {
|
|
622
|
+
return new CliValidationError(
|
|
623
|
+
`${err.cause.message}
|
|
624
|
+
${command2.helpInformation()}`
|
|
625
|
+
);
|
|
626
|
+
}
|
|
627
|
+
if (err.code === "INTERNAL_SERVER_ERROR") {
|
|
628
|
+
return cause;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
return err;
|
|
632
|
+
}
|
|
633
|
+
const numberParser = (val, { fallback = val } = {}) => {
|
|
634
|
+
const number = Number(val);
|
|
635
|
+
return Number.isNaN(number) ? fallback : number;
|
|
636
|
+
};
|
|
637
|
+
const booleanParser = (val, { fallback = val } = {}) => {
|
|
638
|
+
if (val === "true") return true;
|
|
639
|
+
if (val === "false") return false;
|
|
640
|
+
return fallback;
|
|
641
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { JsonSchema7ObjectType, JsonSchema7Type } from "zod-to-json-schema";
|
|
2
|
+
export declare const flattenedProperties: (sch: JsonSchema7Type) => JsonSchema7ObjectType["properties"];
|
|
3
|
+
/** For a union type, returns a list of pairs of properties which *shouldn't* be used together (because they don't appear in the same type variant) */
|
|
4
|
+
export declare const incompatiblePropertyPairs: (sch: JsonSchema7Type) => [string, string][];
|
|
5
|
+
/**
|
|
6
|
+
* Tries fairly hard to build a roughly human-readable description of a json-schema type.
|
|
7
|
+
* A few common properties are given special treatment, most others are just stringified and output in `key: value` format.
|
|
8
|
+
*/
|
|
9
|
+
export declare const getDescription: (v: JsonSchema7Type, depth?: number) => string;
|
|
10
|
+
export declare const getSchemaTypes: (propertyValue: JsonSchema7Type) => ("string" | "boolean" | "number" | (string & {}))[];
|
|
11
|
+
export declare const getEnumChoices: (propertyValue: JsonSchema7Type) => {
|
|
12
|
+
readonly type: "string_enum";
|
|
13
|
+
readonly choices: string[];
|
|
14
|
+
} | {
|
|
15
|
+
readonly type: "number_enum";
|
|
16
|
+
readonly choices: number[];
|
|
17
|
+
} | null;
|