@whooshbang/contract-mock 1.0.0-rc.16

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/CHANGELOG.md ADDED
@@ -0,0 +1,141 @@
1
+ # Changelog
2
+
3
+ All notable changes to the deterministic WhooshBang contract mock are
4
+ recorded here.
5
+
6
+ ## 1.0.0-rc.16
7
+
8
+ - Publish the unchanged deterministic mock as the first public npm candidate,
9
+ with explicit public package metadata and a package-local MIT license.
10
+ - Reissue against contracts `1.0.0-rc.13` and SDK `1.0.0-rc.14`; runtime
11
+ behavior and scenario semantics are unchanged, and `1.0.0-rc.15` remains
12
+ immutable.
13
+
14
+ ## 1.0.0-rc.15
15
+
16
+ - Exclude already-acknowledged events from machine polls, including
17
+ acknowledgements recorded beyond an earlier cursor gap.
18
+ - Report a repeated successful acknowledgement as `existing` with the current
19
+ committed cursor instead of replaying the first response body.
20
+ - Reissue against contracts `1.0.0-rc.12`, SDK `1.0.0-rc.13`, and MCP
21
+ `1.0.0-rc.4`; the published schemas and fixture semantics are unchanged and
22
+ `1.0.0-rc.14` remains immutable.
23
+
24
+ ## 1.0.0-rc.14
25
+
26
+ - Reissue the mock against `@whooshbang/contracts@1.0.0-rc.11` and
27
+ `@whooshbang/sdk@1.0.0-rc.12` for the combined replay-refusal and
28
+ machine-answer-retention contract.
29
+ - Publish `answer_retained: true` on every retained `interaction.received`
30
+ event the mock creates or seeds; consumers may also validate the contract's
31
+ redacted false-without-response shape.
32
+ - Include the new closed `replay_unavailable` Problem code through the public
33
+ contract vocabulary. `1.0.0-rc.13` remains immutable.
34
+
35
+ ## 1.0.0-rc.12
36
+
37
+ - Exercise the WhooshBang MCP tools against the mock over both the stdio and
38
+ the OAuth-secured remote transport, so the coding-agent surface is proven
39
+ against the published contract rather than against a stub.
40
+ - Runtime behaviour, scenarios, fixtures, and the published contract are
41
+ unchanged; only the package's development dependencies and its test suite
42
+ moved.
43
+ - Keep `@whooshbang/contracts@1.0.0-rc.9` immutable.
44
+
45
+ ## 1.0.0-rc.11
46
+
47
+ - Add the deterministic customer-endpoint lifecycle: create with a one-time
48
+ secret reveal, verify, bounded-overlap rotation with a rotation conflict,
49
+ pause, resume, disable, attempt inspection, and cross-environment refusal.
50
+ - Authenticate a locally generated C0-03 machine bearer against its registered
51
+ digest, so digest-only bootstrap, rotation, and revocation are provable end
52
+ to end without the service ever seeing the secret.
53
+ - Default the loopback CLI to the system clock so a caller can send an
54
+ interaction whose expiry is computed from real time; `--clock fixed` restores
55
+ the frozen clock, and the programmatic factory stays frozen.
56
+ - Keep `@whooshbang/contracts@1.0.0-rc.9` immutable.
57
+
58
+ ## 1.0.0-rc.10
59
+
60
+ - Add deterministic per-message and per-interaction response inspection,
61
+ including bounded wait, wake, timeout, and machine-ownership behavior for
62
+ `@whooshbang/contracts@1.0.0-rc.9`.
63
+ - Keep polling side-effect free and `1.0.0-rc.9` immutable.
64
+
65
+ ## 1.0.0-rc.9
66
+
67
+ - Reissue the deterministic mock and SDK compatibility boundary against
68
+ `@whooshbang/contracts@1.0.0-rc.8` without adding endpoint behavior.
69
+ - Keep `1.0.0-rc.8` immutable.
70
+
71
+ ## 1.0.0-rc.8
72
+
73
+ - Reissue the deterministic mock against `@whooshbang/contracts@1.0.0-rc.7`
74
+ with explicit retained/redacted answered-interaction summaries.
75
+ - Keep `1.0.0-rc.7` immutable.
76
+
77
+ ## 1.0.0-rc.7
78
+
79
+ - Reissue the deterministic mock with Hono 4.12.34 to address
80
+ `GHSA-8j4g-w8fx-2239`; contract behavior and fixtures are unchanged.
81
+ - Keep `1.0.0-rc.6` immutable.
82
+
83
+ ## 1.0.0-rc.6
84
+
85
+ - Reissue the deterministic mock and SDK against
86
+ `@whooshbang/contracts@1.0.0-rc.6` after moving the generated production API
87
+ server origin to `https://api.whooshbang.com`.
88
+ - Keep `1.0.0-rc.5` immutable; runtime behavior and pinned V1 Problem Details
89
+ and schema identifiers are unchanged.
90
+
91
+ ## 1.0.0-rc.5
92
+
93
+ - Reissue the deterministic mock against
94
+ `@whooshbang/contracts@1.0.0-rc.5`, including the restored paused binding
95
+ state and paused/revoked subscription fixtures.
96
+ - Keep `1.0.0-rc.4` immutable; no compatibility alias or migration is needed
97
+ for the unpublished, undeployed candidate.
98
+
99
+ ## 1.0.0-rc.4
100
+
101
+ - Rename the unused pre-launch package, CLI, public exports, control header,
102
+ Problem Details origin, fixtures, and examples to the canonical WhooshBang
103
+ identity with no compatibility aliases.
104
+
105
+ ## 1.0.0-rc.3
106
+
107
+ - Add executable API credential create/list/rotate/revoke behavior for the
108
+ approved N1-03 contract amendment, including one-time reveal, safe
109
+ inspection, last-used updates, bounded overlap, and terminal revocation.
110
+ - Reissue the mock and its deterministic scenarios under a new immutable
111
+ candidate identity; `1.0.0-rc.2` remains unchanged.
112
+
113
+ ## 1.0.0-rc.2
114
+
115
+ - Add deterministic human-session fixtures and executable account/project
116
+ administration routes for the N1-02 contract.
117
+ - Cover atomic test/live/default-notifier bootstrap, account-scoped
118
+ idempotency, pagination, slug conflict, and non-enumerating cross-account
119
+ reads under a new immutable candidate identity.
120
+
121
+ ## 1.0.0-rc.1
122
+
123
+ - Promote the exact reviewed Wave 0/C0 mock from `1.0.0-draft.1` without a
124
+ behavioral change.
125
+ - Retain the packaged C0SEC-002 cross-machine answer-origin regression and
126
+ bounded npm/pnpm consumer proofs at the release-candidate version.
127
+ - Keep the mock repository-local, deterministic, and free of live provider,
128
+ account, deployment, or production control dependencies.
129
+
130
+ ## 1.0.0-draft.1
131
+
132
+ - Correct machine interaction-answer routing to enqueue an event only for the
133
+ exact machine client recorded when the message was created.
134
+ - Fail safely without rerouting when that originating client is absent,
135
+ revoked, or no longer matches the message binding.
136
+ - Add permanent cross-machine and unavailable-origin regression coverage for
137
+ C0SEC-002. The canonical WhooshBang contract and JavaScript SDK are
138
+ unchanged.
139
+ - Add the packaged `cross-machine-answer-origin` malicious scenario and execute
140
+ its same-binding, absent-origin, revoked-origin, and mismatched-origin cases
141
+ in both npm and pnpm tarball consumers.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 FlowXO LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,173 @@
1
+ # `@whooshbang/contract-mock`
2
+
3
+ Deterministic, in-memory WhooshBang V1 mock for local contract tests.
4
+ It implements the WhooshBang-owned release candidate without a cloud
5
+ account, network service, database, or production provider.
6
+
7
+ Run the current release candidate directly from the public npm registry:
8
+
9
+ ```sh
10
+ npx @whooshbang/contract-mock@next --port 8787
11
+ ```
12
+
13
+ ## Programmatic and Worker-compatible use
14
+
15
+ Each factory call owns isolated state:
16
+
17
+ ```ts
18
+ import {
19
+ CONTRACT_MOCK_FIXTURE_CREDENTIALS,
20
+ createWhooshBangContractMock,
21
+ } from "@whooshbang/contract-mock";
22
+
23
+ const mock = createWhooshBangContractMock({ scenario: "nominal" });
24
+ const response = await mock.fetch(
25
+ new Request("https://api.mock.whooshbang.test/v1/capabilities", {
26
+ headers: {
27
+ authorization:
28
+ `Bearer ${CONTRACT_MOCK_FIXTURE_CREDENTIALS.projectTest}`,
29
+ },
30
+ }),
31
+ );
32
+ ```
33
+
34
+ The mock also executes the account-scoped administration contract. Use
35
+ `humanSession` (or `otherHumanSession` for isolation tests) with
36
+ `GET /v1/account`, `GET/POST /v1/projects`, and
37
+ `GET /v1/projects/{project_id}`. Project creation atomically returns synthetic
38
+ `test` and `live` environments and one `Default` notifier for each.
39
+
40
+ The same human session can execute the environment credential lifecycle:
41
+ `GET/POST /v1/projects/{project_id}/environments/{environment_id}/credentials`
42
+ and the nested `rotate` and `revoke` actions. The mock emits a deterministic
43
+ synthetic `wb_pc1` bearer only from create/rotate, stores it outside the
44
+ inspection snapshot, models bounded overlap, updates safe last-used metadata
45
+ on authentication, and returns `credential_invalid` after revocation. These
46
+ values are local fixtures, not production credential material.
47
+
48
+ Project credentials also execute the customer-endpoint lifecycle under
49
+ `/v1/projects/{project_id}/environments/{environment_id}/customer-endpoints`:
50
+ create with a one-time secret reveal, verify, bounded-overlap rotation, pause,
51
+ resume, disable, and attempt inspection. Environment-scoped paths need an
52
+ environment identifier the bearer does not carry, so
53
+ `CONTRACT_MOCK_FIXTURE_ENVIRONMENTS` publishes the synthetic project and
54
+ environment identifiers the fixture credentials authenticate against. A path
55
+ naming another project or environment is refused as `resource_not_found`.
56
+
57
+ A machine bearer the caller generated locally authenticates once its digest is
58
+ registered through `POST /v1/machine-clients/{id}/credentials`. The mock hashes
59
+ the presented bearer and compares, exactly as the service does, so digest-only
60
+ bootstrap, rotation, and revocation are provable without the mock ever holding
61
+ a readable machine secret.
62
+
63
+ The fetch-only entry point is structurally compatible with a Worker module and
64
+ does not expose the test control API:
65
+
66
+ ```ts
67
+ import {
68
+ createWhooshBangContractMockWorker,
69
+ } from "@whooshbang/contract-mock/worker";
70
+
71
+ export default createWhooshBangContractMockWorker({
72
+ scenario: "nominal",
73
+ });
74
+ ```
75
+
76
+ This package deliberately includes no Wrangler configuration or deployment
77
+ workflow. The Worker-compatible entry is for runtime-neutral contract tests,
78
+ not a hosted or production WhooshBang service.
79
+
80
+ ## Loopback CLI
81
+
82
+ After building or installing the package:
83
+
84
+ ```sh
85
+ whooshbang-contract-mock --port 8787 --scenario nominal
86
+ ```
87
+
88
+ Defaults are `--host 127.0.0.1`, `--port 0` (an ephemeral port),
89
+ `--scenario nominal`, `--clock system`, and no HTTP control surface. Supported
90
+ flags are `--host`, `--port`, `--scenario`, `--clock`, and `--control-token`.
91
+ Supplying a control token restricts the listener to `127.0.0.1`, `::1`, or
92
+ `localhost`; the token must contain 8-4096 visible ASCII characters.
93
+
94
+ The listener is a running service, so its clock advances with the host by
95
+ default: a frozen clock would reject every interaction whose expiry a caller
96
+ computes from the real time. Pass `--clock fixed` for byte-identical
97
+ timestamps. The programmatic factory stays frozen either way.
98
+
99
+ ## Fixtures and scenarios
100
+
101
+ All credentials are synthetic local fixtures:
102
+
103
+ | Exported key | Bearer token | Identity |
104
+ | --- | --- | --- |
105
+ | `humanSession` | `human-session-token` | default account administrator |
106
+ | `otherHumanSession` | `human-other-session-token` | isolated account administrator |
107
+ | `projectTest` | `project-test-token` | default test project |
108
+ | `liveProject` | `project-live-token` | default live project |
109
+ | `projectA` | `project-a-token` | isolated test project A |
110
+ | `projectB` | `project-b-token` | isolated test project B |
111
+ | `readOnly` | `project-read-only-token` | read-only default project |
112
+ | `machineA` | `machine-a-token` | machine stream A |
113
+ | `machineB` | `machine-b-token` | machine stream B |
114
+
115
+ `CONTRACT_MOCK_SCENARIOS` is the typed scenario catalog. The same 25 names are
116
+ available in the exported JSON subpath
117
+ `@whooshbang/contract-mock/scenarios/v1/manifest.json`:
118
+
119
+ ```text
120
+ nominal
121
+ repeated-idempotent-message
122
+ idempotency-conflict
123
+ unbound-subscriber
124
+ retryable-provider-failure
125
+ terminal-provider-rejection
126
+ ambiguous-provider-outcome
127
+ cancellation-before-send
128
+ cancellation-too-late
129
+ interaction-confirm
130
+ interaction-select
131
+ interaction-input
132
+ interaction-duplicate
133
+ interaction-stale
134
+ interaction-expired
135
+ interaction-unauthorized
136
+ machine-event-replay
137
+ machine-cursor-contiguous
138
+ machine-poison-quarantine
139
+ cross-machine-rejection
140
+ cross-machine-answer-origin
141
+ signed-webhook-success
142
+ signed-webhook-retry
143
+ signed-webhook-terminal
144
+ rate-limited
145
+ ```
146
+
147
+ `cross-machine-answer-origin` is the executable C0SEC-002 fixture. Supply
148
+ synthetic same-binding machine credentials through the mock options, create an
149
+ interactive message as machine B, and submit its answer through
150
+ `mock.control.submitInteraction`. Only B may receive the resulting machine
151
+ event. If B's recorded client is absent, revoked, or no longer matches the
152
+ message binding, the interaction still resolves for the customer event while
153
+ the machine event fails closed instead of rerouting to machine A or another
154
+ same-binding peer.
155
+
156
+ ## Explicit test control
157
+
158
+ The programmatic factory exposes `mock.control` directly. HTTP control routes
159
+ exist only in the loopback CLI when `--control-token` is supplied. Every request
160
+ must send that exact token in `WhooshBang-Contract-Mock-Token`.
161
+
162
+ | Route | Purpose |
163
+ | --- | --- |
164
+ | `GET /__contract-mock/snapshot` | inspect redacted deterministic state |
165
+ | `POST /__contract-mock/reset` | reset to a named scenario |
166
+ | `POST /__contract-mock/provider-outcomes` | replace provider outcomes |
167
+ | `POST /__contract-mock/webhook-responses` | replace receiver responses |
168
+ | `POST /__contract-mock/interactions` | submit a synthetic interaction |
169
+ | `POST /__contract-mock/run-pending` | run pending provider work |
170
+
171
+ Control routes are absent from OpenAPI and from the Worker-compatible entry.
172
+ Never expose the CLI control listener or fixture credentials as a production
173
+ service.
package/dist/app.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ import type { WhooshBangContractMock, WhooshBangContractMockOptions } from "./types.js";
2
+ /**
3
+ * Creates one isolated, in-memory WhooshBang contract mock.
4
+ *
5
+ * The returned programmatic control is test-only and has no HTTP route. The
6
+ * separate Node CLI is the only module that can mount the token guard.
7
+ */
8
+ export declare function createWhooshBangContractMock(options?: WhooshBangContractMockOptions): WhooshBangContractMock;
package/dist/app.js ADDED
@@ -0,0 +1,16 @@
1
+ import { createWhooshBangContractMockCore } from "./core.js";
2
+ /**
3
+ * Creates one isolated, in-memory WhooshBang contract mock.
4
+ *
5
+ * The returned programmatic control is test-only and has no HTTP route. The
6
+ * separate Node CLI is the only module that can mount the token guard.
7
+ */
8
+ export function createWhooshBangContractMock(options = {}) {
9
+ const mock = createWhooshBangContractMockCore(options);
10
+ return {
11
+ control: mock.control,
12
+ fetch: (request) => mock.fetch(request),
13
+ inspect: () => mock.inspect(),
14
+ };
15
+ }
16
+ //# sourceMappingURL=app.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gCAAgC,EAAE,MAAM,WAAW,CAAC;AAO7D;;;;;GAKG;AACH,MAAM,UAAU,4BAA4B,CAC1C,UAAyC,EAAE;IAE3C,MAAM,IAAI,GAAG,gCAAgC,CAAC,OAAO,CAAC,CAAC;IACvD,OAAO;QACL,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,KAAK,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC;QACvC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE;KAC9B,CAAC;AACJ,CAAC"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+ import type { Server } from "node:http";
3
+ import type { ContractMockScenarioName } from "./types.js";
4
+ export interface ContractMockCliOptions {
5
+ /**
6
+ * `system` advances timestamps with the host clock; `fixed` freezes them.
7
+ *
8
+ * The loopback listener is a running service, so it defaults to `system`:
9
+ * a frozen clock rejects every interaction whose expiry a caller computes
10
+ * from the real time. The programmatic factory stays frozen for contract
11
+ * tests that need byte-identical output.
12
+ */
13
+ readonly clock?: "fixed" | "system";
14
+ readonly controlToken?: string;
15
+ readonly host: string;
16
+ readonly port: number;
17
+ readonly scenario: ContractMockScenarioName;
18
+ }
19
+ export interface RunningContractMock {
20
+ readonly host: string;
21
+ readonly port: number;
22
+ readonly server: Server;
23
+ close(): Promise<void>;
24
+ }
25
+ export declare function parseContractMockCliArgs(args: readonly string[]): ContractMockCliOptions;
26
+ export declare function startContractMockCli(options: ContractMockCliOptions): Promise<RunningContractMock>;
package/dist/cli.js ADDED
@@ -0,0 +1,249 @@
1
+ #!/usr/bin/env node
2
+ import { realpathSync } from "node:fs";
3
+ import { createServer } from "node:http";
4
+ import { pathToFileURL } from "node:url";
5
+ import { validateProblem } from "@whooshbang/contracts";
6
+ import { mountContractMockControlSurface } from "./control-surface.js";
7
+ import { createWhooshBangContractMockCore } from "./core.js";
8
+ import { isContractMockScenarioName } from "./scenarios.js";
9
+ const MAX_ADAPTER_REQUEST_BYTES = 1024 * 1024;
10
+ const MAX_ADAPTER_DIAGNOSTIC_SEQUENCE = 999_999_999;
11
+ function isLoopbackHost(value) {
12
+ return (value === "127.0.0.1" ||
13
+ value === "::1" ||
14
+ value.toLowerCase() === "localhost");
15
+ }
16
+ function optionValue(args, index) {
17
+ const value = args[index + 1];
18
+ if (value === undefined || value.startsWith("--")) {
19
+ throw new RangeError(`Missing value for ${args[index] ?? "option"}.`);
20
+ }
21
+ return value;
22
+ }
23
+ export function parseContractMockCliArgs(args) {
24
+ let clock = "system";
25
+ let controlToken;
26
+ let host = "127.0.0.1";
27
+ let port = 0;
28
+ let scenario = "nominal";
29
+ for (let index = 0; index < args.length; index += 1) {
30
+ const argument = args[index];
31
+ if (argument === "--port") {
32
+ const value = optionValue(args, index);
33
+ if (!/^\d+$/u.test(value)) {
34
+ throw new RangeError("--port must be an integer from 0 through 65535.");
35
+ }
36
+ port = Number(value);
37
+ if (port < 0 || port > 65_535) {
38
+ throw new RangeError("--port must be an integer from 0 through 65535.");
39
+ }
40
+ index += 1;
41
+ }
42
+ else if (argument === "--scenario") {
43
+ const value = optionValue(args, index);
44
+ if (!isContractMockScenarioName(value)) {
45
+ throw new RangeError("Unknown contract-mock scenario.");
46
+ }
47
+ scenario = value;
48
+ index += 1;
49
+ }
50
+ else if (argument === "--control-token") {
51
+ controlToken = optionValue(args, index);
52
+ index += 1;
53
+ }
54
+ else if (argument === "--host") {
55
+ host = optionValue(args, index);
56
+ index += 1;
57
+ }
58
+ else if (argument === "--clock") {
59
+ const value = optionValue(args, index);
60
+ if (value !== "fixed" && value !== "system") {
61
+ throw new RangeError("--clock must be system or fixed.");
62
+ }
63
+ clock = value;
64
+ index += 1;
65
+ }
66
+ else {
67
+ throw new RangeError("Unknown contract-mock CLI option.");
68
+ }
69
+ }
70
+ if (controlToken !== undefined && !isLoopbackHost(host)) {
71
+ throw new RangeError("Control mode may only bind to an explicit loopback address.");
72
+ }
73
+ return {
74
+ clock,
75
+ ...(controlToken === undefined ? {} : { controlToken }),
76
+ host,
77
+ port,
78
+ scenario,
79
+ };
80
+ }
81
+ async function readNodeRequest(request) {
82
+ if (request.method === "GET" || request.method === "HEAD") {
83
+ return undefined;
84
+ }
85
+ const chunks = [];
86
+ let length = 0;
87
+ for await (const incomingChunk of request) {
88
+ const chunk = incomingChunk;
89
+ const bytes = typeof chunk === "string"
90
+ ? new TextEncoder().encode(chunk)
91
+ : chunk instanceof Uint8Array
92
+ ? Uint8Array.from(chunk)
93
+ : undefined;
94
+ if (bytes === undefined) {
95
+ throw new TypeError("The local adapter received an invalid body chunk.");
96
+ }
97
+ length += bytes.byteLength;
98
+ if (length > MAX_ADAPTER_REQUEST_BYTES) {
99
+ throw new RangeError("Request exceeded the local adapter limit.");
100
+ }
101
+ chunks.push(bytes);
102
+ }
103
+ const body = new Uint8Array(length);
104
+ let offset = 0;
105
+ for (const chunk of chunks) {
106
+ body.set(chunk, offset);
107
+ offset += chunk.byteLength;
108
+ }
109
+ return body;
110
+ }
111
+ function requestHeaders(request) {
112
+ const headers = new Headers();
113
+ for (let index = 0; index < request.rawHeaders.length; index += 2) {
114
+ const name = request.rawHeaders[index];
115
+ const value = request.rawHeaders[index + 1];
116
+ if (name !== undefined && value !== undefined) {
117
+ headers.append(name, value);
118
+ }
119
+ }
120
+ return headers;
121
+ }
122
+ function createAdapterDiagnosticIdGenerator() {
123
+ let sequence = 0;
124
+ return () => {
125
+ if (sequence >= MAX_ADAPTER_DIAGNOSTIC_SEQUENCE) {
126
+ throw new Error("The local adapter diagnostic sequence is exhausted.");
127
+ }
128
+ sequence += 1;
129
+ return `diag_adapter_${String(sequence).padStart(9, "0")}`;
130
+ };
131
+ }
132
+ function adapterProblem(diagnosticId) {
133
+ const value = {
134
+ code: "request_invalid",
135
+ detail: "The local mock request could not be processed.",
136
+ diagnostic_id: diagnosticId,
137
+ retryable: false,
138
+ status: 400,
139
+ title: "Request invalid",
140
+ type: "https://whooshbang.flowxo.com/problems/request_invalid",
141
+ };
142
+ if (!validateProblem(value)) {
143
+ throw new Error("The local adapter Problem is not contract-valid.");
144
+ }
145
+ return value;
146
+ }
147
+ function writeAdapterProblem(outgoing, diagnosticId) {
148
+ const problem = adapterProblem(diagnosticId);
149
+ outgoing.statusCode = problem.status;
150
+ outgoing.setHeader("content-type", "application/problem+json");
151
+ outgoing.setHeader("whooshbang-diagnostic-id", problem.diagnostic_id);
152
+ outgoing.end(JSON.stringify(problem));
153
+ }
154
+ export async function startContractMockCli(options) {
155
+ if (options.controlToken !== undefined && !isLoopbackHost(options.host)) {
156
+ throw new RangeError("Control mode may only bind to an explicit loopback address.");
157
+ }
158
+ const mock = createWhooshBangContractMockCore({
159
+ ...(options.clock === "fixed"
160
+ ? {}
161
+ : { clock: { now: () => new Date() } }),
162
+ scenario: options.scenario,
163
+ });
164
+ if (options.controlToken !== undefined) {
165
+ mountContractMockControlSurface(mock, options.controlToken);
166
+ }
167
+ const nextAdapterDiagnosticId = createAdapterDiagnosticIdGenerator();
168
+ const server = createServer((incoming, outgoing) => {
169
+ void (async () => {
170
+ try {
171
+ const body = await readNodeRequest(incoming);
172
+ const requestBody = body === undefined ? undefined : Uint8Array.from(body).buffer;
173
+ const request = new Request(new URL(incoming.url ?? "/", `http://${incoming.headers.host ?? `${options.host}:${options.port}`}`), {
174
+ ...(requestBody === undefined ? {} : { body: requestBody }),
175
+ headers: requestHeaders(incoming),
176
+ method: incoming.method ?? "GET",
177
+ });
178
+ const response = await mock.fetch(request);
179
+ outgoing.statusCode = response.status;
180
+ response.headers.forEach((value, name) => {
181
+ outgoing.setHeader(name, value);
182
+ });
183
+ outgoing.end(new Uint8Array(await response.arrayBuffer()));
184
+ }
185
+ catch {
186
+ writeAdapterProblem(outgoing, nextAdapterDiagnosticId());
187
+ }
188
+ })();
189
+ });
190
+ await new Promise((resolve, reject) => {
191
+ server.once("error", reject);
192
+ server.listen(options.port, options.host, () => {
193
+ server.off("error", reject);
194
+ resolve();
195
+ });
196
+ });
197
+ const address = server.address();
198
+ return {
199
+ close: () => new Promise((resolve, reject) => {
200
+ server.close((error) => {
201
+ if (error === undefined) {
202
+ resolve();
203
+ }
204
+ else {
205
+ reject(error);
206
+ }
207
+ });
208
+ }),
209
+ host: options.host,
210
+ port: address.port,
211
+ server,
212
+ };
213
+ }
214
+ async function main() {
215
+ const options = parseContractMockCliArgs(process.argv.slice(2));
216
+ const running = await startContractMockCli(options);
217
+ process.stdout.write(`${JSON.stringify({
218
+ clock: options.clock ?? "system",
219
+ control_enabled: options.controlToken !== undefined,
220
+ event: "contract_mock_started",
221
+ host: running.host,
222
+ port: running.port,
223
+ scenario: options.scenario,
224
+ })}\n`);
225
+ const close = () => {
226
+ void running.close().finally(() => process.exit(0));
227
+ };
228
+ process.once("SIGINT", close);
229
+ process.once("SIGTERM", close);
230
+ }
231
+ function isMainModule() {
232
+ const entryPath = process.argv[1];
233
+ if (entryPath === undefined) {
234
+ return false;
235
+ }
236
+ try {
237
+ return import.meta.url === pathToFileURL(realpathSync(entryPath)).href;
238
+ }
239
+ catch {
240
+ return false;
241
+ }
242
+ }
243
+ if (isMainModule()) {
244
+ void main().catch(() => {
245
+ process.stderr.write("The contract mock could not start safely.\n");
246
+ process.exitCode = 1;
247
+ });
248
+ }
249
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { Hono } from "hono";
2
+ import type { ContractMockControl, ContractMockSnapshot } from "./types.js";
3
+ interface ControlSurfaceTarget {
4
+ readonly app: Hono;
5
+ readonly control: ContractMockControl;
6
+ inspect(): ContractMockSnapshot;
7
+ }
8
+ /**
9
+ * Mounts the CLI-only, test-only HTTP control surface. This module is not
10
+ * imported by either public package entry point or the Worker entry point.
11
+ */
12
+ export declare function mountContractMockControlSurface(target: ControlSurfaceTarget, token: string): void;
13
+ export {};