@zahid15/mockline 0.0.0-stage → 0.1.1
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 +24 -0
- package/CODE_OF_CONDUCT.md +20 -0
- package/CONTRIBUTING.md +27 -0
- package/LICENSE +21 -0
- package/README.md +364 -2
- package/SECURITY.md +28 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.js +3628 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +1057 -0
- package/dist/index.js +3241 -0
- package/dist/index.js.map +1 -0
- package/docs/architecture.md +70 -0
- package/docs/behavior.md +31 -0
- package/docs/benchmark.md +47 -0
- package/docs/config.schema.json +508 -0
- package/docs/configuration.md +214 -0
- package/docs/generation.md +26 -0
- package/docs/release.md +32 -0
- package/docs/screenshots/demo.png +0 -0
- package/docs/stateful.md +19 -0
- package/examples/README.md +21 -0
- package/examples/docker/compose.yaml +7 -0
- package/examples/petstore/mockline.yaml +43 -0
- package/examples/petstore/openapi.yaml +169 -0
- package/examples/programmatic.ts +23 -0
- package/examples/workflows/e2e.yml +24 -0
- package/package.json +98 -4
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# Configuration reference
|
|
2
|
+
|
|
3
|
+
Mockline reads an explicit file passed by `--config`, or discovers the first
|
|
4
|
+
`mockline.yaml`, `mockline.yml`, or `mockline.json` in the current directory.
|
|
5
|
+
The config is validated at startup; malformed config is an error rather than
|
|
6
|
+
silently falling back to defaults. CLI options override matching values.
|
|
7
|
+
|
|
8
|
+
The editor schema is [`config.schema.json`](config.schema.json). Regenerate it
|
|
9
|
+
after changing `src/config/schema.ts` with `corepack pnpm schema:config`.
|
|
10
|
+
|
|
11
|
+
## Top-level fields
|
|
12
|
+
|
|
13
|
+
| Field | Default | Meaning |
|
|
14
|
+
| ------------------ | ---------------- | ------------------------------------------------------------------------------- |
|
|
15
|
+
| `port` | `4010` | TCP port; `0` requests an ephemeral port. |
|
|
16
|
+
| `host` | `127.0.0.1` | Bind address. Non-loopback addresses expose the mock to other hosts. |
|
|
17
|
+
| `seed` | `0` when omitted | Base for deterministic response generation. |
|
|
18
|
+
| `prefer` | `example` | `example` selects documented examples first; `generate` generates from schemas. |
|
|
19
|
+
| `latency` | `0` | Delay in milliseconds; see [Latency and errors](#latency-and-errors). |
|
|
20
|
+
| `validate` | `warn` | `off`, `warn`, or `strict` request validation. |
|
|
21
|
+
| `enforceAuth` | `false` | Require declared security credentials to be present. Does not verify them. |
|
|
22
|
+
| `requestBodyLimit` | `1048576` | Maximum request-body size in bytes. |
|
|
23
|
+
| `requestTimeoutMs` | `30000` | HTTP request/header timeout in milliseconds. |
|
|
24
|
+
|
|
25
|
+
## Nested fields
|
|
26
|
+
|
|
27
|
+
### `errors`
|
|
28
|
+
|
|
29
|
+
```yaml
|
|
30
|
+
errors:
|
|
31
|
+
rate: 0.05
|
|
32
|
+
statuses: [500, 503]
|
|
33
|
+
perOperationRates:
|
|
34
|
+
createPet: 0.2
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`rate` and operation-specific rates are between 0 and 1. An operation-specific
|
|
38
|
+
entry takes precedence over `rate`; `statuses` is the pool used for injected
|
|
39
|
+
failures. `X-Mockline-Error: true` forces the first configured status.
|
|
40
|
+
|
|
41
|
+
### `stateful`
|
|
42
|
+
|
|
43
|
+
```yaml
|
|
44
|
+
stateful:
|
|
45
|
+
enabled: true
|
|
46
|
+
seedItems: 3
|
|
47
|
+
idProperty: id
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Stateful behavior is in-memory and only activates for structurally detected
|
|
51
|
+
REST collections (see [Stateful mode](../README.md#stateful-mode)). Numeric IDs
|
|
52
|
+
are allocated for numeric schemas; string IDs are generated from the declared
|
|
53
|
+
schema. `seedItems` must be a non-negative integer.
|
|
54
|
+
|
|
55
|
+
### `generation`
|
|
56
|
+
|
|
57
|
+
```yaml
|
|
58
|
+
generation:
|
|
59
|
+
hints: true
|
|
60
|
+
counter: false
|
|
61
|
+
maxDepth: 3
|
|
62
|
+
minArrayItems: 1
|
|
63
|
+
maxArrayItems: 3
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
- `hints` enables field-name hints, or supplies a map of property names to
|
|
67
|
+
string/number/boolean/null/primitive-array values. Hints are applied only
|
|
68
|
+
where a schema does not already constrain the generated value.
|
|
69
|
+
- `counter` varies generation by per-operation request count. The default is
|
|
70
|
+
repeatable output for the same seed and operation.
|
|
71
|
+
- `maxDepth` bounds recursive schemas (0–20).
|
|
72
|
+
- `minArrayItems` and `maxArrayItems` bound generated array lengths. Use
|
|
73
|
+
non-negative integers; keep the minimum no greater than the maximum.
|
|
74
|
+
|
|
75
|
+
### `overrides`
|
|
76
|
+
|
|
77
|
+
Overrides are keyed by `operationId` or by a case-insensitive `METHOD /path`
|
|
78
|
+
key. Fixed overrides can set `status`, `body`, `headers`, and `delay`:
|
|
79
|
+
|
|
80
|
+
```yaml
|
|
81
|
+
overrides:
|
|
82
|
+
listPets:
|
|
83
|
+
status: 200
|
|
84
|
+
body:
|
|
85
|
+
- id: 1
|
|
86
|
+
name: Fixed response
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
A `sequence` provides successive responses; it holds on the last item by
|
|
90
|
+
default, or cycles with `loop: true`:
|
|
91
|
+
|
|
92
|
+
```yaml
|
|
93
|
+
overrides:
|
|
94
|
+
getPet:
|
|
95
|
+
loop: true
|
|
96
|
+
sequence:
|
|
97
|
+
- status: 200
|
|
98
|
+
body: { id: 1, name: First }
|
|
99
|
+
- status: 404
|
|
100
|
+
body: { title: Not Found, status: 404 }
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Conditional `rules` are checked in order; the first matching rule wins. A
|
|
104
|
+
single `when`/`response` pair is also supported. Condition locations are
|
|
105
|
+
`query`, `header`, and `body`; operations are `eq` (default), `contains`, and
|
|
106
|
+
`regex`:
|
|
107
|
+
|
|
108
|
+
```yaml
|
|
109
|
+
overrides:
|
|
110
|
+
listPets:
|
|
111
|
+
rules:
|
|
112
|
+
- when: { location: query, name: demo, op: eq, value: empty }
|
|
113
|
+
response: { status: 200, body: [] }
|
|
114
|
+
- when: { location: header, name: x-fixture, op: contains, value: pets }
|
|
115
|
+
response: { status: 200, body: [{ id: 7, name: Matched }] }
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
For a body condition, `name` may be a dotted property path such as `user.id`
|
|
119
|
+
or a JSON Pointer-like path such as `/user/id`. Regex conditions use JavaScript
|
|
120
|
+
regular-expression syntax; keep config patterns simple and trusted.
|
|
121
|
+
|
|
122
|
+
### `proxy`
|
|
123
|
+
|
|
124
|
+
```yaml
|
|
125
|
+
proxy:
|
|
126
|
+
url: https://api.example.test/v1
|
|
127
|
+
all: false
|
|
128
|
+
allowPrivate: false
|
|
129
|
+
timeoutMs: 5000
|
|
130
|
+
recordFile: ./proxy-examples.json
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
With `all: false`, only unmatched requests are proxied. `all: true` forwards all
|
|
134
|
+
non-inspector requests. The upstream must use HTTP(S). Private/loopback/link-
|
|
135
|
+
local destinations are blocked unless `allowPrivate` is explicitly enabled.
|
|
136
|
+
`timeoutMs` is a positive integer. `recordFile` stores upstream response
|
|
137
|
+
examples as JSON.
|
|
138
|
+
|
|
139
|
+
### `cors`
|
|
140
|
+
|
|
141
|
+
```yaml
|
|
142
|
+
cors:
|
|
143
|
+
enabled: true
|
|
144
|
+
origins: ['*']
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
CORS is enabled by default for local development. `origins` accepts exact
|
|
148
|
+
origin strings; `*` allows any origin. Disable it with `--no-cors` or set
|
|
149
|
+
`enabled: false`.
|
|
150
|
+
|
|
151
|
+
### `recording`
|
|
152
|
+
|
|
153
|
+
```yaml
|
|
154
|
+
recording:
|
|
155
|
+
enabled: true
|
|
156
|
+
file: ./mockline-records.jsonl
|
|
157
|
+
maxRequests: 500
|
|
158
|
+
redactHeaders: [authorization, cookie, set-cookie, x-api-key, api-key]
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Records are kept in a bounded in-memory ring. If `file` is set, each record is
|
|
162
|
+
also appended as JSONL. Common credential headers are always redacted; this list
|
|
163
|
+
adds more names. Header-name matching is case-insensitive. Request bodies are
|
|
164
|
+
not redacted—use a private file and avoid recording sensitive payloads.
|
|
165
|
+
|
|
166
|
+
### `controlHeaders`
|
|
167
|
+
|
|
168
|
+
```yaml
|
|
169
|
+
controlHeaders:
|
|
170
|
+
allow: true
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
When enabled, `Prefer` and `X-Mockline-*` control headers can influence example,
|
|
174
|
+
status, delay, and injected-error selection. Disable them when requests come
|
|
175
|
+
from clients that must not control mock behavior.
|
|
176
|
+
|
|
177
|
+
### `inspector`
|
|
178
|
+
|
|
179
|
+
```yaml
|
|
180
|
+
inspector:
|
|
181
|
+
enabled: false
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
On a loopback bind the inspector is enabled by default unless explicitly set to
|
|
185
|
+
`false`. On non-loopback binds it is disabled by default unless explicitly set
|
|
186
|
+
to `true` (or `--inspector` is passed). It has no authentication; expose it only
|
|
187
|
+
on a trusted interface.
|
|
188
|
+
|
|
189
|
+
## Latency and errors
|
|
190
|
+
|
|
191
|
+
Latency accepts a fixed number, a two-number inclusive range, or a distribution
|
|
192
|
+
object:
|
|
193
|
+
|
|
194
|
+
```yaml
|
|
195
|
+
latency: 25
|
|
196
|
+
# or: latency: [10, 80]
|
|
197
|
+
# or:
|
|
198
|
+
# latency:
|
|
199
|
+
# min: 10
|
|
200
|
+
# max: 80
|
|
201
|
+
# distribution: normal # uniform (default) or normal
|
|
202
|
+
# A fixed form is also available: latency: { fixed: 25 }
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
CLI `--delay` accepts a fixed number or `min-max` range and overrides config
|
|
206
|
+
latency. `X-Mockline-Delay` (when control headers are allowed) has higher
|
|
207
|
+
precedence than a per-override delay, which has higher precedence than global
|
|
208
|
+
latency.
|
|
209
|
+
|
|
210
|
+
## Example files
|
|
211
|
+
|
|
212
|
+
- [`../examples/petstore/mockline.yaml`](../examples/petstore/mockline.yaml)
|
|
213
|
+
- [`../examples/petstore/openapi.yaml`](../examples/petstore/openapi.yaml)
|
|
214
|
+
- [`../README.md`](../README.md) for CLI, API, security, and Docker usage.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Schema generation
|
|
2
|
+
|
|
3
|
+
Responses use a seed derived from the configured seed, HTTP method, matched
|
|
4
|
+
path template and optional operation-local counter. generation.counter is false
|
|
5
|
+
by default, so repeated requests to one operation return the same generated
|
|
6
|
+
body and requests to other routes do not alter it. Date generation uses a fixed
|
|
7
|
+
2020-01-01 reference time so wall-clock time does not change seeded output.
|
|
8
|
+
|
|
9
|
+
Generation respects supported primitive constraints, formats, object properties,
|
|
10
|
+
required fields, arrays, compositions and local schema references. writeOnly
|
|
11
|
+
response properties and readOnly request properties are omitted. Property hints
|
|
12
|
+
apply only when stronger constraints are absent; hints can be disabled or set
|
|
13
|
+
per property in config. OpenAPI 3.0 uses the draft-07 validator with nullable
|
|
14
|
+
support; OpenAPI 3.1 uses the JSON Schema 2020-12 validator.
|
|
15
|
+
|
|
16
|
+
Simple regex patterns are generated directly; complex patterns use candidate
|
|
17
|
+
fallbacks and warnings. Every candidate is schema-validated, including calls
|
|
18
|
+
without an OpenAPI root. Up to twelve deterministic attempts are made. If no
|
|
19
|
+
valid candidate can be generated, the library throws and the server returns a
|
|
20
|
+
500 problem response; it does not silently return invalid generated data.
|
|
21
|
+
|
|
22
|
+
Depth limits prune optional recursive fields. Schemas requiring infinite nested
|
|
23
|
+
objects have no finite JSON value and fail with a bounded error. Not every valid
|
|
24
|
+
JSON Schema can be synthesized: complex regexes, constrained composition
|
|
25
|
+
intersections and unsupported keywords may fail generation even though the spec
|
|
26
|
+
itself is valid. Examples are the practical fallback for those operations.
|
package/docs/release.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Releasing Mockline
|
|
2
|
+
|
|
3
|
+
GitHub repository: zahidhasann88/mockline. npm package: @zahid15/mockline.
|
|
4
|
+
The CLI remains mockline. The npm account must own the zahid15 scope.
|
|
5
|
+
|
|
6
|
+
## First npm publication
|
|
7
|
+
|
|
8
|
+
Push the repository and wait for all CI jobs. npm trusted publishing is configured
|
|
9
|
+
from an existing package's settings. For a new package, first publish the checked
|
|
10
|
+
0.1.0 build interactively from your own npm account with npm login and npm publish
|
|
11
|
+
--access public. Review npm pack --dry-run before publishing. Do not upload npm
|
|
12
|
+
credentials to GitHub. This first local publication does not carry Actions OIDC
|
|
13
|
+
provenance; future CI publications do.
|
|
14
|
+
|
|
15
|
+
After the package exists, add a GitHub trusted publisher in its npm settings:
|
|
16
|
+
owner zahidhasann88, repository mockline, workflow release.yml, environment npm.
|
|
17
|
+
Allow npm publish. Create the npm GitHub environment and repository variable
|
|
18
|
+
PUBLISH_TO_NPM=true. The workflow uses Node 22 and npm 11 with id-token: write.
|
|
19
|
+
See https://docs.npmjs.com/trusted-publishers/ for current registry requirements.
|
|
20
|
+
|
|
21
|
+
GHCR publication is separate: enable repository variable PUBLISH_DOCKER=true
|
|
22
|
+
only when ready. The image is ghcr.io/zahidhasann88/mockline. GitHub release
|
|
23
|
+
assets are created independently of npm/GHCR publication.
|
|
24
|
+
|
|
25
|
+
## Each release
|
|
26
|
+
|
|
27
|
+
Run the checks documented in CONTRIBUTING.md. Regenerate the config schema,
|
|
28
|
+
review tarball contents, update package.json and CHANGELOG.md, commit and push.
|
|
29
|
+
Only after main CI passes, push a matching annotated v<version> tag. Release
|
|
30
|
+
checks validate the tag/version before publishing and test the tagged checkout.
|
|
31
|
+
An existing npm version is skipped on retries. Manual dispatch accepts an
|
|
32
|
+
existing tag; it never changes where that tag points. Never move a published tag.
|
|
Binary file
|
package/docs/stateful.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# In-memory state
|
|
2
|
+
|
|
3
|
+
A collection requires GET /things with an array response, POST /things, and
|
|
4
|
+
GET /things/{id} whose item schema matches the array item schema and contains
|
|
5
|
+
the configured ID property (id by default). Matching PUT/PATCH/DELETE item
|
|
6
|
+
routes participate; other operations use normal response selection.
|
|
7
|
+
|
|
8
|
+
Seeded items default to three; stateful.seedItems: 0 starts empty. POST stores
|
|
9
|
+
an item and assigns an ID if needed, GET lists or retrieves, PUT replaces,
|
|
10
|
+
PATCH shallow-merges, and DELETE returns 204. Declared limit/offset query
|
|
11
|
+
parameters paginate lists. Missing GET/PUT/PATCH items return 404. Deleting a
|
|
12
|
+
missing item still returns 204. Overrides and response-selection controls do
|
|
13
|
+
not execute the state handler. reset() and POST /__mockline/reset regenerate
|
|
14
|
+
seed data and reset deterministic counters; request history remains available.
|
|
15
|
+
|
|
16
|
+
This is a development store, not a database: state is lost on restart, updates
|
|
17
|
+
are shallow, relations and transactions are absent, and arbitrary resource
|
|
18
|
+
wrappers or workflow schemas are not detected. Constrained ID namespaces may
|
|
19
|
+
be exhausted; use a broad integer/string ID schema for stateful demos.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Examples
|
|
2
|
+
|
|
3
|
+
- `petstore/openapi.yaml` and `petstore/mockline.yaml`: CLI, config, seeded
|
|
4
|
+
stateful CRUD, and a conditional override.
|
|
5
|
+
- `programmatic.ts`: typed Node.js library usage. Build Mockline first, install
|
|
6
|
+
the published package or use this repository after building, then run with a TS
|
|
7
|
+
runner such as `pnpm exec tsx examples/programmatic.ts`.
|
|
8
|
+
|
|
9
|
+
From the repository root, try the CLI example with:
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
mockline routes examples/petstore/openapi.yaml
|
|
13
|
+
mockline validate examples/petstore/openapi.yaml
|
|
14
|
+
mockline serve examples/petstore/openapi.yaml --config examples/petstore/mockline.yaml --port 4010
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The sample config binds only to `127.0.0.1`. To exercise the conditional
|
|
18
|
+
`listPets` override, request `GET /v1/pets?demo=empty`.
|
|
19
|
+
|
|
20
|
+
- `docker/compose.yaml`: local-only published port and a read-only spec mount.
|
|
21
|
+
- `workflows/e2e.yml`: start the published CLI and wait for a healthy server.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Keep the mock on loopback for local development.
|
|
2
|
+
port: 4010
|
|
3
|
+
host: 127.0.0.1
|
|
4
|
+
seed: 42
|
|
5
|
+
prefer: example
|
|
6
|
+
validate: warn
|
|
7
|
+
latency: 0
|
|
8
|
+
|
|
9
|
+
stateful:
|
|
10
|
+
enabled: true
|
|
11
|
+
seedItems: 3
|
|
12
|
+
idProperty: id
|
|
13
|
+
|
|
14
|
+
generation:
|
|
15
|
+
hints: true
|
|
16
|
+
counter: false
|
|
17
|
+
maxDepth: 3
|
|
18
|
+
minArrayItems: 1
|
|
19
|
+
maxArrayItems: 3
|
|
20
|
+
|
|
21
|
+
recording:
|
|
22
|
+
enabled: true
|
|
23
|
+
maxRequests: 500
|
|
24
|
+
redactHeaders:
|
|
25
|
+
- authorization
|
|
26
|
+
- cookie
|
|
27
|
+
- set-cookie
|
|
28
|
+
- x-api-key
|
|
29
|
+
- api-key
|
|
30
|
+
- x-auth-token
|
|
31
|
+
|
|
32
|
+
# A conditional override is selected before stateful CRUD responses.
|
|
33
|
+
overrides:
|
|
34
|
+
listPets:
|
|
35
|
+
rules:
|
|
36
|
+
- when:
|
|
37
|
+
location: query
|
|
38
|
+
name: demo
|
|
39
|
+
op: eq
|
|
40
|
+
value: empty
|
|
41
|
+
response:
|
|
42
|
+
status: 200
|
|
43
|
+
body: []
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
openapi: 3.0.3
|
|
2
|
+
info:
|
|
3
|
+
title: Mockline Petstore Fixture
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
servers:
|
|
6
|
+
- url: https://api.example.test/v1
|
|
7
|
+
paths:
|
|
8
|
+
/pets:
|
|
9
|
+
get:
|
|
10
|
+
operationId: listPets
|
|
11
|
+
parameters:
|
|
12
|
+
- name: limit
|
|
13
|
+
in: query
|
|
14
|
+
schema: { type: integer, minimum: 1, maximum: 100 }
|
|
15
|
+
- name: filter
|
|
16
|
+
in: query
|
|
17
|
+
style: deepObject
|
|
18
|
+
explode: true
|
|
19
|
+
schema:
|
|
20
|
+
type: object
|
|
21
|
+
properties:
|
|
22
|
+
active: { type: boolean }
|
|
23
|
+
- name: X-Trace
|
|
24
|
+
in: header
|
|
25
|
+
schema: { type: string }
|
|
26
|
+
responses:
|
|
27
|
+
'200':
|
|
28
|
+
description: A page of pets
|
|
29
|
+
content:
|
|
30
|
+
application/json:
|
|
31
|
+
examples:
|
|
32
|
+
kitten:
|
|
33
|
+
summary: Small sample list
|
|
34
|
+
value:
|
|
35
|
+
- id: 17
|
|
36
|
+
name: Miso
|
|
37
|
+
tag: cat
|
|
38
|
+
createdAt: '2025-01-02T03:04:05Z'
|
|
39
|
+
schema:
|
|
40
|
+
type: array
|
|
41
|
+
items: { $ref: '#/components/schemas/Pet' }
|
|
42
|
+
post:
|
|
43
|
+
operationId: createPet
|
|
44
|
+
requestBody:
|
|
45
|
+
required: true
|
|
46
|
+
content:
|
|
47
|
+
application/json:
|
|
48
|
+
schema: { $ref: '#/components/schemas/PetInput' }
|
|
49
|
+
responses:
|
|
50
|
+
'201':
|
|
51
|
+
description: Created pet
|
|
52
|
+
content:
|
|
53
|
+
application/json:
|
|
54
|
+
schema: { $ref: '#/components/schemas/Pet' }
|
|
55
|
+
/pets/mine:
|
|
56
|
+
get:
|
|
57
|
+
operationId: getMyPets
|
|
58
|
+
responses:
|
|
59
|
+
'200':
|
|
60
|
+
description: Static path wins over petId
|
|
61
|
+
content:
|
|
62
|
+
application/json:
|
|
63
|
+
example: { id: 88, name: My pet, tag: dog }
|
|
64
|
+
/pets/{petId}:
|
|
65
|
+
parameters:
|
|
66
|
+
- name: petId
|
|
67
|
+
in: path
|
|
68
|
+
required: true
|
|
69
|
+
schema: { type: integer, minimum: 1 }
|
|
70
|
+
get:
|
|
71
|
+
operationId: getPet
|
|
72
|
+
responses:
|
|
73
|
+
'200':
|
|
74
|
+
description: Pet details
|
|
75
|
+
content:
|
|
76
|
+
application/json:
|
|
77
|
+
schema: { $ref: '#/components/schemas/Pet' }
|
|
78
|
+
'404':
|
|
79
|
+
description: Not found
|
|
80
|
+
content:
|
|
81
|
+
application/problem+json:
|
|
82
|
+
example: { title: Not Found, status: 404 }
|
|
83
|
+
put:
|
|
84
|
+
operationId: replacePet
|
|
85
|
+
requestBody:
|
|
86
|
+
required: true
|
|
87
|
+
content:
|
|
88
|
+
application/json:
|
|
89
|
+
schema: { $ref: '#/components/schemas/PetInput' }
|
|
90
|
+
responses:
|
|
91
|
+
'200':
|
|
92
|
+
description: Replaced pet
|
|
93
|
+
content:
|
|
94
|
+
application/json:
|
|
95
|
+
schema: { $ref: '#/components/schemas/Pet' }
|
|
96
|
+
patch:
|
|
97
|
+
operationId: patchPet
|
|
98
|
+
requestBody:
|
|
99
|
+
required: true
|
|
100
|
+
content:
|
|
101
|
+
application/json:
|
|
102
|
+
schema: { $ref: '#/components/schemas/PetInput' }
|
|
103
|
+
responses:
|
|
104
|
+
'200':
|
|
105
|
+
description: Updated pet
|
|
106
|
+
content:
|
|
107
|
+
application/json:
|
|
108
|
+
schema: { $ref: '#/components/schemas/Pet' }
|
|
109
|
+
delete:
|
|
110
|
+
operationId: deletePet
|
|
111
|
+
responses:
|
|
112
|
+
'204':
|
|
113
|
+
description: Deleted pet
|
|
114
|
+
/private:
|
|
115
|
+
get:
|
|
116
|
+
operationId: readPrivate
|
|
117
|
+
security:
|
|
118
|
+
- ApiKeyAuth: []
|
|
119
|
+
responses:
|
|
120
|
+
'200':
|
|
121
|
+
description: Protected response
|
|
122
|
+
content:
|
|
123
|
+
application/json:
|
|
124
|
+
example: { ok: true }
|
|
125
|
+
/hello:
|
|
126
|
+
get:
|
|
127
|
+
operationId: hello
|
|
128
|
+
responses:
|
|
129
|
+
'200':
|
|
130
|
+
description: Plain text greeting
|
|
131
|
+
content:
|
|
132
|
+
text/plain:
|
|
133
|
+
example: hello from mockline
|
|
134
|
+
/accept:
|
|
135
|
+
get:
|
|
136
|
+
operationId: acceptedMedia
|
|
137
|
+
responses:
|
|
138
|
+
'200':
|
|
139
|
+
description: Negotiated body
|
|
140
|
+
content:
|
|
141
|
+
application/json:
|
|
142
|
+
example: { format: json }
|
|
143
|
+
text/plain:
|
|
144
|
+
example: plain
|
|
145
|
+
components:
|
|
146
|
+
securitySchemes:
|
|
147
|
+
ApiKeyAuth:
|
|
148
|
+
type: apiKey
|
|
149
|
+
in: header
|
|
150
|
+
name: X-API-Key
|
|
151
|
+
schemas:
|
|
152
|
+
PetInput:
|
|
153
|
+
type: object
|
|
154
|
+
required: [name]
|
|
155
|
+
properties:
|
|
156
|
+
name: { type: string, minLength: 2 }
|
|
157
|
+
tag: { type: string, enum: [cat, dog, bird] }
|
|
158
|
+
password: { type: string, writeOnly: true }
|
|
159
|
+
id: { type: integer, readOnly: true, minimum: 1 }
|
|
160
|
+
Pet:
|
|
161
|
+
allOf:
|
|
162
|
+
- $ref: '#/components/schemas/PetInput'
|
|
163
|
+
- type: object
|
|
164
|
+
required: [id, email, createdAt]
|
|
165
|
+
properties:
|
|
166
|
+
id: { type: integer, readOnly: true, minimum: 1 }
|
|
167
|
+
email: { type: string, format: email }
|
|
168
|
+
createdAt: { type: string, format: date-time }
|
|
169
|
+
password: { type: string, writeOnly: true }
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { createMockServer } from '@zahid15/mockline';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
|
|
5
|
+
const exampleDirectory = resolve(fileURLToPath(new URL('.', import.meta.url)));
|
|
6
|
+
const server = await createMockServer({
|
|
7
|
+
spec: resolve(exampleDirectory, 'petstore/openapi.yaml'),
|
|
8
|
+
port: 0,
|
|
9
|
+
seed: 42,
|
|
10
|
+
config: {
|
|
11
|
+
validate: 'warn',
|
|
12
|
+
stateful: { enabled: true, seedItems: 2 },
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
console.log(`Mock API listening at ${server.url}`);
|
|
18
|
+
const response = await fetch(`${server.url}/v1/pets`);
|
|
19
|
+
console.log(await response.json());
|
|
20
|
+
console.log(`Recorded ${server.requests({ method: 'GET' }).length} GET request(s).`);
|
|
21
|
+
} finally {
|
|
22
|
+
await server.close();
|
|
23
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: Mock API example
|
|
2
|
+
on: workflow_dispatch
|
|
3
|
+
permissions:
|
|
4
|
+
contents: read
|
|
5
|
+
jobs:
|
|
6
|
+
e2e:
|
|
7
|
+
runs-on: ubuntu-latest
|
|
8
|
+
steps:
|
|
9
|
+
- uses: actions/checkout@v4
|
|
10
|
+
- uses: actions/setup-node@v4
|
|
11
|
+
with:
|
|
12
|
+
node-version: 22
|
|
13
|
+
- name: Start Mockline (after its first npm release)
|
|
14
|
+
run: |
|
|
15
|
+
npm install --global @zahid15/mockline@0.1.0
|
|
16
|
+
mockline serve examples/petstore/openapi.yaml --port 4010 --seed 42 > mockline.log 2>&1 &
|
|
17
|
+
for attempt in $(seq 1 30); do
|
|
18
|
+
if curl --fail --silent http://127.0.0.1:4010/__mockline/health; then exit 0; fi
|
|
19
|
+
sleep 1
|
|
20
|
+
done
|
|
21
|
+
cat mockline.log
|
|
22
|
+
exit 1
|
|
23
|
+
- name: Check the example route
|
|
24
|
+
run: curl --fail http://127.0.0.1:4010/v1/pets
|
package/package.json
CHANGED
|
@@ -1,6 +1,100 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zahid15/mockline",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "A local mock API server and TypeScript library powered by OpenAPI.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=20"
|
|
10
|
+
},
|
|
11
|
+
"packageManager": "pnpm@9.15.9",
|
|
12
|
+
"bin": {
|
|
13
|
+
"mockline": "dist/cli.js"
|
|
14
|
+
},
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"import": "./dist/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./cli": {
|
|
22
|
+
"import": "./dist/cli.js"
|
|
23
|
+
},
|
|
24
|
+
"./package.json": "./package.json"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist",
|
|
28
|
+
"docs",
|
|
29
|
+
"examples",
|
|
30
|
+
"README.md",
|
|
31
|
+
"LICENSE",
|
|
32
|
+
"CHANGELOG.md",
|
|
33
|
+
"CODE_OF_CONDUCT.md",
|
|
34
|
+
"CONTRIBUTING.md",
|
|
35
|
+
"SECURITY.md"
|
|
36
|
+
],
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "git+https://github.com/zahidhasann88/mockline.git"
|
|
40
|
+
},
|
|
41
|
+
"bugs": {
|
|
42
|
+
"url": "https://github.com/zahidhasann88/mockline/issues"
|
|
43
|
+
},
|
|
44
|
+
"homepage": "https://github.com/zahidhasann88/mockline#readme",
|
|
45
|
+
"keywords": [
|
|
46
|
+
"openapi",
|
|
47
|
+
"mock-server",
|
|
48
|
+
"api",
|
|
49
|
+
"testing",
|
|
50
|
+
"typescript",
|
|
51
|
+
"cli"
|
|
52
|
+
],
|
|
53
|
+
"publishConfig": {
|
|
54
|
+
"access": "public",
|
|
55
|
+
"provenance": true
|
|
56
|
+
},
|
|
57
|
+
"scripts": {
|
|
58
|
+
"build": "tsup",
|
|
59
|
+
"clean": "node -e \"for (const p of ['dist', 'coverage']) require('node:fs').rmSync(p, { recursive: true, force: true })\"",
|
|
60
|
+
"lint": "eslint src tests scripts *.ts eslint.config.js",
|
|
61
|
+
"format": "prettier --write .",
|
|
62
|
+
"format:check": "prettier --check .",
|
|
63
|
+
"typecheck": "tsc --noEmit",
|
|
64
|
+
"test": "tsup && vitest run --coverage",
|
|
65
|
+
"test:watch": "vitest",
|
|
66
|
+
"schema:config": "tsx scripts/generate-config-schema.ts",
|
|
67
|
+
"publint": "publint",
|
|
68
|
+
"attw": "attw --pack --profile esm-only",
|
|
69
|
+
"benchmark": "tsx scripts/benchmark.ts",
|
|
70
|
+
"pack:check": "tsup && node scripts/verify-pack.mjs",
|
|
71
|
+
"demo": "pnpm build && node scripts/demo.mjs"
|
|
72
|
+
},
|
|
73
|
+
"dependencies": {
|
|
74
|
+
"@apidevtools/swagger-parser": "^12.1.0",
|
|
75
|
+
"@faker-js/faker": "^9.9.0",
|
|
76
|
+
"ajv": "^8.17.1",
|
|
77
|
+
"ajv-formats": "^3.0.1",
|
|
78
|
+
"cac": "^6.7.14",
|
|
79
|
+
"chokidar": "^4.0.3",
|
|
80
|
+
"picocolors": "^1.1.1",
|
|
81
|
+
"yaml": "^2.7.0",
|
|
82
|
+
"zod": "^3.25.76"
|
|
83
|
+
},
|
|
84
|
+
"devDependencies": {
|
|
85
|
+
"@arethetypeswrong/cli": "^0.18.2",
|
|
86
|
+
"@eslint/js": "^9.26.0",
|
|
87
|
+
"@types/node": "^22.15.0",
|
|
88
|
+
"@vitest/coverage-v8": "^3.1.4",
|
|
89
|
+
"eslint": "^9.26.0",
|
|
90
|
+
"prettier": "^3.5.3",
|
|
91
|
+
"publint": "^0.3.12",
|
|
92
|
+
"tsup": "^8.4.0",
|
|
93
|
+
"tsx": "^4.19.4",
|
|
94
|
+
"typescript": "^5.8.3",
|
|
95
|
+
"typescript-eslint": "^8.31.0",
|
|
96
|
+
"vitest": "^3.1.4",
|
|
97
|
+
"zod-to-json-schema": "^3.24.5"
|
|
98
|
+
},
|
|
99
|
+
"author": "Zahid Hasan <jahidhasann67@gmail.com>"
|
|
100
|
+
}
|