wayfarer-cli 2.0.0 → 2.0.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/README.md CHANGED
@@ -1,30 +1,231 @@
1
1
  # wayfarer-cli
2
2
 
3
- Runs a [Wayfarer](https://github.com/AshwinSathian/wayfarer) collection, a
4
- Wayfarer workspace backup or a Postman collection from a terminal or a CI
5
- job, with the engine the Wayfarer app sends with: the same scripts, the
6
- same variables and vault, the same results. It exits with `1` when a test
7
- fails or a request gets no response.
3
+ Run a [Wayfarer](https://wayfarer.ashwinsathian.com) or Postman collection
4
+ from a terminal or a CI job. The command line sends with the same engine as
5
+ the Wayfarer app: the same script sandbox, variables, vault and assertions.
6
+ A run here gives the results the same run gives in the app, and a test in the
7
+ repository checks that in Chromium, Firefox and WebKit.
8
8
 
9
9
  ```bash
10
- npx wayfarer-cli run collection.json -e environment.json -d data.csv --allow-scripts --reporter cli,junit
11
- ```
12
-
13
- - `-e` an environment (Wayfarer or Postman), `-d` a data file (CSV or JSON),
14
- `-n` iterations, `--bail`, `--timeout <ms>`, `--insecure`.
15
- - `--reporter cli,json,junit` writes `wayfarer-run.json` and
16
- `wayfarer-run.junit.xml` (in `--out`, default the current folder).
17
- - **A file's scripts run only with `--allow-scripts`.** Without it, a
18
- collection that has a script is not run and nothing is sent: read a
19
- file's scripts before you allow them.
20
- - A request that uses a vault secret needs the vault: a workspace backup
21
- brings it, or `--vault-file` names an exported one. The passphrase is read
22
- from `WAYFARER_VAULT_PASSPHRASE`.
23
- - Every request goes straight from Node: no CORS, no Local Bridge.
24
-
25
- Node.js 22.12 or later. The full guide, with what the command line does not
26
- do and a GitHub Actions example, is
27
- [`docs/cli.md`](https://github.com/AshwinSathian/wayfarer/blob/main/docs/cli.md).
28
-
29
- MIT licence. The packages bundled into `dist/wayfarer-cli.mjs` are listed,
30
- with their licences, in `dist/THIRD_PARTY_LICENSES.txt`.
10
+ npx wayfarer-cli run orders.postman_collection.json -e local.postman_environment.json --allow-scripts
11
+ ```
12
+
13
+ ```
14
+ ✓ Create order: 200 OK, 16 ms
15
+ ✓ status is 200
16
+ ✓ the SKU was sent
17
+ ✓ Fetch order: 200 OK, 2 ms
18
+ ✓ the SKU came back
19
+
20
+ 2 results in 1 iteration; 0 failed; 3 of 3 tests passed.
21
+ ```
22
+
23
+ When a test fails, the line says what was expected, and the exit code is `1`:
24
+
25
+ ```
26
+ ✗ Fetch order: 200 OK, 3 ms
27
+ ✗ the SKU came back: expected 'ABC-2' to deeply equal 'ABC-1'
28
+
29
+ 2 results in 1 iteration; 1 failed; 2 of 3 tests passed.
30
+ ```
31
+
32
+ ## Install
33
+
34
+ It needs Node.js 22.12 or later. Run it with `npx`, or install it:
35
+
36
+ ```bash
37
+ npm install --global wayfarer-cli
38
+ wayfarer-cli --version
39
+ ```
40
+
41
+ In a project, add it as a dev dependency and pin the major version
42
+ (`wayfarer-cli@2`): a new major version may change what a run does.
43
+
44
+ ## What it runs
45
+
46
+ ```bash
47
+ wayfarer-cli run <file> [options]
48
+ ```
49
+
50
+ `<file>` can be any of:
51
+
52
+ - a Wayfarer collection (Export in the app);
53
+ - a Wayfarer workspace backup (Settings → Backup). It brings its active
54
+ environment, its globals and its vault. A backup with several collections
55
+ needs `--collection <name>`;
56
+ - a Postman collection, v2.0 or v2.1;
57
+ - an Insomnia export or an OpenAPI description, read the way the app
58
+ imports them.
59
+
60
+ ## Options
61
+
62
+ | Option | What it does |
63
+ |---|---|
64
+ | `-e`, `--environment <file>` | The variables: a Wayfarer environments file or a Postman environment. |
65
+ | `-d`, `--data <file>` | A data file, CSV or JSON. Each row is one iteration. |
66
+ | `-n`, `--iterations <n>` | How many iterations. Default: one per row of the data file, or 1. |
67
+ | `--collection <name>` | Which collection to run, from a file that holds several. |
68
+ | `--reporter <list>` | `cli`, `json`, `junit`, comma-separated. Default `cli`. |
69
+ | `--out <dir>` | Where `json` and `junit` reports go. Default: the current folder. |
70
+ | `--bail` | Stop at the first failure. |
71
+ | `--timeout <ms>` | Give up on a request after this long. Default: no limit. |
72
+ | `--insecure` | Don't verify TLS certificates, for every request of the run. |
73
+ | `--allow-scripts` | Run the file's scripts (see below). |
74
+ | `--vault-file <file>` | A vault exported from the app, for requests that use a vault secret. |
75
+ | `-v`, `--version` | Print the version. |
76
+ | `-h`, `--help` | Print the usage. |
77
+
78
+ ### Exit codes
79
+
80
+ | Code | Meaning |
81
+ |---|---|
82
+ | `0` | Every request got a response and every test passed. |
83
+ | `1` | A test failed, a request got no response, or a script ended in an error. |
84
+ | `2` | Nothing was run. The message on stderr says why. |
85
+
86
+ ## Scripts run only when you allow them
87
+
88
+ In the app, scripts from an imported file run only after you've read them and
89
+ marked them as trusted. A file passed to the command line has had no such
90
+ review, so a collection with any pre-request or post-response script is not
91
+ run until you add `--allow-scripts`:
92
+
93
+ ```
94
+ $ npx wayfarer-cli run orders.postman_collection.json -e local.postman_environment.json
95
+ This collection has scripts, in the request "Create order", the request "Fetch order". A file's scripts have not been reviewed: read them, and run again with --allow-scripts only if you trust them. Nothing was sent.
96
+ ```
97
+
98
+ That run exits `2`. With the flag, every script in the file runs in QuickJS
99
+ (compiled to WebAssembly), with the same allow-list of globals as the app.
100
+ There is no `process`, and `require` gives only the five bundled libraries
101
+ (chai, crypto-js, lodash, moment, uuid), so a script can't read your files or
102
+ environment variables. It can still send
103
+ requests of its own with `pm.sendRequest` and change the request it belongs
104
+ to, so read a file's scripts before you allow them.
105
+
106
+ What `pm` supports is listed row by row in
107
+ [docs/postman-compatibility.md](https://github.com/AshwinSathian/wayfarer/blob/main/docs/postman-compatibility.md).
108
+ That file is generated from the cases the test suite runs.
109
+
110
+ ## Coming from Newman
111
+
112
+ Most flags carry over:
113
+
114
+ | Newman | wayfarer-cli |
115
+ |---|---|
116
+ | `newman run c.json` | `wayfarer-cli run c.json` (add `--allow-scripts` if it has scripts) |
117
+ | `-e env.json` | `-e env.json` |
118
+ | `-d data.csv` | `-d data.csv` |
119
+ | `-n 3` | `-n 3` |
120
+ | `--bail` | `--bail` |
121
+ | `--timeout-request 5000` | `--timeout 5000` |
122
+ | `-k`, `--insecure` | `--insecure` |
123
+ | `-r cli,junit --reporter-junit-export out/report.xml` | `--reporter cli,junit --out out` (the file is `wayfarer-run.junit.xml`) |
124
+
125
+ Not supported: a globals file (`-g`), `--env-var`, and running a single
126
+ folder (`--folder`). Put those values in the environment file, or use a
127
+ workspace backup, which carries its globals.
128
+
129
+ The order of a run follows Newman, including `setNextRequest`, skipped
130
+ requests and data-file iterations. That order is checked against
131
+ golden files written by Newman.
132
+
133
+ ## Reports
134
+
135
+ `--reporter json` writes `wayfarer-run.json` and `--reporter junit` writes
136
+ `wayfarer-run.junit.xml`, the same files the app's run view saves. Both go
137
+ in `--out`, which is created if it doesn't exist (2.0.1 and later). The
138
+ JUnit file has one `<testsuite>` per request and one `<testcase>` per test:
139
+
140
+ ```xml
141
+ <testsuites name="Orders API" tests="3" failures="0" errors="0" time="0.019">
142
+ <testsuite name="Create order" id="01a2e75e-…" tests="2" failures="0" errors="0" skipped="0" time="0.016">
143
+ <testcase name="status is 200" classname="Create order"/>
144
+ <testcase name="the SKU was sent" classname="Create order"/>
145
+ </testsuite>
146
+ …
147
+ </testsuites>
148
+ ```
149
+
150
+ ## Secrets and the vault
151
+
152
+ A Wayfarer variable can be protected: its value is a reference to a secret
153
+ in the app's encrypted vault, written `{{$secret.<id>}}`. A request that
154
+ uses one needs the vault. A workspace backup includes it, or `--vault-file`
155
+ names one exported from the app. The passphrase comes from the
156
+ `WAYFARER_VAULT_PASSPHRASE` environment variable, never from an argument,
157
+ because other users of the machine can see a process's arguments.
158
+
159
+ Without the vault, such a request isn't sent: a secret's reference never goes
160
+ out as text. Secrets are masked in both reports, as they are in the app's
161
+ history. When a script sets a protected variable, the new value lasts for the
162
+ rest of the run; the vault file isn't written.
163
+
164
+ The app asks before a pre-request script sends a request with a vault secret
165
+ to a different host. Nobody can answer that in a terminal, so the answer is
166
+ no: the request isn't sent, and the run ends with the reason.
167
+
168
+ ## In CI
169
+
170
+ A GitHub Actions job that runs a collection on every push, keeps the JUnit
171
+ report and fails when a test fails:
172
+
173
+ ```yaml
174
+ name: API tests
175
+ on: [push]
176
+ jobs:
177
+ api:
178
+ runs-on: ubuntu-latest
179
+ steps:
180
+ - uses: actions/checkout@v5
181
+ - uses: actions/setup-node@v5
182
+ with:
183
+ node-version: 24
184
+ - name: Run the collection
185
+ run: npx --yes wayfarer-cli@2 run api/collection.json -e api/ci.environment.json --allow-scripts --reporter cli,junit --out reports
186
+ env:
187
+ # Only needed for requests that use a vault secret.
188
+ WAYFARER_VAULT_PASSPHRASE: ${{ secrets.WAYFARER_VAULT_PASSPHRASE }}
189
+ - uses: actions/upload-artifact@v4
190
+ if: always()
191
+ with:
192
+ name: api-report
193
+ path: reports/wayfarer-run.junit.xml
194
+ ```
195
+
196
+ Every release is published from this repository's
197
+ [`release.yml`](https://github.com/AshwinSathian/wayfarer/blob/main/.github/workflows/release.yml)
198
+ with an npm provenance attestation. `npm audit signatures` in a project that
199
+ installs it checks the attestation.
200
+
201
+ ## Where requests go
202
+
203
+ Every request goes straight from Node. Node has no CORS, no mixed-content
204
+ rule and no forbidden headers, so a route set in the app (Auto, Local
205
+ Bridge) is ignored here, and nothing is ever sent twice.
206
+
207
+ For a proxy, set `NODE_USE_ENV_PROXY=1` together with `HTTPS_PROXY`,
208
+ `HTTP_PROXY` and `NO_PROXY`. That is Node's own proxy support, in Node 22.21
209
+ and 24.5 and later.
210
+
211
+ ## Not supported
212
+
213
+ - **OAuth 2.0 tokens.** A request with OAuth 2.0 auth isn't sent, and the
214
+ result says so. Get the token in an earlier CI step and pass it as a
215
+ variable.
216
+ - **Files in a request body.** A file you pick in the app stays in that
217
+ browser and isn't in the collection file, so such a request isn't sent.
218
+ - **Cookies.** There's no cookie jar: a request carries only the cookies
219
+ it sets itself.
220
+ - **Event streams and WebSockets.** An event stream is read as one body
221
+ when it ends, so give a `--timeout` for one that doesn't end. A `ws:` or
222
+ `wss:` address gets no response.
223
+
224
+ The full guide is
225
+ [docs/cli.md](https://github.com/AshwinSathian/wayfarer/blob/main/docs/cli.md).
226
+
227
+ ## Licence
228
+
229
+ MIT. The packages bundled into `dist/wayfarer-cli.mjs` are listed with their
230
+ licences in `dist/THIRD_PARTY_LICENSES.txt`. The script engine,
231
+ `quickjs-emscripten`, is an ordinary dependency and carries its own.
@@ -7445,7 +7445,7 @@ var init_uuid = __esm({
7445
7445
  });
7446
7446
 
7447
7447
  // src/main.ts
7448
- import { readFile, writeFile } from "node:fs/promises";
7448
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
7449
7449
  import { basename, join } from "node:path";
7450
7450
  import { parseArgs } from "node:util";
7451
7451
 
@@ -8477,7 +8477,6 @@ function validateVaultFile(value) {
8477
8477
  if (typeof vault !== "object" || vault === null) {
8478
8478
  fail("vault", "Value must be an object.");
8479
8479
  } else {
8480
- if (field2(vault, "v") !== 2) fail("vault.v", "Value must be 2.");
8481
8480
  const kdf = field2(vault, "kdf");
8482
8481
  if (typeof kdf !== "object" || kdf === null) {
8483
8482
  fail("vault.kdf", "Value must be an object.");
@@ -8512,7 +8511,6 @@ function validateVaultFile(value) {
8512
8511
  fail(`${path}.envelope`, "Value must be an object.");
8513
8512
  return;
8514
8513
  }
8515
- if (field2(envelope, "v") !== 2) fail(`${path}.envelope.v`, "Value must be 2.");
8516
8514
  if (!isBase64Url(field2(envelope, "iv"))) fail(`${path}.envelope.iv`, "Value must be base64url text.");
8517
8515
  if (!isBase64Url(field2(envelope, "ct"))) fail(`${path}.envelope.ct`, "Value must be base64url text.");
8518
8516
  });
@@ -13267,7 +13265,7 @@ async function main(argv) {
13267
13265
  }
13268
13266
  });
13269
13267
  if (values.version) {
13270
- console.log("2.0.0");
13268
+ console.log("2.0.1");
13271
13269
  return 0;
13272
13270
  }
13273
13271
  const [command, file, ...rest] = positionals;
@@ -13306,6 +13304,11 @@ ${USAGE}`);
13306
13304
  }
13307
13305
  if (values.insecure) process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = "0";
13308
13306
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
13307
+ if (reporters.has("json") || reporters.has("junit")) {
13308
+ await mkdir(values.out, { recursive: true }).catch((error) => {
13309
+ throw new Refusal(`--out ${values.out}: ${error instanceof Error ? error.message : String(error)}`);
13310
+ });
13311
+ }
13309
13312
  const report = await runCollection(
13310
13313
  { source: read.source, ...environment2 && { environment: environment2 }, globals: read.globals, ...vault && { vault }, allowScripts: values["allow-scripts"], timeoutMs },
13311
13314
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wayfarer-cli",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "Runs Wayfarer and Postman collections from the command line, with the engine the Wayfarer app sends with: the same scripts, variables, vault and results.",
5
5
  "license": "MIT",
6
6
  "author": "Ashwin Sathian",