@apisurf/wire-db
v0.1.4
Published
Local SQLite store for HTTP traffic recorded by wire: schema, migrations, writer and read queries.
Maintainers
Readme
@apisurf/wire-db
The SQLite store behind @apisurf/wire:
schema, migrations, the run writer and the read queries.
You probably want @apisurf/wire instead. That package is the wire CLI —
it records a script's HTTP traffic and gives you wire ls, wire get and
wire sql to read it back. This one is the library underneath, published so
that anything else reading a wire.sqlite file agrees with the CLI about what
is in it rather than re-deriving the schema.
pnpm add @apisurf/wire-dbNeeds Node >= 20. better-sqlite3 is a native addon, so installing this
compiles or downloads a prebuilt binary.
Opening a database
import { openDb, resolveDbPath } from "@apisurf/wire-db";
const db = openDb({ path: "./wire.sqlite" }); // created and migrated
const reader = openDb({ path: "./wire.sqlite", mustExist: true, skipMigrations: true });openDb returns a better-sqlite3 Database. It creates the file and brings
it up to the current schema version unless you opt out:
| Option | |
| ---------------- | -------------------------------------------------------------------------- |
| path | Database file. Relative paths resolve against cwd. Default wire.sqlite |
| cwd | Base for a relative path. Default process.cwd() |
| mustExist | Throw instead of creating the file |
| skipMigrations | Do not migrate — for readers that expect an already-migrated file |
It sets WAL, synchronous=NORMAL, a 64 MiB page cache, memory temp store and
foreign keys. WAL is what lets a reader open the file while a run is still
writing to it.
resolveDbPath applies the same path rules without opening anything.
Reading
Query the views, not the base tables: v_runs, v_requests, v_headers,
v_bodies, v_messages, v_checks, plus the tags table and the body_fts
full-text index.
The query helpers are the statements the CLI and the UI share:
import { openDb, listRuns, listRequests, getRequestDetail } from "@apisurf/wire-db";
const db = openDb({ mustExist: true, skipMigrations: true });
const runs = listRuns(db, 20); // limit defaults to 200
const requests = listRequests(db, runs[0].id);
const detail = getRequestDetail(db, requests[0].id);getRun, getRunAnalytics, getRequestDetail, listRuns, listRunsForEntry,
listRequests, listMessages, listChecks, listRunTags, listScripts.
Projected reads
ENTITIES is the entity map behind wire get and wire ls — six entities
(run, request, check, body, message, header), each with the view it
reads, the column it keys on, its default field set and the filters it accepts.
selectEntity runs a projected read against one; findEntity resolves
request, requests or v_requests to the same definition.
import { openDb, findEntity, selectEntity } from "@apisurf/wire-db";
const entity = findEntity("requests");
const rows = selectEntity(db, entity, {
fields: entity.defaultFields,
filters: { run: 7 }, // filter names, not columns — validated against entity.filters
limit: 20,
});A field name the entity does not have throws UnknownFieldError, which carries
the entity and the valid names. PREVIEW_CHARS is the width of the bounded
preview field that stands in for a payload.
Describing the file
listRelations, relationExists, describeRelation, entityFields and
untypedColumns read the schema out of the file at runtime — this is what
wire schema prints, so it describes the database in hand rather than the
schema this build was compiled against.
Writing
RunWriter is the write path: one instance per run, batched inserts, interned
strings and content-addressed payloads.
import { openDb, RunWriter } from "@apisurf/wire-db";
const writer = new RunWriter(db, {
uid: randomUUID(),
label: "nightly",
entry: "/abs/path/to/flow.ts",
cwd: process.cwd(),
nodeVersion: process.versions.node,
startedAt: Date.now(),
});
writer.write(records); // captured requests, messages and headers
writer.writeChecks(checks); // check() / assert() outcomes
writer.finish({ finishedAt: Date.now(), exitStatus: "ok" });Payloads are deduped by SHA-256, so the same response body recorded a hundred
times is stored once. Text payloads are indexed into body_fts.
Schema version
import { SCHEMA_VERSION } from "@apisurf/wire-db";SCHEMA_VERSION is the migration count this build writes, and it is what
wire --version reports. A file written by a newer build refuses to open on an
older one — comparing these two numbers is the explanation for that error.
Migrations are forward-only and applied in order on open.
License
ISC. Full documentation: https://github.com/apisurf/wire#readme
