@hellaweb3/wiremock-testcontainers-nodejs 0.2.0 → 0.3.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/dist/index.cjs CHANGED
@@ -1,194 +1 @@
1
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- let node_fs = require("node:fs");
3
- let node_path = require("node:path");
4
- let testcontainers = require("testcontainers");
5
- let node_module = require("node:module");
6
- let node_url = require("node:url");
7
- // src/sources.ts
8
- const PACKAGE_NAME = /^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/u;
9
- function fail(message) {
10
- throw new Error(message);
11
- }
12
- function within(context, fn) {
13
- try {
14
- return fn();
15
- } catch (error) {
16
- throw new Error(`${context}: ${error.message}`, { cause: error });
17
- }
18
- }
19
- const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
20
- const show = (value) => value instanceof URL ? value.href : JSON.stringify(value) ?? String(value);
21
- const text = (value, field) => typeof value === "string" && value.trim() !== "" ? value : fail(`"${field}" must be a non-empty string, got ${show(value)}`);
22
- const toPath = (value, field) => {
23
- if (!(value instanceof URL) && !text(value, field).startsWith("file:")) return (0, node_path.resolve)(value);
24
- try {
25
- return (0, node_url.fileURLToPath)(value);
26
- } catch {
27
- return fail(`"${field}" must be a path or a file: URL, got ${show(value)}`);
28
- }
29
- };
30
- const existingFile = (path) => {
31
- const stats = (0, node_fs.statSync)(path, { throwIfNoEntry: false });
32
- return stats?.isFile() ? path : fail(`"${path}" ${stats ? "is not a file" : "does not exist"}`);
33
- };
34
- const packageRoot = (name) => {
35
- if (!PACKAGE_NAME.test(name)) fail(`"${name}" is not a valid npm package name`);
36
- return ((0, node_module.createRequire)((0, node_path.join)(process.cwd(), "noop.js")).resolve.paths(name) ?? []).map((dir) => (0, node_path.join)(dir, name)).find((dir) => (0, node_fs.existsSync)((0, node_path.join)(dir, "package.json"))) ?? fail(`package "${name}" is not installed (searched from "${process.cwd()}")`);
37
- };
38
- function resolveFile(source) {
39
- if (typeof source === "string" || source instanceof URL) return existingFile(toPath(source, "source"));
40
- if (!isRecord(source)) return fail(`unsupported source ${show(source)}; expected a path, a file: URL, { file }, { package, file } or { module, file }`);
41
- const { file, package: pkg, module } = source;
42
- if (pkg !== void 0 && module !== void 0) fail("a source may set \"package\" or \"module\", not both");
43
- if (pkg !== void 0) {
44
- const root = packageRoot(text(pkg, "package"));
45
- const path = (0, node_path.resolve)(root, text(file, "file"));
46
- return (0, node_path.relative)(root, path).split(node_path.sep)[0] === ".." ? fail(`"${String(file)}" must point to a file inside package "${pkg}"`) : existingFile(path);
47
- }
48
- if (module === void 0) return existingFile(toPath(file, "file"));
49
- const anchor = toPath(module, "module");
50
- return existingFile((0, node_path.resolve)((0, node_fs.statSync)(anchor, { throwIfNoEntry: false })?.isDirectory() ? anchor : (0, node_path.dirname)(anchor), text(file, "file")));
51
- }
52
- const containerName = (name) => {
53
- const value = text(name, "name");
54
- return value.split("/").some((part) => [
55
- "",
56
- ".",
57
- ".."
58
- ].includes(part) || /[\\\0]/u.test(part)) ? fail(`name "${value}" must be a relative path using "/" separators, without empty, "." or ".." segments`) : value;
59
- };
60
- const inlineJson = (json) => typeof json === "string" ? json : JSON.stringify(json, null, 2) ?? fail("\"json\" must be a JSON string or an object");
61
- const parseJson = (json) => {
62
- try {
63
- return JSON.parse(json);
64
- } catch (error) {
65
- return fail(`invalid JSON (${error.message})`);
66
- }
67
- };
68
- function stubIds(json) {
69
- const value = parseJson(json);
70
- const many = isRecord(value) && "mappings" in value;
71
- const stubs = many ? value.mappings : [value];
72
- if (!Array.isArray(stubs) || stubs.length === 0) fail("\"mappings\" must be a non-empty array of stub mappings");
73
- return stubs.flatMap((stub, index) => {
74
- if (!isRecord(stub) || !isRecord(stub.request) || !isRecord(stub.response)) fail(`${many ? `mappings[${index}]` : "the mapping"} is not a WireMock stub mapping ("request" and "response" objects are required)`);
75
- const id = stub.id ?? stub.uuid;
76
- return typeof id === "string" ? [id] : [];
77
- });
78
- }
79
- const stubIdsOf = (path) => {
80
- try {
81
- return path.endsWith(".json") ? stubIds((0, node_fs.readFileSync)(path, "utf8")) : [];
82
- } catch {
83
- return [];
84
- }
85
- };
86
- const listFiles = (dir) => (0, node_fs.readdirSync)(dir, {
87
- recursive: true,
88
- withFileTypes: true
89
- }).filter((entry) => !entry.isDirectory()).map((entry) => (0, node_path.relative)(dir, (0, node_path.join)(entry.parentPath, entry.name)).split(node_path.sep).join("/"));
90
- // src/index.ts
91
- const MAPPINGS = "/home/wiremock/mappings";
92
- const FILES = "/home/wiremock/__files";
93
- const INLINE = "inline JSON";
94
- var WiremockContainer = class extends testcontainers.GenericContainer {
95
- #origins = /* @__PURE__ */ new Map();
96
- #stubIds = /* @__PURE__ */ new Map();
97
- constructor(image = "wiremock/wiremock") {
98
- super(image);
99
- this.withExposedPorts(8080);
100
- this.withWaitStrategy(testcontainers.Wait.forListeningPorts());
101
- }
102
- withMappings(directory) {
103
- within("WiremockContainer.withMappings", () => {
104
- const mappings = (0, node_path.join)(directory, "mappings");
105
- if (!(0, node_fs.existsSync)(mappings)) fail(`"${mappings}" does not exist`);
106
- if (!(0, node_fs.statSync)(mappings).isDirectory()) fail(`"${mappings}" is not a directory`);
107
- const files = (0, node_path.join)(directory, "__files");
108
- const dirs = [{
109
- source: mappings,
110
- target: MAPPINGS
111
- }, ...(0, node_fs.existsSync)(files) ? [{
112
- source: files,
113
- target: FILES
114
- }] : []];
115
- this.#claim(dirs.flatMap(({ source, target }) => listFiles(source).map((name) => ({
116
- target: node_path.posix.join(target, name),
117
- origin: (0, node_path.resolve)(source, name),
118
- ids: target === MAPPINGS ? stubIdsOf((0, node_path.join)(source, name)) : []
119
- }))));
120
- this.withCopyDirectoriesToContainer(dirs);
121
- });
122
- return this;
123
- }
124
- withMapping(name, source) {
125
- within(`WiremockContainer.withMapping("${name}")`, () => {
126
- const file = containerName(name);
127
- const target = node_path.posix.join(MAPPINGS, file.endsWith(".json") ? file : `${file}.json`);
128
- const inline = isRecord(source) && "json" in source;
129
- const origin = inline ? INLINE : resolveFile(source);
130
- const content = inline ? inlineJson(source.json) : (0, node_fs.readFileSync)(origin);
131
- const ids = within(origin, () => stubIds(content.toString()));
132
- this.withCopyContentToContainer(this.#claim([{
133
- target,
134
- origin,
135
- ids
136
- }]).map(() => ({
137
- content,
138
- target
139
- })));
140
- });
141
- return this;
142
- }
143
- withFiles(...files) {
144
- within("WiremockContainer.withFiles", () => {
145
- const claims = files.map((file) => {
146
- const origin = resolveFile(file);
147
- const name = isRecord(file) && file.name !== void 0 ? containerName(file.name) : (0, node_path.basename)(origin);
148
- return {
149
- target: node_path.posix.join(FILES, name),
150
- origin
151
- };
152
- });
153
- this.withCopyFilesToContainer(this.#claim(claims).map(({ origin, target }) => ({
154
- source: origin,
155
- target
156
- })));
157
- });
158
- return this;
159
- }
160
- async start() {
161
- const startedContainer = await super.start();
162
- return new StartedWiremockContainer(startedContainer, `http://${startedContainer.getHost()}:${startedContainer.getMappedPort(8080)}`);
163
- }
164
- #claim(claims) {
165
- const origins = new Map(this.#origins);
166
- const owners = new Map(this.#stubIds);
167
- const fresh = claims.filter(({ target, origin, ids = [] }) => {
168
- const existing = origins.get(target);
169
- if (existing === origin && origin !== INLINE) return false;
170
- if (existing) fail(`"${target}" is already registered from ${existing}; registering ${origin} would overwrite it`);
171
- for (const id of ids) {
172
- const owner = owners.get(id);
173
- if (owner) fail(`stub id "${id}" in ${origin} is already used by "${owner}"; WireMock refuses to start with duplicate stub ids`);
174
- owners.set(id, target);
175
- }
176
- origins.set(target, origin);
177
- return true;
178
- });
179
- [this.#origins, this.#stubIds] = [origins, owners];
180
- return fresh;
181
- }
182
- };
183
- var StartedWiremockContainer = class extends testcontainers.AbstractStartedContainer {
184
- #rpcUrl;
185
- constructor(startedTestContainer, url) {
186
- super(startedTestContainer);
187
- this.#rpcUrl = url;
188
- }
189
- get rpcUrl() {
190
- return this.#rpcUrl;
191
- }
192
- };
193
- exports.StartedWiremockContainer = StartedWiremockContainer;
194
- exports.WiremockContainer = WiremockContainer;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const require_runtime=require("./runtime-6blQsklJ.cjs");let node_fs=require("node:fs");let node_path=require("node:path");let testcontainers=require("testcontainers");let node_module=require("node:module");let node_url=require("node:url");const PACKAGE_NAME=/^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/u;function fail(message){throw new Error(message)}function within(context,fn){try{return fn()}catch(error){throw new Error(`${context}: ${error.message}`,{cause:error})}}const isRecord=value=>typeof value===`object`&&value!==null&&!Array.isArray(value);const show=value=>value instanceof URL?value.href:JSON.stringify(value)??String(value);const text=(value,field)=>typeof value===`string`&&value.trim()!==``?value:fail(`"${field}" must be a non-empty string, got ${show(value)}`);const toPath=(value,field)=>{if(!(value instanceof URL)&&!text(value,field).startsWith(`file:`))return(0,node_path.resolve)(value);try{return(0,node_url.fileURLToPath)(value)}catch{return fail(`"${field}" must be a path or a file: URL, got ${show(value)}`)}};const existingFile=path=>{const stats=(0,node_fs.statSync)(path,{throwIfNoEntry:false});return stats?.isFile()?path:fail(`"${path}" ${stats?`is not a file`:`does not exist`}`)};const packageRoot=name=>{if(!PACKAGE_NAME.test(name))fail(`"${name}" is not a valid npm package name`);return((0,node_module.createRequire)((0,node_path.join)(process.cwd(),`noop.js`)).resolve.paths(name)??[]).map(dir=>(0,node_path.join)(dir,name)).find(dir=>(0,node_fs.existsSync)((0,node_path.join)(dir,`package.json`)))??fail(`package "${name}" is not installed (searched from "${process.cwd()}")`)};function resolveFile(source){if(typeof source===`string`||source instanceof URL)return existingFile(toPath(source,`source`));if(!isRecord(source))return fail(`unsupported source ${show(source)}; expected a path, a file: URL, { file }, { package, file } or { module, file }`);const{file,package:pkg,module}=source;if(pkg!==void 0&&module!==void 0)fail(`a source may set "package" or "module", not both`);if(pkg!==void 0){const root=packageRoot(text(pkg,`package`));const path=(0,node_path.resolve)(root,text(file,`file`));return(0,node_path.relative)(root,path).split(node_path.sep)[0]===`..`?fail(`"${String(file)}" must point to a file inside package "${pkg}"`):existingFile(path)}if(module===void 0)return existingFile(toPath(file,`file`));const anchor=toPath(module,`module`);return existingFile((0,node_path.resolve)((0,node_fs.statSync)(anchor,{throwIfNoEntry:false})?.isDirectory()?anchor:(0,node_path.dirname)(anchor),text(file,`file`)))}const containerName=name=>{const value=text(name,`name`);return value.split(`/`).some(part=>[``,`.`,`..`].includes(part)||/[\\\0]/u.test(part))?fail(`name "${value}" must be a relative path using "/" separators, without empty, "." or ".." segments`):value};const inlineJson=json=>typeof json===`string`?json:JSON.stringify(json,null,2)??fail(`"json" must be a JSON string or an object`);const parseJson=json=>{try{return JSON.parse(json)}catch(error){return fail(`invalid JSON (${error.message})`)}};function stubIds(json){const value=parseJson(json);const many=isRecord(value)&&`mappings`in value;const stubs=many?value.mappings:[value];if(!Array.isArray(stubs)||stubs.length===0)fail(`"mappings" must be a non-empty array of stub mappings`);return stubs.flatMap((stub,index)=>{if(!isRecord(stub)||!isRecord(stub.request)||!isRecord(stub.response))fail(`${many?`mappings[${index}]`:`the mapping`} is not a WireMock stub mapping ("request" and "response" objects are required)`);const id=stub.id??stub.uuid;return typeof id===`string`?[id]:[]})}const stubIdsOf=path=>{try{return path.endsWith(`.json`)?stubIds((0,node_fs.readFileSync)(path,`utf8`)):[]}catch{return[]}};const listFiles=dir=>(0,node_fs.readdirSync)(dir,{recursive:true,withFileTypes:true}).filter(entry=>!entry.isDirectory()).map(entry=>(0,node_path.relative)(dir,(0,node_path.join)(entry.parentPath,entry.name)).split(node_path.sep).join(`/`));const MAPPINGS=`/home/wiremock/mappings`;const FILES=`/home/wiremock/__files`;const INLINE=`inline JSON`;var WiremockContainer=class extends testcontainers.GenericContainer{#origins=/* @__PURE__ */ new Map;#stubIds=/* @__PURE__ */ new Map;constructor(image=`wiremock/wiremock`){super(image);this.withExposedPorts(8080);this.withWaitStrategy(testcontainers.Wait.forListeningPorts())}withMappings(directory){within(`WiremockContainer.withMappings`,()=>{const mappings=(0,node_path.join)(directory,`mappings`);if(!(0,node_fs.existsSync)(mappings))fail(`"${mappings}" does not exist`);if(!(0,node_fs.statSync)(mappings).isDirectory())fail(`"${mappings}" is not a directory`);const files=(0,node_path.join)(directory,`__files`);const dirs=[{source:mappings,target:MAPPINGS},...(0,node_fs.existsSync)(files)?[{source:files,target:FILES}]:[]];this.#claim(dirs.flatMap(({source,target})=>listFiles(source).map(name=>({target:node_path.posix.join(target,name),origin:(0,node_path.resolve)(source,name),ids:target===MAPPINGS?stubIdsOf((0,node_path.join)(source,name)):[]}))));this.withCopyDirectoriesToContainer(dirs)});return this}withMapping(name,source){within(`WiremockContainer.withMapping("${name}")`,()=>{const file=containerName(name);const target=node_path.posix.join(MAPPINGS,file.endsWith(`.json`)?file:`${file}.json`);const inline=isRecord(source)&&`json`in source;const origin=inline?INLINE:resolveFile(source);const content=inline?inlineJson(source.json):(0,node_fs.readFileSync)(origin);const ids=within(origin,()=>stubIds(content.toString()));this.withCopyContentToContainer(this.#claim([{target,origin,ids}]).map(()=>({content,target})))});return this}withFiles(...files){within(`WiremockContainer.withFiles`,()=>{const claims=files.map(file=>{const origin=resolveFile(file);const name=isRecord(file)&&file.name!==void 0?containerName(file.name):(0,node_path.basename)(origin);return{target:node_path.posix.join(FILES,name),origin}});this.withCopyFilesToContainer(this.#claim(claims).map(({origin,target})=>({source:origin,target})))});return this}async start(){const runtime=require_runtime.t();const startedContainer=runtime?await runtime.start(this.#startRequest()):await super.start();return new StartedWiremockContainer(startedContainer,`http://${startedContainer.getHost()}:${startedContainer.getMappedPort(8080)}`)}#startRequest(){return{image:this.imageName.string,name:this.createOpts.name,command:[...this.createOpts.Cmd??[]],environment:{...this.environment},directories:this.directoriesToCopy.map(({source,target})=>({source,target})),files:this.filesToCopy.map(({source,target})=>({source,target})),contents:this.contentsToCopy.map(({content,target})=>({content,target}))}}#claim(claims){const origins=new Map(this.#origins);const owners=new Map(this.#stubIds);const fresh=claims.filter(({target,origin,ids=[]})=>{const existing=origins.get(target);if(existing===origin&&origin!==INLINE)return false;if(existing)fail(`"${target}" is already registered from ${existing}; registering ${origin} would overwrite it`);for(const id of ids){const owner=owners.get(id);if(owner)fail(`stub id "${id}" in ${origin} is already used by "${owner}"; WireMock refuses to start with duplicate stub ids`);owners.set(id,target)}origins.set(target,origin);return true});[this.#origins,this.#stubIds]=[origins,owners];return fresh}};const ADMIN_PATH=`/__admin`;var StartedWiremockContainer=class extends testcontainers.AbstractStartedContainer{#rpcUrl;constructor(startedTestContainer,url){super(startedTestContainer);this.#rpcUrl=url}get rpcUrl(){return this.#rpcUrl}getUrl(path=``){if(path===``)return this.#rpcUrl;const suffix=path.startsWith(`/`)||path.startsWith(`?`)?path:`/${path}`;return`${this.#rpcUrl}${suffix}`}getAdminUrl(){return`${this.#rpcUrl}${ADMIN_PATH}`}getMappingUrl(){return`${this.getAdminUrl()}/mappings`}async isRunning(){try{const health=await fetch(`${this.getAdminUrl()}/health`);if(health.status!==404)return health.ok;return(await fetch(this.getMappingUrl())).ok}catch{return false}}async waitUntilReady(options={}){const{timeout=3e4,interval=250}=options;const deadline=Date.now()+timeout;const poll=async()=>{if(await this.isRunning())return;if(Date.now()>=deadline)throw new Error(`WireMock at ${this.#rpcUrl} was not ready within ${timeout}ms`);await new Promise(_resolve=>setTimeout(_resolve,interval));return poll()};await poll()}async reset(){await this.#admin(`POST`,`/reset`)}async resetRequests(){await this.#admin(`DELETE`,`/requests`)}async countRequests(request){const{count}=await(await this.#admin(`POST`,`/requests/count`,request)).json();return count}async findRequests(request){const{requests}=await(await this.#admin(`POST`,`/requests/find`,request)).json();return requests??[]}async#admin(method,path,body){const response=await fetch(`${this.getAdminUrl()}${path}`,{method,...body===void 0?{}:{headers:{"Content-Type":`application/json`},body:JSON.stringify(body)}});if(!response.ok){const detail=await response.text().catch(()=>``);throw new Error(`WireMock admin request ${method} ${ADMIN_PATH}${path} failed with ${response.status} ${response.statusText}${detail?`: ${detail}`:``}`)}return response}};exports.StartedWiremockContainer=StartedWiremockContainer;exports.WiremockContainer=WiremockContainer;
package/dist/index.d.cts CHANGED
@@ -1,11 +1,7 @@
1
1
  import { AbstractStartedContainer, GenericContainer, StartedTestContainer } from "testcontainers";
2
2
  /** A path (relative to the working directory) or a `file:` URL. */
3
3
  type WiremockPath = string | URL;
4
- /**
5
- * A fixture file: a local path or `file:` URL, `{ file }`, a file inside an installed npm
6
- * package `{ package, file }` (its `exports` are ignored), or a file next to a module
7
- * `{ module: import.meta.url | __filename | __dirname, file }`.
8
- */
4
+ /** A path or `file:` URL, `{ file }`, a file of an installed package `{ package, file }`, or one next to a module `{ module, file }`. */
9
5
  type WiremockFileSource = WiremockPath | {
10
6
  readonly file: WiremockPath;
11
7
  } | {
@@ -27,87 +23,102 @@ type WiremockMappingSource = WiremockFileSource | {
27
23
  readonly mappings: readonly WiremockStubMapping[];
28
24
  };
29
25
  };
30
- /** A file source; object forms may set `name`, the path below `__files/` (default: the base name). */
26
+ /** A file source; object forms may set `name`, the path below `__files/`. */
31
27
  type WiremockStaticFile = WiremockPath | (Exclude<WiremockFileSource, WiremockPath> & {
32
28
  readonly name?: string;
33
29
  });
34
- /**
35
- * A Testcontainer for Wiremock. Stubs are registered with {@link withMappings} (a directory),
36
- * {@link withMapping} (one named stub) and {@link withFiles} (response bodies), in any
37
- * combination. Registrations are validated immediately and may not overwrite one another.
38
- *
39
- * @example
40
- * ```typescript
41
- * const container = await new WiremockContainer().start();
42
- * ```
43
- */
30
+ /** A WireMock Testcontainer. Registrations are validated at once and may not overwrite one another. */
44
31
  export declare class WiremockContainer extends GenericContainer {
45
32
  #private;
46
- /**
47
- * Creates a new WiremockContainer.
48
- * @example
49
- * ```typescript
50
- * const container = await new WiremockContainer().start();
51
- * ```
52
- * @param image The docker image to use. Defaults to "wiremock/wiremock".
53
- */
33
+ /** @param image Docker image, default `wiremock/wiremock`. */
54
34
  constructor(image?: string);
55
35
  /**
56
- * Adds mappings to the Wiremock container.
57
- * @example
58
- * ```typescript
59
- * const container = await new WiremockContainer()
60
- * .withMappings("./test/__mocks__/wiremock")
61
- * .start();
62
- * ```
63
- * @param directory The directory containing `mappings/` and, optionally, `__files/`.
64
- * @throws If `mappings/` is not a directory in `directory`, or a file would overwrite an earlier registration.
36
+ * Copies `directory/mappings/` (required) and `directory/__files/` (optional) into the container.
37
+ * @throws If `mappings/` is missing or a file would overwrite an earlier registration.
65
38
  */
66
39
  withMappings(directory: string): this;
67
40
  /**
68
- * Registers one stub at `mappings/<name>.json`, validated and copied byte-for-byte.
69
- * @example
70
- * ```typescript
71
- * new WiremockContainer()
72
- * .withMapping("cards", "./test/fixtures/cards.json")
73
- * .withMapping("user", { package: "@acme/fixtures", file: "wiremock/user.json" })
74
- * .withMapping("balance", { module: import.meta.url, file: "./balance.json" })
75
- * .withMapping("ping", { json: { request: { url: "/ping" }, response: { status: 200 } } });
76
- * ```
77
- * @param name File name below `mappings/`, may contain `/`; `.json` is appended unless present.
78
- * @throws If the name or source is invalid, the source is not a WireMock mapping, or it would overwrite an earlier registration.
41
+ * Registers one stub at `mappings/<name>.json` (`name` may contain `/`).
42
+ * @example .withMapping("ping", { json: { request: { url: "/ping" }, response: { status: 200 } } })
43
+ * @throws If the source is invalid, not a WireMock stub, or would overwrite an earlier registration.
79
44
  */
80
45
  withMapping(name: string, source: WiremockMappingSource): this;
81
46
  /**
82
- * Registers response bodies at `__files/<name>` (default: the base name), copied as-is.
83
- * @example
84
- * ```typescript
85
- * new WiremockContainer().withFiles("./test/fixtures/body.json", { file: "./logo.png", name: "img/logo.png" });
86
- * ```
87
- * @throws If a source or name is invalid, or a file would overwrite an earlier registration; nothing is registered then.
47
+ * Registers response bodies at `__files/<name>` (default: the base name). All or nothing.
48
+ * @throws If a source or name is invalid or a file would overwrite an earlier registration.
88
49
  */
89
50
  withFiles(...files: WiremockStaticFile[]): this;
90
- /**
91
- * Starts the container and returns a {@link StartedWiremockContainer}.
92
- * @returns A promise that resolves to the started container.
93
- */
51
+ /** Starts WireMock – in Docker, or locally once `./no-docker` is installed. */
94
52
  override start(): Promise<StartedWiremockContainer>;
95
53
  }
96
- /**
97
- * A started Wiremock container with helper methods for interacting with the node.
98
- */
54
+ /** WireMock [request-matching](https://wiremock.org/docs/request-matching/) criteria; unknown fields pass through. */
55
+ export interface WiremockRequestCriteria {
56
+ /** E.g. `"GET"`; `"ANY"` matches every method. */
57
+ readonly method?: string;
58
+ /** Exact path and query string. */
59
+ readonly url?: string;
60
+ /** Exact path, any query string. */
61
+ readonly urlPath?: string;
62
+ /** Regex for the path. */
63
+ readonly urlPathPattern?: string;
64
+ /** Regex for path and query string. */
65
+ readonly urlPattern?: string;
66
+ /** E.g. `{ "Content-Type": { equalTo: "application/json" } }`. */
67
+ readonly headers?: Readonly<Record<string, unknown>>;
68
+ /** E.g. `{ page: { equalTo: "1" } }`. */
69
+ readonly queryParameters?: Readonly<Record<string, unknown>>;
70
+ /** E.g. `[{ equalToJson: { id: 1 } }]`. */
71
+ readonly bodyPatterns?: readonly object[];
72
+ readonly [field: string]: unknown;
73
+ }
74
+ /** A request from WireMock's journal; fields not modelled here are kept as-is. */
75
+ export interface RecordedRequest {
76
+ readonly id?: string;
77
+ /** Path and query string, e.g. `"/cards?page=1"`. */
78
+ readonly url: string;
79
+ readonly absoluteUrl?: string;
80
+ readonly method: string;
81
+ readonly headers?: Readonly<Record<string, unknown>>;
82
+ readonly queryParams?: Readonly<Record<string, unknown>>;
83
+ readonly body?: string;
84
+ /** Epoch milliseconds. */
85
+ readonly loggedDate?: number;
86
+ readonly [field: string]: unknown;
87
+ }
88
+ /** Options for {@link StartedWiremockContainer.waitUntilReady}. */
89
+ export interface WaitUntilReadyOptions {
90
+ /** Milliseconds before giving up. Default `30_000`. */
91
+ readonly timeout?: number;
92
+ /** Milliseconds between probes. Default `250`. */
93
+ readonly interval?: number;
94
+ }
95
+ /** A started WireMock container with URL, health, reset and verification helpers. */
99
96
  export declare class StartedWiremockContainer extends AbstractStartedContainer {
100
97
  #private;
101
- /**
102
- * Creates a new StartedWiremockContainer.
103
- * @param startedTestContainer The underlying TestContainer.
104
- * @param url The URL of the started container.
105
- */
98
+ /** Created by {@link WiremockContainer.start}; `url` is WireMock's base URL. */
106
99
  constructor(startedTestContainer: StartedTestContainer, url: string);
100
+ /** Base URL, e.g. `http://localhost:32768`. */
101
+ get rpcUrl(): string;
102
+ /** Absolute URL for `path`, e.g. `getUrl("cards?page=1")`; no argument → {@link rpcUrl}. */
103
+ getUrl(path?: string): string;
104
+ /** Admin API base URL: `<rpcUrl>/__admin`. */
105
+ getAdminUrl(): string;
106
+ /** Stub mappings endpoint: `<rpcUrl>/__admin/mappings`. */
107
+ getMappingUrl(): string;
108
+ /** Whether WireMock responds healthily. Never throws. */
109
+ isRunning(): Promise<boolean>;
107
110
  /**
108
- * Gets the URL of the Wiremock node.
111
+ * Polls {@link isRunning} until healthy.
112
+ * @throws If not healthy within `options.timeout`.
109
113
  */
110
- get rpcUrl(): string;
114
+ waitUntilReady(options?: WaitUntilReadyOptions): Promise<void>;
115
+ /** Restores registered stubs and scenarios and clears the journal – no restart. */
116
+ reset(): Promise<void>;
117
+ /** Clears the request journal; stubs are kept. */
118
+ resetRequests(): Promise<void>;
119
+ /** Number of journal entries matching `request` since the last reset. */
120
+ countRequests(request: WiremockRequestCriteria): Promise<number>;
121
+ /** Journal entries matching `request` since the last reset. */
122
+ findRequests(request: WiremockRequestCriteria): Promise<RecordedRequest[]>;
111
123
  }
112
- export type { WiremockFileSource, WiremockMappingSource, WiremockPath, WiremockStaticFile, WiremockStubMapping };
113
- //# sourceMappingURL=index.d.cts.map
124
+ export type { WiremockFileSource, WiremockMappingSource, WiremockPath, WiremockStaticFile, WiremockStubMapping };
package/dist/index.d.ts CHANGED
@@ -1,11 +1,7 @@
1
1
  import { AbstractStartedContainer, GenericContainer, StartedTestContainer } from "testcontainers";
2
2
  /** A path (relative to the working directory) or a `file:` URL. */
3
3
  type WiremockPath = string | URL;
4
- /**
5
- * A fixture file: a local path or `file:` URL, `{ file }`, a file inside an installed npm
6
- * package `{ package, file }` (its `exports` are ignored), or a file next to a module
7
- * `{ module: import.meta.url | __filename | __dirname, file }`.
8
- */
4
+ /** A path or `file:` URL, `{ file }`, a file of an installed package `{ package, file }`, or one next to a module `{ module, file }`. */
9
5
  type WiremockFileSource = WiremockPath | {
10
6
  readonly file: WiremockPath;
11
7
  } | {
@@ -27,87 +23,102 @@ type WiremockMappingSource = WiremockFileSource | {
27
23
  readonly mappings: readonly WiremockStubMapping[];
28
24
  };
29
25
  };
30
- /** A file source; object forms may set `name`, the path below `__files/` (default: the base name). */
26
+ /** A file source; object forms may set `name`, the path below `__files/`. */
31
27
  type WiremockStaticFile = WiremockPath | (Exclude<WiremockFileSource, WiremockPath> & {
32
28
  readonly name?: string;
33
29
  });
34
- /**
35
- * A Testcontainer for Wiremock. Stubs are registered with {@link withMappings} (a directory),
36
- * {@link withMapping} (one named stub) and {@link withFiles} (response bodies), in any
37
- * combination. Registrations are validated immediately and may not overwrite one another.
38
- *
39
- * @example
40
- * ```typescript
41
- * const container = await new WiremockContainer().start();
42
- * ```
43
- */
30
+ /** A WireMock Testcontainer. Registrations are validated at once and may not overwrite one another. */
44
31
  export declare class WiremockContainer extends GenericContainer {
45
32
  #private;
46
- /**
47
- * Creates a new WiremockContainer.
48
- * @example
49
- * ```typescript
50
- * const container = await new WiremockContainer().start();
51
- * ```
52
- * @param image The docker image to use. Defaults to "wiremock/wiremock".
53
- */
33
+ /** @param image Docker image, default `wiremock/wiremock`. */
54
34
  constructor(image?: string);
55
35
  /**
56
- * Adds mappings to the Wiremock container.
57
- * @example
58
- * ```typescript
59
- * const container = await new WiremockContainer()
60
- * .withMappings("./test/__mocks__/wiremock")
61
- * .start();
62
- * ```
63
- * @param directory The directory containing `mappings/` and, optionally, `__files/`.
64
- * @throws If `mappings/` is not a directory in `directory`, or a file would overwrite an earlier registration.
36
+ * Copies `directory/mappings/` (required) and `directory/__files/` (optional) into the container.
37
+ * @throws If `mappings/` is missing or a file would overwrite an earlier registration.
65
38
  */
66
39
  withMappings(directory: string): this;
67
40
  /**
68
- * Registers one stub at `mappings/<name>.json`, validated and copied byte-for-byte.
69
- * @example
70
- * ```typescript
71
- * new WiremockContainer()
72
- * .withMapping("cards", "./test/fixtures/cards.json")
73
- * .withMapping("user", { package: "@acme/fixtures", file: "wiremock/user.json" })
74
- * .withMapping("balance", { module: import.meta.url, file: "./balance.json" })
75
- * .withMapping("ping", { json: { request: { url: "/ping" }, response: { status: 200 } } });
76
- * ```
77
- * @param name File name below `mappings/`, may contain `/`; `.json` is appended unless present.
78
- * @throws If the name or source is invalid, the source is not a WireMock mapping, or it would overwrite an earlier registration.
41
+ * Registers one stub at `mappings/<name>.json` (`name` may contain `/`).
42
+ * @example .withMapping("ping", { json: { request: { url: "/ping" }, response: { status: 200 } } })
43
+ * @throws If the source is invalid, not a WireMock stub, or would overwrite an earlier registration.
79
44
  */
80
45
  withMapping(name: string, source: WiremockMappingSource): this;
81
46
  /**
82
- * Registers response bodies at `__files/<name>` (default: the base name), copied as-is.
83
- * @example
84
- * ```typescript
85
- * new WiremockContainer().withFiles("./test/fixtures/body.json", { file: "./logo.png", name: "img/logo.png" });
86
- * ```
87
- * @throws If a source or name is invalid, or a file would overwrite an earlier registration; nothing is registered then.
47
+ * Registers response bodies at `__files/<name>` (default: the base name). All or nothing.
48
+ * @throws If a source or name is invalid or a file would overwrite an earlier registration.
88
49
  */
89
50
  withFiles(...files: WiremockStaticFile[]): this;
90
- /**
91
- * Starts the container and returns a {@link StartedWiremockContainer}.
92
- * @returns A promise that resolves to the started container.
93
- */
51
+ /** Starts WireMock – in Docker, or locally once `./no-docker` is installed. */
94
52
  override start(): Promise<StartedWiremockContainer>;
95
53
  }
96
- /**
97
- * A started Wiremock container with helper methods for interacting with the node.
98
- */
54
+ /** WireMock [request-matching](https://wiremock.org/docs/request-matching/) criteria; unknown fields pass through. */
55
+ export interface WiremockRequestCriteria {
56
+ /** E.g. `"GET"`; `"ANY"` matches every method. */
57
+ readonly method?: string;
58
+ /** Exact path and query string. */
59
+ readonly url?: string;
60
+ /** Exact path, any query string. */
61
+ readonly urlPath?: string;
62
+ /** Regex for the path. */
63
+ readonly urlPathPattern?: string;
64
+ /** Regex for path and query string. */
65
+ readonly urlPattern?: string;
66
+ /** E.g. `{ "Content-Type": { equalTo: "application/json" } }`. */
67
+ readonly headers?: Readonly<Record<string, unknown>>;
68
+ /** E.g. `{ page: { equalTo: "1" } }`. */
69
+ readonly queryParameters?: Readonly<Record<string, unknown>>;
70
+ /** E.g. `[{ equalToJson: { id: 1 } }]`. */
71
+ readonly bodyPatterns?: readonly object[];
72
+ readonly [field: string]: unknown;
73
+ }
74
+ /** A request from WireMock's journal; fields not modelled here are kept as-is. */
75
+ export interface RecordedRequest {
76
+ readonly id?: string;
77
+ /** Path and query string, e.g. `"/cards?page=1"`. */
78
+ readonly url: string;
79
+ readonly absoluteUrl?: string;
80
+ readonly method: string;
81
+ readonly headers?: Readonly<Record<string, unknown>>;
82
+ readonly queryParams?: Readonly<Record<string, unknown>>;
83
+ readonly body?: string;
84
+ /** Epoch milliseconds. */
85
+ readonly loggedDate?: number;
86
+ readonly [field: string]: unknown;
87
+ }
88
+ /** Options for {@link StartedWiremockContainer.waitUntilReady}. */
89
+ export interface WaitUntilReadyOptions {
90
+ /** Milliseconds before giving up. Default `30_000`. */
91
+ readonly timeout?: number;
92
+ /** Milliseconds between probes. Default `250`. */
93
+ readonly interval?: number;
94
+ }
95
+ /** A started WireMock container with URL, health, reset and verification helpers. */
99
96
  export declare class StartedWiremockContainer extends AbstractStartedContainer {
100
97
  #private;
101
- /**
102
- * Creates a new StartedWiremockContainer.
103
- * @param startedTestContainer The underlying TestContainer.
104
- * @param url The URL of the started container.
105
- */
98
+ /** Created by {@link WiremockContainer.start}; `url` is WireMock's base URL. */
106
99
  constructor(startedTestContainer: StartedTestContainer, url: string);
100
+ /** Base URL, e.g. `http://localhost:32768`. */
101
+ get rpcUrl(): string;
102
+ /** Absolute URL for `path`, e.g. `getUrl("cards?page=1")`; no argument → {@link rpcUrl}. */
103
+ getUrl(path?: string): string;
104
+ /** Admin API base URL: `<rpcUrl>/__admin`. */
105
+ getAdminUrl(): string;
106
+ /** Stub mappings endpoint: `<rpcUrl>/__admin/mappings`. */
107
+ getMappingUrl(): string;
108
+ /** Whether WireMock responds healthily. Never throws. */
109
+ isRunning(): Promise<boolean>;
107
110
  /**
108
- * Gets the URL of the Wiremock node.
111
+ * Polls {@link isRunning} until healthy.
112
+ * @throws If not healthy within `options.timeout`.
109
113
  */
110
- get rpcUrl(): string;
114
+ waitUntilReady(options?: WaitUntilReadyOptions): Promise<void>;
115
+ /** Restores registered stubs and scenarios and clears the journal – no restart. */
116
+ reset(): Promise<void>;
117
+ /** Clears the request journal; stubs are kept. */
118
+ resetRequests(): Promise<void>;
119
+ /** Number of journal entries matching `request` since the last reset. */
120
+ countRequests(request: WiremockRequestCriteria): Promise<number>;
121
+ /** Journal entries matching `request` since the last reset. */
122
+ findRequests(request: WiremockRequestCriteria): Promise<RecordedRequest[]>;
111
123
  }
112
- export type { WiremockFileSource, WiremockMappingSource, WiremockPath, WiremockStaticFile, WiremockStubMapping };
113
- //# sourceMappingURL=index.d.ts.map
124
+ export type { WiremockFileSource, WiremockMappingSource, WiremockPath, WiremockStaticFile, WiremockStubMapping };