pact-foundation/pact-js
github.com/pact-foundation/pact-js ↗ · 318 files · 1.3 MB
Pact JS Fast, easy and reliable testing for your APIs and microservices.
Overview
Pact JS is a TypeScript contract-testing SDK for teams building HTTP APIs and message-based integrations. Consumer tests define expected interactions, exercise application clients or message handlers, and generate pact files; provider verification checks those contracts against running services or registered message producers. Its distinctive architecture combines versioned JavaScript builders with native pact-core operations, an Express verification proxy, flexible matching rules, and plugins for additional content types and transports.
System map
The repository delivers an npm SDK whose public entrypoint exposes several generations of contract-testing APIs. JavaScript owns builder state, callbacks, and verification middleware, while pact-core owns native contract and mock-server operations. Generated pact files connect consumer and provider testing; release and documentation workflows distribute the package and synchronize documentation.
- src/index.ts exports PactV2, Verifier, legacy message APIs, V3 exports, and V4 exports; PactV4 is aliased as Pact and MatchersV3 as Matchers.
- PactV3 and PactV4 create ConsumerPact handles through makeConsumerPact and attach pact-js version metadata; their default specification versions are V3 and V4 respectively.
- The API generations use different orchestration structures: V2 exposes setup/addInteraction/verify, V3 owns a mutable current interaction, and V4 returns staged HTTP, synchronous, asynchronous, and GraphQL builders.
- Provider verification creates a temporary Express/http-proxy server with state and message endpoints, then passes its address and a message transport to serviceFactory.verifyPacts.
- Contract persistence is delegated to pact-core writePactFile calls using caller-selected directories or ./pacts; V2 additionally passes merge-versus-overwrite behavior.
- VerifierOptions combines pact-core options with local ProxyOptions; documented broker integration includes pactBrokerUrl, consumerVersionSelectors, broker authentication, and publishVerificationResult.
- package.json requires Node >=22 and defines npm scripts for tsc compilation/distribution, Biome formatting/linting, tsc --noEmit, Vitest, and regression testing; tsconfig emits declarations and source maps into dist.
- Publishing is dispatch-driven: latest checks out master and runs release.sh with SKIP_EXAMPLES=true, while the 9.x workflow checks out 9.x.x; Markdown pushes on master dispatch a docs.pact.io update.
Feature catalog
The public surface supports HTTP request/response contracts, asynchronous event contracts, synchronous messages, and GraphQL over HTTP. Matching APIs express flexible contracts while supplying concrete examples to consumer tests. Provider callbacks, broker options, interaction metadata, and plugins support more involved verification workflows.
- V4 HTTP tests configure method/path, query parameters, headers, body, and expected status through fluent builders, then execute a callback against a dynamic mock server with configurable host, port, TLS, and CORS.
- MatchersV3 supplies type, regex, numeric, bounded-array, key/value, status-code, date/time, UUID, URL, equality, inclusion, null, and array-containing declarations.
- HTTP builders accept binary files, Buffer bodies, multipart files with an optional boundary, and explicit matching rules; XmlBuilder and XmlElement construct XML templates containing text and repeated-element matchers.
- addGraphQLInteraction supports operation names, variables, queries, and mutations; query text is validated and wrapped in a whitespace-flexible regex inside an application/json request body.
- V4 asynchronous messages support JSON or binary content, metadata, and explicit rules; synchronous messages separately configure request and response content and expose Request plus a Response array to callbacks.
- Provider verification supports named state setup/teardown handlers, beforeEach/afterEach hooks, Express request filters, message producer registries, TLS client credentials, and optional broker result publication.
- given accepts state parameters, fromProviderState declares generator expressions, and provider callbacks can return generated values; the account example connects ${accountId} to the handler's returned accountId.
- HTTP and message builders support pending flags, comments, test names, and grouped references; usingPlugin loads named/versioned plugins, and message plugin builders can start custom transports.
Data flow
Consumer inputs become native interaction declarations rather than a JavaScript implementation of contract matching. Native mock servers or reified messages feed application callbacks, and successful orchestration writes contracts to disk. Provider verification routes native requests through JavaScript middleware, state callbacks, and message producers before returning results to the caller.
- matcherValueOrString preserves string inputs and JSON-stringifies other values; RequestBuilder translates query/header arrays into indexed native withQuery/withRequestHeader calls and serializes JSON bodies.
- Status matchers split into a concrete example passed to withStatus and status matching rules passed to withResponseMatchingRules; ordinary matcher reification recursively extracts examples.
- Explicit Rules inputs are checked for recognized parts and rule-array/path structure, then converted to part/path/matchers maps; conversion removes pact:generator:type and emits entries only for truthy rule.path.
- V4 HTTP executeTest captures callback errors, reads native mismatches and match success, and on its normal success branch writes a pact before mock-server cleanup, plugin cleanup, and parent reset.
- Asynchronous reification parses native JSON into contents.content plus metadata and base64-decodes content when the builder marked it binary; synchronous callbacks receive native request and response contents directly.
- Provider-state POST bodies become ProviderState values; setupStates selects stateHandlers[state.state], invokes setup or teardown with params, and returns callback data as JSON for native verification.
- The verification proxy parses JSON, form, and raw bodies before requestFilter; forwarding reconstructs Buffers or JSON objects into a readable body stream and uses providerBaseUrl, TLS options, and HTTPS_PROXY/HTTP_PROXY.
- /_messages dispatches by description, copies request metadata, decodes encoded synchronous content, and invokes the producer; metadata-bearing outputs become base64 JSON headers plus a content-type-aware response, while errors return HTTP 500.
Behavior traces
Three concrete flows were followed through source: V4 HTTP consumer testing, HTTP provider verification, and V4 asynchronous message testing. Each trace follows declaration, execution, and persistence or result handling. The examples show application clients and handlers participating in those flows rather than treating the library's builders as the entire test.
- HTTP consumer startup: new Pact constructs PactV4 → setup → makeConsumerPact with default V4 → addMetadata; addInteraction then creates a native interaction and an UnconfiguredInteraction carrying a parent-reset callback.
- HTTP declaration: given/uponReceiving configure native state and description → withRequest configures method/path and runs RequestBuilder → willRespondWith sets status and runs ResponseBuilder → returns InteractionWithResponse.
- HTTP execution: InteractionWithResponse.executeTest → shared executeTest → native transport mock startup → UserServiceClient.getUser sends Axios GET → callback assertions → mismatch checks → writePactFile/cleanup/reset; callback errors are rethrown and failure branches skip writing.
- Provider startup: the example starts createApp on port 3001 → constructs Verifier with the generated pact path and state handlers → verifyProvider creates hooks state and proxy → waitForServerReady → runProviderVerification → verifyPacts.
- Provider state and request replay: /_pactSetup runs hook middleware before requestFilter and state dispatch → setupStates invokes the named callback → clearUsers/seedUser populate the example Map → other requests reach proxy.web → createApp returns /users or /users/:id JSON.
- Provider completion: registerHooks records hook exceptions while continuing middleware; after verifyPacts resolves, verifyProvider closes the server and rejects recorded hook errors, and its catch branch also closes the server before rethrowing.
- Async message declaration: PactV4.addAsynchronousInteraction → newAsynchronousMessage → given → expectsToReceive → AsynchronousMessageBuilder.withJSONContent/withMetadata; the OrderPlaced example supplies matching declarations and queue metadata.
- Async execution and persistence: AsynchronousMessageWithContent.executeTest → executeNonTransportTest → reifyMessage/JSON.parse → v4SynchronousBodyHandler extracts contents.content → application handler; callback failure cleans without writing, while success writes ./pacts or opts.dir, cleans plugins, and resets.
Testing signals
Tests and CI were inspected, not executed; their existence establishes intended checks rather than passing results. Root Vitest combines mocked unit tests with native-backed integration tests, while examples and regression packages run consumer suites before provider suites. This is a bounded inspection, and the selected lifecycle specs do not establish coverage of every mismatch, persistence, transport, or cleanup failure branch.
- Root npm test maps to vitest run with a plugin-installing pretest; Vitest discovers src/**/*.spec.ts, uses forks and 60-second timeouts, writes JUnit output, and configures V8 coverage with 50% line/statement thresholds.
- The inspected V3 and V4 HTTP lifecycle specs replace ConsumerPact operations with mocks and assert CORS, HTTP/HTTPS selection, and a negative startup result; those files do not test mismatch rejection or write/cleanup exceptions.
- hooks.spec.ts drives synthetic setup/teardown requests to test once-per-interaction execution, multiple states, subsequent interactions after hook failure, continued next(), and retried setup idempotence.
- Verifier tests mock the proxy and check closure on resolution/rejection plus exclusion of TLS credentials from core options; proxyRequest tests inspect target credentials, SSL/origin options, proxy-env precedence, and readable buffers, without establishing a real mTLS handshake.
- pact.integration.spec.ts uses actual Axios and TCP interactions, reads generated pact JSON to assert metadata/references, and exercises the MATT plugin over HTTP and synchronous TCP.
- Regression configs explicitly enumerate consumer/provider files and alias the SDK to src/index.ts; provider configuration substitutes an aws-sdk mock and inlines graphql-http, while the package runs consumers before providers after cleaning pact outputs.
- Example suites cover application-client HTTP calls, GraphQL query requests, message handling/production, and provider-generated IDs; their manifests separate consumer and provider commands and order them in npm test.
- Test CI defines Node 22/24/26 across macOS, Linux x64/ARM, and Windows, plus Alpine, lint, format, examples, and regression jobs; the completion job rejects failed, cancelled, or skipped dependencies and unit jobs upload coverage/test reports.
Risk map
These findings are source observations and explicitly labeled hypotheses, not confirmed vulnerabilities. Sensitive boundaries include temporary HTTP listeners, request logging, caller-selected filesystem paths, and plugin executables installed outside the checkout. Failure handling deserves particular attention because ordinary state errors, experimental mismatch filtering, and cleanup exceptions follow different paths.
- State-handler exceptions are logged and answered with HTTP 200, unlike hook exceptions accumulated for final rejection; stateHandler.spec.ts explicitly asserts this successful HTTP response after a rejected setup callback.
- Hypothesis: debug/trace output can expose sensitive test data because request/response tracers include bodies and headers and verifier trace logging serializes core options, which may contain broker authentication fields. (inferred)
- Hypothesis: legacy MessageProviderPact may expose its callback dispatcher beyond loopback because setupProxyServer calls listen() without a host and registers a catch-all handler; the modern verifier explicitly defaults its bind host to 127.0.0.1. (inferred)
- Filesystem/process boundaries are caller-sensitive: binaryFile reads the supplied path without repo-root restriction, pact output uses opts.dir, and root pretest downloads a CLI into ~/.pact/bin then executes it to install MATT; the inspected downloader contains no checksum-validation step.
- Any nonempty PACT_EXPERIMENTAL_FEATURE_ALLOW_MISSING_REQUESTS value activates filtering that removes plugin mismatch objects and request-mismatch entries; HTTP execution rejects native failure only when filtered errors remain, so this flag materially changes validation.
- Hypothesis: native mismatch inspection or pact-write exceptions can bypass remaining HTTP cleanup because these calls sit outside the callback try/catch and cleanup writes before stopping servers/plugins, without a finally guard. (inferred)
- The public V4UnconfiguredInteraction interface advertises withCompleteRequest, but UnconfiguredInteraction.withCompleteRequest immediately throws an unimplemented error.
- Request.send creates an HTTPS agent with rejectUnauthorized=false and maxBodyLength=Infinity for caller-provided URLs; this differs from Verifier's default validateSSL=true, so the helper cannot be assumed to inherit verifier TLS behavior.
Extension points
Extension points are primarily typed builder stages and caller-provided callback registries. Matching declarations and explicit Rules provide two paths into native matching, while plugins add content and transport support. Changes to public capabilities must account for exports, types, concrete builders, and cleanup behavior together.
- src/index.ts is the public export aggregation point; V4ConsumerPact factories in PactV4 create HTTP, synchronous, asynchronous, and GraphQL stages and supply their reset callbacks.
- HTTP builder callbacks are typed by V4RequestBuilderFunc/V4ResponseBuilderFunc and instantiated as RequestBuilder/ResponseBuilder, making those interfaces and classes the paired extension points for request/response options.
- Matcher helpers return declarative objects using pact:matcher:type, value, and optional generator properties; isMatcher and recursive reify define how declarations are recognized and reduced to examples.
- Advanced matching plugs in through validateRules/convertRulesToFFI, used by HTTP and message builders; adding a new rule part requires considering validateRules' fixed path/body/header/query/status list.
- StateHandlers is a string-keyed registry of promise-returning functions or setup/teardown objects; setupStates performs exact state-name lookup and passes state params to the selected lifecycle function.
- messageProviders dispatches by interaction description; providerWithMetadata adapts producers to metadata-bearing responses, while synchronousBodyHandler/asynchronousBodyHandler and their V4 counterparts adapt consumer body functions.
- requestFilter is an Express RequestHandler installed before state/message/catch-all dispatch; beforeEach/afterEach hooks are separate setup-path middleware with per-verification HooksState.
- usingPlugin calls native addPlugin and switches to plugin-capable stages; RequestWithPluginBuilder extends RequestBuilder with pluginContents, and synchronous plugin startTransport accepts transport/address/config while Verifier appends its message transport.
Contributor handoff
Tests and CI were inspected, not executed, and this inventory is bounded rather than exhaustive. Begin future work at the public export and the specific generation of API being changed, then follow its native calls and lifecycle tests. Preserve compatibility, inspect npm lifecycle side effects before requesting execution, and use source behavior to resolve documentation drift.
- Read src/index.ts, src/v4/index.ts, and the migration import table first: unqualified Pact now means V4 and Matchers means V3, while PactV2 preserves the older HTTP interface.
- Use the root package's Node >=22 requirement for this checkout; README still describes Node 16+ for v12 and example manifests advertise >=20, so those older statements do not establish current root support.
- Formatting conventions are Biome, single-quoted JavaScript, and two-space LF files; npm run check composes format and lint, and lint includes tsc --noEmit. CONTRIBUTING requests tests and separation of unrelated changes.
- For later authorized validation, select the affected Vitest spec before broad suites; src/v4/http/index.spec.ts targets transport configuration and hooks.spec.ts targets interaction-boundary behavior. Neither these tests nor configured CI jobs were executed in this investigation.
- Inspect lifecycle scripts before execution: root npm test downloads/installs plugins under ~/.pact, regression npm test removes pact directories, and the plugin example's pretest invokes a CLI command whose failure is suppressed with || true.
- Preserve consumer-before-provider sequencing: example and regression npm test commands generate contracts first, provider examples read those generated pact paths, and regression configs alias the SDK to local TypeScript source.
- Keep message envelopes generation-specific: legacy MessageConsumerPact delivers contents directly, V4 async adapters extract contents.content, and synchronous V4 callbacks expose Request.content plus Response[].content.
- Review reset timing when changing synchronous execution: SynchronousMessageWithResponse.executeTest invokes cleanupFn immediately after starting executeNonTransportTest, whose completion cleanup invokes it again; the supplied callback recreates the PactV4 handle.
Languages
| Language | Files | Share of source |
|---|---|---|
| TypeScript | 194 | 93% |
| JSON | 33 | docs/config |
| Markdown | 30 | docs/config |
| Shell | 14 | 7% |
| YAML | 11 | docs/config |
| JavaScript | 1 | 0% |
Start reading here
Probable source entrypoint: src/index.ts — inferred from the directory tree and manifest, not the upstream docs.
Source roots
src/ (105 files)
Source layout — src/
| Path | Files |
|---|---|
src/common/ | 13 |
src/dsl/ | 32 |
src/errors/ | 4 |
src/httpPact/ | 5 |
src/v3/ | 11 |
src/v4/ | 28 |
src/xml/ | 6 |
src/index.ts | 1 |
src/messageConsumerPact.spec.ts | 1 |
src/messageConsumerPact.ts | 1 |
src/messageProviderPact.spec.ts | 1 |
src/messageProviderPact.ts | 1 |
src/pact.integration.spec.ts | 1 |
Repository structure
| Top-level path | Files |
|---|---|
src/ | 105 |
regression/ | 85 |
examples/ | 60 |
docs/ | 17 |
scripts/ | 16 |
.github/ | 14 |
Docs found: CONTRIBUTING.mdREADME.mddocs/consumer.mddocs/graphql.mddocs/matching.mddocs/messages.mddocs/migrations/16.mddocs/migrations/9-10.mddocs/plugins.mddocs/provider.md
Package
package.json — @pact-foundation/pact v17.1.4, 36 declared dependencies.
@babel/cli@babel/core@babel/preset-env@biomejs/biome@pact-foundation/pact-core@scarf/scarf@tsconfig/node22@types/express@types/http-proxy@types/lodash@types/nock@types/node
CI & config entrypoints
README.md— Repository readmesrc/index.ts— TS entrypoint.github/workflows/publish.yml— GitHub Actions workflow.github/workflows/publish-9x.yml— GitHub Actions workflow.github/workflows/test.yml— GitHub Actions workflow.github/workflows/trigger_pact_docs_update.yml— GitHub Actions workflowpackage.json— npm scripts: check, check:fix, clean, compile, dist, docker:alpine:attach, docker:alpine:build, docker:debian:attachregression/package.json— npm scripts: pretest, test, test:consumer, test:providerexamples/graphql/package.json— npm scripts: test, test:consumer, test:providerexamples/http/package.json— npm scripts: test, test:consumer, test:provider
Structural health signals
Average score 9.2/10 across 96 analyzed source files; 1 hotspot surfaced.
src/v3/display.ts— 166 lines, score 6.4 (Deep nesting, Moderate churn)src/v3/pact.ts— 340 lines, score 7.1 (Long function/block, High churn)src/httpPact/tracing.ts— 64 lines, score 7.6 (Deep nesting, Moderate churn)
These are deterministic review leads — size, churn, and structural signals — not proof of a bug in this project.
Recent history
18f30a1aa44a2026-10-02 — chore(deps): update dependency fast-xml-parser to v5.11.2 (#1967)3d758398475c2026-10-01 — chore(deps): update dependency graphql-http to v1.23.1 (#1966)e5cb0c6dfd242026-09-29 — chore(deps): update dependency @types/node to v24.19.0 (#1965)c96d2ddef2672026-09-28 — chore(deps): lock file maintenance (#1963)5cc953326b522026-09-28 — chore(deps): update dependency serverless to v4.43.0 (#1962)