sproutboat 0.52.1 → 0.53.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/CHANGELOG.md +20 -0
- package/README.md +6 -2
- package/SURFACE.md +5 -4
- package/package.json +7 -7
- package/src/dev.ts +24 -24
- package/src/env-file.ts +248 -0
- package/src/main.ts +87 -10
- package/src/run.ts +168 -0
- package/src/surface.ts +14 -4
- package/src/tail-log.ts +471 -0
- package/src/tail.ts +40 -3
package/src/tail.ts
CHANGED
|
@@ -22,6 +22,12 @@ export type TailArgs = {
|
|
|
22
22
|
samplingRate: string | undefined;
|
|
23
23
|
search: string | undefined;
|
|
24
24
|
outcome: string[];
|
|
25
|
+
/** #268: follow a standalone binary's SB_LOG=json records in this file ("-" for stdin) instead of the control plane. */
|
|
26
|
+
file: string | undefined;
|
|
27
|
+
/** With --file: how many of the last matching records to print before following. */
|
|
28
|
+
lines: number;
|
|
29
|
+
/** With --file: false prints the snapshot and exits. */
|
|
30
|
+
follow: boolean;
|
|
25
31
|
};
|
|
26
32
|
|
|
27
33
|
export type TailLog = { level: string; message: JsonValue[]; timestamp: number };
|
|
@@ -47,9 +53,19 @@ export type TailFrame =
|
|
|
47
53
|
| { type: "closed"; reason: string };
|
|
48
54
|
|
|
49
55
|
export const TAIL_USAGE =
|
|
50
|
-
"tail [project-dir] [--format pretty|json] [--status ok|error|<code>|5xx] [--method GET,POST] [--sampling-rate 0.1] [--search <text>] [--outcome <name>] | tail --recent | tail --sprout";
|
|
56
|
+
"tail [project-dir] [--format pretty|json] [--status ok|error|<code>|5xx] [--method GET,POST] [--sampling-rate 0.1] [--search <text>] [--outcome <name>] | tail --recent | tail --sprout | tail --file <log|-> [--lines <n>] [--no-follow] [--format pretty|json] [--status <s>] [--method <m>] [--search <text>]";
|
|
51
57
|
|
|
52
|
-
const VALUE_FLAGS = [
|
|
58
|
+
const VALUE_FLAGS = [
|
|
59
|
+
"--format",
|
|
60
|
+
"--status",
|
|
61
|
+
"--method",
|
|
62
|
+
"--sampling-rate",
|
|
63
|
+
"--search",
|
|
64
|
+
"--outcome",
|
|
65
|
+
"--file",
|
|
66
|
+
"--lines",
|
|
67
|
+
] as const;
|
|
68
|
+
const MAX_LINES = 100_000;
|
|
53
69
|
|
|
54
70
|
/** Parse `tail` arguments; `error` is a usage error message. */
|
|
55
71
|
export function parseTailArgs(args: string[]): { args: TailArgs } | { error: string } {
|
|
@@ -63,7 +79,11 @@ export function parseTailArgs(args: string[]): { args: TailArgs } | { error: str
|
|
|
63
79
|
samplingRate: undefined,
|
|
64
80
|
search: undefined,
|
|
65
81
|
outcome: [],
|
|
82
|
+
file: undefined,
|
|
83
|
+
lines: 10,
|
|
84
|
+
follow: true,
|
|
66
85
|
};
|
|
86
|
+
let linesGiven = false;
|
|
67
87
|
const split = (value: string) =>
|
|
68
88
|
value
|
|
69
89
|
.split(",")
|
|
@@ -73,6 +93,7 @@ export function parseTailArgs(args: string[]): { args: TailArgs } | { error: str
|
|
|
73
93
|
const arg = args[index];
|
|
74
94
|
if (arg === "--recent") parsed.recent = true;
|
|
75
95
|
else if (arg === "--sprout") parsed.sprout = true;
|
|
96
|
+
else if (arg === "--no-follow") parsed.follow = false;
|
|
76
97
|
else if (arg.startsWith("--")) {
|
|
77
98
|
const eq = arg.indexOf("=");
|
|
78
99
|
const flag = eq < 0 ? arg : arg.slice(0, eq);
|
|
@@ -87,12 +108,28 @@ export function parseTailArgs(args: string[]): { args: TailArgs } | { error: str
|
|
|
87
108
|
else if (flag === "--method") parsed.method.push(...split(value).map((method) => method.toUpperCase()));
|
|
88
109
|
else if (flag === "--outcome") parsed.outcome.push(...split(value));
|
|
89
110
|
else if (flag === "--sampling-rate") parsed.samplingRate = value;
|
|
90
|
-
else parsed.
|
|
111
|
+
else if (flag === "--file") parsed.file = value;
|
|
112
|
+
else if (flag === "--lines") {
|
|
113
|
+
const lines = Number(value);
|
|
114
|
+
if (value.trim() === "" || !Number.isInteger(lines) || lines < 0 || lines > MAX_LINES)
|
|
115
|
+
return { error: `tail: --lines must be a whole number from 0 to ${MAX_LINES} (got "${value}")` };
|
|
116
|
+
parsed.lines = lines;
|
|
117
|
+
linesGiven = true;
|
|
118
|
+
} else parsed.search = value;
|
|
91
119
|
} else if (parsed.dir === undefined) parsed.dir = arg;
|
|
92
120
|
else return { error: `tail: unexpected argument "${arg}"` };
|
|
93
121
|
}
|
|
94
122
|
const filtered =
|
|
95
123
|
parsed.status.length || parsed.method.length || parsed.outcome.length || parsed.samplingRate || parsed.search;
|
|
124
|
+
if (parsed.file === undefined) {
|
|
125
|
+
if (linesGiven) return { error: "tail: --lines needs --file" };
|
|
126
|
+
if (!parsed.follow) return { error: "tail: --no-follow needs --file" };
|
|
127
|
+
} else {
|
|
128
|
+
if (parsed.recent || parsed.sprout) return { error: "tail: --file is an alternative to --recent and --sprout" };
|
|
129
|
+
if (parsed.dir !== undefined) return { error: "tail: --file reads a log, not a project" };
|
|
130
|
+
if (parsed.samplingRate !== undefined || parsed.outcome.length)
|
|
131
|
+
return { error: "tail: --sampling-rate and --outcome are not available with --file" };
|
|
132
|
+
}
|
|
96
133
|
if (parsed.recent && parsed.sprout) return { error: "tail: --recent and --sprout are alternatives" };
|
|
97
134
|
if ((parsed.recent || parsed.sprout) && filtered)
|
|
98
135
|
return { error: "tail: filters apply to the live stream, not --recent or --sprout" };
|