lacspace-http 0.1.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/LICENSE +51 -0
- package/README.md +224 -0
- package/dist/cli.js +1284 -0
- package/dist/lib.cjs +909 -0
- package/dist/lib.d.cts +290 -0
- package/dist/lib.d.ts +290 -0
- package/dist/lib.js +862 -0
- package/package.json +47 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
Lacspace Free Licence
|
|
2
|
+
Version 1.0, August 2026
|
|
3
|
+
|
|
4
|
+
Copyright (c) 2026 Lacspace
|
|
5
|
+
|
|
6
|
+
PREAMBLE
|
|
7
|
+
|
|
8
|
+
This software is published by Lacspace under the Lacspace Free Licence — a free,
|
|
9
|
+
permissive licence that lets you use this software for any purpose, including in
|
|
10
|
+
commercial products and services, at no cost. It grants the same freedoms as
|
|
11
|
+
common permissive open-source licences; the only condition is that this notice
|
|
12
|
+
travels with the software. The canonical, always-current text of this licence is
|
|
13
|
+
maintained at https://lacspace.com/licenses/lacspace-free-1.0
|
|
14
|
+
|
|
15
|
+
GRANT OF RIGHTS
|
|
16
|
+
|
|
17
|
+
Permission is hereby granted, free of charge, to any person or organisation
|
|
18
|
+
obtaining a copy of this software and its associated documentation and data files
|
|
19
|
+
(the "Software"), to deal in the Software without restriction, including without
|
|
20
|
+
limitation the rights to use, copy, modify, merge, publish, distribute,
|
|
21
|
+
sublicense, and/or sell copies of the Software, and to permit persons to whom the
|
|
22
|
+
Software is furnished to do so, subject to the conditions below. These rights are
|
|
23
|
+
granted for any purpose, personal or commercial, and are perpetual, worldwide,
|
|
24
|
+
non-exclusive, and royalty-free.
|
|
25
|
+
|
|
26
|
+
CONDITIONS
|
|
27
|
+
|
|
28
|
+
The above copyright notice, this permission notice, and the name of this licence
|
|
29
|
+
("Lacspace Free Licence") shall be included in all copies or substantial portions
|
|
30
|
+
of the Software.
|
|
31
|
+
|
|
32
|
+
TRADEMARKS
|
|
33
|
+
|
|
34
|
+
This licence does not grant permission to use the trade names, trademarks, service
|
|
35
|
+
marks, logos, or product names of Lacspace, except as required to reproduce the
|
|
36
|
+
notice above or to describe the origin of the Software in a truthful manner.
|
|
37
|
+
|
|
38
|
+
DISCLAIMER OF WARRANTY AND LIMITATION OF LIABILITY
|
|
39
|
+
|
|
40
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
41
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
42
|
+
FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
43
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN
|
|
44
|
+
AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
|
|
45
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
The Lacspace Free Licence is a source-available, permissive licence and is not (as
|
|
50
|
+
of this version) an OSI-approved licence. In substance it grants the same freedoms
|
|
51
|
+
as the MIT Licence. Learn more at https://lacspace.com/licenses
|
package/README.md
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# lacspace-http
|
|
2
|
+
|
|
3
|
+
**A keyless, zero-dependency terminal API client + `.http` file runner** — a local Postman/httpie you drive from the shell. Fire off HTTP requests with a friendly CLI, or run a `.http`/`.rest` file of named requests with `{{variables}}`, response **capture & chaining** (grab a token from one response, reuse it in the next) and **assertions** — so a file doubles as an API test suite you can run in CI.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx lacspace-http https://httpbin.org/get
|
|
7
|
+
# 200 OK · 214ms · 1.2 KB
|
|
8
|
+
# { "args": {}, "headers": { … }, "url": "https://httpbin.org/get" }
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
No API key. No account. No telemetry. Nothing leaves your machine except the requests you ask it to send. Built entirely on the global `fetch` and Node built-ins — the `.http` parser, the JSON-path evaluator and the assertion engine are all hand-written (no `eval`), so there are **zero runtime dependencies**.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# one-off, no install
|
|
17
|
+
npx lacspace-http https://httpbin.org/get
|
|
18
|
+
|
|
19
|
+
# or globally
|
|
20
|
+
npm i -g lacspace-http
|
|
21
|
+
|
|
22
|
+
# or as a library
|
|
23
|
+
npm i lacspace-http
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Two ways to use it
|
|
27
|
+
|
|
28
|
+
### 1. Ad-hoc requests
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
lacspace-http [METHOD] <url> [options]
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The method defaults to `GET` (or `POST` when you supply a body).
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# GET with a bearer token, show response headers
|
|
38
|
+
lacspace-http https://api.example.com/me -b "$TOKEN" -i
|
|
39
|
+
|
|
40
|
+
# POST JSON with the -j shorthand (k=v is a string, k:=v is raw JSON)
|
|
41
|
+
lacspace-http POST https://httpbin.org/post -j name=Ada -j admin:=true -j age:=30
|
|
42
|
+
|
|
43
|
+
# POST a urlencoded form
|
|
44
|
+
lacspace-http POST https://httpbin.org/post --form email=a@b.com --form plan=pro
|
|
45
|
+
|
|
46
|
+
# Basic auth + query params + a timeout
|
|
47
|
+
lacspace-http https://api.example.com/search -u user:pass -q q=cats -q page=2 --timeout 5000
|
|
48
|
+
|
|
49
|
+
# Save the body to a file (meta still prints to stderr)
|
|
50
|
+
lacspace-http https://example.com/data.json -o data.json
|
|
51
|
+
|
|
52
|
+
# Print the equivalent curl (credentials masked unless --show-secrets)
|
|
53
|
+
lacspace-http GET https://api.example.com/me -b "$TOKEN" --curl
|
|
54
|
+
# curl -H 'Authorization: Bearer ***' -L 'https://api.example.com/me'
|
|
55
|
+
|
|
56
|
+
# Machine-readable record for scripts / jq
|
|
57
|
+
lacspace-http https://httpbin.org/get --json | jq .status
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Meta (status line, timing, size, headers) goes to **stderr**; the response body goes to **stdout**, so `| jq` and `> file` just work.
|
|
61
|
+
|
|
62
|
+
### 2. Run a `.http` / `.rest` file
|
|
63
|
+
|
|
64
|
+
The well-known REST-client format: requests separated by `###`, an optional `# @name`, a request line, headers, a blank line, then a body. lacspace-http adds `# @capture` and `# @assert` directives.
|
|
65
|
+
|
|
66
|
+
```http
|
|
67
|
+
# api.http
|
|
68
|
+
# @name login
|
|
69
|
+
POST {{host}}/login
|
|
70
|
+
Content-Type: application/json
|
|
71
|
+
|
|
72
|
+
{ "user": "ada", "pass": "{{password}}" }
|
|
73
|
+
# @capture token = body.$.access_token
|
|
74
|
+
# @assert status == 200
|
|
75
|
+
# @assert body.$.ok == true
|
|
76
|
+
|
|
77
|
+
###
|
|
78
|
+
# @name me
|
|
79
|
+
GET {{host}}/me
|
|
80
|
+
Authorization: Bearer {{token}}
|
|
81
|
+
# @assert status == 200
|
|
82
|
+
# @assert body.$.user == "ada"
|
|
83
|
+
# @assert header.content-type contains json
|
|
84
|
+
# @assert time < 800
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
lacspace-http run api.http --var host=http://localhost:3000 --var password=hunter2
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
◆ lacspace-http run api.http
|
|
93
|
+
|
|
94
|
+
✓ login → 200 38ms
|
|
95
|
+
captured token=TOK-42
|
|
96
|
+
✓ assert status == 200 (actual: 200)
|
|
97
|
+
✓ assert body.$.ok == true (actual: true)
|
|
98
|
+
✓ me → 200 3ms
|
|
99
|
+
✓ assert status == 200 (actual: 200)
|
|
100
|
+
✓ assert body.$.user == "ada" (actual: ada)
|
|
101
|
+
✓ assert header.content-type contains json (actual: application/json)
|
|
102
|
+
✓ assert time < 800 (actual: 3)
|
|
103
|
+
✓ 2 passed · 2 request(s)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
`login` grabs `access_token` from the JSON body; `me` reuses it via `{{token}}`. Any failed assertion (or transport error) makes the whole run **exit non-zero** — drop `lacspace-http run api.http` into a CI step and it's a smoke test.
|
|
107
|
+
|
|
108
|
+
#### Variables
|
|
109
|
+
|
|
110
|
+
`{{name}}` is resolved, in order of precedence, from:
|
|
111
|
+
|
|
112
|
+
1. **captured** values (from an earlier `# @capture` in the same run),
|
|
113
|
+
2. CLI `--var k=v` (repeatable),
|
|
114
|
+
3. an env block from `--env-file` + `--env <name>`.
|
|
115
|
+
|
|
116
|
+
Env files can be VS Code REST-client style `http-client.env.json`:
|
|
117
|
+
|
|
118
|
+
```json
|
|
119
|
+
{
|
|
120
|
+
"$shared": { "password": "hunter2" },
|
|
121
|
+
"dev": { "host": "http://localhost:3000" },
|
|
122
|
+
"prod": { "host": "https://api.example.com" }
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
lacspace-http run api.http --env-file http-client.env.json --env dev
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
…or a plain `.env` file (`KEY=VALUE` lines). A few system variables are also available: `{{$timestamp}}`, `{{$isoTimestamp}}`, `{{$guid}}`, `{{$randomInt min max}}`, `{{$processEnv NAME}}`.
|
|
131
|
+
|
|
132
|
+
#### Assertions
|
|
133
|
+
|
|
134
|
+
| Form | Example |
|
|
135
|
+
| --- | --- |
|
|
136
|
+
| status code | `# @assert status == 200` |
|
|
137
|
+
| JSON body (json-path) | `# @assert body.$.data.items[0].id == 1` |
|
|
138
|
+
| response header | `# @assert header.content-type contains json` |
|
|
139
|
+
| request time (ms) | `# @assert time < 800` |
|
|
140
|
+
| existence / emptiness | `# @assert body.$.error empty` · `body.$.token exists` |
|
|
141
|
+
| regex | `# @assert header.location matches ^/v2/` |
|
|
142
|
+
|
|
143
|
+
Operators: `==` `!=` `<` `<=` `>` `>=` `contains` `matches` `exists` `empty`. The `body.<path>` and `header.<name>` grammar is the same one `# @capture <name> = <source>` uses.
|
|
144
|
+
|
|
145
|
+
## CLI reference
|
|
146
|
+
|
|
147
|
+
| Flag | Description |
|
|
148
|
+
| --- | --- |
|
|
149
|
+
| `-H, --header "K: V"` | Add a request header (repeatable) |
|
|
150
|
+
| `-q, --query k=v` | Add a query-string param (repeatable) |
|
|
151
|
+
| `-d, --data <str>` | Raw request body (`@file` reads a file, `@-` reads stdin) |
|
|
152
|
+
| `--json '<obj>'` | JSON body from a literal object/array (+ `content-type`) |
|
|
153
|
+
| `-j k=v` | JSON body shorthand (`k:=v` for a raw JSON value) |
|
|
154
|
+
| `--form k=v` | urlencoded form field (repeatable) |
|
|
155
|
+
| `-b, --bearer <token>` | `Authorization: Bearer <token>` |
|
|
156
|
+
| `-u, --user user:pass` | HTTP Basic auth |
|
|
157
|
+
| `--timeout <ms>` | Abort after *ms* (default `30000`) |
|
|
158
|
+
| `--no-redirect` | Do not follow redirects |
|
|
159
|
+
| `--max-redirects N` | Follow at most *N* redirects (default `5`) |
|
|
160
|
+
| `--max-size <n>` | Cap the response body read, e.g. `10mb` (default 10 MB) |
|
|
161
|
+
| `-i, --include` | Show response headers |
|
|
162
|
+
| `-v, --verbose` | Show the request line, headers and body too |
|
|
163
|
+
| `-o, --out <file>` | Write the response body to a file |
|
|
164
|
+
| `--curl` | Print the equivalent `curl` command (secrets masked) |
|
|
165
|
+
| `--show-secrets` | Do not mask credentials in `--curl` / `--verbose` |
|
|
166
|
+
| `--fail` | Exit non-zero on a non-2xx response |
|
|
167
|
+
| `--json` | Emit a machine record `{ status, headers, timeMs, body }` |
|
|
168
|
+
| `--env <name>` | (run) Select an env block from a JSON env file |
|
|
169
|
+
| `--env-file <path>` | (run) Load vars from a `.json` (needs `--env`) or `.env` file |
|
|
170
|
+
| `--var k=v` | (run) Set/override a variable (repeatable) |
|
|
171
|
+
| `--name <req>` | (run) Run only the request with this `# @name` |
|
|
172
|
+
| `-h, --help` | Show help |
|
|
173
|
+
| `--version` | Print the version |
|
|
174
|
+
|
|
175
|
+
`NO_COLOR` is respected. Coloured JSON is only emitted when stdout is a TTY, so piping stays clean.
|
|
176
|
+
|
|
177
|
+
## Library API
|
|
178
|
+
|
|
179
|
+
Everything the CLI does is exposed as a typed, dual ESM/CJS library.
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
import {
|
|
183
|
+
assembleRequest, sendRequest, toCurl, runHttpFile,
|
|
184
|
+
parseHttpFile, evalPath, runAssertion,
|
|
185
|
+
} from "lacspace-http";
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
| Export | Signature |
|
|
189
|
+
| --- | --- |
|
|
190
|
+
| `assembleRequest(url, opts?)` | `(string, AssembleOptions) => RequestSpec` — build a request from friendly options |
|
|
191
|
+
| `sendRequest(spec, opts?)` | `(RequestSpec, SendOptions) => Promise<ResponseRecord>` — send it (redirects, timeout, size cap) |
|
|
192
|
+
| `toCurl(spec, opts?)` | `(RequestSpec, CurlOptions) => string` — the equivalent curl (secrets masked by default) |
|
|
193
|
+
| `runHttpFile(source, opts?)` | `(string, RunOptions) => Promise<RunResult>` — run a `.http` document |
|
|
194
|
+
| `parseHttpFile(source)` | `(string) => HttpFileRequest[]` — just parse, no I/O |
|
|
195
|
+
| `resolveVars(text, scope)` | `(string, VarScope) => { text, missing }` — expand `{{vars}}` |
|
|
196
|
+
| `parseEnvJson(json, env)` / `parseDotenv(text)` | env-file loaders |
|
|
197
|
+
| `evalPath(root, path)` / `parseJsonPath(path)` | the JSON-path evaluator |
|
|
198
|
+
| `parseAssertion` / `evalAssertion` / `runAssertion` | the assertion engine |
|
|
199
|
+
| `humanSize` / `statusColor` / `prettyJson` | output helpers |
|
|
200
|
+
|
|
201
|
+
`sendRequest` and `runHttpFile` accept a `fetchImpl` so you can inject a mock in tests — nothing here touches the network on its own.
|
|
202
|
+
|
|
203
|
+
## Security
|
|
204
|
+
|
|
205
|
+
lacspace-http is a **local developer tool you drive**, so it will happily reach any host you point it at — including `localhost`, which is the primary use case. It deliberately does **not** block private/internal addresses. It does, however:
|
|
206
|
+
|
|
207
|
+
- always apply a default **timeout** (30 s) so a hung server can't wedge your script;
|
|
208
|
+
- **cap the response body** read at 10 MB (`--max-size`) to avoid runaway memory;
|
|
209
|
+
- follow redirects **manually** and honour `--max-redirects`, and **flag cross-host redirects** rather than following them silently;
|
|
210
|
+
- **mask credentials** (`-b`/`-u`) in `--curl` and `--verbose` output unless you pass `--show-secrets`.
|
|
211
|
+
|
|
212
|
+
## Limitations
|
|
213
|
+
|
|
214
|
+
- HTTP(S) only — no HTTP/2 push, WebSockets, gRPC or multipart file uploads (yet).
|
|
215
|
+
- Cookies are not persisted across requests in a file run (send them explicitly via headers).
|
|
216
|
+
- The JSON-path dialect is deliberately small: keys, array indices (incl. negative) and quoted keys — no wildcards or filters.
|
|
217
|
+
- Bodies are read as UTF-8 text; binary responses are best saved with `-o`.
|
|
218
|
+
- `--data @file` reads the whole file into memory (no streaming uploads).
|
|
219
|
+
|
|
220
|
+
## Licence
|
|
221
|
+
|
|
222
|
+
Lacspace Free Licence v1.0 — see [LICENSE](./LICENSE). Free to use, no attribution required.
|
|
223
|
+
|
|
224
|
+
Part of the [Lacspace](https://lacspace.com) developer tools — small, sharp, keyless CLIs. See more at [developer.lacspace.com/tools](https://developer.lacspace.com/tools).
|