@leemour/cli-core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +106 -0
- package/dist/config.d.ts +20 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +52 -0
- package/dist/config.js.map +1 -0
- package/dist/credentials.d.ts +48 -0
- package/dist/credentials.d.ts.map +1 -0
- package/dist/credentials.js +103 -0
- package/dist/credentials.js.map +1 -0
- package/dist/errors.d.ts +34 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +36 -0
- package/dist/errors.js.map +1 -0
- package/dist/exit-codes.d.ts +11 -0
- package/dist/exit-codes.d.ts.map +1 -0
- package/dist/exit-codes.js +25 -0
- package/dist/exit-codes.js.map +1 -0
- package/dist/http/index.d.ts +20 -0
- package/dist/http/index.d.ts.map +1 -0
- package/dist/http/index.js +46 -0
- package/dist/http/index.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/keyring.d.ts +27 -0
- package/dist/keyring.d.ts.map +1 -0
- package/dist/keyring.js +45 -0
- package/dist/keyring.js.map +1 -0
- package/dist/logger.d.ts +12 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +7 -0
- package/dist/logger.js.map +1 -0
- package/dist/logging.d.ts +26 -0
- package/dist/logging.d.ts.map +1 -0
- package/dist/logging.js +55 -0
- package/dist/logging.js.map +1 -0
- package/dist/paths.d.ts +28 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +23 -0
- package/dist/paths.js.map +1 -0
- package/dist/pretty.d.ts +10 -0
- package/dist/pretty.d.ts.map +1 -0
- package/dist/pretty.js +76 -0
- package/dist/pretty.js.map +1 -0
- package/dist/renderer.d.ts +32 -0
- package/dist/renderer.d.ts.map +1 -0
- package/dist/renderer.js +48 -0
- package/dist/renderer.js.map +1 -0
- package/dist/retry.d.ts +27 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +26 -0
- package/dist/retry.js.map +1 -0
- package/dist/streams.d.ts +24 -0
- package/dist/streams.d.ts.map +1 -0
- package/dist/streams.js +26 -0
- package/dist/streams.js.map +1 -0
- package/dist/testing/index.d.ts +24 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +29 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/time.d.ts +21 -0
- package/dist/time.d.ts.map +1 -0
- package/dist/time.js +19 -0
- package/dist/time.js.map +1 -0
- package/package.json +64 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one seam through which the OS keychain is reached.
|
|
3
|
+
*
|
|
4
|
+
* **What backs it, per platform** — `@napi-rs/keyring` 2.1.0, checked 2026-09-19:
|
|
5
|
+
* macOS uses the Keychain and Windows the Credential Manager, both part of the OS; Linux and the
|
|
6
|
+
* BSDs use the Secret Service over D-Bus, which a headless machine, a container, WSL or a CI
|
|
7
|
+
* runner usually does not have. There is nothing to install either way — the package ships
|
|
8
|
+
* prebuilt binaries for twelve platform triples including musl, so no Rust toolchain and no
|
|
9
|
+
* node-gyp — but on Linux **the daemon may simply not be there**, which is why every caller must
|
|
10
|
+
* handle this throwing. `Credentials` does, by warning once and writing a 0600 file instead.
|
|
11
|
+
* Injected like `fetch` in core, so
|
|
12
|
+
* "no test touched the developer's real keychain" is a property of the code rather than a hope
|
|
13
|
+
* about every test remembering to opt out.
|
|
14
|
+
*/
|
|
15
|
+
export interface KeyringStore {
|
|
16
|
+
get(service: string, account: string): string | null;
|
|
17
|
+
set(service: string, account: string, secret: string): void;
|
|
18
|
+
delete(service: string, account: string): boolean;
|
|
19
|
+
}
|
|
20
|
+
export declare const systemKeyring: KeyringStore;
|
|
21
|
+
/** An in-memory stand-in. Every test uses this; none can reach the real keychain. */
|
|
22
|
+
export declare const memoryKeyring: (initial?: Record<string, string>) => KeyringStore & {
|
|
23
|
+
entries: Map<string, string>;
|
|
24
|
+
};
|
|
25
|
+
/** Refuses every operation, so the fallback path can be tested. */
|
|
26
|
+
export declare const brokenKeyring: (message?: string) => KeyringStore;
|
|
27
|
+
//# sourceMappingURL=keyring.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyring.d.ts","sourceRoot":"","sources":["../src/keyring.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;IACpD,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3D,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAA;CAClD;AAoBD,eAAO,MAAM,aAAa,EAAE,YAQ3B,CAAA;AAED,qFAAqF;AACrF,eAAO,MAAM,aAAa,GACxB,UAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,KACnC,YAAY,GAAG;IAAE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAY/C,CAAA;AAED,mEAAmE;AACnE,eAAO,MAAM,aAAa,GAAI,gBAAuC,KAAG,YAUtE,CAAA"}
|
package/dist/keyring.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
const require = createRequire(import.meta.url);
|
|
3
|
+
let cached;
|
|
4
|
+
// Loaded on first use, not at import: `@napi-rs/keyring` is a native module, and a machine
|
|
5
|
+
// where its binary is missing must still be able to run in file mode.
|
|
6
|
+
const entryClass = () => {
|
|
7
|
+
cached ??= require("@napi-rs/keyring").Entry;
|
|
8
|
+
return cached;
|
|
9
|
+
};
|
|
10
|
+
export const systemKeyring = {
|
|
11
|
+
// Returns null for an absent entry rather than throwing — measured against @napi-rs/keyring
|
|
12
|
+
// on 2026-09-13, and the difference matters: a throw here means the keychain is unusable.
|
|
13
|
+
get: (service, account) => new (entryClass())(service, account).getPassword(),
|
|
14
|
+
set: (service, account, secret) => {
|
|
15
|
+
;
|
|
16
|
+
new (entryClass())(service, account).setPassword(secret);
|
|
17
|
+
},
|
|
18
|
+
delete: (service, account) => new (entryClass())(service, account).deletePassword(),
|
|
19
|
+
};
|
|
20
|
+
/** An in-memory stand-in. Every test uses this; none can reach the real keychain. */
|
|
21
|
+
export const memoryKeyring = (initial = {}) => {
|
|
22
|
+
const entries = new Map(Object.entries(initial));
|
|
23
|
+
const key = (service, account) => `${service}:${account}`;
|
|
24
|
+
return {
|
|
25
|
+
entries,
|
|
26
|
+
get: (service, account) => entries.get(key(service, account)) ?? null,
|
|
27
|
+
set: (service, account, secret) => {
|
|
28
|
+
entries.set(key(service, account), secret);
|
|
29
|
+
},
|
|
30
|
+
delete: (service, account) => entries.delete(key(service, account)),
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
/** Refuses every operation, so the fallback path can be tested. */
|
|
34
|
+
export const brokenKeyring = (message = "no secret service available") => ({
|
|
35
|
+
get: () => {
|
|
36
|
+
throw new Error(message);
|
|
37
|
+
},
|
|
38
|
+
set: () => {
|
|
39
|
+
throw new Error(message);
|
|
40
|
+
},
|
|
41
|
+
delete: () => {
|
|
42
|
+
throw new Error(message);
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
//# sourceMappingURL=keyring.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyring.js","sourceRoot":"","sources":["../src/keyring.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AA8B3C,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAC9C,IAAI,MAAoC,CAAA;AAExC,2FAA2F;AAC3F,sEAAsE;AACtE,MAAM,UAAU,GAAG,GAAqB,EAAE;IACxC,MAAM,KAAM,OAAO,CAAC,kBAAkB,CAAiC,CAAC,KAAK,CAAA;IAC7E,OAAO,MAAM,CAAA;AACf,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,aAAa,GAAiB;IACzC,4FAA4F;IAC5F,0FAA0F;IAC1F,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,WAAW,EAAE;IAC7E,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE;QAChC,CAAC;QAAA,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,CAAA;IAC3D,CAAC;IACD,MAAM,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,cAAc,EAAE;CACpF,CAAA;AAED,qFAAqF;AACrF,MAAM,CAAC,MAAM,aAAa,GAAG,CAC3B,UAAkC,EAAE,EACa,EAAE;IACnD,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAA;IAChD,MAAM,GAAG,GAAG,CAAC,OAAe,EAAE,OAAe,EAAE,EAAE,CAAC,GAAG,OAAO,IAAI,OAAO,EAAE,CAAA;IAEzE,OAAO;QACL,OAAO;QACP,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,IAAI,IAAI;QACrE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE;YAChC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,CAAA;QAC5C,CAAC;QACD,MAAM,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;KACpE,CAAA;AACH,CAAC,CAAA;AAED,mEAAmE;AACnE,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,OAAO,GAAG,6BAA6B,EAAgB,EAAE,CAAC,CAAC;IACvF,GAAG,EAAE,GAAG,EAAE;QACR,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAA;IAC1B,CAAC;IACD,GAAG,EAAE,GAAG,EAAE;QACR,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAA;IAC1B,CAAC;IACD,MAAM,EAAE,GAAG,EAAE;QACX,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAA;IAC1B,CAAC;CACF,CAAC,CAAA"}
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The only logging contract core knows about. The CLI adapts Pino to it; a Worker can pass
|
|
3
|
+
* its own. Core never imports a logging library — see docs/ARCHITECTURE.md.
|
|
4
|
+
*/
|
|
5
|
+
export interface Logger {
|
|
6
|
+
debug(event: object, message?: string): void;
|
|
7
|
+
info(event: object, message?: string): void;
|
|
8
|
+
warn(event: object, message?: string): void;
|
|
9
|
+
error(event: object, message?: string): void;
|
|
10
|
+
}
|
|
11
|
+
export declare const noopLogger: Logger;
|
|
12
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,MAAM;IACrB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5C,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3C,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3C,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC7C;AAED,eAAO,MAAM,UAAU,EAAE,MAKxB,CAAA"}
|
package/dist/logger.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAWA,MAAM,CAAC,MAAM,UAAU,GAAW;IAChC,KAAK,EAAE,GAAG,EAAE,GAAE,CAAC;IACf,IAAI,EAAE,GAAG,EAAE,GAAE,CAAC;IACd,IAAI,EAAE,GAAG,EAAE,GAAE,CAAC;IACd,KAAK,EAAE,GAAG,EAAE,GAAE,CAAC;CAChB,CAAA"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Logger } from "./logger.js";
|
|
2
|
+
/**
|
|
3
|
+
* Field names whose value must never reach a log file. This is the **second** line of defence, not
|
|
4
|
+
* the first: a secret should not be handed to a logger at all. Redaction catches the one that
|
|
5
|
+
* slipped into a nested object nobody thought about.
|
|
6
|
+
*/
|
|
7
|
+
export declare const REDACTED_FIELDS: string[];
|
|
8
|
+
export interface FileLogger extends Logger {
|
|
9
|
+
/** Must be awaited before the process exits, or the tail of the file is lost. */
|
|
10
|
+
close(): Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
export interface FileLoggerOptions {
|
|
13
|
+
/** Where the JSON lines go. Omit for a logger that writes nowhere. */
|
|
14
|
+
path?: string;
|
|
15
|
+
level?: string;
|
|
16
|
+
base?: Record<string, unknown>;
|
|
17
|
+
/** Added to `REDACTED_FIELDS` — the names only this program knows about. */
|
|
18
|
+
redact?: string[];
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Structured records for machines: JSON lines, no ANSI ever, secrets redacted. The terminal is a
|
|
22
|
+
* different concern and goes through the renderer — a spinner frame must never reach a log file,
|
|
23
|
+
* and a log record must never reach stdout.
|
|
24
|
+
*/
|
|
25
|
+
export declare const createFileLogger: ({ path, level, base, redact, }?: FileLoggerOptions) => FileLogger;
|
|
26
|
+
//# sourceMappingURL=logging.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logging.d.ts","sourceRoot":"","sources":["../src/logging.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC;;;;GAIG;AACH,eAAO,MAAM,eAAe,UAa3B,CAAA;AAED,MAAM,WAAW,UAAW,SAAQ,MAAM;IACxC,iFAAiF;IACjF,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,sEAAsE;IACtE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,4EAA4E;IAC5E,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;CAClB;AAED;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAI,iCAK9B,iBAAsB,KAAG,UA2B3B,CAAA"}
|
package/dist/logging.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { createWriteStream } from "node:fs";
|
|
2
|
+
import { pino } from "pino";
|
|
3
|
+
/**
|
|
4
|
+
* Field names whose value must never reach a log file. This is the **second** line of defence, not
|
|
5
|
+
* the first: a secret should not be handed to a logger at all. Redaction catches the one that
|
|
6
|
+
* slipped into a nested object nobody thought about.
|
|
7
|
+
*/
|
|
8
|
+
export const REDACTED_FIELDS = [
|
|
9
|
+
"authorization",
|
|
10
|
+
"Authorization",
|
|
11
|
+
"apiKey",
|
|
12
|
+
"api_key",
|
|
13
|
+
"token",
|
|
14
|
+
"accessToken",
|
|
15
|
+
"access_token",
|
|
16
|
+
"refreshToken",
|
|
17
|
+
"password",
|
|
18
|
+
"secret",
|
|
19
|
+
"verifyCode",
|
|
20
|
+
"phone",
|
|
21
|
+
];
|
|
22
|
+
/**
|
|
23
|
+
* Structured records for machines: JSON lines, no ANSI ever, secrets redacted. The terminal is a
|
|
24
|
+
* different concern and goes through the renderer — a spinner frame must never reach a log file,
|
|
25
|
+
* and a log record must never reach stdout.
|
|
26
|
+
*/
|
|
27
|
+
export const createFileLogger = ({ path, level = "info", base = {}, redact = [], } = {}) => {
|
|
28
|
+
if (!path)
|
|
29
|
+
return { ...silent, close: async () => { } };
|
|
30
|
+
// A plain append stream, not a worker transport: logging must not need a second process, and
|
|
31
|
+
// `sync: false` drops the tail of the file when a command exits — which is exactly the failure
|
|
32
|
+
// the log exists to explain.
|
|
33
|
+
const file = createWriteStream(path, { flags: "a" });
|
|
34
|
+
const fields = [...new Set([...REDACTED_FIELDS, ...redact])];
|
|
35
|
+
const paths = fields.flatMap((field) => [field, `*.${field}`, `*.*.${field}`]);
|
|
36
|
+
const logger = pino({ level, base, redact: { paths, censor: "[redacted]" }, timestamp: pino.stdTimeFunctions.isoTime }, file);
|
|
37
|
+
return {
|
|
38
|
+
debug: (event, message) => logger.debug(event, message),
|
|
39
|
+
info: (event, message) => logger.info(event, message),
|
|
40
|
+
warn: (event, message) => logger.warn(event, message),
|
|
41
|
+
error: (event, message) => logger.error(event, message),
|
|
42
|
+
close: () => new Promise((resolve) => {
|
|
43
|
+
file.end(() => {
|
|
44
|
+
resolve();
|
|
45
|
+
});
|
|
46
|
+
}),
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
const silent = {
|
|
50
|
+
debug: () => { },
|
|
51
|
+
info: () => { },
|
|
52
|
+
warn: () => { },
|
|
53
|
+
error: () => { },
|
|
54
|
+
};
|
|
55
|
+
//# sourceMappingURL=logging.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logging.js","sourceRoot":"","sources":["../src/logging.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAG3B;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,eAAe;IACf,eAAe;IACf,QAAQ;IACR,SAAS;IACT,OAAO;IACP,aAAa;IACb,cAAc;IACd,cAAc;IACd,UAAU;IACV,QAAQ;IACR,YAAY;IACZ,OAAO;CACR,CAAA;AAgBD;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,EAC/B,IAAI,EACJ,KAAK,GAAG,MAAM,EACd,IAAI,GAAG,EAAE,EACT,MAAM,GAAG,EAAE,MACU,EAAE,EAAc,EAAE;IACvC,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC,EAAE,CAAA;IAEtD,6FAA6F;IAC7F,+FAA+F;IAC/F,6BAA6B;IAC7B,MAAM,IAAI,GAAG,iBAAiB,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAA;IACpD,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,eAAe,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,CAAA;IAC5D,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,EAAE,EAAE,OAAO,KAAK,EAAE,CAAC,CAAC,CAAA;IAE9E,MAAM,MAAM,GAAG,IAAI,CACjB,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,EAClG,IAAI,CACL,CAAA;IAED,OAAO;QACL,KAAK,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC;QACvD,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC;QACrD,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC;QACrD,KAAK,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC;QACvD,KAAK,EAAE,GAAG,EAAE,CACV,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YAC5B,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE;gBACZ,OAAO,EAAE,CAAA;YACX,CAAC,CAAC,CAAA;QACJ,CAAC,CAAC;KACL,CAAA;AACH,CAAC,CAAA;AAED,MAAM,MAAM,GAAW;IACrB,KAAK,EAAE,GAAG,EAAE,GAAE,CAAC;IACf,IAAI,EAAE,GAAG,EAAE,GAAE,CAAC;IACd,IAAI,EAAE,GAAG,EAAE,GAAE,CAAC;IACd,KAAK,EAAE,GAAG,EAAE,GAAE,CAAC;CAChB,CAAA"}
|
package/dist/paths.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface Paths {
|
|
2
|
+
/** `config.json`, and the credential file when the keyring is unavailable. */
|
|
3
|
+
config: string;
|
|
4
|
+
/** Everything the program writes for itself: sessions, run artifacts, databases. */
|
|
5
|
+
state: string;
|
|
6
|
+
/** Data that can be deleted without losing anything the user typed. */
|
|
7
|
+
cache: string;
|
|
8
|
+
}
|
|
9
|
+
export interface PathsOptions {
|
|
10
|
+
/** Directory name under the OS convention — the command's name, not the package's. */
|
|
11
|
+
appName: string;
|
|
12
|
+
/**
|
|
13
|
+
* Environment variable prefix for the overrides, e.g. `MAX` gives `MAX_CONFIG_DIR`.
|
|
14
|
+
* Defaults to the app name, upper-cased, with anything that is not a letter or digit as `_`.
|
|
15
|
+
*/
|
|
16
|
+
prefix?: string;
|
|
17
|
+
env?: NodeJS.ProcessEnv;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* `env-paths` rather than a hand-rolled `~/.config`, so macOS and Windows land where those systems
|
|
21
|
+
* expect. Each directory is overridable by environment variable, which is what makes a test —
|
|
22
|
+
* and a throwaway profile — possible without touching the real ones.
|
|
23
|
+
*/
|
|
24
|
+
export declare const resolvePaths: ({ appName, prefix, env }: PathsOptions) => Paths;
|
|
25
|
+
/** Whether any of the three directories came from the environment rather than the OS convention. */
|
|
26
|
+
export declare const pathsAreOverridden: ({ appName, prefix, env }: PathsOptions) => boolean;
|
|
27
|
+
export declare const configFilePath: (configDir: string, fileName?: string) => string;
|
|
28
|
+
//# sourceMappingURL=paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,KAAK;IACpB,8EAA8E;IAC9E,MAAM,EAAE,MAAM,CAAA;IACd,oFAAoF;IACpF,KAAK,EAAE,MAAM,CAAA;IACb,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,YAAY;IAC3B,sFAAsF;IACtF,OAAO,EAAE,MAAM,CAAA;IACf;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;CACxB;AAED;;;;GAIG;AACH,eAAO,MAAM,YAAY,GAAI,0BAAwC,YAAY,KAAG,KASnF,CAAA;AAED,oGAAoG;AACpG,eAAO,MAAM,kBAAkB,GAAI,0BAAwC,YAAY,KAAG,OAGzF,CAAA;AAED,eAAO,MAAM,cAAc,GAAI,WAAW,MAAM,EAAE,iBAAwB,KAAG,MAAmC,CAAA"}
|
package/dist/paths.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import envPaths from "env-paths";
|
|
3
|
+
/**
|
|
4
|
+
* `env-paths` rather than a hand-rolled `~/.config`, so macOS and Windows land where those systems
|
|
5
|
+
* expect. Each directory is overridable by environment variable, which is what makes a test —
|
|
6
|
+
* and a throwaway profile — possible without touching the real ones.
|
|
7
|
+
*/
|
|
8
|
+
export const resolvePaths = ({ appName, prefix, env = process.env }) => {
|
|
9
|
+
const base = envPaths(appName, { suffix: "" });
|
|
10
|
+
const key = (name) => `${prefix ?? appName.toUpperCase().replace(/[^A-Z0-9]/g, "_")}_${name}`;
|
|
11
|
+
return {
|
|
12
|
+
config: env[key("CONFIG_DIR")] ?? base.config,
|
|
13
|
+
state: env[key("STATE_DIR")] ?? base.data,
|
|
14
|
+
cache: env[key("CACHE_DIR")] ?? base.cache,
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
/** Whether any of the three directories came from the environment rather than the OS convention. */
|
|
18
|
+
export const pathsAreOverridden = ({ appName, prefix, env = process.env }) => {
|
|
19
|
+
const stem = prefix ?? appName.toUpperCase().replace(/[^A-Z0-9]/g, "_");
|
|
20
|
+
return ["CONFIG_DIR", "STATE_DIR", "CACHE_DIR"].some((name) => env[`${stem}_${name}`] !== undefined);
|
|
21
|
+
};
|
|
22
|
+
export const configFilePath = (configDir, fileName = "config.json") => join(configDir, fileName);
|
|
23
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,QAAQ,MAAM,WAAW,CAAA;AAsBhC;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,EAAgB,EAAS,EAAE;IAC1F,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAA;IAC9C,MAAM,GAAG,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,GAAG,MAAM,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,YAAY,EAAE,GAAG,CAAC,IAAI,IAAI,EAAE,CAAA;IAErG,OAAO;QACL,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,IAAI,IAAI,CAAC,MAAM;QAC7C,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI;QACzC,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK;KAC3C,CAAA;AACH,CAAC,CAAA;AAED,oGAAoG;AACpG,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,EAAgB,EAAW,EAAE;IAClG,MAAM,IAAI,GAAG,MAAM,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,YAAY,EAAE,GAAG,CAAC,CAAA;IACvE,OAAO,CAAC,YAAY,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC,KAAK,SAAS,CAAC,CAAA;AACtG,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,SAAiB,EAAE,QAAQ,GAAG,aAAa,EAAU,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAA"}
|
package/dist/pretty.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface PrettyOptions {
|
|
2
|
+
color: boolean;
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* Three shapes and a fallback, not a renderer per endpoint. A list of like objects becomes a
|
|
6
|
+
* table, a single object becomes labelled lines, everything else is printed as JSON — which is
|
|
7
|
+
* honest about the fact that we do not know what it is.
|
|
8
|
+
*/
|
|
9
|
+
export declare const renderPretty: (value: unknown, options: PrettyOptions) => string;
|
|
10
|
+
//# sourceMappingURL=pretty.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pretty.d.ts","sourceRoot":"","sources":["../src/pretty.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,OAAO,CAAA;CACf;AAED;;;;GAIG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,OAAO,EAAE,SAAS,aAAa,KAAG,MAmBrE,CAAA"}
|
package/dist/pretty.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import Table from "cli-table3";
|
|
2
|
+
import pc from "picocolors";
|
|
3
|
+
/**
|
|
4
|
+
* Three shapes and a fallback, not a renderer per endpoint. A list of like objects becomes a
|
|
5
|
+
* table, a single object becomes labelled lines, everything else is printed as JSON — which is
|
|
6
|
+
* honest about the fact that we do not know what it is.
|
|
7
|
+
*/
|
|
8
|
+
export const renderPretty = (value, options) => {
|
|
9
|
+
const paint = palette(options.color);
|
|
10
|
+
if (Array.isArray(value)) {
|
|
11
|
+
if (value.length === 0)
|
|
12
|
+
return paint.dim("(nothing)");
|
|
13
|
+
return isRowLike(value) ? renderTable(value, paint) : renderJson(value);
|
|
14
|
+
}
|
|
15
|
+
if (isPlainObject(value)) {
|
|
16
|
+
const listKey = Object.keys(value).find((key) => Array.isArray(value[key]) && isRowLike(value[key]));
|
|
17
|
+
if (listKey) {
|
|
18
|
+
const rest = Object.fromEntries(Object.entries(value).filter(([key]) => key !== listKey));
|
|
19
|
+
const table = renderTable(value[listKey], paint);
|
|
20
|
+
return Object.keys(rest).length > 0 ? `${table}\n\n${renderFields(rest, paint)}` : table;
|
|
21
|
+
}
|
|
22
|
+
return renderFields(value, paint);
|
|
23
|
+
}
|
|
24
|
+
return renderJson(value);
|
|
25
|
+
};
|
|
26
|
+
const palette = (color) => ({
|
|
27
|
+
dim: (text) => (color ? pc.dim(text) : text),
|
|
28
|
+
bold: (text) => (color ? pc.bold(text) : text),
|
|
29
|
+
label: (text) => (color ? pc.cyan(text) : text),
|
|
30
|
+
});
|
|
31
|
+
const isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
32
|
+
const isRowLike = (value) => value.length > 0 && value.every((item) => isPlainObject(item)) && value.length <= 500;
|
|
33
|
+
const renderTable = (rows, paint) => {
|
|
34
|
+
const columns = [...new Set(rows.flatMap((row) => Object.keys(row)))];
|
|
35
|
+
const table = new Table({
|
|
36
|
+
head: columns.map((column) => paint.bold(column)),
|
|
37
|
+
// `chars: {}` is not enough — cli-table3 falls back to its own defaults for anything unset.
|
|
38
|
+
style: { head: [], border: [], "padding-left": 0, "padding-right": 2 },
|
|
39
|
+
chars: BORDERLESS,
|
|
40
|
+
});
|
|
41
|
+
for (const row of rows)
|
|
42
|
+
table.push(columns.map((column) => cell(row[column])));
|
|
43
|
+
return table.toString();
|
|
44
|
+
};
|
|
45
|
+
const renderFields = (fields, paint) => {
|
|
46
|
+
const width = Math.max(...Object.keys(fields).map((key) => key.length));
|
|
47
|
+
return Object.entries(fields)
|
|
48
|
+
.map(([key, value]) => `${paint.label(key.padEnd(width))} ${cell(value)}`)
|
|
49
|
+
.join("\n");
|
|
50
|
+
};
|
|
51
|
+
const cell = (value) => {
|
|
52
|
+
if (value === null || value === undefined)
|
|
53
|
+
return "";
|
|
54
|
+
if (typeof value === "object")
|
|
55
|
+
return JSON.stringify(value);
|
|
56
|
+
return String(value);
|
|
57
|
+
};
|
|
58
|
+
const renderJson = (value) => JSON.stringify(value, null, 2);
|
|
59
|
+
const BORDERLESS = {
|
|
60
|
+
top: "",
|
|
61
|
+
"top-mid": "",
|
|
62
|
+
"top-left": "",
|
|
63
|
+
"top-right": "",
|
|
64
|
+
bottom: "",
|
|
65
|
+
"bottom-mid": "",
|
|
66
|
+
"bottom-left": "",
|
|
67
|
+
"bottom-right": "",
|
|
68
|
+
left: "",
|
|
69
|
+
"left-mid": "",
|
|
70
|
+
mid: "",
|
|
71
|
+
"mid-mid": "",
|
|
72
|
+
right: "",
|
|
73
|
+
"right-mid": "",
|
|
74
|
+
middle: "",
|
|
75
|
+
};
|
|
76
|
+
//# sourceMappingURL=pretty.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pretty.js","sourceRoot":"","sources":["../src/pretty.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type Streams } from "./streams.js";
|
|
2
|
+
/**
|
|
3
|
+
* What a renderer can be asked for. `auto` is not here on purpose: choosing between a person and a
|
|
4
|
+
* pipe is the caller's job and happens once, so a renderer can never be handed a mode it has to
|
|
5
|
+
* resolve.
|
|
6
|
+
*/
|
|
7
|
+
export declare const RENDER_FORMATS: readonly ["pretty", "json", "jsonl"];
|
|
8
|
+
export type RenderFormat = (typeof RENDER_FORMATS)[number];
|
|
9
|
+
export interface Renderer {
|
|
10
|
+
/** The one thing a caller asked for. In a machine mode this is the whole of stdout. */
|
|
11
|
+
result(value: unknown): void;
|
|
12
|
+
/** Each item on its own line. Only meaningful in `jsonl`; elsewhere it renders the whole list. */
|
|
13
|
+
stream(items: Iterable<unknown>): void;
|
|
14
|
+
note(message: string): void;
|
|
15
|
+
success(message: string): void;
|
|
16
|
+
warn(message: string): void;
|
|
17
|
+
failure(message: string): void;
|
|
18
|
+
}
|
|
19
|
+
export interface RendererOptions {
|
|
20
|
+
format: RenderFormat;
|
|
21
|
+
color: boolean;
|
|
22
|
+
streams?: Streams;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* **Every diagnostic goes to stderr, in every mode — pretty included.**
|
|
26
|
+
*
|
|
27
|
+
* The brief only requires that of the machine modes, but making it unconditional means §44 holds
|
|
28
|
+
* by construction instead of by a mode check somebody can forget. It also means piping human
|
|
29
|
+
* output still gives you the content and not the commentary.
|
|
30
|
+
*/
|
|
31
|
+
export declare const createRenderer: ({ format, color, streams }: RendererOptions) => Renderer;
|
|
32
|
+
//# sourceMappingURL=renderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../src/renderer.ts"],"names":[],"mappings":"AACA,OAAO,EAAkB,KAAK,OAAO,EAAE,MAAM,cAAc,CAAA;AAE3D;;;;GAIG;AACH,eAAO,MAAM,cAAc,sCAAuC,CAAA;AAClE,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAA;AAE1D,MAAM,WAAW,QAAQ;IACvB,uFAAuF;IACvF,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAA;IAC5B,kGAAkG;IAClG,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,IAAI,CAAA;IACtC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAC/B;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,YAAY,CAAA;IACpB,KAAK,EAAE,OAAO,CAAA;IACd,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,GAAI,4BAA6C,eAAe,KAAG,QAkC7F,CAAA"}
|
package/dist/renderer.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { renderPretty } from "./pretty.js";
|
|
2
|
+
import { processStreams } from "./streams.js";
|
|
3
|
+
/**
|
|
4
|
+
* What a renderer can be asked for. `auto` is not here on purpose: choosing between a person and a
|
|
5
|
+
* pipe is the caller's job and happens once, so a renderer can never be handed a mode it has to
|
|
6
|
+
* resolve.
|
|
7
|
+
*/
|
|
8
|
+
export const RENDER_FORMATS = ["pretty", "json", "jsonl"];
|
|
9
|
+
/**
|
|
10
|
+
* **Every diagnostic goes to stderr, in every mode — pretty included.**
|
|
11
|
+
*
|
|
12
|
+
* The brief only requires that of the machine modes, but making it unconditional means §44 holds
|
|
13
|
+
* by construction instead of by a mode check somebody can forget. It also means piping human
|
|
14
|
+
* output still gives you the content and not the commentary.
|
|
15
|
+
*/
|
|
16
|
+
export const createRenderer = ({ format, color, streams = processStreams }) => {
|
|
17
|
+
const mark = (symbol, message) => `${symbol} ${message}`;
|
|
18
|
+
const diagnostics = {
|
|
19
|
+
note: (message) => streams.diagnostic(format === "pretty" ? mark("·", message) : message),
|
|
20
|
+
success: (message) => streams.diagnostic(format === "pretty" ? mark("✓", message) : message),
|
|
21
|
+
warn: (message) => streams.diagnostic(format === "pretty" ? mark("!", message) : message),
|
|
22
|
+
failure: (message) => streams.diagnostic(format === "pretty" ? mark("✗", message) : message),
|
|
23
|
+
};
|
|
24
|
+
if (format === "jsonl") {
|
|
25
|
+
return {
|
|
26
|
+
...diagnostics,
|
|
27
|
+
result: (value) => streams.data(JSON.stringify(value)),
|
|
28
|
+
stream: (items) => {
|
|
29
|
+
for (const item of items)
|
|
30
|
+
streams.data(JSON.stringify(item));
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
if (format === "pretty") {
|
|
35
|
+
return {
|
|
36
|
+
...diagnostics,
|
|
37
|
+
result: (value) => streams.data(renderPretty(value, { color })),
|
|
38
|
+
stream: (items) => streams.data(renderPretty([...items], { color })),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
// json — exactly one deterministic value, and nothing else, ever.
|
|
42
|
+
return {
|
|
43
|
+
...diagnostics,
|
|
44
|
+
result: (value) => streams.data(JSON.stringify(value)),
|
|
45
|
+
stream: (items) => streams.data(JSON.stringify([...items])),
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
//# sourceMappingURL=renderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderer.js","sourceRoot":"","sources":["../src/renderer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,EAAE,cAAc,EAAgB,MAAM,cAAc,CAAA;AAE3D;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAU,CAAA;AAoBlE;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,cAAc,EAAmB,EAAY,EAAE;IACvG,MAAM,IAAI,GAAG,CAAC,MAAc,EAAE,OAAe,EAAE,EAAE,CAAC,GAAG,MAAM,IAAI,OAAO,EAAE,CAAA;IAExE,MAAM,WAAW,GAAG;QAClB,IAAI,EAAE,CAAC,OAAe,EAAE,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;QACjG,OAAO,EAAE,CAAC,OAAe,EAAE,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;QACpG,IAAI,EAAE,CAAC,OAAe,EAAE,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;QACjG,OAAO,EAAE,CAAC,OAAe,EAAE,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;KACrG,CAAA;IAED,IAAI,MAAM,KAAK,OAAO,EAAE,CAAC;QACvB,OAAO;YACL,GAAG,WAAW;YACd,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;YACtD,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;gBAChB,KAAK,MAAM,IAAI,IAAI,KAAK;oBAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAA;YAC9D,CAAC;SACF,CAAA;IACH,CAAC;IAED,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;QACxB,OAAO;YACL,GAAG,WAAW;YACd,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;YAC/D,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,GAAG,KAAK,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;SACrE,CAAA;IACH,CAAC;IAED,kEAAkE;IAClE,OAAO;QACL,GAAG,WAAW;QACd,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACtD,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC;KAC5D,CAAA;AACH,CAAC,CAAA"}
|
package/dist/retry.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ErrorCode } from "./errors.js";
|
|
2
|
+
export interface RetryConfig {
|
|
3
|
+
/** Attempts after the first. One means two attempts in total. */
|
|
4
|
+
retries: number;
|
|
5
|
+
baseDelayMs: number;
|
|
6
|
+
maxDelayMs: number;
|
|
7
|
+
/** Beyond this we stop waiting and hand back a structured error instead of blocking. */
|
|
8
|
+
maxRetryAfterMs: number;
|
|
9
|
+
}
|
|
10
|
+
export declare const DEFAULT_RETRY: RetryConfig;
|
|
11
|
+
/**
|
|
12
|
+
* Full jitter: the wait is uniform over `[0, min(cap, base · 2^n))`. Picking the exponential value
|
|
13
|
+
* itself synchronizes every client that failed at the same moment, which is the stampede the
|
|
14
|
+
* backoff exists to avoid.
|
|
15
|
+
*
|
|
16
|
+
* `attempt` is the attempt that just failed, counting from 1.
|
|
17
|
+
*/
|
|
18
|
+
export declare const backoffMs: (attempt: number, config: RetryConfig, random: () => number) => number;
|
|
19
|
+
/**
|
|
20
|
+
* The three outcomes where nothing came back, and so there is nothing to classify.
|
|
21
|
+
*
|
|
22
|
+
* **This says the attempt failed without an answer — never that the operation is safe to repeat.**
|
|
23
|
+
* A request that died after leaving may well have been carried out, which is why a write gets
|
|
24
|
+
* `outcome_unknown` rather than a retry.
|
|
25
|
+
*/
|
|
26
|
+
export declare const isTransportFailure: (code: ErrorCode) => boolean;
|
|
27
|
+
//# sourceMappingURL=retry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../src/retry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAE5C,MAAM,WAAW,WAAW;IAC1B,iEAAiE;IACjE,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;IAClB,wFAAwF;IACxF,eAAe,EAAE,MAAM,CAAA;CACxB;AAED,eAAO,MAAM,aAAa,EAAE,WAK3B,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,SAAS,GAAI,SAAS,MAAM,EAAE,QAAQ,WAAW,EAAE,QAAQ,MAAM,MAAM,KAAG,MAGtF,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,GAAI,MAAM,SAAS,KAAG,OACmB,CAAA"}
|
package/dist/retry.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export const DEFAULT_RETRY = {
|
|
2
|
+
retries: 1,
|
|
3
|
+
baseDelayMs: 250,
|
|
4
|
+
maxDelayMs: 10_000,
|
|
5
|
+
maxRetryAfterMs: 30_000,
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Full jitter: the wait is uniform over `[0, min(cap, base · 2^n))`. Picking the exponential value
|
|
9
|
+
* itself synchronizes every client that failed at the same moment, which is the stampede the
|
|
10
|
+
* backoff exists to avoid.
|
|
11
|
+
*
|
|
12
|
+
* `attempt` is the attempt that just failed, counting from 1.
|
|
13
|
+
*/
|
|
14
|
+
export const backoffMs = (attempt, config, random) => {
|
|
15
|
+
const ceiling = Math.min(config.maxDelayMs, config.baseDelayMs * 2 ** (attempt - 1));
|
|
16
|
+
return Math.round(random() * ceiling);
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* The three outcomes where nothing came back, and so there is nothing to classify.
|
|
20
|
+
*
|
|
21
|
+
* **This says the attempt failed without an answer — never that the operation is safe to repeat.**
|
|
22
|
+
* A request that died after leaving may well have been carried out, which is why a write gets
|
|
23
|
+
* `outcome_unknown` rather than a retry.
|
|
24
|
+
*/
|
|
25
|
+
export const isTransportFailure = (code) => code === "timeout" || code === "network_error" || code === "cancelled";
|
|
26
|
+
//# sourceMappingURL=retry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.js","sourceRoot":"","sources":["../src/retry.ts"],"names":[],"mappings":"AAWA,MAAM,CAAC,MAAM,aAAa,GAAgB;IACxC,OAAO,EAAE,CAAC;IACV,WAAW,EAAE,GAAG;IAChB,UAAU,EAAE,MAAM;IAClB,eAAe,EAAE,MAAM;CACxB,CAAA;AAED;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,OAAe,EAAE,MAAmB,EAAE,MAAoB,EAAU,EAAE;IAC9F,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,WAAW,GAAG,CAAC,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAA;IACpF,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,OAAO,CAAC,CAAA;AACvC,CAAC,CAAA;AAED;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,IAAe,EAAW,EAAE,CAC7D,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,eAAe,IAAI,IAAI,KAAK,WAAW,CAAA"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The line every machine mode depends on: **stdout carries data, stderr carries everything
|
|
3
|
+
* else.** Two functions and no formatting decisions — the renderer that chooses tables, colour
|
|
4
|
+
* and spinners is `CLI-5` and sits on top of this, never beside it.
|
|
5
|
+
*/
|
|
6
|
+
export interface Streams {
|
|
7
|
+
data: (text: string) => void;
|
|
8
|
+
diagnostic: (text: string) => void;
|
|
9
|
+
/**
|
|
10
|
+
* A line that replaces itself — a progress counter, and nothing else.
|
|
11
|
+
*
|
|
12
|
+
* It lives here rather than beside the renderer so that a test capturing streams captures this
|
|
13
|
+
* too: the invariant it could break is rule 3, and an output channel the machine-output test
|
|
14
|
+
* cannot see is exactly how that gets broken quietly. An empty string clears the line.
|
|
15
|
+
*/
|
|
16
|
+
progress?: (text: string) => void;
|
|
17
|
+
}
|
|
18
|
+
export declare const processStreams: Streams;
|
|
19
|
+
/** Collects both halves separately, so a test can assert nothing leaked across. */
|
|
20
|
+
export declare const captureStreams: () => Streams & {
|
|
21
|
+
stdout: string[];
|
|
22
|
+
stderr: string[];
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=streams.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"streams.d.ts","sourceRoot":"","sources":["../src/streams.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC5B,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAClC;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CAClC;AAED,eAAO,MAAM,cAAc,EAAE,OAY5B,CAAA;AAED,mFAAmF;AACnF,eAAO,MAAM,cAAc,QAAO,OAAO,GAAG;IAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAW/E,CAAA"}
|
package/dist/streams.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export const processStreams = {
|
|
2
|
+
data: (text) => {
|
|
3
|
+
process.stdout.write(`${text}\n`);
|
|
4
|
+
},
|
|
5
|
+
diagnostic: (text) => {
|
|
6
|
+
process.stderr.write(`${text}\n`);
|
|
7
|
+
},
|
|
8
|
+
progress: (text) => {
|
|
9
|
+
// Carriage return and no newline, so the next line overwrites this one. Padded to the width of
|
|
10
|
+
// what was there before, or the tail of a longer previous line is left on screen.
|
|
11
|
+
process.stderr.write(`\r${text.padEnd(72)}${text === "" ? "\r" : ""}`);
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
/** Collects both halves separately, so a test can assert nothing leaked across. */
|
|
15
|
+
export const captureStreams = () => {
|
|
16
|
+
const stdout = [];
|
|
17
|
+
const stderr = [];
|
|
18
|
+
return {
|
|
19
|
+
stdout,
|
|
20
|
+
stderr,
|
|
21
|
+
data: (text) => stdout.push(text),
|
|
22
|
+
diagnostic: (text) => stderr.push(text),
|
|
23
|
+
progress: (text) => stderr.push(text),
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=streams.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"streams.js","sourceRoot":"","sources":["../src/streams.ts"],"names":[],"mappings":"AAkBA,MAAM,CAAC,MAAM,cAAc,GAAY;IACrC,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE;QACb,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,IAAI,CAAC,CAAA;IACnC,CAAC;IACD,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE;QACnB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,IAAI,CAAC,CAAA;IACnC,CAAC;IACD,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE;QACjB,+FAA+F;QAC/F,kFAAkF;QAClF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;IACxE,CAAC;CACF,CAAA;AAED,mFAAmF;AACnF,MAAM,CAAC,MAAM,cAAc,GAAG,GAAqD,EAAE;IACnF,MAAM,MAAM,GAAa,EAAE,CAAA;IAC3B,MAAM,MAAM,GAAa,EAAE,CAAA;IAE3B,OAAO;QACL,MAAM;QACN,MAAM;QACN,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;QACjC,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;QACvC,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;KACtC,CAAA;AACH,CAAC,CAAA"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The seams a test needs, so that "no test touched the real machine" is a property of the code
|
|
3
|
+
* rather than a hope about every test remembering to opt out.
|
|
4
|
+
*/
|
|
5
|
+
export { brokenKeyring, memoryKeyring } from "../keyring.js";
|
|
6
|
+
export { captureStreams } from "../streams.js";
|
|
7
|
+
import type { MonotonicClock, SleepLike, WallClock } from "../time.js";
|
|
8
|
+
export interface FakeClock {
|
|
9
|
+
clock: MonotonicClock;
|
|
10
|
+
now: WallClock;
|
|
11
|
+
sleep: SleepLike;
|
|
12
|
+
/** Every sleep asked for, in order, with the reason the caller gave. Nothing actually waits. */
|
|
13
|
+
waits: {
|
|
14
|
+
ms: number;
|
|
15
|
+
reason?: string;
|
|
16
|
+
}[];
|
|
17
|
+
advance(ms: number): void;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Time that never passes unless a test says so. A retry test that really sleeps 250 ms is a retry
|
|
21
|
+
* test nobody runs, so `sleep` resolves at once and records what it was asked for.
|
|
22
|
+
*/
|
|
23
|
+
export declare const fakeClock: (startMs?: number) => FakeClock;
|
|
24
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAE9C,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAEtE,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,cAAc,CAAA;IACrB,GAAG,EAAE,SAAS,CAAA;IACd,KAAK,EAAE,SAAS,CAAA;IAChB,gGAAgG;IAChG,KAAK,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IACxC,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B;AAED;;;GAGG;AACH,eAAO,MAAM,SAAS,GAAI,gBAAW,KAAG,SAiBvC,CAAA"}
|