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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner-ByEGj869.mjs","names":[],"sources":["../src/core/types.ts","../src/adapters/reporters/console.ts","../src/adapters/reporters/json.ts","../src/adapters/reporters/markdown.ts","../src/adapters/storage/json.ts","../src/adapters/storage/memory.ts","../src/utils/runtime.ts","../src/adapters/storage/sqlite.ts","../src/adapters/storage/index.ts","../src/core/config.ts","../src/utils/duration.ts","../src/utils/http.ts","../src/utils/stats.ts","../src/core/runner.ts"],"sourcesContent":["export interface RequestResult {\n\ttimestamp: string;\n\tendpoint: string;\n\tmethod: string;\n\turl: string;\n\tstatus: number | null;\n\tduration_ms: number;\n\tresponse_body: string | null;\n\terror: string | null;\n}\n\nexport interface EndpointConfig {\n\tname: string;\n\turl: string;\n\tmethod?: string;\n\theaders?: Record<string, string>;\n\tbody?: unknown;\n\ttimeout?: string;\n\tweight?: number;\n}\n\nexport interface ThresholdConfig {\n\terror_rate?: string;\n\tp50?: string;\n\tp95?: string;\n\tp99?: string;\n\tmax?: string;\n}\n\nexport interface DripfeedConfig {\n\tinterval?: string;\n\tduration?: string;\n\ttimeout?: string;\n\tstorage?: 'sqlite' | 'json' | 'memory';\n\tdb?: string;\n\trotation?: 'weighted-random' | 'round-robin' | 'sequential';\n\theaders?: Record<string, string>;\n\tendpoints: EndpointConfig[];\n\tthresholds?: ThresholdConfig;\n}\n\n/** Check if an HTTP status code represents a successful response */\nexport const isSuccess = (status: number | null): boolean =>\n\tstatus !== null && status >= 200 && status < 400;\n\nexport interface EndpointStats {\n\tname: string;\n\trequests: number;\n\tavg_ms: number;\n\tp95_ms: number;\n\terror_count: number;\n}\n\nexport interface ErrorGroup {\n\tendpoint: string;\n\tstatus: number | null;\n\tcount: number;\n\tsample_body: string | null;\n}\n\nexport interface ThresholdResult {\n\tname: string;\n\ttarget: string;\n\tactual: string;\n\tpassed: boolean;\n}\n\nexport interface LatencyStats {\n\tmin: number;\n\tavg: number;\n\tp50: number;\n\tp95: number;\n\tp99: number;\n\tmax: number;\n}\n\nexport interface SoakStats {\n\tduration_s: number;\n\ttotal_requests: number;\n\tsuccess_count: number;\n\tfailure_count: number;\n\tuptime_pct: number;\n\tlatency: LatencyStats;\n\tstatus_codes: Record<number, number>;\n\tendpoints: EndpointStats[];\n\terrors: ErrorGroup[];\n\tthresholds?: ThresholdResult[];\n}\n\nexport interface SoakTestHandle {\n\tstart: () => Promise<void>;\n\tstop: () => Promise<SoakStats>;\n\trun: (opts?: { duration?: string }) => Promise<SoakStats>;\n}\n","import { isSuccess, type RequestResult, type SoakStats } from '../../core/types.js';\nimport type { Reporter } from './interface.js';\n\nconst GREEN = '\\x1b[32m';\nconst RED = '\\x1b[31m';\nconst DIM = '\\x1b[2m';\nconst BOLD = '\\x1b[1m';\nconst RESET = '\\x1b[0m';\nconst CYAN = '\\x1b[36m';\nconst YELLOW = '\\x1b[33m';\n\nconst pad = (s: string, len: number) => s.padEnd(len);\nconst rpad = (s: string, len: number) => s.padStart(len);\n\nexport const createConsoleReporter = (): Reporter => {\n\tlet requestNum = 0;\n\n\treturn {\n\t\tonRequest(result: RequestResult, counts: { ok: number; fail: number }) {\n\t\t\trequestNum++;\n\t\t\tconst ok = isSuccess(result.status);\n\t\t\tconst icon = ok ? `${GREEN}\\u2713${RESET}` : `${RED}\\u2717${RESET}`;\n\t\t\tconst num = rpad(`#${requestNum}`, 6);\n\t\t\tconst name = pad(result.endpoint, 24);\n\t\t\tconst status = result.status\n\t\t\t\t? ok\n\t\t\t\t\t? `${GREEN}${result.status}${RESET}`\n\t\t\t\t\t: `${RED}${result.status}${RESET}`\n\t\t\t\t: `${RED}ERR${RESET}`;\n\t\t\tconst duration = rpad(`${result.duration_ms}ms`, 8);\n\t\t\tconst total = counts.ok + counts.fail;\n\t\t\tconst pct = total > 0 ? ((counts.ok / total) * 100).toFixed(1) : '100.0';\n\t\t\tconst summary = `${DIM}ok:${counts.ok} fail:${counts.fail} (${pct}%)${RESET}`;\n\n\t\t\tprocess.stdout.write(`${icon} ${num} ${name} ${status} ${duration} | ${summary}\\n`);\n\n\t\t\tif (!ok && result.error) {\n\t\t\t\tprocess.stdout.write(` ${DIM}${result.error}${RESET}\\n`);\n\t\t\t}\n\t\t},\n\n\t\tonComplete(stats: SoakStats) {\n\t\t\tconst divider = `${DIM}${'─'.repeat(70)}${RESET}`;\n\t\t\tprocess.stdout.write(`\\n${divider}\\n`);\n\t\t\tprocess.stdout.write(`${BOLD}Soak Test Summary${RESET}\\n`);\n\t\t\tprocess.stdout.write(`${divider}\\n\\n`);\n\n\t\t\tprocess.stdout.write(` Duration: ${stats.duration_s}s\\n`);\n\t\t\tprocess.stdout.write(` Requests: ${stats.total_requests}\\n`);\n\t\t\tprocess.stdout.write(\n\t\t\t\t` Success: ${GREEN}${stats.success_count}${RESET} Failures: ${stats.failure_count > 0 ? RED : ''}${stats.failure_count}${RESET}\\n`,\n\t\t\t);\n\t\t\tprocess.stdout.write(` Uptime: ${stats.uptime_pct}%\\n\\n`);\n\n\t\t\tprocess.stdout.write(` ${CYAN}Latency${RESET}\\n`);\n\t\t\tprocess.stdout.write(` min: ${stats.latency.min}ms avg: ${stats.latency.avg}ms `);\n\t\t\tprocess.stdout.write(`p50: ${stats.latency.p50}ms p95: ${stats.latency.p95}ms `);\n\t\t\tprocess.stdout.write(`p99: ${stats.latency.p99}ms max: ${stats.latency.max}ms\\n\\n`);\n\n\t\t\tif (stats.endpoints.length > 0) {\n\t\t\t\tprocess.stdout.write(` ${CYAN}Endpoints${RESET}\\n`);\n\t\t\t\tfor (const ep of stats.endpoints) {\n\t\t\t\t\tconst errPart = ep.error_count > 0 ? ` ${RED}${ep.error_count} errors${RESET}` : '';\n\t\t\t\t\tprocess.stdout.write(\n\t\t\t\t\t\t` ${pad(ep.name, 24)} ${rpad(String(ep.requests), 5)} reqs avg: ${rpad(String(ep.avg_ms), 5)}ms p95: ${rpad(String(ep.p95_ms), 5)}ms${errPart}\\n`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tprocess.stdout.write('\\n');\n\t\t\t}\n\n\t\t\tif (stats.errors.length > 0) {\n\t\t\t\tprocess.stdout.write(` ${RED}Errors${RESET}\\n`);\n\t\t\t\tfor (const err of stats.errors.slice(0, 10)) {\n\t\t\t\t\tconst status = err.status ?? 'NET';\n\t\t\t\t\tprocess.stdout.write(\n\t\t\t\t\t\t` ${pad(err.endpoint, 24)} ${YELLOW}${status}${RESET} x${err.count}\\n`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tprocess.stdout.write('\\n');\n\t\t\t}\n\n\t\t\tif (stats.thresholds) {\n\t\t\t\tprocess.stdout.write(` ${CYAN}Thresholds${RESET}\\n`);\n\t\t\t\tfor (const t of stats.thresholds) {\n\t\t\t\t\tconst icon = t.passed ? `${GREEN}\\u2713${RESET}` : `${RED}\\u2717${RESET}`;\n\t\t\t\t\tprocess.stdout.write(\n\t\t\t\t\t\t` ${icon} ${pad(t.name, 12)} target: ${pad(t.target, 12)} actual: ${t.actual}\\n`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst allPassed = stats.thresholds.every((t) => t.passed);\n\t\t\t\tif (!allPassed) {\n\t\t\t\t\tprocess.stdout.write(`\\n ${RED}${BOLD}THRESHOLDS FAILED${RESET}\\n`);\n\t\t\t\t}\n\t\t\t\tprocess.stdout.write('\\n');\n\t\t\t}\n\t\t},\n\t};\n};\n","import { writeFile } from 'node:fs/promises';\nimport type { SoakStats } from '../../core/types.js';\nimport type { Reporter } from './interface.js';\n\nexport const createJsonReporter = (outputPath?: string): Reporter => ({\n\tonRequest() {},\n\tonComplete(stats: SoakStats) {\n\t\tconst json = JSON.stringify(stats, null, 2);\n\t\tif (outputPath) {\n\t\t\twriteFile(outputPath, json).catch((err) => {\n\t\t\t\tprocess.stderr.write(\n\t\t\t\t\t`[dripfeed] Failed to write report to ${outputPath}: ${err.message}\\n`,\n\t\t\t\t);\n\t\t\t});\n\t\t} else {\n\t\t\tprocess.stdout.write(`${json}\\n`);\n\t\t}\n\t},\n});\n","import { writeFile } from 'node:fs/promises';\nimport type { SoakStats } from '../../core/types.js';\nimport type { Reporter } from './interface.js';\n\nconst generateMarkdown = (stats: SoakStats): string => {\n\tconst lines: string[] = [\n\t\t'# Soak Test Report',\n\t\t'',\n\t\t'## Summary',\n\t\t'',\n\t\t`| Metric | Value |`,\n\t\t`|--------|-------|`,\n\t\t`| Duration | ${stats.duration_s}s |`,\n\t\t`| Total Requests | ${stats.total_requests} |`,\n\t\t`| Success | ${stats.success_count} |`,\n\t\t`| Failures | ${stats.failure_count} |`,\n\t\t`| Uptime | ${stats.uptime_pct}% |`,\n\t\t'',\n\t\t'## Latency',\n\t\t'',\n\t\t'| Metric | Value |',\n\t\t'|--------|-------|',\n\t\t`| Min | ${stats.latency.min}ms |`,\n\t\t`| Avg | ${stats.latency.avg}ms |`,\n\t\t`| P50 | ${stats.latency.p50}ms |`,\n\t\t`| P95 | ${stats.latency.p95}ms |`,\n\t\t`| P99 | ${stats.latency.p99}ms |`,\n\t\t`| Max | ${stats.latency.max}ms |`,\n\t\t'',\n\t];\n\n\tif (stats.endpoints.length > 0) {\n\t\tlines.push(\n\t\t\t'## Endpoints',\n\t\t\t'',\n\t\t\t'| Endpoint | Requests | Avg | P95 | Errors |',\n\t\t\t'|----------|----------|-----|-----|--------|',\n\t\t);\n\t\tfor (const ep of stats.endpoints) {\n\t\t\tlines.push(\n\t\t\t\t`| ${ep.name} | ${ep.requests} | ${ep.avg_ms}ms | ${ep.p95_ms}ms | ${ep.error_count} |`,\n\t\t\t);\n\t\t}\n\t\tlines.push('');\n\t}\n\n\tif (stats.errors.length > 0) {\n\t\tlines.push('## Errors', '', '| Endpoint | Status | Count |', '|----------|--------|-------|');\n\t\tfor (const err of stats.errors) {\n\t\t\tlines.push(`| ${err.endpoint} | ${err.status ?? 'Network'} | ${err.count} |`);\n\t\t}\n\t\tlines.push('');\n\t}\n\n\tif (stats.thresholds) {\n\t\tlines.push(\n\t\t\t'## Thresholds',\n\t\t\t'',\n\t\t\t'| Check | Target | Actual | Result |',\n\t\t\t'|-------|--------|--------|--------|',\n\t\t);\n\t\tfor (const t of stats.thresholds) {\n\t\t\tconst icon = t.passed ? 'PASS' : 'FAIL';\n\t\t\tlines.push(`| ${t.name} | ${t.target} | ${t.actual} | ${icon} |`);\n\t\t}\n\t\tlines.push('');\n\t}\n\n\treturn lines.join('\\n');\n};\n\nexport const createMarkdownReporter = (outputPath?: string): Reporter => ({\n\tonRequest() {},\n\tonComplete(stats: SoakStats) {\n\t\tconst md = generateMarkdown(stats);\n\t\tif (outputPath) {\n\t\t\twriteFile(outputPath, md).catch((err) => {\n\t\t\t\tprocess.stderr.write(\n\t\t\t\t\t`[dripfeed] Failed to write report to ${outputPath}: ${err.message}\\n`,\n\t\t\t\t);\n\t\t\t});\n\t\t} else {\n\t\t\tprocess.stdout.write(`${md}\\n`);\n\t\t}\n\t},\n});\n","import { readFile, writeFile } from 'node:fs/promises';\nimport type { RequestResult } from '../../core/types.js';\nimport type { StorageAdapter } from './interface.js';\n\nconst FLUSH_INTERVAL = 10;\n\nexport const createJsonStorage = (filePath: string): StorageAdapter => {\n\tlet buffer: RequestResult[] = [];\n\tlet flushed: RequestResult[] = [];\n\n\tconst flush = async () => {\n\t\tif (buffer.length === 0) return;\n\t\tflushed = [...flushed, ...buffer];\n\t\tbuffer = [];\n\t\tawait writeFile(filePath, JSON.stringify(flushed, null, 2));\n\t};\n\n\treturn {\n\t\tinit: async () => {\n\t\t\ttry {\n\t\t\t\tconst data = await readFile(filePath, 'utf-8');\n\t\t\t\tflushed = JSON.parse(data);\n\t\t\t} catch {\n\t\t\t\tflushed = [];\n\t\t\t}\n\t\t},\n\t\trecord: async (result) => {\n\t\t\tbuffer.push(result);\n\t\t\tif (buffer.length >= FLUSH_INTERVAL) await flush();\n\t\t},\n\t\tgetAll: async () => [...flushed, ...buffer],\n\t\tclose: async () => {\n\t\t\tawait flush();\n\t\t},\n\t};\n};\n","import type { RequestResult } from '../../core/types.js';\nimport type { StorageAdapter } from './interface.js';\n\nexport const createMemoryStorage = (): StorageAdapter => {\n\tconst results: RequestResult[] = [];\n\n\treturn {\n\t\tinit: async () => {},\n\t\trecord: async (result) => {\n\t\t\tresults.push(result);\n\t\t},\n\t\tgetAll: async () => [...results],\n\t\tclose: async () => {},\n\t};\n};\n","declare const Bun: unknown;\ndeclare const Deno: unknown;\n\nexport const isBun = typeof globalThis !== 'undefined' && 'Bun' in globalThis;\nexport const isDeno = typeof globalThis !== 'undefined' && 'Deno' in globalThis;\nexport const isNode = !isBun && !isDeno;\n","import type { RequestResult } from '../../core/types.js';\nimport { isBun } from '../../utils/runtime.js';\nimport type { StorageAdapter } from './interface.js';\n\nconst CREATE_TABLE = `\nCREATE TABLE IF NOT EXISTS results (\n\tid INTEGER PRIMARY KEY AUTOINCREMENT,\n\ttimestamp TEXT NOT NULL,\n\tendpoint TEXT NOT NULL,\n\tmethod TEXT NOT NULL,\n\turl TEXT NOT NULL,\n\tstatus INTEGER,\n\tduration_ms REAL NOT NULL,\n\tresponse_body TEXT,\n\terror TEXT\n)`;\n\nconst INSERT = `\nINSERT INTO results (timestamp, endpoint, method, url, status, duration_ms, response_body, error)\nVALUES (?, ?, ?, ?, ?, ?, ?, ?)`;\n\n// Unified interface for both bun:sqlite and better-sqlite3\ninterface SqliteDb {\n\texec(sql: string): void;\n\tprepare(sql: string): SqliteStatement;\n\tclose(): void;\n}\n\ninterface SqliteStatement {\n\trun(...params: unknown[]): void;\n\tall(...params: unknown[]): Record<string, unknown>[];\n}\n\nconst openBunSqlite = async (path: string): Promise<SqliteDb> => {\n\tconst { Database } = await import('bun:sqlite');\n\tconst db = new Database(path);\n\tdb.exec('PRAGMA journal_mode = WAL');\n\treturn {\n\t\texec: (sql) => db.exec(sql),\n\t\tprepare: (sql) => {\n\t\t\tconst stmt = db.prepare(sql);\n\t\t\treturn {\n\t\t\t\trun: (...params) => stmt.run(...params),\n\t\t\t\tall: (...params) => stmt.all(...params) as Record<string, unknown>[],\n\t\t\t};\n\t\t},\n\t\tclose: () => db.close(),\n\t};\n};\n\nconst openBetterSqlite = async (path: string): Promise<SqliteDb> => {\n\tlet mod: { default: (path: string) => SqliteDb };\n\ttry {\n\t\tmod = await import('better-sqlite3' as string);\n\t} catch {\n\t\tthrow new Error(\n\t\t\t'SQLite storage requires \"better-sqlite3\" on Node.js. Install it:\\n npm install better-sqlite3\\nOr use storage: \"json\" in your dripfeed config.',\n\t\t);\n\t}\n\tconst db = mod.default(path);\n\tdb.exec('PRAGMA journal_mode = WAL');\n\treturn {\n\t\texec: (sql: string) => db.exec(sql),\n\t\tprepare: (sql: string) => {\n\t\t\tconst stmt = db.prepare(sql);\n\t\t\treturn {\n\t\t\t\trun: (...params: unknown[]) => stmt.run(...params),\n\t\t\t\tall: (...params: unknown[]) => stmt.all(...params) as Record<string, unknown>[],\n\t\t\t};\n\t\t},\n\t\tclose: () => db.close(),\n\t};\n};\n\nexport const createSqliteStorage = (dbPath: string): StorageAdapter => {\n\tlet db: SqliteDb | null = null;\n\tlet insertStmt: SqliteStatement | null = null;\n\n\treturn {\n\t\tinit: async () => {\n\t\t\tdb = isBun ? await openBunSqlite(dbPath) : await openBetterSqlite(dbPath);\n\t\t\tdb.exec(CREATE_TABLE);\n\t\t\tinsertStmt = db.prepare(INSERT);\n\t\t},\n\t\trecord: async (result) => {\n\t\t\tinsertStmt?.run(\n\t\t\t\tresult.timestamp,\n\t\t\t\tresult.endpoint,\n\t\t\t\tresult.method,\n\t\t\t\tresult.url,\n\t\t\t\tresult.status,\n\t\t\t\tresult.duration_ms,\n\t\t\t\tresult.response_body,\n\t\t\t\tresult.error,\n\t\t\t);\n\t\t},\n\t\tgetAll: async () => {\n\t\t\tconst rows = db?.prepare('SELECT * FROM results ORDER BY id').all() ?? [];\n\t\t\treturn rows.map(\n\t\t\t\t(row) =>\n\t\t\t\t\t({\n\t\t\t\t\t\ttimestamp: row.timestamp as string,\n\t\t\t\t\t\tendpoint: row.endpoint as string,\n\t\t\t\t\t\tmethod: row.method as string,\n\t\t\t\t\t\turl: row.url as string,\n\t\t\t\t\t\tstatus: row.status as number | null,\n\t\t\t\t\t\tduration_ms: row.duration_ms as number,\n\t\t\t\t\t\tresponse_body: row.response_body as string | null,\n\t\t\t\t\t\terror: row.error as string | null,\n\t\t\t\t\t}) satisfies RequestResult,\n\t\t\t);\n\t\t},\n\t\tclose: async () => {\n\t\t\tdb?.close();\n\t\t},\n\t};\n};\n","import type { DripfeedConfig } from '../../core/types.js';\nimport type { StorageAdapter } from './interface.js';\nimport { createJsonStorage } from './json.js';\nimport { createMemoryStorage } from './memory.js';\nimport { createSqliteStorage } from './sqlite.js';\n\nexport type { StorageAdapter } from './interface.js';\nexport { createJsonStorage } from './json.js';\nexport { createMemoryStorage } from './memory.js';\nexport { createSqliteStorage } from './sqlite.js';\n\nexport const createStorage = (config: DripfeedConfig): StorageAdapter => {\n\tconst type = config.storage ?? 'sqlite';\n\tswitch (type) {\n\t\tcase 'memory':\n\t\t\treturn createMemoryStorage();\n\t\tcase 'json':\n\t\t\treturn createJsonStorage(config.db ?? 'dripfeed-results.json');\n\t\tcase 'sqlite':\n\t\t\treturn createSqliteStorage(config.db ?? 'dripfeed-results.db');\n\t}\n};\n","import { loadConfig } from 'c12';\nimport { z } from 'zod';\n\nconst endpointSchema = z.object({\n\tname: z.string(),\n\turl: z.string().url(),\n\tmethod: z.string().default('GET'),\n\theaders: z.record(z.string(), z.string()).optional(),\n\tbody: z.unknown().optional(),\n\ttimeout: z.string().optional(),\n\tweight: z.number().positive().default(1),\n});\n\nconst thresholdSchema = z.object({\n\terror_rate: z.string().optional(),\n\tp50: z.string().optional(),\n\tp95: z.string().optional(),\n\tp99: z.string().optional(),\n\tmax: z.string().optional(),\n});\n\nexport const configSchema = z.object({\n\tinterval: z.string().default('3s'),\n\tduration: z.string().optional(),\n\ttimeout: z.string().default('30s'),\n\tstorage: z.enum(['sqlite', 'json', 'memory']).default('sqlite'),\n\tdb: z.string().optional(),\n\trotation: z.enum(['weighted-random', 'round-robin', 'sequential']).default('weighted-random'),\n\theaders: z.record(z.string(), z.string()).optional(),\n\tendpoints: z.array(endpointSchema).min(1, 'At least one endpoint is required'),\n\tthresholds: thresholdSchema.optional(),\n});\n\nexport type ParsedConfig = z.infer<typeof configSchema>;\n\n/** Parse and validate a raw config object into a fully-typed ParsedConfig with defaults applied.\n * Use this when creating a soak test programmatically without a config file. */\nexport const parseConfig = (raw: unknown): ParsedConfig => configSchema.parse(raw);\n\nconst interpolateEnv = (value: unknown): unknown => {\n\tif (typeof value === 'string') {\n\t\treturn value.replace(/\\$\\{(\\w+)\\}/g, (_, key: string) => {\n\t\t\tconst val = process.env[key];\n\t\t\tif (val === undefined) {\n\t\t\t\tprocess.stderr.write(`[dripfeed] Warning: environment variable \"${key}\" is not set\\n`);\n\t\t\t}\n\t\t\treturn val ?? '';\n\t\t});\n\t}\n\tif (Array.isArray(value)) return value.map(interpolateEnv);\n\tif (value && typeof value === 'object') {\n\t\treturn Object.fromEntries(Object.entries(value).map(([k, v]) => [k, interpolateEnv(v)]));\n\t}\n\treturn value;\n};\n\nexport const loadDripfeedConfig = async (\n\toverrides?: Partial<ParsedConfig>,\n): Promise<ParsedConfig> => {\n\tconst { config } = await loadConfig({ name: 'dripfeed' });\n\tconst merged = { ...config, ...overrides };\n\tconst interpolated = interpolateEnv(merged);\n\treturn configSchema.parse(interpolated);\n};\n","const UNITS: Record<string, number> = {\n\tms: 1,\n\ts: 1_000,\n\tm: 60_000,\n\th: 3_600_000,\n\td: 86_400_000,\n};\n\nconst DURATION_RE = /^(\\d+(?:\\.\\d+)?)\\s*(ms|s|m|h|d)$/;\n\nexport const parseDuration = (input: string): number => {\n\tconst match = input.trim().match(DURATION_RE);\n\tif (!match) throw new Error(`Invalid duration: \"${input}\". Expected format: \"3s\", \"10m\", \"2h\"`);\n\tconst value = Number.parseFloat(match[1] ?? '0');\n\tconst unit = UNITS[match[2] ?? 'ms'] ?? 1;\n\treturn Math.round(value * unit);\n};\n","import type { EndpointConfig, RequestResult } from '../core/types.js';\nimport { parseDuration } from './duration.js';\n\nexport const timedFetch = async (\n\tendpoint: EndpointConfig,\n\tglobalHeaders?: Record<string, string>,\n\ttimeout = '30s',\n): Promise<RequestResult> => {\n\tconst timeoutMs = parseDuration(endpoint.timeout ?? timeout);\n\tconst headers = { ...globalHeaders, ...endpoint.headers };\n\tconst method = endpoint.method ?? 'GET';\n\tconst url = endpoint.url;\n\tconst start = performance.now();\n\n\ttry {\n\t\tconst response = await fetch(url, {\n\t\t\tmethod,\n\t\t\theaders,\n\t\t\tbody: endpoint.body ? JSON.stringify(endpoint.body) : undefined,\n\t\t\tsignal: AbortSignal.timeout(timeoutMs),\n\t\t\tredirect: 'follow',\n\t\t});\n\n\t\tconst durationMs = Math.round(performance.now() - start);\n\t\tconst isError = response.status >= 400;\n\t\tconst body = isError ? await response.text().catch(() => null) : null;\n\n\t\treturn {\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tendpoint: endpoint.name,\n\t\t\tmethod,\n\t\t\turl,\n\t\t\tstatus: response.status,\n\t\t\tduration_ms: durationMs,\n\t\t\tresponse_body: body,\n\t\t\terror: null,\n\t\t};\n\t} catch (err) {\n\t\tconst durationMs = Math.round(performance.now() - start);\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\n\t\treturn {\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tendpoint: endpoint.name,\n\t\t\tmethod,\n\t\t\turl,\n\t\t\tstatus: null,\n\t\t\tduration_ms: durationMs,\n\t\t\tresponse_body: null,\n\t\t\terror: message,\n\t\t};\n\t}\n};\n","import {\n\ttype EndpointStats,\n\ttype ErrorGroup,\n\tisSuccess,\n\ttype LatencyStats,\n\ttype RequestResult,\n\ttype SoakStats,\n\ttype ThresholdConfig,\n\ttype ThresholdResult,\n} from '../core/types.js';\nimport { parseDuration } from './duration.js';\n\nexport const percentile = (sorted: number[], p: number): number => {\n\tif (sorted.length === 0) return 0;\n\tconst idx = (p / 100) * (sorted.length - 1);\n\tconst lower = Math.floor(idx);\n\tconst upper = Math.ceil(idx);\n\tconst lowerVal = sorted[lower] ?? 0;\n\tconst upperVal = sorted[upper] ?? 0;\n\tif (lower === upper) return lowerVal;\n\treturn lowerVal + (upperVal - lowerVal) * (idx - lower);\n};\n\nconst computeLatency = (durations: number[]): LatencyStats => {\n\tif (durations.length === 0) {\n\t\treturn { min: 0, avg: 0, p50: 0, p95: 0, p99: 0, max: 0 };\n\t}\n\tconst sorted = [...durations].sort((a, b) => a - b);\n\tconst sum = sorted.reduce((a, b) => a + b, 0);\n\treturn {\n\t\tmin: sorted[0] ?? 0,\n\t\tavg: Math.round(sum / sorted.length),\n\t\tp50: Math.round(percentile(sorted, 50)),\n\t\tp95: Math.round(percentile(sorted, 95)),\n\t\tp99: Math.round(percentile(sorted, 99)),\n\t\tmax: sorted.at(-1) ?? 0,\n\t};\n};\n\nconst computeEndpointStats = (results: RequestResult[]): EndpointStats[] => {\n\tconst byEndpoint = new Map<string, RequestResult[]>();\n\tfor (const r of results) {\n\t\tconst list = byEndpoint.get(r.endpoint) ?? [];\n\t\tlist.push(r);\n\t\tbyEndpoint.set(r.endpoint, list);\n\t}\n\n\treturn [...byEndpoint.entries()].map(([name, items]) => {\n\t\tconst durations = items.map((r) => r.duration_ms);\n\t\tconst sorted = [...durations].sort((a, b) => a - b);\n\t\tconst sum = durations.reduce((a, b) => a + b, 0);\n\t\treturn {\n\t\t\tname,\n\t\t\trequests: items.length,\n\t\t\tavg_ms: Math.round(sum / items.length),\n\t\t\tp95_ms: Math.round(percentile(sorted, 95)),\n\t\t\terror_count: items.filter((r) => !isSuccess(r.status)).length,\n\t\t};\n\t});\n};\n\nconst computeErrors = (results: RequestResult[]): ErrorGroup[] => {\n\tconst key = (r: RequestResult) => `${r.endpoint}:${r.status}`;\n\tconst groups = new Map<string, ErrorGroup>();\n\n\tfor (const r of results) {\n\t\tif (isSuccess(r.status)) continue;\n\t\tconst k = key(r);\n\t\tconst existing = groups.get(k);\n\t\tif (existing) {\n\t\t\texisting.count++;\n\t\t} else {\n\t\t\tgroups.set(k, {\n\t\t\t\tendpoint: r.endpoint,\n\t\t\t\tstatus: r.status,\n\t\t\t\tcount: 1,\n\t\t\t\tsample_body: r.response_body,\n\t\t\t});\n\t\t}\n\t}\n\n\treturn [...groups.values()].sort((a, b) => b.count - a.count);\n};\n\nconst parseThresholdValue = (s: string): number => {\n\tconst cleaned = s.replace(/[<>=%\\s]/g, '');\n\tif (cleaned.endsWith('ms')) return Number.parseFloat(cleaned);\n\tif (cleaned.endsWith('s')) return Number.parseFloat(cleaned) * 1000;\n\treturn Number.parseFloat(cleaned);\n};\n\nconst evaluateThresholds = (\n\tthresholds: ThresholdConfig,\n\tlatency: LatencyStats,\n\terrorRate: number,\n): ThresholdResult[] => {\n\tconst results: ThresholdResult[] = [];\n\n\tif (thresholds.error_rate) {\n\t\tconst target = parseThresholdValue(thresholds.error_rate);\n\t\tresults.push({\n\t\t\tname: 'error_rate',\n\t\t\ttarget: thresholds.error_rate,\n\t\t\tactual: `${errorRate.toFixed(2)}%`,\n\t\t\tpassed: errorRate < target,\n\t\t});\n\t}\n\n\tconst latencyChecks: Array<[keyof ThresholdConfig, keyof LatencyStats]> = [\n\t\t['p50', 'p50'],\n\t\t['p95', 'p95'],\n\t\t['p99', 'p99'],\n\t\t['max', 'max'],\n\t];\n\n\tfor (const [configKey, statKey] of latencyChecks) {\n\t\tconst threshold = thresholds[configKey];\n\t\tif (!threshold) continue;\n\t\tconst targetMs = parseDuration(threshold.replace(/[<>\\s]/g, ''));\n\t\tresults.push({\n\t\t\tname: configKey,\n\t\t\ttarget: threshold,\n\t\t\tactual: `${latency[statKey]}ms`,\n\t\t\tpassed: latency[statKey] < targetMs,\n\t\t});\n\t}\n\n\treturn results;\n};\n\nexport const computeStats = (\n\tresults: RequestResult[],\n\tstartTime: Date,\n\tthresholds?: ThresholdConfig,\n\tendTime?: Date,\n): SoakStats => {\n\tconst end = endTime ?? new Date();\n\tconst durationS = Math.round((end.getTime() - startTime.getTime()) / 1000);\n\tconst durations = results.map((r) => r.duration_ms);\n\tconst latency = computeLatency(durations);\n\n\tconst successCount = results.filter((r) => isSuccess(r.status)).length;\n\tconst failureCount = results.length - successCount;\n\tconst uptimePct = results.length > 0 ? (successCount / results.length) * 100 : 100;\n\n\tconst statusCodes: Record<number, number> = {};\n\tfor (const r of results) {\n\t\tif (r.status !== null) {\n\t\t\tstatusCodes[r.status] = (statusCodes[r.status] ?? 0) + 1;\n\t\t}\n\t}\n\n\tconst errorRate = results.length > 0 ? (failureCount / results.length) * 100 : 0;\n\n\treturn {\n\t\tduration_s: durationS,\n\t\ttotal_requests: results.length,\n\t\tsuccess_count: successCount,\n\t\tfailure_count: failureCount,\n\t\tuptime_pct: Math.round(uptimePct * 100) / 100,\n\t\tlatency,\n\t\tstatus_codes: statusCodes,\n\t\tendpoints: computeEndpointStats(results),\n\t\terrors: computeErrors(results),\n\t\tthresholds: thresholds ? evaluateThresholds(thresholds, latency, errorRate) : undefined,\n\t};\n};\n","import type { Reporter } from '../adapters/reporters/interface.js';\nimport { createStorage } from '../adapters/storage/index.js';\nimport { parseDuration } from '../utils/duration.js';\nimport { timedFetch } from '../utils/http.js';\nimport { computeStats } from '../utils/stats.js';\nimport type { ParsedConfig } from './config.js';\nimport {\n\ttype DripfeedConfig,\n\ttype EndpointConfig,\n\tisSuccess,\n\ttype RequestResult,\n\ttype SoakStats,\n\ttype SoakTestHandle,\n} from './types.js';\n\ntype PickEndpoint = (endpoints: EndpointConfig[]) => EndpointConfig;\n\nconst createWeightedRandom = (): PickEndpoint => {\n\treturn (endpoints) => {\n\t\tconst totalWeight = endpoints.reduce((sum, ep) => sum + (ep.weight ?? 1), 0);\n\t\tlet rand = Math.random() * totalWeight;\n\t\tfor (const ep of endpoints) {\n\t\t\trand -= ep.weight ?? 1;\n\t\t\tif (rand <= 0) return ep;\n\t\t}\n\t\t// Unreachable when endpoints is non-empty (validated by Zod min(1)),\n\t\t// but satisfies TS without non-null assertion\n\t\treturn endpoints[endpoints.length - 1] as EndpointConfig;\n\t};\n};\n\nconst createRoundRobin = (): PickEndpoint => {\n\tlet idx = -1;\n\treturn (endpoints) => {\n\t\tidx = (idx + 1) % endpoints.length;\n\t\treturn endpoints[idx] as EndpointConfig;\n\t};\n};\n\nconst createPicker = (rotation: string): PickEndpoint => {\n\tswitch (rotation) {\n\t\tcase 'round-robin':\n\t\tcase 'sequential':\n\t\t\treturn createRoundRobin();\n\t\tdefault:\n\t\t\treturn createWeightedRandom();\n\t}\n};\n\n// Safely coerce ParsedConfig endpoints to EndpointConfig[]\nconst toEndpoints = (config: ParsedConfig): EndpointConfig[] =>\n\tconfig.endpoints.map((ep) => ({\n\t\tname: ep.name,\n\t\turl: ep.url,\n\t\tmethod: ep.method,\n\t\theaders: ep.headers as Record<string, string> | undefined,\n\t\tbody: ep.body,\n\t\ttimeout: ep.timeout,\n\t\tweight: ep.weight,\n\t}));\n\nexport const createSoakTest = (\n\tconfig: ParsedConfig,\n\treporters: Reporter[] = [],\n): SoakTestHandle => {\n\tconst storage = createStorage(config as unknown as DripfeedConfig);\n\tconst endpoints = toEndpoints(config);\n\tconst globalHeaders = config.headers as Record<string, string> | undefined;\n\tconst pick = createPicker(config.rotation);\n\tconst intervalMs = Math.max(100, parseDuration(config.interval));\n\tlet timer: ReturnType<typeof setInterval> | null = null;\n\tlet startTime: Date | null = null;\n\tlet okCount = 0;\n\tlet failCount = 0;\n\tlet running = false;\n\n\tconst tick = async () => {\n\t\tif (!running) return;\n\t\tconst endpoint = pick(endpoints);\n\t\tconst result: RequestResult = await timedFetch(endpoint, globalHeaders, config.timeout);\n\t\tif (!running) return;\n\t\tawait storage.record(result);\n\n\t\tisSuccess(result.status) ? okCount++ : failCount++;\n\n\t\tfor (const reporter of reporters) {\n\t\t\treporter.onRequest(result, { ok: okCount, fail: failCount });\n\t\t}\n\t};\n\n\tconst getStats = async (): Promise<SoakStats> => {\n\t\tconst results = await storage.getAll();\n\t\treturn computeStats(results, startTime ?? new Date(), config.thresholds);\n\t};\n\n\tconst safeTick = () => {\n\t\ttick().catch((err) => {\n\t\t\tprocess.stderr.write(`[dripfeed] tick error: ${err instanceof Error ? err.message : err}\\n`);\n\t\t});\n\t};\n\n\tconst start = async () => {\n\t\tif (running) return;\n\t\trunning = true;\n\t\tawait storage.init();\n\t\tstartTime = new Date();\n\t\tsafeTick();\n\t\ttimer = setInterval(safeTick, intervalMs);\n\t};\n\n\tconst stop = async (): Promise<SoakStats> => {\n\t\trunning = false;\n\t\tif (timer) {\n\t\t\tclearInterval(timer);\n\t\t\ttimer = null;\n\t\t}\n\t\tconst stats = await getStats();\n\t\tfor (const reporter of reporters) {\n\t\t\treporter.onComplete(stats);\n\t\t}\n\t\tawait storage.close();\n\t\treturn stats;\n\t};\n\n\tconst run = async (opts?: { duration?: string }): Promise<SoakStats> => {\n\t\tconst durationStr = opts?.duration ?? config.duration;\n\t\tawait start();\n\n\t\tif (durationStr) {\n\t\t\tconst durationMs = parseDuration(durationStr);\n\t\t\tawait new Promise<void>((resolve) => {\n\t\t\t\tsetTimeout(() => resolve(), durationMs);\n\t\t\t});\n\t\t} else {\n\t\t\tawait new Promise<void>((resolve) => {\n\t\t\t\tconst handler = () => {\n\t\t\t\t\tprocess.removeListener('SIGINT', handler);\n\t\t\t\t\tprocess.removeListener('SIGTERM', handler);\n\t\t\t\t\tresolve();\n\t\t\t\t};\n\t\t\t\tprocess.on('SIGINT', handler);\n\t\t\t\tprocess.on('SIGTERM', handler);\n\t\t\t});\n\t\t}\n\n\t\treturn stop();\n\t};\n\n\treturn { start, stop, run };\n};\n"],"mappings":"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"}
|