dripfeed 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 +355 -0
- package/dist/cli.cjs +270 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +273 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/index.cjs +21 -0
- package/dist/index.d.cts +187 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +187 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +2 -0
- package/dist/runner-ByEGj869.mjs +647 -0
- package/dist/runner-ByEGj869.mjs.map +1 -0
- package/dist/runner-Dc1JRBps.cjs +758 -0
- package/package.json +98 -0
package/dist/cli.mjs
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { _ as createJsonReporter, g as createMarkdownReporter, n as computeStats, s as loadDripfeedConfig, t as createSoakTest, u as createSqliteStorage, v as createConsoleReporter } from "./runner-ByEGj869.mjs";
|
|
3
|
+
import { defineCommand, runMain } from "citty";
|
|
4
|
+
//#region src/cli.ts
|
|
5
|
+
const VALID_REPORT_FORMATS = [
|
|
6
|
+
"console",
|
|
7
|
+
"json",
|
|
8
|
+
"markdown"
|
|
9
|
+
];
|
|
10
|
+
const VALID_EXPORT_FORMATS = ["csv", "json"];
|
|
11
|
+
const validateFormat = (format, valid, command) => {
|
|
12
|
+
if (!valid.includes(format)) {
|
|
13
|
+
process.stderr.write(`Unsupported format "${format}" for ${command}. Use: ${valid.join(", ")}\n`);
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
runMain(defineCommand({
|
|
18
|
+
meta: {
|
|
19
|
+
name: "dripfeed",
|
|
20
|
+
version: "0.1.0",
|
|
21
|
+
description: "SQLite-native API soak testing. Drip, not firehose."
|
|
22
|
+
},
|
|
23
|
+
subCommands: {
|
|
24
|
+
run: defineCommand({
|
|
25
|
+
meta: {
|
|
26
|
+
name: "run",
|
|
27
|
+
description: "Start a soak test"
|
|
28
|
+
},
|
|
29
|
+
args: {
|
|
30
|
+
duration: {
|
|
31
|
+
type: "string",
|
|
32
|
+
alias: "d",
|
|
33
|
+
description: "Test duration (e.g. \"30s\", \"5m\", \"2h\")"
|
|
34
|
+
},
|
|
35
|
+
interval: {
|
|
36
|
+
type: "string",
|
|
37
|
+
alias: "i",
|
|
38
|
+
description: "Request interval (e.g. \"3s\", \"500ms\")"
|
|
39
|
+
},
|
|
40
|
+
db: {
|
|
41
|
+
type: "string",
|
|
42
|
+
description: "SQLite database path"
|
|
43
|
+
},
|
|
44
|
+
report: {
|
|
45
|
+
type: "string",
|
|
46
|
+
alias: "r",
|
|
47
|
+
description: "Report format: console, json, markdown",
|
|
48
|
+
default: "console"
|
|
49
|
+
},
|
|
50
|
+
output: {
|
|
51
|
+
type: "string",
|
|
52
|
+
alias: "o",
|
|
53
|
+
description: "Report output file path"
|
|
54
|
+
},
|
|
55
|
+
quiet: {
|
|
56
|
+
type: "boolean",
|
|
57
|
+
alias: "q",
|
|
58
|
+
description: "Suppress live output",
|
|
59
|
+
default: false
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
run: async ({ args }) => {
|
|
63
|
+
const reportFormat = args.report ?? "console";
|
|
64
|
+
validateFormat(reportFormat, VALID_REPORT_FORMATS, "report");
|
|
65
|
+
const overrides = {};
|
|
66
|
+
if (args.duration) overrides.duration = args.duration;
|
|
67
|
+
if (args.interval) overrides.interval = args.interval;
|
|
68
|
+
if (args.db) overrides.db = args.db;
|
|
69
|
+
let config;
|
|
70
|
+
try {
|
|
71
|
+
config = await loadDripfeedConfig(overrides);
|
|
72
|
+
} catch (err) {
|
|
73
|
+
if (err && typeof err === "object" && "issues" in err) {
|
|
74
|
+
process.stderr.write("Invalid config. Run `dripfeed init` to create a starter config.\n");
|
|
75
|
+
const { issues } = err;
|
|
76
|
+
process.stderr.write(`Details: ${JSON.stringify(issues, null, 2)}\n`);
|
|
77
|
+
} else process.stderr.write(`Config error: ${err instanceof Error ? err.message : err}\n`);
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
const reporters = [];
|
|
81
|
+
const shouldQuiet = args.quiet || reportFormat !== "console";
|
|
82
|
+
if (!shouldQuiet) reporters.push(createConsoleReporter());
|
|
83
|
+
if (reportFormat === "json") reporters.push(createJsonReporter(args.output));
|
|
84
|
+
else if (reportFormat === "markdown") reporters.push(createMarkdownReporter(args.output));
|
|
85
|
+
if (!shouldQuiet) {
|
|
86
|
+
const interval = config.interval ?? "3s";
|
|
87
|
+
const duration = args.duration ? ` for ${args.duration}` : "";
|
|
88
|
+
process.stdout.write(`\ndripfeed v0.1.0 — every ${interval}${duration} | Ctrl+C to stop\n\n`);
|
|
89
|
+
}
|
|
90
|
+
if ((await createSoakTest(config, reporters).run({ duration: args.duration })).thresholds?.some((t) => !t.passed)) process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
}),
|
|
93
|
+
init: defineCommand({
|
|
94
|
+
meta: {
|
|
95
|
+
name: "init",
|
|
96
|
+
description: "Generate a starter dripfeed config file"
|
|
97
|
+
},
|
|
98
|
+
args: { format: {
|
|
99
|
+
type: "string",
|
|
100
|
+
description: "Config format: ts, json",
|
|
101
|
+
default: "ts"
|
|
102
|
+
} },
|
|
103
|
+
run: async ({ args }) => {
|
|
104
|
+
const { writeFile, access } = await import("node:fs/promises");
|
|
105
|
+
const format = args.format ?? "ts";
|
|
106
|
+
if (format !== "ts" && format !== "json") {
|
|
107
|
+
process.stderr.write(`Unsupported format "${format}". Use: ts, json\n`);
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
const filename = format === "ts" ? "dripfeed.config.ts" : "dripfeed.config.json";
|
|
111
|
+
try {
|
|
112
|
+
await access(filename);
|
|
113
|
+
process.stderr.write(`${filename} already exists. Delete it first or use a different format.\n`);
|
|
114
|
+
process.exit(1);
|
|
115
|
+
} catch {}
|
|
116
|
+
if (format === "ts") await writeFile(filename, `import type { DripfeedConfig } from 'dripfeed';
|
|
117
|
+
|
|
118
|
+
const config: DripfeedConfig = {
|
|
119
|
+
\tinterval: '3s',
|
|
120
|
+
\ttimeout: '30s',
|
|
121
|
+
\tstorage: 'sqlite',
|
|
122
|
+
\trotation: 'weighted-random',
|
|
123
|
+
\tendpoints: [
|
|
124
|
+
\t\t{
|
|
125
|
+
\t\t\tname: 'health',
|
|
126
|
+
\t\t\turl: 'https://api.example.com/health',
|
|
127
|
+
\t\t},
|
|
128
|
+
\t\t{
|
|
129
|
+
\t\t\tname: 'users',
|
|
130
|
+
\t\t\turl: 'https://api.example.com/v1/users',
|
|
131
|
+
\t\t\tweight: 3,
|
|
132
|
+
\t\t},
|
|
133
|
+
\t],
|
|
134
|
+
\tthresholds: {
|
|
135
|
+
\t\terror_rate: '< 1%',
|
|
136
|
+
\t\tp95: '< 500ms',
|
|
137
|
+
\t},
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
export default config;
|
|
141
|
+
`);
|
|
142
|
+
else await writeFile(filename, JSON.stringify({
|
|
143
|
+
interval: "3s",
|
|
144
|
+
timeout: "30s",
|
|
145
|
+
storage: "sqlite",
|
|
146
|
+
rotation: "weighted-random",
|
|
147
|
+
endpoints: [{
|
|
148
|
+
name: "health",
|
|
149
|
+
url: "https://api.example.com/health"
|
|
150
|
+
}, {
|
|
151
|
+
name: "users",
|
|
152
|
+
url: "https://api.example.com/v1/users",
|
|
153
|
+
weight: 3
|
|
154
|
+
}],
|
|
155
|
+
thresholds: {
|
|
156
|
+
error_rate: "< 1%",
|
|
157
|
+
p95: "< 500ms"
|
|
158
|
+
}
|
|
159
|
+
}, null, 2));
|
|
160
|
+
process.stdout.write(`Created ${filename}\n`);
|
|
161
|
+
}
|
|
162
|
+
}),
|
|
163
|
+
report: defineCommand({
|
|
164
|
+
meta: {
|
|
165
|
+
name: "report",
|
|
166
|
+
description: "Generate a report from an existing SQLite database"
|
|
167
|
+
},
|
|
168
|
+
args: {
|
|
169
|
+
db: {
|
|
170
|
+
type: "string",
|
|
171
|
+
description: "SQLite database path",
|
|
172
|
+
default: "dripfeed-results.db"
|
|
173
|
+
},
|
|
174
|
+
format: {
|
|
175
|
+
type: "string",
|
|
176
|
+
description: "Report format: console, json, markdown",
|
|
177
|
+
default: "console"
|
|
178
|
+
},
|
|
179
|
+
output: {
|
|
180
|
+
type: "string",
|
|
181
|
+
alias: "o",
|
|
182
|
+
description: "Output file path"
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
run: async ({ args }) => {
|
|
186
|
+
const format = args.format ?? "console";
|
|
187
|
+
validateFormat(format, VALID_REPORT_FORMATS, "report");
|
|
188
|
+
const storage = createSqliteStorage(args.db ?? "dripfeed-results.db");
|
|
189
|
+
await storage.init();
|
|
190
|
+
const results = await storage.getAll();
|
|
191
|
+
await storage.close();
|
|
192
|
+
if (results.length === 0) {
|
|
193
|
+
process.stdout.write("No results found in database.\n");
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
const stats = computeStats(results, new Date(results[0]?.timestamp ?? Date.now()), void 0, new Date(results[results.length - 1]?.timestamp ?? Date.now()));
|
|
197
|
+
if (format === "console") createConsoleReporter().onComplete(stats);
|
|
198
|
+
else if (format === "json") createJsonReporter(args.output).onComplete(stats);
|
|
199
|
+
else if (format === "markdown") createMarkdownReporter(args.output).onComplete(stats);
|
|
200
|
+
if (args.output) process.stdout.write(`Report written to ${args.output}\n`);
|
|
201
|
+
}
|
|
202
|
+
}),
|
|
203
|
+
export: defineCommand({
|
|
204
|
+
meta: {
|
|
205
|
+
name: "export",
|
|
206
|
+
description: "Export results from SQLite to CSV or JSON"
|
|
207
|
+
},
|
|
208
|
+
args: {
|
|
209
|
+
db: {
|
|
210
|
+
type: "string",
|
|
211
|
+
description: "SQLite database path",
|
|
212
|
+
default: "dripfeed-results.db"
|
|
213
|
+
},
|
|
214
|
+
format: {
|
|
215
|
+
type: "string",
|
|
216
|
+
description: "Export format: csv, json",
|
|
217
|
+
default: "csv"
|
|
218
|
+
},
|
|
219
|
+
output: {
|
|
220
|
+
type: "string",
|
|
221
|
+
alias: "o",
|
|
222
|
+
description: "Output file path"
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
run: async ({ args }) => {
|
|
226
|
+
const format = args.format ?? "csv";
|
|
227
|
+
validateFormat(format, VALID_EXPORT_FORMATS, "export");
|
|
228
|
+
const { writeFile } = await import("node:fs/promises");
|
|
229
|
+
const storage = createSqliteStorage(args.db ?? "dripfeed-results.db");
|
|
230
|
+
await storage.init();
|
|
231
|
+
const results = await storage.getAll();
|
|
232
|
+
await storage.close();
|
|
233
|
+
let output;
|
|
234
|
+
if (format === "json") output = JSON.stringify(results, null, 2);
|
|
235
|
+
else {
|
|
236
|
+
const headers = [
|
|
237
|
+
"timestamp",
|
|
238
|
+
"endpoint",
|
|
239
|
+
"method",
|
|
240
|
+
"url",
|
|
241
|
+
"status",
|
|
242
|
+
"duration_ms",
|
|
243
|
+
"error",
|
|
244
|
+
"response_body"
|
|
245
|
+
];
|
|
246
|
+
const escapeCsv = (s) => {
|
|
247
|
+
if (s === null) return "";
|
|
248
|
+
return s.includes(",") || s.includes("\"") || s.includes("\n") ? `"${s.replace(/"/g, "\"\"")}"` : s;
|
|
249
|
+
};
|
|
250
|
+
const rows = results.map((r) => [
|
|
251
|
+
r.timestamp,
|
|
252
|
+
r.endpoint,
|
|
253
|
+
r.method,
|
|
254
|
+
r.url,
|
|
255
|
+
r.status ?? "",
|
|
256
|
+
r.duration_ms,
|
|
257
|
+
escapeCsv(r.error),
|
|
258
|
+
escapeCsv(r.response_body)
|
|
259
|
+
].join(","));
|
|
260
|
+
output = [headers.join(","), ...rows].join("\n");
|
|
261
|
+
}
|
|
262
|
+
if (args.output) {
|
|
263
|
+
await writeFile(args.output, output);
|
|
264
|
+
process.stdout.write(`Exported ${results.length} results to ${args.output}\n`);
|
|
265
|
+
} else process.stdout.write(`${output}\n`);
|
|
266
|
+
}
|
|
267
|
+
})
|
|
268
|
+
}
|
|
269
|
+
}));
|
|
270
|
+
//#endregion
|
|
271
|
+
export {};
|
|
272
|
+
|
|
273
|
+
//# sourceMappingURL=cli.mjs.map
|
package/dist/cli.mjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.mjs","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { defineCommand, runMain } from 'citty';\nimport { createConsoleReporter } from './adapters/reporters/console.js';\nimport type { Reporter } from './adapters/reporters/interface.js';\nimport { createJsonReporter } from './adapters/reporters/json.js';\nimport { createMarkdownReporter } from './adapters/reporters/markdown.js';\nimport { createSqliteStorage } from './adapters/storage/sqlite.js';\nimport { loadDripfeedConfig, type ParsedConfig } from './core/config.js';\nimport { createSoakTest } from './core/runner.js';\nimport { computeStats } from './utils/stats.js';\n\nconst VALID_REPORT_FORMATS = ['console', 'json', 'markdown'] as const;\nconst VALID_EXPORT_FORMATS = ['csv', 'json'] as const;\n\nconst validateFormat = (format: string, valid: readonly string[], command: string) => {\n\tif (!valid.includes(format)) {\n\t\tprocess.stderr.write(\n\t\t\t`Unsupported format \"${format}\" for ${command}. Use: ${valid.join(', ')}\\n`,\n\t\t);\n\t\tprocess.exit(1);\n\t}\n};\n\nconst run = defineCommand({\n\tmeta: { name: 'run', description: 'Start a soak test' },\n\targs: {\n\t\tduration: { type: 'string', alias: 'd', description: 'Test duration (e.g. \"30s\", \"5m\", \"2h\")' },\n\t\tinterval: { type: 'string', alias: 'i', description: 'Request interval (e.g. \"3s\", \"500ms\")' },\n\t\tdb: { type: 'string', description: 'SQLite database path' },\n\t\treport: {\n\t\t\ttype: 'string',\n\t\t\talias: 'r',\n\t\t\tdescription: 'Report format: console, json, markdown',\n\t\t\tdefault: 'console',\n\t\t},\n\t\toutput: { type: 'string', alias: 'o', description: 'Report output file path' },\n\t\tquiet: { type: 'boolean', alias: 'q', description: 'Suppress live output', default: false },\n\t},\n\trun: async ({ args }) => {\n\t\tconst reportFormat = args.report ?? 'console';\n\t\tvalidateFormat(reportFormat, VALID_REPORT_FORMATS, 'report');\n\n\t\tconst overrides: Record<string, unknown> = {};\n\t\tif (args.duration) overrides.duration = args.duration;\n\t\tif (args.interval) overrides.interval = args.interval;\n\t\tif (args.db) overrides.db = args.db;\n\n\t\tlet config: ParsedConfig | undefined;\n\t\ttry {\n\t\t\tconfig = await loadDripfeedConfig(overrides);\n\t\t} catch (err) {\n\t\t\tif (err && typeof err === 'object' && 'issues' in err) {\n\t\t\t\tprocess.stderr.write('Invalid config. Run `dripfeed init` to create a starter config.\\n');\n\t\t\t\tconst { issues } = err as { issues: unknown };\n\t\t\t\tprocess.stderr.write(`Details: ${JSON.stringify(issues, null, 2)}\\n`);\n\t\t\t} else {\n\t\t\t\tprocess.stderr.write(`Config error: ${err instanceof Error ? err.message : err}\\n`);\n\t\t\t}\n\t\t\tprocess.exit(1);\n\t\t}\n\n\t\tconst reporters: Reporter[] = [];\n\t\t// Auto-quiet when using json/markdown report to avoid mixing outputs\n\t\tconst shouldQuiet = args.quiet || reportFormat !== 'console';\n\t\tif (!shouldQuiet) {\n\t\t\treporters.push(createConsoleReporter());\n\t\t}\n\n\t\tif (reportFormat === 'json') {\n\t\t\treporters.push(createJsonReporter(args.output));\n\t\t} else if (reportFormat === 'markdown') {\n\t\t\treporters.push(createMarkdownReporter(args.output));\n\t\t}\n\n\t\t// Startup banner (only in console mode)\n\t\tif (!shouldQuiet) {\n\t\t\tconst interval = config.interval ?? '3s';\n\t\t\tconst duration = args.duration ? ` for ${args.duration}` : '';\n\t\t\tprocess.stdout.write(`\\ndripfeed v0.1.0 — every ${interval}${duration} | Ctrl+C to stop\\n\\n`);\n\t\t}\n\n\t\tconst test = createSoakTest(config, reporters);\n\t\tconst stats = await test.run({ duration: args.duration });\n\n\t\tif (stats.thresholds?.some((t) => !t.passed)) {\n\t\t\tprocess.exit(1);\n\t\t}\n\t},\n});\n\nconst init = defineCommand({\n\tmeta: { name: 'init', description: 'Generate a starter dripfeed config file' },\n\targs: {\n\t\tformat: {\n\t\t\ttype: 'string',\n\t\t\tdescription: 'Config format: ts, json',\n\t\t\tdefault: 'ts',\n\t\t},\n\t},\n\trun: async ({ args }) => {\n\t\tconst { writeFile, access } = await import('node:fs/promises');\n\t\tconst format = args.format ?? 'ts';\n\n\t\tif (format !== 'ts' && format !== 'json') {\n\t\t\tprocess.stderr.write(`Unsupported format \"${format}\". Use: ts, json\\n`);\n\t\t\tprocess.exit(1);\n\t\t}\n\n\t\tconst filename = format === 'ts' ? 'dripfeed.config.ts' : 'dripfeed.config.json';\n\n\t\t// Check if file exists\n\t\ttry {\n\t\t\tawait access(filename);\n\t\t\tprocess.stderr.write(\n\t\t\t\t`${filename} already exists. Delete it first or use a different format.\\n`,\n\t\t\t);\n\t\t\tprocess.exit(1);\n\t\t} catch {\n\t\t\t// File doesn't exist, proceed\n\t\t}\n\n\t\tif (format === 'ts') {\n\t\t\tconst content = `import type { DripfeedConfig } from 'dripfeed';\n\nconst config: DripfeedConfig = {\n\\tinterval: '3s',\n\\ttimeout: '30s',\n\\tstorage: 'sqlite',\n\\trotation: 'weighted-random',\n\\tendpoints: [\n\\t\\t{\n\\t\\t\\tname: 'health',\n\\t\\t\\turl: 'https://api.example.com/health',\n\\t\\t},\n\\t\\t{\n\\t\\t\\tname: 'users',\n\\t\\t\\turl: 'https://api.example.com/v1/users',\n\\t\\t\\tweight: 3,\n\\t\\t},\n\\t],\n\\tthresholds: {\n\\t\\terror_rate: '< 1%',\n\\t\\tp95: '< 500ms',\n\\t},\n};\n\nexport default config;\n`;\n\t\t\tawait writeFile(filename, content);\n\t\t} else {\n\t\t\tconst content = {\n\t\t\t\tinterval: '3s',\n\t\t\t\ttimeout: '30s',\n\t\t\t\tstorage: 'sqlite',\n\t\t\t\trotation: 'weighted-random',\n\t\t\t\tendpoints: [\n\t\t\t\t\t{ name: 'health', url: 'https://api.example.com/health' },\n\t\t\t\t\t{ name: 'users', url: 'https://api.example.com/v1/users', weight: 3 },\n\t\t\t\t],\n\t\t\t\tthresholds: { error_rate: '< 1%', p95: '< 500ms' },\n\t\t\t};\n\t\t\tawait writeFile(filename, JSON.stringify(content, null, 2));\n\t\t}\n\n\t\tprocess.stdout.write(`Created ${filename}\\n`);\n\t},\n});\n\nconst report = defineCommand({\n\tmeta: { name: 'report', description: 'Generate a report from an existing SQLite database' },\n\targs: {\n\t\tdb: {\n\t\t\ttype: 'string',\n\t\t\tdescription: 'SQLite database path',\n\t\t\tdefault: 'dripfeed-results.db',\n\t\t},\n\t\tformat: {\n\t\t\ttype: 'string',\n\t\t\tdescription: 'Report format: console, json, markdown',\n\t\t\tdefault: 'console',\n\t\t},\n\t\toutput: { type: 'string', alias: 'o', description: 'Output file path' },\n\t},\n\trun: async ({ args }) => {\n\t\tconst format = args.format ?? 'console';\n\t\tvalidateFormat(format, VALID_REPORT_FORMATS, 'report');\n\n\t\tconst dbPath = args.db ?? 'dripfeed-results.db';\n\t\tconst storage = createSqliteStorage(dbPath);\n\t\tawait storage.init();\n\t\tconst results = await storage.getAll();\n\t\tawait storage.close();\n\n\t\tif (results.length === 0) {\n\t\t\tprocess.stdout.write('No results found in database.\\n');\n\t\t\treturn;\n\t\t}\n\n\t\t// Use first result timestamp as start, last as end for accurate duration\n\t\tconst firstTimestamp = new Date(results[0]?.timestamp ?? Date.now());\n\t\tconst lastTimestamp = new Date(results[results.length - 1]?.timestamp ?? Date.now());\n\t\tconst stats = computeStats(results, firstTimestamp, undefined, lastTimestamp);\n\n\t\tif (format === 'console') {\n\t\t\tcreateConsoleReporter().onComplete(stats);\n\t\t} else if (format === 'json') {\n\t\t\tcreateJsonReporter(args.output).onComplete(stats);\n\t\t} else if (format === 'markdown') {\n\t\t\tcreateMarkdownReporter(args.output).onComplete(stats);\n\t\t}\n\n\t\tif (args.output) {\n\t\t\tprocess.stdout.write(`Report written to ${args.output}\\n`);\n\t\t}\n\t},\n});\n\nconst exportCmd = defineCommand({\n\tmeta: { name: 'export', description: 'Export results from SQLite to CSV or JSON' },\n\targs: {\n\t\tdb: {\n\t\t\ttype: 'string',\n\t\t\tdescription: 'SQLite database path',\n\t\t\tdefault: 'dripfeed-results.db',\n\t\t},\n\t\tformat: { type: 'string', description: 'Export format: csv, json', default: 'csv' },\n\t\toutput: { type: 'string', alias: 'o', description: 'Output file path' },\n\t},\n\trun: async ({ args }) => {\n\t\tconst format = args.format ?? 'csv';\n\t\tvalidateFormat(format, VALID_EXPORT_FORMATS, 'export');\n\n\t\tconst { writeFile } = await import('node:fs/promises');\n\t\tconst dbPath = args.db ?? 'dripfeed-results.db';\n\t\tconst storage = createSqliteStorage(dbPath);\n\t\tawait storage.init();\n\t\tconst results = await storage.getAll();\n\t\tawait storage.close();\n\n\t\tlet output: string;\n\n\t\tif (format === 'json') {\n\t\t\toutput = JSON.stringify(results, null, 2);\n\t\t} else {\n\t\t\tconst headers = [\n\t\t\t\t'timestamp',\n\t\t\t\t'endpoint',\n\t\t\t\t'method',\n\t\t\t\t'url',\n\t\t\t\t'status',\n\t\t\t\t'duration_ms',\n\t\t\t\t'error',\n\t\t\t\t'response_body',\n\t\t\t];\n\t\t\tconst escapeCsv = (s: string | null) => {\n\t\t\t\tif (s === null) return '';\n\t\t\t\treturn s.includes(',') || s.includes('\"') || s.includes('\\n')\n\t\t\t\t\t? `\"${s.replace(/\"/g, '\"\"')}\"`\n\t\t\t\t\t: s;\n\t\t\t};\n\t\t\tconst rows = results.map((r) =>\n\t\t\t\t[\n\t\t\t\t\tr.timestamp,\n\t\t\t\t\tr.endpoint,\n\t\t\t\t\tr.method,\n\t\t\t\t\tr.url,\n\t\t\t\t\tr.status ?? '',\n\t\t\t\t\tr.duration_ms,\n\t\t\t\t\tescapeCsv(r.error),\n\t\t\t\t\tescapeCsv(r.response_body),\n\t\t\t\t].join(','),\n\t\t\t);\n\t\t\toutput = [headers.join(','), ...rows].join('\\n');\n\t\t}\n\n\t\tif (args.output) {\n\t\t\tawait writeFile(args.output, output);\n\t\t\tprocess.stdout.write(`Exported ${results.length} results to ${args.output}\\n`);\n\t\t} else {\n\t\t\tprocess.stdout.write(`${output}\\n`);\n\t\t}\n\t},\n});\n\nconst main = defineCommand({\n\tmeta: {\n\t\tname: 'dripfeed',\n\t\tversion: '0.1.0',\n\t\tdescription: 'SQLite-native API soak testing. Drip, not firehose.',\n\t},\n\tsubCommands: { run, init, report, export: exportCmd },\n});\n\nrunMain(main);\n"],"mappings":"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"}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_runner = require("./runner-Dc1JRBps.cjs");
|
|
3
|
+
exports.computeStats = require_runner.computeStats;
|
|
4
|
+
exports.configSchema = require_runner.configSchema;
|
|
5
|
+
exports.createConsoleReporter = require_runner.createConsoleReporter;
|
|
6
|
+
exports.createJsonReporter = require_runner.createJsonReporter;
|
|
7
|
+
exports.createJsonStorage = require_runner.createJsonStorage;
|
|
8
|
+
exports.createMarkdownReporter = require_runner.createMarkdownReporter;
|
|
9
|
+
exports.createMemoryStorage = require_runner.createMemoryStorage;
|
|
10
|
+
exports.createSoakTest = require_runner.createSoakTest;
|
|
11
|
+
exports.createSqliteStorage = require_runner.createSqliteStorage;
|
|
12
|
+
exports.createStorage = require_runner.createStorage;
|
|
13
|
+
exports.isBun = require_runner.isBun;
|
|
14
|
+
exports.isDeno = require_runner.isDeno;
|
|
15
|
+
exports.isNode = require_runner.isNode;
|
|
16
|
+
exports.isSuccess = require_runner.isSuccess;
|
|
17
|
+
exports.loadDripfeedConfig = require_runner.loadDripfeedConfig;
|
|
18
|
+
exports.parseConfig = require_runner.parseConfig;
|
|
19
|
+
exports.parseDuration = require_runner.parseDuration;
|
|
20
|
+
exports.percentile = require_runner.percentile;
|
|
21
|
+
exports.timedFetch = require_runner.timedFetch;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
//#region src/core/types.d.ts
|
|
4
|
+
interface RequestResult {
|
|
5
|
+
timestamp: string;
|
|
6
|
+
endpoint: string;
|
|
7
|
+
method: string;
|
|
8
|
+
url: string;
|
|
9
|
+
status: number | null;
|
|
10
|
+
duration_ms: number;
|
|
11
|
+
response_body: string | null;
|
|
12
|
+
error: string | null;
|
|
13
|
+
}
|
|
14
|
+
interface EndpointConfig {
|
|
15
|
+
name: string;
|
|
16
|
+
url: string;
|
|
17
|
+
method?: string;
|
|
18
|
+
headers?: Record<string, string>;
|
|
19
|
+
body?: unknown;
|
|
20
|
+
timeout?: string;
|
|
21
|
+
weight?: number;
|
|
22
|
+
}
|
|
23
|
+
interface ThresholdConfig {
|
|
24
|
+
error_rate?: string;
|
|
25
|
+
p50?: string;
|
|
26
|
+
p95?: string;
|
|
27
|
+
p99?: string;
|
|
28
|
+
max?: string;
|
|
29
|
+
}
|
|
30
|
+
interface DripfeedConfig {
|
|
31
|
+
interval?: string;
|
|
32
|
+
duration?: string;
|
|
33
|
+
timeout?: string;
|
|
34
|
+
storage?: 'sqlite' | 'json' | 'memory';
|
|
35
|
+
db?: string;
|
|
36
|
+
rotation?: 'weighted-random' | 'round-robin' | 'sequential';
|
|
37
|
+
headers?: Record<string, string>;
|
|
38
|
+
endpoints: EndpointConfig[];
|
|
39
|
+
thresholds?: ThresholdConfig;
|
|
40
|
+
}
|
|
41
|
+
/** Check if an HTTP status code represents a successful response */
|
|
42
|
+
declare const isSuccess: (status: number | null) => boolean;
|
|
43
|
+
interface EndpointStats {
|
|
44
|
+
name: string;
|
|
45
|
+
requests: number;
|
|
46
|
+
avg_ms: number;
|
|
47
|
+
p95_ms: number;
|
|
48
|
+
error_count: number;
|
|
49
|
+
}
|
|
50
|
+
interface ErrorGroup {
|
|
51
|
+
endpoint: string;
|
|
52
|
+
status: number | null;
|
|
53
|
+
count: number;
|
|
54
|
+
sample_body: string | null;
|
|
55
|
+
}
|
|
56
|
+
interface ThresholdResult {
|
|
57
|
+
name: string;
|
|
58
|
+
target: string;
|
|
59
|
+
actual: string;
|
|
60
|
+
passed: boolean;
|
|
61
|
+
}
|
|
62
|
+
interface LatencyStats {
|
|
63
|
+
min: number;
|
|
64
|
+
avg: number;
|
|
65
|
+
p50: number;
|
|
66
|
+
p95: number;
|
|
67
|
+
p99: number;
|
|
68
|
+
max: number;
|
|
69
|
+
}
|
|
70
|
+
interface SoakStats {
|
|
71
|
+
duration_s: number;
|
|
72
|
+
total_requests: number;
|
|
73
|
+
success_count: number;
|
|
74
|
+
failure_count: number;
|
|
75
|
+
uptime_pct: number;
|
|
76
|
+
latency: LatencyStats;
|
|
77
|
+
status_codes: Record<number, number>;
|
|
78
|
+
endpoints: EndpointStats[];
|
|
79
|
+
errors: ErrorGroup[];
|
|
80
|
+
thresholds?: ThresholdResult[];
|
|
81
|
+
}
|
|
82
|
+
interface SoakTestHandle {
|
|
83
|
+
start: () => Promise<void>;
|
|
84
|
+
stop: () => Promise<SoakStats>;
|
|
85
|
+
run: (opts?: {
|
|
86
|
+
duration?: string;
|
|
87
|
+
}) => Promise<SoakStats>;
|
|
88
|
+
}
|
|
89
|
+
//#endregion
|
|
90
|
+
//#region src/adapters/reporters/interface.d.ts
|
|
91
|
+
interface Reporter {
|
|
92
|
+
onRequest(result: RequestResult, counts: {
|
|
93
|
+
ok: number;
|
|
94
|
+
fail: number;
|
|
95
|
+
}): void;
|
|
96
|
+
onComplete(stats: SoakStats): void;
|
|
97
|
+
}
|
|
98
|
+
//#endregion
|
|
99
|
+
//#region src/adapters/reporters/console.d.ts
|
|
100
|
+
declare const createConsoleReporter: () => Reporter;
|
|
101
|
+
//#endregion
|
|
102
|
+
//#region src/adapters/reporters/json.d.ts
|
|
103
|
+
declare const createJsonReporter: (outputPath?: string) => Reporter;
|
|
104
|
+
//#endregion
|
|
105
|
+
//#region src/adapters/reporters/markdown.d.ts
|
|
106
|
+
declare const createMarkdownReporter: (outputPath?: string) => Reporter;
|
|
107
|
+
//#endregion
|
|
108
|
+
//#region src/adapters/storage/interface.d.ts
|
|
109
|
+
interface StorageAdapter {
|
|
110
|
+
init(): Promise<void>;
|
|
111
|
+
record(result: RequestResult): Promise<void>;
|
|
112
|
+
getAll(): Promise<RequestResult[]>;
|
|
113
|
+
close(): Promise<void>;
|
|
114
|
+
}
|
|
115
|
+
//#endregion
|
|
116
|
+
//#region src/adapters/storage/json.d.ts
|
|
117
|
+
declare const createJsonStorage: (filePath: string) => StorageAdapter;
|
|
118
|
+
//#endregion
|
|
119
|
+
//#region src/adapters/storage/memory.d.ts
|
|
120
|
+
declare const createMemoryStorage: () => StorageAdapter;
|
|
121
|
+
//#endregion
|
|
122
|
+
//#region src/adapters/storage/sqlite.d.ts
|
|
123
|
+
declare const createSqliteStorage: (dbPath: string) => StorageAdapter;
|
|
124
|
+
//#endregion
|
|
125
|
+
//#region src/adapters/storage/index.d.ts
|
|
126
|
+
declare const createStorage: (config: DripfeedConfig) => StorageAdapter;
|
|
127
|
+
//#endregion
|
|
128
|
+
//#region src/core/config.d.ts
|
|
129
|
+
declare const configSchema: z.ZodObject<{
|
|
130
|
+
interval: z.ZodDefault<z.ZodString>;
|
|
131
|
+
duration: z.ZodOptional<z.ZodString>;
|
|
132
|
+
timeout: z.ZodDefault<z.ZodString>;
|
|
133
|
+
storage: z.ZodDefault<z.ZodEnum<{
|
|
134
|
+
sqlite: "sqlite";
|
|
135
|
+
json: "json";
|
|
136
|
+
memory: "memory";
|
|
137
|
+
}>>;
|
|
138
|
+
db: z.ZodOptional<z.ZodString>;
|
|
139
|
+
rotation: z.ZodDefault<z.ZodEnum<{
|
|
140
|
+
"weighted-random": "weighted-random";
|
|
141
|
+
"round-robin": "round-robin";
|
|
142
|
+
sequential: "sequential";
|
|
143
|
+
}>>;
|
|
144
|
+
headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
145
|
+
endpoints: z.ZodArray<z.ZodObject<{
|
|
146
|
+
name: z.ZodString;
|
|
147
|
+
url: z.ZodString;
|
|
148
|
+
method: z.ZodDefault<z.ZodString>;
|
|
149
|
+
headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
150
|
+
body: z.ZodOptional<z.ZodUnknown>;
|
|
151
|
+
timeout: z.ZodOptional<z.ZodString>;
|
|
152
|
+
weight: z.ZodDefault<z.ZodNumber>;
|
|
153
|
+
}, z.core.$strip>>;
|
|
154
|
+
thresholds: z.ZodOptional<z.ZodObject<{
|
|
155
|
+
error_rate: z.ZodOptional<z.ZodString>;
|
|
156
|
+
p50: z.ZodOptional<z.ZodString>;
|
|
157
|
+
p95: z.ZodOptional<z.ZodString>;
|
|
158
|
+
p99: z.ZodOptional<z.ZodString>;
|
|
159
|
+
max: z.ZodOptional<z.ZodString>;
|
|
160
|
+
}, z.core.$strip>>;
|
|
161
|
+
}, z.core.$strip>;
|
|
162
|
+
type ParsedConfig = z.infer<typeof configSchema>;
|
|
163
|
+
/** Parse and validate a raw config object into a fully-typed ParsedConfig with defaults applied.
|
|
164
|
+
* Use this when creating a soak test programmatically without a config file. */
|
|
165
|
+
declare const parseConfig: (raw: unknown) => ParsedConfig;
|
|
166
|
+
declare const loadDripfeedConfig: (overrides?: Partial<ParsedConfig>) => Promise<ParsedConfig>;
|
|
167
|
+
//#endregion
|
|
168
|
+
//#region src/core/runner.d.ts
|
|
169
|
+
declare const createSoakTest: (config: ParsedConfig, reporters?: Reporter[]) => SoakTestHandle;
|
|
170
|
+
//#endregion
|
|
171
|
+
//#region src/utils/duration.d.ts
|
|
172
|
+
declare const parseDuration: (input: string) => number;
|
|
173
|
+
//#endregion
|
|
174
|
+
//#region src/utils/http.d.ts
|
|
175
|
+
declare const timedFetch: (endpoint: EndpointConfig, globalHeaders?: Record<string, string>, timeout?: string) => Promise<RequestResult>;
|
|
176
|
+
//#endregion
|
|
177
|
+
//#region src/utils/runtime.d.ts
|
|
178
|
+
declare const isBun: boolean;
|
|
179
|
+
declare const isDeno: boolean;
|
|
180
|
+
declare const isNode: boolean;
|
|
181
|
+
//#endregion
|
|
182
|
+
//#region src/utils/stats.d.ts
|
|
183
|
+
declare const percentile: (sorted: number[], p: number) => number;
|
|
184
|
+
declare const computeStats: (results: RequestResult[], startTime: Date, thresholds?: ThresholdConfig, endTime?: Date) => SoakStats;
|
|
185
|
+
//#endregion
|
|
186
|
+
export { type DripfeedConfig, type EndpointConfig, type EndpointStats, type ErrorGroup, type LatencyStats, type ParsedConfig, type Reporter, type RequestResult, type SoakStats, type SoakTestHandle, type StorageAdapter, type ThresholdConfig, type ThresholdResult, computeStats, configSchema, createConsoleReporter, createJsonReporter, createJsonStorage, createMarkdownReporter, createMemoryStorage, createSoakTest, createSqliteStorage, createStorage, isBun, isDeno, isNode, isSuccess, loadDripfeedConfig, parseConfig, parseDuration, percentile, timedFetch };
|
|
187
|
+
//# sourceMappingURL=index.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/core/types.ts","../src/adapters/reporters/interface.ts","../src/adapters/reporters/console.ts","../src/adapters/reporters/json.ts","../src/adapters/reporters/markdown.ts","../src/adapters/storage/interface.ts","../src/adapters/storage/json.ts","../src/adapters/storage/memory.ts","../src/adapters/storage/sqlite.ts","../src/adapters/storage/index.ts","../src/core/config.ts","../src/core/runner.ts","../src/utils/duration.ts","../src/utils/http.ts","../src/utils/runtime.ts","../src/utils/stats.ts"],"mappings":"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"}
|