@pipelex/sdk 0.33.0 → 0.34.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.
Files changed (72) hide show
  1. package/README.md +20 -1
  2. package/dist/cli/args.d.ts +41 -0
  3. package/dist/cli/args.js +96 -0
  4. package/dist/cli/args.js.map +1 -0
  5. package/dist/cli/bundle.d.ts +45 -0
  6. package/dist/cli/bundle.js +148 -0
  7. package/dist/cli/bundle.js.map +1 -0
  8. package/dist/cli/environment.d.ts +36 -0
  9. package/dist/cli/environment.js +72 -0
  10. package/dist/cli/environment.js.map +1 -0
  11. package/dist/cli/help.d.ts +4 -0
  12. package/dist/cli/help.js +56 -0
  13. package/dist/cli/help.js.map +1 -0
  14. package/dist/cli/inputs.d.ts +13 -0
  15. package/dist/cli/inputs.js +70 -0
  16. package/dist/cli/inputs.js.map +1 -0
  17. package/dist/cli/io.d.ts +79 -0
  18. package/dist/cli/io.js +96 -0
  19. package/dist/cli/io.js.map +1 -0
  20. package/dist/cli/main.d.ts +18 -0
  21. package/dist/cli/main.js +58 -0
  22. package/dist/cli/main.js.map +1 -0
  23. package/dist/cli/method.d.ts +56 -0
  24. package/dist/cli/method.js +108 -0
  25. package/dist/cli/method.js.map +1 -0
  26. package/dist/cli/present.d.ts +43 -0
  27. package/dist/cli/present.js +191 -0
  28. package/dist/cli/present.js.map +1 -0
  29. package/dist/cli/run.d.ts +17 -0
  30. package/dist/cli/run.js +290 -0
  31. package/dist/cli/run.js.map +1 -0
  32. package/dist/cli/script.d.ts +23 -0
  33. package/dist/cli/script.js +212 -0
  34. package/dist/cli/script.js.map +1 -0
  35. package/dist/cli/source.d.ts +49 -0
  36. package/dist/cli/source.js +71 -0
  37. package/dist/cli/source.js.map +1 -0
  38. package/dist/cli.d.ts +10 -0
  39. package/dist/cli.js +65 -0
  40. package/dist/cli.js.map +1 -0
  41. package/dist/client.d.ts +49 -7
  42. package/dist/client.js +152 -26
  43. package/dist/client.js.map +1 -1
  44. package/dist/errors.d.ts +42 -4
  45. package/dist/errors.js +43 -4
  46. package/dist/errors.js.map +1 -1
  47. package/dist/hooks/validate-client.d.ts +3 -3
  48. package/dist/hooks/validate-client.js +3 -3
  49. package/dist/index.d.ts +3 -3
  50. package/dist/index.js +2 -2
  51. package/dist/index.js.map +1 -1
  52. package/dist/prepare-inputs.d.ts +6 -0
  53. package/dist/prepare-inputs.js +21 -10
  54. package/dist/prepare-inputs.js.map +1 -1
  55. package/dist/runs.d.ts +28 -1
  56. package/dist/runs.js +2 -1
  57. package/dist/runs.js.map +1 -1
  58. package/dist/upload.d.ts +6 -0
  59. package/dist/upload.js +9 -4
  60. package/dist/upload.js.map +1 -1
  61. package/dist/user-agent.d.ts +3 -2
  62. package/dist/user-agent.js +3 -2
  63. package/dist/user-agent.js.map +1 -1
  64. package/dist/version.d.ts +1 -1
  65. package/dist/version.js +1 -1
  66. package/docs/architecture.md +8 -3
  67. package/docs/cli.md +123 -0
  68. package/docs/client-identification.md +1 -1
  69. package/docs/errors.md +5 -3
  70. package/docs/input-preparation.md +6 -2
  71. package/docs/run-results.md +20 -0
  72. package/package.json +5 -1
@@ -0,0 +1,70 @@
1
+ /**
2
+ * The inputs `--inputs` names: a JSON object, one entry per input, read from a file or, for `-`,
3
+ * from stdin. It is the shape `mthds run --inputs` and `cli-python`'s `--inputs` take, and the
4
+ * shape `--inputs-template` prints.
5
+ */
6
+ import { readFile } from "node:fs/promises";
7
+ import { resolve } from "node:path";
8
+ import { systemReason } from "./bundle.js";
9
+ import { untilInterrupted, usageError } from "./io.js";
10
+ // A leading byte-order mark is dropped, as RFC 8259 lets a JSON reader do: Windows PowerShell 5.1
11
+ // writes one with `Out-File -Encoding utf8`. A bundle's mark is kept, since it is sent as written.
12
+ const UTF8 = new TextDecoder("utf-8", { fatal: true });
13
+ /**
14
+ * Read and parse the inputs. A relative path resolves against the current directory.
15
+ *
16
+ * @throws {CommandError} A usage error for an unreadable file or stdin, text that is not UTF-8 or
17
+ * not JSON, and JSON that is not an object.
18
+ */
19
+ export async function readInputs(source, io) {
20
+ const label = source === "-" ? "stdin" : `the inputs file "${source}"`;
21
+ const read = source === "-" ? () => io.readStdin() : () => readFile(resolve(source));
22
+ // Raced with the interrupt: stdin, a named pipe or a file on a stalled mount can keep the read
23
+ // waiting.
24
+ const bytes = await untilInterrupted(async () => {
25
+ try {
26
+ return await read();
27
+ }
28
+ catch (error) {
29
+ throw usageError(`cannot read ${label}.`, [`Reason: ${systemReason(error)}`]);
30
+ }
31
+ }, io.interrupt);
32
+ let text;
33
+ try {
34
+ text = UTF8.decode(bytes);
35
+ }
36
+ catch {
37
+ throw usageError(`${label} is not UTF-8 text.`);
38
+ }
39
+ let parsed;
40
+ try {
41
+ parsed = JSON.parse(text);
42
+ }
43
+ catch (error) {
44
+ throw usageError(`${label} is not valid JSON.`, [
45
+ `Reason: ${error instanceof Error ? error.message : String(error)}`,
46
+ ]);
47
+ }
48
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
49
+ throw usageError(`${label} must hold a JSON object, one entry per input, and holds ${jsonKind(parsed)}.`, ["Run with --inputs-template to see the object this method takes."]);
50
+ }
51
+ return parsed;
52
+ }
53
+ /** What a JSON value is, said the way the refusal says it. */
54
+ function jsonKind(value) {
55
+ if (value === null)
56
+ return "null";
57
+ if (Array.isArray(value))
58
+ return "an array";
59
+ switch (typeof value) {
60
+ case "string":
61
+ return "a string";
62
+ case "number":
63
+ return "a number";
64
+ case "boolean":
65
+ return "a boolean";
66
+ default:
67
+ return "not an object";
68
+ }
69
+ }
70
+ //# sourceMappingURL=inputs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inputs.js","sourceRoot":"","sources":["../../src/cli/inputs.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAGvD,kGAAkG;AAClG,mGAAmG;AACnG,MAAM,IAAI,GAAG,IAAI,WAAW,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AAEvD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,MAAc,EAAE,EAAa;IAC5D,MAAM,KAAK,GAAG,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,oBAAoB,MAAM,GAAG,CAAC;IACvE,MAAM,IAAI,GAAG,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IACrF,+FAA+F;IAC/F,WAAW;IACX,MAAM,KAAK,GAAG,MAAM,gBAAgB,CAAC,KAAK,IAAI,EAAE;QAC9C,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,EAAE,CAAC;QACtB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,UAAU,CAAC,eAAe,KAAK,GAAG,EAAE,CAAC,WAAW,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;QAChF,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC;IACjB,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,UAAU,CAAC,GAAG,KAAK,qBAAqB,CAAC,CAAC;IAClD,CAAC;IACD,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,UAAU,CAAC,GAAG,KAAK,qBAAqB,EAAE;YAC9C,WAAW,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;SACpE,CAAC,CAAC;IACL,CAAC;IACD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3E,MAAM,UAAU,CACd,GAAG,KAAK,4DAA4D,QAAQ,CAAC,MAAM,CAAC,GAAG,EACvF,CAAC,iEAAiE,CAAC,CACpE,CAAC;IACJ,CAAC;IACD,OAAO,MAAiC,CAAC;AAC3C,CAAC;AAED,8DAA8D;AAC9D,SAAS,QAAQ,CAAC,KAAc;IAC9B,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,MAAM,CAAC;IAClC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,UAAU,CAAC;IAC5C,QAAQ,OAAO,KAAK,EAAE,CAAC;QACrB,KAAK,QAAQ;YACX,OAAO,UAAU,CAAC;QACpB,KAAK,QAAQ;YACX,OAAO,UAAU,CAAC;QACpB,KAAK,SAAS;YACZ,OAAO,WAAW,CAAC;QACrB;YACE,OAAO,eAAe,CAAC;IAC3B,CAAC;AACH,CAAC"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * What the `pipelex-sdk` command reads from and writes to, and the ways it stops early.
3
+ *
4
+ * The command never touches `process` directly, apart from the current directory that relative
5
+ * paths resolve against: `src/cli.ts`, the executable, hands it the real streams, environment and
6
+ * Ctrl-C, and the test suite hands it its own. That is what lets one recorded case table drive
7
+ * the command in-process, in this SDK and in its Python twin (`docs/cli.md`).
8
+ */
9
+ /** The command's world: its environment, its three streams and the interrupt. */
10
+ export interface CommandIO {
11
+ /**
12
+ * The environment the command reads: `PIPELEX_API_KEY`, `PIPELEX_BASE_URL` and the test-only
13
+ * `PIPELEX_SDK_POLL_INTERVAL_MS`. Nothing else is read from it, and no `.env` file is read.
14
+ */
15
+ readonly env: Readonly<Record<string, string | undefined>>;
16
+ /**
17
+ * All of stdin, as bytes. Read only for `--inputs -`. It rejects with the system's error when
18
+ * stdin cannot be read; a process started with stdin closed reads it as empty, since Node opens
19
+ * the null device on a standard stream it starts without.
20
+ */
21
+ readStdin(): Promise<Uint8Array>;
22
+ /** Write to stdout, which carries the result and nothing else. */
23
+ writeStdout(text: string): void;
24
+ /** Write to stderr, which carries everything that is not the result. */
25
+ writeStderr(text: string): void;
26
+ /** Aborted when the person interrupts the command (Ctrl-C). */
27
+ readonly interrupt: AbortSignal;
28
+ }
29
+ /** The command's exit codes (`docs/cli.md`): a script reads stdout, and these are presentation. */
30
+ export declare const EXIT_OK = 0;
31
+ export declare const EXIT_FAILED = 1;
32
+ export declare const EXIT_USAGE = 2;
33
+ export declare const EXIT_INTERRUPTED = 130;
34
+ /**
35
+ * A failure the command words itself: a usage error (exit code 2), a refusal it reads off an
36
+ * answer (exit code 1), or an interrupt (exit code 130). Its message is the sentence after
37
+ * `Error: `, or the whole first line when `bare`, as an interrupt's is; `details` are the lines
38
+ * printed under it, as they are.
39
+ */
40
+ export declare class CommandError extends Error {
41
+ readonly exitCode: number;
42
+ readonly details: readonly string[];
43
+ readonly bare: boolean;
44
+ constructor(message: string, options: {
45
+ exitCode: number;
46
+ details?: readonly string[];
47
+ bare?: boolean;
48
+ });
49
+ }
50
+ /** A usage error: something the person can fix on the command line or in the environment. */
51
+ export declare function usageError(message: string, details?: readonly string[]): CommandError;
52
+ /** The person interrupted the command while it was waiting on something. */
53
+ export declare class Interrupted extends Error {
54
+ constructor();
55
+ }
56
+ /**
57
+ * Start a step and wait for it, unless the person interrupts first, which rejects with
58
+ * {@link Interrupted}.
59
+ *
60
+ * The step is given as a function, so that an interrupt that has already landed starts nothing:
61
+ * a request is never sent once the person has asked to stop, and what the command then says ("No
62
+ * run was started") stays true. An interrupt that lands while the step starts is caught as soon
63
+ * as it returns.
64
+ *
65
+ * Several of the SDK's requests take no abort signal (the input preparation, the start request,
66
+ * the blocking execute), so the command stops waiting for them rather than cancelling them; the
67
+ * executable exits right after it has said so. The promise left behind is given a handler, so its
68
+ * later failure is never reported as unhandled.
69
+ */
70
+ export declare function untilInterrupted<T>(start: () => Promise<T>, signal: AbortSignal): Promise<T>;
71
+ /** Write each line to stderr, each ended by a newline. */
72
+ export declare function writeLines(io: CommandIO, lines: readonly string[]): void;
73
+ /**
74
+ * Let a reader of one of the process's output streams stop early: `| head` closes the pipe, and
75
+ * the next write fails with `EPIPE`, which, unhandled, would end the process mid-run, a run it
76
+ * started included. There is nothing left to tell that reader, so the error is dropped; any other
77
+ * error on the stream is thrown as it would have been.
78
+ */
79
+ export declare function ignoreClosedPipe(stream: NodeJS.EventEmitter): void;
package/dist/cli/io.js ADDED
@@ -0,0 +1,96 @@
1
+ /**
2
+ * What the `pipelex-sdk` command reads from and writes to, and the ways it stops early.
3
+ *
4
+ * The command never touches `process` directly, apart from the current directory that relative
5
+ * paths resolve against: `src/cli.ts`, the executable, hands it the real streams, environment and
6
+ * Ctrl-C, and the test suite hands it its own. That is what lets one recorded case table drive
7
+ * the command in-process, in this SDK and in its Python twin (`docs/cli.md`).
8
+ */
9
+ /** The command's exit codes (`docs/cli.md`): a script reads stdout, and these are presentation. */
10
+ export const EXIT_OK = 0;
11
+ export const EXIT_FAILED = 1;
12
+ export const EXIT_USAGE = 2;
13
+ export const EXIT_INTERRUPTED = 130;
14
+ /**
15
+ * A failure the command words itself: a usage error (exit code 2), a refusal it reads off an
16
+ * answer (exit code 1), or an interrupt (exit code 130). Its message is the sentence after
17
+ * `Error: `, or the whole first line when `bare`, as an interrupt's is; `details` are the lines
18
+ * printed under it, as they are.
19
+ */
20
+ export class CommandError extends Error {
21
+ exitCode;
22
+ details;
23
+ bare;
24
+ constructor(message, options) {
25
+ super(message);
26
+ this.name = "CommandError";
27
+ this.exitCode = options.exitCode;
28
+ this.details = options.details ?? [];
29
+ this.bare = options.bare ?? false;
30
+ }
31
+ }
32
+ /** A usage error: something the person can fix on the command line or in the environment. */
33
+ export function usageError(message, details = []) {
34
+ return new CommandError(message, { exitCode: EXIT_USAGE, details });
35
+ }
36
+ /** The person interrupted the command while it was waiting on something. */
37
+ export class Interrupted extends Error {
38
+ constructor() {
39
+ super("Interrupted.");
40
+ this.name = "Interrupted";
41
+ }
42
+ }
43
+ /**
44
+ * Start a step and wait for it, unless the person interrupts first, which rejects with
45
+ * {@link Interrupted}.
46
+ *
47
+ * The step is given as a function, so that an interrupt that has already landed starts nothing:
48
+ * a request is never sent once the person has asked to stop, and what the command then says ("No
49
+ * run was started") stays true. An interrupt that lands while the step starts is caught as soon
50
+ * as it returns.
51
+ *
52
+ * Several of the SDK's requests take no abort signal (the input preparation, the start request,
53
+ * the blocking execute), so the command stops waiting for them rather than cancelling them; the
54
+ * executable exits right after it has said so. The promise left behind is given a handler, so its
55
+ * later failure is never reported as unhandled.
56
+ */
57
+ export function untilInterrupted(start, signal) {
58
+ if (signal.aborted)
59
+ return Promise.reject(new Interrupted());
60
+ const promise = start();
61
+ if (signal.aborted) {
62
+ promise.catch(() => undefined);
63
+ return Promise.reject(new Interrupted());
64
+ }
65
+ return new Promise((resolve, reject) => {
66
+ const onAbort = () => {
67
+ promise.catch(() => undefined);
68
+ reject(new Interrupted());
69
+ };
70
+ signal.addEventListener("abort", onAbort, { once: true });
71
+ promise.then((value) => {
72
+ signal.removeEventListener("abort", onAbort);
73
+ resolve(value);
74
+ }, (error) => {
75
+ signal.removeEventListener("abort", onAbort);
76
+ reject(error);
77
+ });
78
+ });
79
+ }
80
+ /** Write each line to stderr, each ended by a newline. */
81
+ export function writeLines(io, lines) {
82
+ io.writeStderr(lines.map((line) => `${line}\n`).join(""));
83
+ }
84
+ /**
85
+ * Let a reader of one of the process's output streams stop early: `| head` closes the pipe, and
86
+ * the next write fails with `EPIPE`, which, unhandled, would end the process mid-run, a run it
87
+ * started included. There is nothing left to tell that reader, so the error is dropped; any other
88
+ * error on the stream is thrown as it would have been.
89
+ */
90
+ export function ignoreClosedPipe(stream) {
91
+ stream.on("error", (error) => {
92
+ if (error.code !== "EPIPE")
93
+ throw error;
94
+ });
95
+ }
96
+ //# sourceMappingURL=io.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"io.js","sourceRoot":"","sources":["../../src/cli/io.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * The `pipelex-sdk` command: `run` runs a method and prints its main output, `script` writes the
3
+ * per-method shell script. `docs/cli.md` describes both, and `tests/fixtures/cli-cases.json`
4
+ * records what each prints for every case, which the Python twin answers too.
5
+ *
6
+ * Nothing in the SDK imports this module or its siblings, and the package entry does not export
7
+ * them (`.dependency-cruiser.cjs`): the command sits at the top of the dependency graph, and reaches
8
+ * the SDK only through its public barrel, as any other caller would.
9
+ */
10
+ import type { CommandIO } from "./io.js";
11
+ export type { CommandIO } from "./io.js";
12
+ /**
13
+ * Run the command on `argv`, the arguments after the command's own name, and return its exit
14
+ * code: 0 when it did what it was asked, 1 when the run failed or the API refused it, 2 for a
15
+ * usage error and 130 when interrupted. Every failure is printed on stderr before it returns; it
16
+ * never throws.
17
+ */
18
+ export declare function runCommand(argv: readonly string[], io: CommandIO): Promise<number>;
@@ -0,0 +1,58 @@
1
+ /**
2
+ * The `pipelex-sdk` command: `run` runs a method and prints its main output, `script` writes the
3
+ * per-method shell script. `docs/cli.md` describes both, and `tests/fixtures/cli-cases.json`
4
+ * records what each prints for every case, which the Python twin answers too.
5
+ *
6
+ * Nothing in the SDK imports this module or its siblings, and the package entry does not export
7
+ * them (`.dependency-cruiser.cjs`): the command sits at the top of the dependency graph, and reaches
8
+ * the SDK only through its public barrel, as any other caller would.
9
+ */
10
+ import { SDK_VERSION } from "../index.js";
11
+ import { MAIN_HELP } from "./help.js";
12
+ import { EXIT_OK, usageError, writeLines } from "./io.js";
13
+ import { presentError } from "./present.js";
14
+ import { runCommandRun } from "./run.js";
15
+ import { runCommandScript } from "./script.js";
16
+ /**
17
+ * Run the command on `argv`, the arguments after the command's own name, and return its exit
18
+ * code: 0 when it did what it was asked, 1 when the run failed or the API refused it, 2 for a
19
+ * usage error and 130 when interrupted. Every failure is printed on stderr before it returns; it
20
+ * never throws.
21
+ */
22
+ export async function runCommand(argv, io) {
23
+ try {
24
+ const [command, ...rest] = argv;
25
+ switch (command) {
26
+ case undefined:
27
+ throw usageError("name a command: run or script.", [
28
+ "Run 'pipelex-sdk --help' to see what each does.",
29
+ ]);
30
+ case "--help":
31
+ case "-h":
32
+ // The help wins whatever follows it, as it does in a subcommand.
33
+ io.writeStdout(MAIN_HELP);
34
+ return EXIT_OK;
35
+ case "--version":
36
+ // Unlike the help, the version is not printed over a command line it does not answer.
37
+ if (rest.length > 0) {
38
+ throw usageError(`unexpected argument "${rest[0]}" after --version.`, [
39
+ "Run 'pipelex-sdk --version' alone to print the version.",
40
+ ]);
41
+ }
42
+ io.writeStdout(`${SDK_VERSION}\n`);
43
+ return EXIT_OK;
44
+ case "run":
45
+ return await runCommandRun(rest, io);
46
+ case "script":
47
+ return await runCommandScript(rest, io);
48
+ default:
49
+ throw usageError(command.startsWith("-") ? `unknown option ${command}.` : `unknown command "${command}".`, ["The commands are run and script. Run 'pipelex-sdk --help' to see what each does."]);
50
+ }
51
+ }
52
+ catch (error) {
53
+ const presented = presentError(error);
54
+ writeLines(io, presented.lines);
55
+ return presented.exitCode;
56
+ }
57
+ }
58
+ //# sourceMappingURL=main.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1D,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAI/C;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAuB,EAAE,EAAa;IACrE,IAAI,CAAC;QACH,MAAM,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;QAChC,QAAQ,OAAO,EAAE,CAAC;YAChB,KAAK,SAAS;gBACZ,MAAM,UAAU,CAAC,gCAAgC,EAAE;oBACjD,iDAAiD;iBAClD,CAAC,CAAC;YACL,KAAK,QAAQ,CAAC;YACd,KAAK,IAAI;gBACP,iEAAiE;gBACjE,EAAE,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;gBAC1B,OAAO,OAAO,CAAC;YACjB,KAAK,WAAW;gBACd,sFAAsF;gBACtF,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACpB,MAAM,UAAU,CAAC,wBAAwB,IAAI,CAAC,CAAC,CAAC,oBAAoB,EAAE;wBACpE,yDAAyD;qBAC1D,CAAC,CAAC;gBACL,CAAC;gBACD,EAAE,CAAC,WAAW,CAAC,GAAG,WAAW,IAAI,CAAC,CAAC;gBACnC,OAAO,OAAO,CAAC;YACjB,KAAK,KAAK;gBACR,OAAO,MAAM,aAAa,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACvC,KAAK,QAAQ;gBACX,OAAO,MAAM,gBAAgB,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAC1C;gBACE,MAAM,UAAU,CACd,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,kBAAkB,OAAO,GAAG,CAAC,CAAC,CAAC,oBAAoB,OAAO,IAAI,EACxF,CAAC,kFAAkF,CAAC,CACrF,CAAC;QACN,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,SAAS,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;QACtC,UAAU,CAAC,EAAE,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC;QAChC,OAAO,SAAS,CAAC,QAAQ,CAAC;IAC5B,CAAC;AACH,CAAC"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * How the command names a method and a pipe: the forms of `--method`, the qualified form of
3
+ * `--pipe`, and the names and quoting `script` derives from them.
4
+ */
5
+ /** The prefix of a published method's address. */
6
+ export declare const ADDRESS_PREFIX = "github.com/";
7
+ /** The prefix of a stored method's catalog id. */
8
+ export declare const CATALOG_ID_PREFIX = "mt_";
9
+ /** A `--method` value, told apart by its shape alone. */
10
+ export type MethodSelector = {
11
+ readonly kind: "address";
12
+ readonly methodRef: string;
13
+ } | {
14
+ readonly kind: "catalog";
15
+ readonly methodId: string;
16
+ } | {
17
+ readonly kind: "path";
18
+ readonly path: string;
19
+ };
20
+ /**
21
+ * Tell a `--method` value's form by its shape: `mt_…` is a catalog id, `github.com/…` an address,
22
+ * and anything else a path to a `.mthds` file or a bundle directory. A file or directory that
23
+ * happens to be named `mt_…` is reached as `./mt_…`, so that what a value names never depends on
24
+ * what the current directory holds.
25
+ *
26
+ * @throws {CommandError} A usage error for an `mt_…` value holding a character no catalog id
27
+ * holds (a letter, a digit, `_` or `-`), such as `mt_review.mthds`, which is a path written
28
+ * without its `./`.
29
+ */
30
+ export declare function classifyMethod(value: string): MethodSelector;
31
+ /**
32
+ * Hold `--pipe` to the qualified ref, `domain.pipe_code`, which the input preparation and the
33
+ * pipe I/O route require. A bare code is refused, and so is an `alias->domain.pipe_code` ref,
34
+ * which names a dependency package's pipe rather than one of the method's own.
35
+ */
36
+ export declare function checkPipeRef(value: string): void;
37
+ /** Whether a value holds a control character, which no line of a written script may carry. */
38
+ export declare function hasControlCharacter(value: string): boolean;
39
+ /**
40
+ * An address's tag, the text after its last `@`, or `undefined` for an address with none. An
41
+ * empty tag counts as none.
42
+ */
43
+ export declare function addressTag(methodRef: string): string | undefined;
44
+ /** An address's last path segment, without its tag: the default name of the script it gets. */
45
+ export declare function addressName(methodRef: string): string;
46
+ /**
47
+ * A catalog method's name as a script's file name: decomposed (NFKD), stripped of its combining
48
+ * marks, lowercased, every run of characters outside `a-z` and `0-9` turned into one `-`, and
49
+ * trimmed of `-` at both ends. `Résumé Review (v2)` becomes `resume-review-v2`; a name of no
50
+ * letter or digit becomes the empty string.
51
+ */
52
+ export declare function kebabCase(text: string): string;
53
+ /** Why a script's file name cannot be used, or `undefined` when it can. */
54
+ export declare function scriptNameProblem(name: string): string | undefined;
55
+ /** A value as one single-quoted shell word, an inner `'` spelled `'\''`. */
56
+ export declare function shellQuote(value: string): string;
@@ -0,0 +1,108 @@
1
+ /**
2
+ * How the command names a method and a pipe: the forms of `--method`, the qualified form of
3
+ * `--pipe`, and the names and quoting `script` derives from them.
4
+ */
5
+ import { usageError } from "./io.js";
6
+ /** The prefix of a published method's address. */
7
+ export const ADDRESS_PREFIX = "github.com/";
8
+ /** The prefix of a stored method's catalog id. */
9
+ export const CATALOG_ID_PREFIX = "mt_";
10
+ /** The characters a catalog id is made of, which the API's run route accepts and no other. */
11
+ const CATALOG_ID_SHAPE = /^[A-Za-z0-9_-]+$/;
12
+ /**
13
+ * Tell a `--method` value's form by its shape: `mt_…` is a catalog id, `github.com/…` an address,
14
+ * and anything else a path to a `.mthds` file or a bundle directory. A file or directory that
15
+ * happens to be named `mt_…` is reached as `./mt_…`, so that what a value names never depends on
16
+ * what the current directory holds.
17
+ *
18
+ * @throws {CommandError} A usage error for an `mt_…` value holding a character no catalog id
19
+ * holds (a letter, a digit, `_` or `-`), such as `mt_review.mthds`, which is a path written
20
+ * without its `./`.
21
+ */
22
+ export function classifyMethod(value) {
23
+ if (value.startsWith(CATALOG_ID_PREFIX)) {
24
+ if (!CATALOG_ID_SHAPE.test(value)) {
25
+ throw usageError(`--method "${value}" is not a catalog id: a catalog id holds only letters, digits, _ and -.`, [`To name a local file or directory whose name starts with mt_, write it as ./${value}.`]);
26
+ }
27
+ return { kind: "catalog", methodId: value };
28
+ }
29
+ if (value.startsWith(ADDRESS_PREFIX))
30
+ return { kind: "address", methodRef: value };
31
+ return { kind: "path", path: value };
32
+ }
33
+ /**
34
+ * Hold `--pipe` to the qualified ref, `domain.pipe_code`, which the input preparation and the
35
+ * pipe I/O route require. A bare code is refused, and so is an `alias->domain.pipe_code` ref,
36
+ * which names a dependency package's pipe rather than one of the method's own.
37
+ */
38
+ export function checkPipeRef(value) {
39
+ if (value.includes("->")) {
40
+ throw usageError(`--pipe "${value}" names a dependency package's pipe; name one of the method's own pipes as domain.pipe_code.`);
41
+ }
42
+ if (!value.includes(".")) {
43
+ throw usageError(`--pipe takes a pipe's qualified ref, domain.pipe_code, and "${value}" has no domain.`, [`Write it as <domain>.${value}, the domain being the one its .mthds file declares.`]);
44
+ }
45
+ }
46
+ /** Whether a value holds a control character, which no line of a written script may carry. */
47
+ export function hasControlCharacter(value) {
48
+ for (let index = 0; index < value.length; index += 1) {
49
+ const code = value.charCodeAt(index);
50
+ if (code < 0x20 || code === 0x7f)
51
+ return true;
52
+ }
53
+ return false;
54
+ }
55
+ /**
56
+ * An address's tag, the text after its last `@`, or `undefined` for an address with none. An
57
+ * empty tag counts as none.
58
+ */
59
+ export function addressTag(methodRef) {
60
+ const at = methodRef.lastIndexOf("@");
61
+ if (at < 0)
62
+ return undefined;
63
+ const tag = methodRef.slice(at + 1);
64
+ return tag === "" ? undefined : tag;
65
+ }
66
+ /** An address's last path segment, without its tag: the default name of the script it gets. */
67
+ export function addressName(methodRef) {
68
+ const at = methodRef.lastIndexOf("@");
69
+ const base = at < 0 ? methodRef : methodRef.slice(0, at);
70
+ const segments = base.split("/").filter((segment) => segment !== "");
71
+ return segments[segments.length - 1] ?? "";
72
+ }
73
+ /**
74
+ * A catalog method's name as a script's file name: decomposed (NFKD), stripped of its combining
75
+ * marks, lowercased, every run of characters outside `a-z` and `0-9` turned into one `-`, and
76
+ * trimmed of `-` at both ends. `Résumé Review (v2)` becomes `resume-review-v2`; a name of no
77
+ * letter or digit becomes the empty string.
78
+ */
79
+ export function kebabCase(text) {
80
+ return text
81
+ .normalize("NFKD")
82
+ .replace(/\p{M}/gu, "")
83
+ .toLowerCase()
84
+ .replace(/[^a-z0-9]+/g, "-")
85
+ .replace(/^-+|-+$/g, "");
86
+ }
87
+ /** Why a script's file name cannot be used, or `undefined` when it can. */
88
+ export function scriptNameProblem(name) {
89
+ if (name === "")
90
+ return "is empty";
91
+ if (name === "." || name === "..")
92
+ return `"${name}" names a directory`;
93
+ if (name.includes("/"))
94
+ return `"${name}" holds a /, and --dir is where the script goes`;
95
+ // On every platform, so both SDKs refuse the same names: Windows reads a \ as a /, and
96
+ // `..\outside` would leave --dir there.
97
+ if (name.includes("\\")) {
98
+ return `"${name}" holds a \\, which Windows reads as a /, and --dir is where the script goes`;
99
+ }
100
+ if (hasControlCharacter(name))
101
+ return "holds a control character";
102
+ return undefined;
103
+ }
104
+ /** A value as one single-quoted shell word, an inner `'` spelled `'\''`. */
105
+ export function shellQuote(value) {
106
+ return `'${value.replaceAll("'", "'\\''")}'`;
107
+ }
108
+ //# sourceMappingURL=method.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"method.js","sourceRoot":"","sources":["../../src/cli/method.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * What the command prints: a finished run's result on stdout, and every failure on stderr.
3
+ *
4
+ * A failure is worded by the command from the error's typed fields, never from an SDK message
5
+ * whose wording is the SDK's own, wherever the fields carry what a person needs: the API's reason,
6
+ * the next step it advises and the request id support asks for. That keeps the two commands, this
7
+ * one and its Python twin, saying the same thing for the same answer, which is what the recorded
8
+ * case table holds them to.
9
+ */
10
+ import type { ValidationErrorItem } from "../index.js";
11
+ import type { CommandIO } from "./io.js";
12
+ /** A failure as the command prints it: its lines, and the exit code it ends with. */
13
+ export interface PresentedError {
14
+ readonly lines: readonly string[];
15
+ readonly exitCode: number;
16
+ }
17
+ /** Word any failure for stderr and choose its exit code. */
18
+ export declare function presentError(error: unknown): PresentedError;
19
+ /** The first line of a method that does not load, after `Error: `. */
20
+ export declare const METHOD_DOES_NOT_LOAD_SENTENCE = "the method does not load.";
21
+ /**
22
+ * The lines under "the method does not load": one per validation item that carries a string
23
+ * `category` and `message`, the rule the SDK reads a failed load's items by, and when there is
24
+ * none, the answer's own message.
25
+ */
26
+ export declare function loadFailureLines(items: unknown, message: unknown): string[];
27
+ /** One validation error, as an indented line naming its file when it has one. */
28
+ export declare function validationLine(item: Pick<ValidationErrorItem, "message"> & {
29
+ source?: unknown;
30
+ }): string;
31
+ /**
32
+ * Whether a run's main output is the absence of one: nothing at all, or the runtime's absence
33
+ * document, matched by its whole shape (exactly its keys, each of the type the runtime writes) and
34
+ * never by its `absent` key alone, since a method's own output may carry such a field. The same
35
+ * rule `cli-python` applies.
36
+ */
37
+ export declare function isAbsence(mainStuff: unknown): boolean;
38
+ /**
39
+ * Print a finished run's main output on stdout, as the method produced it: indented JSON,
40
+ * unvalidated and unrepaired, a list included whichever shape it arrived in. An output the method
41
+ * left absent prints `null`, and a line on stderr says so.
42
+ */
43
+ export declare function printResult(mainStuff: unknown, io: CommandIO): void;