UNPKG

tressi

Version:

Deterministic load testing for API performance validation.

2 lines (1 loc) 7.96 kB
import{Cc as t}from"./chunk-W6IDPYNF.js";var y={CONNECTED:"connected",METRICS:"metrics",TEST:{CANCELLED:"test:cancelled",COMPLETED:"test:completed",FAILED:"test:failed",STARTED:"test:started"}};var a={author:"kevinchatham",bin:{tressi:"dist/cli.js"},dependencies:{"@acrodata/code-editor":"^0.6.0","@codemirror/language-data":"^6.5.2","@hono/node-server":"^1.19.6","@hono/standard-validator":"^0.2.0","@tailwindcss/postcss":"^4.1.17","better-sqlite3":"^12.6.0",chalk:"^5.4.1","cli-table3":"^0.6.5",clipboard:"^2.0.11",commander:"^14.0.0",dayjs:"^1.11.19","emoji-toolkit":"^10.0.0",eventemitter3:"^5.0.1","fuse.js":"^7.1.0","hdr-histogram-js":"^3.0.1",hono:"^4.10.6",kysely:"^0.28.9",marked:"^17.0.2",mermaid:"^11.12.2","ng-apexcharts":"^2.0.3","ngx-markdown":"^21.1.0",ora:"^8.2.0",postcss:"^8.5.6",prismjs:"^1.30.0",rxjs:"~7.8.0",semver:"^7.7.4",tailwindcss:"^4.1.17",tslib:"^2.3.0",undici:"^7.3.0","unique-names-generator":"^4.7.1",xlsx:"https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",zod:"4.0.1"},description:"Deterministic load testing for API performance validation.",devDependencies:{"@biomejs/biome":"2.4.7","@types/jasmine":"~5.1.0","@types/node":"^20.0.0","@types/prismjs":"^1.26.6","@types/semver":"^7.7.1","@vitest/browser-playwright":"^4.0.18","@vitest/coverage-v8":"^4.0.18","@vitest/ui":"^4.0.15","chokidar-cli":"^3.0.0",daisyui:"^5.5.5","jasmine-core":"~5.9.0",jsdom:"^28.1.0",nodemon:"^3.1.11",postcss:"^8.5.3",prettier:"^3.6.2","prettier-plugin-tailwindcss":"^0.7.1",rimraf:"^6.1.0","ts-morph":"^25.0.1",tsup:"^8.5.0",tsx:"^4.20.3",typescript:"~5.9.2",vitest:"^4.0.18"},engines:{node:">=20.0.0"},files:["dist","LICENSE","package.json","README.md"],keywords:["load testing","stress testing","performance","benchmark","api testing","http","dashboard","gui","cli","multithreaded","realtime","automation","autocannon","artillery","wrk"],license:"SEE LICENSE IN LICENSE",main:"dist/index.js",name:"tressi",repository:{directory:"",type:"git",url:"git+https://github.com/kevinchatham/tressi.git"},scripts:{build:"npx rimraf dist && npx rimraf server/dist && npm run cli:build && npm run ui:build && cp -r projects/cli/dist dist",check:"biome check --config-path biome.standalone.json --write . && prettier --write . --config .prettierrc --ignore-path .prettierignore","check:watch":'chokidar "**/*.{ts,html,css,yml,yaml,json,md}" -c "npm run check" --ignore "**/{node_modules,dist}/**"',clean:'npx rimraf --glob "**/package-lock.json" "**/dist" "**/node_modules" "**/.angular"',"cli:build":"npm run build -w projects/cli","cli:run":"npm run run -w projects/cli","cli:serve":"nodemon",docker:"tsx scripts/docker.ts",kill:"fuser -k 3108/tcp && fuser -k 4200/tcp && fuser -k 5000/tcp && fuser -k 8000/tcp && fuser -k 8001/tcp","start:test:server":"npx tsx projects/e2e/setup/test-server.ts",test:"npm run test:cli && npm run test:ui && npm run test:e2e","test:cli":"npm run test -w projects/cli","test:e2e":"npm run test -w projects/e2e","test:install":"npx playwright install --with-deps chromium","test:ui":"npm run test -w projects/ui",typecheck:"npm run typecheck -w projects/cli && npm run typecheck -w projects/shared","ui:build":"npm run build -w projects/ui","ui:build:dev":"npm run build:dev -w projects/ui","ui:serve":"npm run serve -w projects/ui"},type:"commonjs",version:"0.0.20",workspaces:["projects/cli","projects/ui","projects/shared","projects/e2e"]};var h=`https://raw.githubusercontent.com/kevinchatham/tressi/main/schemas/tressi.schema.v${a.version}.json`,u={},f=["GET","POST","PUT","PATCH","DELETE"],i={enabled:!1,errorRateThreshold:1,exitStatusCodes:[500],monitoringWindowSeconds:1},g={earlyExit:i,headers:u,method:"GET",payload:{},rampUpDurationSec:0,rps:1,url:""},p={durationSec:10,headers:u,rampUpDurationSec:0,threads:2,workerEarlyExit:i,workerMemoryLimit:128},c=t.object({enabled:t.boolean().describe("Enable early exit for this endpoint"),errorRateThreshold:t.number().min(1).max(100).describe("Error rate threshold (1-100)"),exitStatusCodes:t.array(t.number().int().min(100).max(599)).describe("HTTP status codes that trigger immediate endpoint stop"),monitoringWindowSeconds:t.number().int().positive().min(1).describe("Time window in seconds for threshold calculation")}).default(i),d=t.object({earlyExit:c.describe("Optional early exit configuration for this specific endpoint"),headers:t.record(t.string(),t.string()).describe("Headers to be sent with this specific request. Merged with global headers."),method:t.enum(f).describe("The HTTP method to use for the request. Defaults to GET."),payload:t.record(t.string(),t.unknown()).or(t.array(t.unknown())).describe("The request payload. Can be a JSON object or an array."),rampUpDurationSec:t.number().int().nonnegative().describe("Per-endpoint ramp up time in seconds. If 0, uses global rampUpDurationSec. Defaults to 0."),rps:t.number().int().min(1).describe("Per-endpoint requests per second limit. Defaults to 1."),url:t.url().describe("The URL to send the request to.")}).default(g).check(e=>{let s=e.value;s.earlyExit.enabled&&(s.earlyExit.errorRateThreshold>0&&s.earlyExit.exitStatusCodes.length>0||e.issues.push({code:"custom",input:e.value,message:`Early Exit for ${e.value.url}: An error rate threshold and at least one exit status code must be provided when enabled`,path:["earlyExit"]}))}),v=t.object({durationSec:t.number().int().min(10).default(10).describe("The total duration of the test in seconds. Defaults to 10."),headers:t.record(t.string(),t.string()).describe("Global headers to be sent with every request."),rampUpDurationSec:t.number().int().min(0).describe("The time in seconds to ramp up to the target RPS. Defaults to 0."),threads:t.number().int().min(1).describe("Number of worker threads to use (defaults to CPU count)"),workerEarlyExit:c.describe("Global early exit configuration (acts as default for endpoints without specific config)"),workerMemoryLimit:t.number().int().min(16).max(512).describe("Memory limit per worker in MB")}).check(e=>{e.value.workerEarlyExit.enabled&&(e.value.workerEarlyExit.errorRateThreshold>0&&e.value.workerEarlyExit.exitStatusCodes.length>0||e.issues.push({code:"custom",input:e.value,message:"An error rate threshold and at least one exit status code must be provided when enabled",path:["workerEarlyExit"]}))}).default(p),n=t.object({$schema:t.string().describe("A URL to the JSON schema for this configuration file."),options:v.describe("Configuration options for the test runner."),requests:t.array(d).min(1,"At least one valid request is required").describe("An array of request configurations.")}).check(e=>{if(e.value.options.rampUpDurationSec>e.value.options.durationSec/2&&e.issues.push({code:"custom",input:e.value,message:"Duration cannot exceed half of the test duration",path:["options","rampUpDurationSec"]}),e.value.requests.forEach((r,o)=>{r.rampUpDurationSec>e.value.options.durationSec/2&&e.issues.push({code:"custom",input:e.value,message:"Duration cannot exceed half of the test duration",path:["requests",o,"rampUpDurationSec"]})}),(e.value.options.rampUpDurationSec>0||e.value.requests.some(r=>r.rampUpDurationSec>0))&&(e.value.requests.every(o=>o.rps>=5)||e.issues.push({code:"custom",input:e.value,message:"All requests must have a target greater than or equal to 5 when Ramp Up Duration is greater than 0",path:["options","rampUpDurationSec"]})),e.value.requests.length>1){let r=e.value.requests.map(l=>l.url);new Set(r).size!==r.length&&e.issues.push({code:"custom",input:e.value,message:"Requests: Duplicate URLs are not allowed",path:["requests"]})}}).default({$schema:h,options:p,requests:[]}),D=structuredClone(n.parse(void 0)),w=structuredClone(d.parse(void 0));function R(e){let s=n.safeParse(e);return s.success?{data:s.data,success:!0}:{error:s.error,success:!1}}var E=new Intl.NumberFormat("en",{notation:"compact"});function I(e){if(e===0)return"0";let s=Math.abs(e);return E.format(s).toLowerCase()}export{y as a,I as b,f as c,g as d,n as e,D as f,R as g};