restura-cli 0.2.14

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 ADDED
@@ -0,0 +1,177 @@
1
+ # restura-cli
2
+
3
+ Run Restura API collections in CI — assert with scripts, get JUnit / HTML / JSON reports.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -g restura-cli
9
+ # or, no install:
10
+ npx restura-cli run ./my-collection
11
+ ```
12
+
13
+ Requires Node.js 24+.
14
+
15
+ ## Quick start
16
+
17
+ Export a collection from the Restura app (File → Export → OpenCollection directory), then:
18
+
19
+ ```bash
20
+ restura run ./my-collection --reporter junit --reporter-output junit=results.xml
21
+ ```
22
+
23
+ Exit code is `0` when every request passed, `1` if any failed, `2` on internal errors (missing collection, bad flags).
24
+
25
+ ## Supported collection formats
26
+
27
+ The loader auto-detects three layouts:
28
+
29
+ | Layout | Detected when… |
30
+ | ---------------------------------------- | --------------------------------------------------------------- |
31
+ | **OpenCollection directory** (preferred) | the target directory contains `opencollection.yml` (or `.yaml`) |
32
+ | **OpenCollection bundled file** | the target path ends in `.yaml`/`.yml` |
33
+ | **Legacy file-collection** (deprecated) | the target directory contains `_collection.yaml` |
34
+
35
+ The legacy format prints a stderr deprecation warning the first time it's loaded.
36
+
37
+ ## Supported protocols
38
+
39
+ - **HTTP / REST** — full support
40
+ - **GraphQL** — runs as HTTP with body type `graphql`
41
+ - **gRPC** — via Connect protocol (JSON-encoded, no proto compilation needed)
42
+ - **SSE** — captures events for `--sse-duration` ms, or until `--sse-events N`
43
+ - **MCP** — single JSON-RPC POST per request
44
+ - **WebSocket** — standalone executor available; not yet wired into the dispatcher (see `executors/websocket.ts`)
45
+
46
+ Header-based auth (Bearer, Basic, API-key, OAuth2 access token) is applied to HTTP/GraphQL, gRPC (as metadata), SSE, and MCP requests. Wire-signed schemes (AWS SigV4, OAuth1, WSSE) are signed at the wire on the HTTP path. `x-www-form-urlencoded` bodies are sent for both inline (`raw`) and structured (OpenCollection field-array) forms; `multipart/form-data` and `protobuf` bodies are not yet supported by the CLI fetcher.
47
+
48
+ ## CLI reference
49
+
50
+ ```
51
+ restura run <collection> [options]
52
+ ```
53
+
54
+ `<collection>` accepts a directory (any supported layout) or a bundled `.yaml`/`.yml` file.
55
+
56
+ | Flag | Default | Description |
57
+ | --------------------------- | ------------- | ---------------------------------------------------------------------------------------------------- |
58
+ | `--env <file>` | | JSON or YAML env file. `${VAR}` placeholders are expanded from `process.env`. |
59
+ | `--reporter <list>` | `live` | Comma-separated. Mix and match: `live`, `json`, `junit`, `html`. |
60
+ | `--output <file>` | | Shorthand for single file reporter. |
61
+ | `--reporter-output <kv...>` | | Per-reporter output: `--reporter-output junit=results.xml html=report.html`. |
62
+ | `--bail` | `false` | Stop on first failure. |
63
+ | `--timeout <ms>` | `30000` | Per-request timeout. |
64
+ | `--allow-localhost` | `false` | Permit requests to `localhost` / `127.0.0.1`. Off by default (SSRF guard). |
65
+ | `--folder <path>` | | Only run requests under this folder path (slash-joined). |
66
+ | `--include <pattern...>` | | Substring or glob (e.g. `users/*`). Repeatable. |
67
+ | `--exclude <pattern...>` | | Same syntax as `--include`. Applied after. |
68
+ | `--data <file>` | | CSV (with header row) or JSON array. Runs the collection once per row; row keys are exposed as vars. |
69
+ | `--max-iterations <n>` | | Cap iterations when a `--data` file is large. |
70
+ | `--retry <n>` | `0` | Retry attempts per failing request. |
71
+ | `--retry-on <list>` | `network,5xx` | Comma-separated triggers: `network`, `5xx`, `4xx`, or specific status codes (`429,503`). |
72
+ | `--sse-duration <ms>` | `5000` | How long to keep SSE streams open. |
73
+ | `--sse-events <n>` | | Stop SSE early after N events. |
74
+ | `--ws-duration <ms>` | `5000` | How long to keep WebSocket connections open. |
75
+ | `--ws-messages <n>` | | Stop WebSocket early after N messages. |
76
+
77
+ ## Scripts and assertions
78
+
79
+ Pre-request and test scripts run in a sandboxed QuickJS WASM VM (no DOM, no filesystem, no network escape; 64 MB memory cap, 5 s sync / 30 s async execution timeout).
80
+
81
+ ```yaml
82
+ # request.http.yaml
83
+ name: Get user
84
+ method: GET
85
+ url: '{{API_BASE}}/users/1'
86
+ testScript: |
87
+ pm.test("status is 200", () => pm.response.to.have.status(200));
88
+ pm.test("response has name", () => {
89
+ pm.expect(pm.response.json()).to.have.property("name");
90
+ });
91
+ ```
92
+
93
+ When a test script runs and defines any `pm.test(...)` assertion, those drive pass/fail. Otherwise pass/fail falls back to the transport outcome (HTTP 2xx, gRPC OK, etc.).
94
+
95
+ Variables set inside a script (`pm.environment.set('K', 'v')`) propagate to subsequent requests in the same run.
96
+
97
+ ## Variables
98
+
99
+ Three layered sources, in order of precedence (later wins):
100
+
101
+ 1. `--env` file
102
+ 2. Collection variables (declared in `opencollection.yml` or `_collection.yaml`)
103
+ 3. Iteration row (when `--data` is set)
104
+
105
+ Substitutions are `{{NAME}}`. Unknown vars are left in place so the upstream sees them and you notice the gap.
106
+
107
+ ### Dynamic helpers
108
+
109
+ Postman-compatible `{{$random*}}` / `{{$timestamp}}` helpers are expanded after user var substitution:
110
+
111
+ | Helper | Example |
112
+ | ---------------------- | --------------------------- |
113
+ | `{{$randomUUID}}` | `f4d2e3...` |
114
+ | `{{$timestamp}}` | `1700000000` (unix seconds) |
115
+ | `{{$isoTimestamp}}` | `2026-05-22T13:42:00Z` |
116
+ | `{{$randomEmail}}` | `alice.42@example.com` |
117
+ | `{{$randomFirstName}}` | `Olivia` |
118
+ | `{{$randomIP}}` | `192.0.2.4` |
119
+
120
+ Full list in `src/lib/shared/dynamicVariables.ts`.
121
+
122
+ ## Data-driven runs
123
+
124
+ ```bash
125
+ restura run ./users-api --data ./users.csv --reporter junit --reporter-output junit=junit.xml
126
+ ```
127
+
128
+ ```csv
129
+ # users.csv
130
+ username,role
131
+ alice,admin
132
+ bob,viewer
133
+ charlie,editor
134
+ ```
135
+
136
+ Each row exposes `username` and `role` as variables, overriding any same-named env or collection variable for that iteration only. JUnit testcase names carry an `[iter N]` suffix so each iteration is distinct in CI dashboards.
137
+
138
+ ## Reporters
139
+
140
+ - **`live`** — coloured progress to stdout. Default.
141
+ - **`json`** — full `RunResult` dumped as JSON. Path required (`--output` or `--reporter-output json=...`).
142
+ - **`junit`** — JUnit XML for CI dashboards. One `<testcase>` per request.
143
+ - **`html`** — self-contained HTML page with embedded data + summary table.
144
+
145
+ Combine with a comma: `--reporter live,junit --reporter-output junit=results.xml`.
146
+
147
+ ## Exit codes
148
+
149
+ | Code | Meaning |
150
+ | ---- | ------------------------------------------------------------------------------- |
151
+ | `0` | Every request passed AND at least one request ran |
152
+ | `1` | One or more requests failed or errored (or no requests matched after filtering) |
153
+ | `2` | Internal error: missing collection, bad reporter name, IO failure, … |
154
+
155
+ ## Troubleshooting
156
+
157
+ - **`No recognised collection layout`** — your target directory needs one of `opencollection.yml`, `opencollection.yaml`, or `_collection.yaml`. Re-export from the Restura app if unsure.
158
+ - **`Invalid URL`** — the URL after `{{var}}` resolution isn't a valid absolute URL. Check that `--env` is loaded and your var names match.
159
+ - **`Localhost URLs are not allowed`** — add `--allow-localhost` for local upstreams. Off by default to prevent SSRF in shared CI.
160
+ - **gRPC requests return `UNKNOWN`** — the upstream likely doesn't speak Connect protocol. The CLI uses Connect-over-HTTP, not gRPC-over-HTTP/2 binary framing.
161
+ - **`auth uses a desktop-only secret handle…`** — your auth references a secret handle that only the desktop app can decrypt. The request is errored (not sent unauthenticated); re-export the collection with inline secret values for CI use.
162
+
163
+ ## Development
164
+
165
+ ```bash
166
+ # from cli/
167
+ npm install
168
+ npm test # vitest
169
+ npm run type-check # tsc --noEmit
170
+ npm run build # tsup → dist/
171
+ ```
172
+
173
+ The CLI imports from the parent project at compile-time via path aliases (`@/`, `@shared/`); `cli/tsconfig.json` controls which parent modules participate in type-checking.
174
+
175
+ ## License
176
+
177
+ MIT.