7 Commits

Author SHA1 Message Date
Morten Olsen
6b74a28989 docs: improved docs 2025-05-18 21:26:05 +02:00
morten-olsen
ad342e5f10 docs: generated README 2025-05-18 19:12:53 +00:00
Morten Olsen
5d485acc97 fix: fix parsing issue 2025-05-18 21:11:51 +02:00
morten-olsen
68f5025527 docs: generated README 2025-05-18 18:56:34 +00:00
Morten Olsen
a9a7bae28f commit on publish 2025-05-18 20:55:28 +02:00
Morten Olsen
b800290d72 feat: support hash ids in http blocks 2025-05-18 20:45:08 +02:00
Morten Olsen
d7b6a3880e feat: load env vars 2025-05-18 20:36:12 +02:00
11 changed files with 126 additions and 34 deletions

View File

@@ -92,7 +92,7 @@ jobs:
release:
permissions:
contents: read
contents: write
packages: write
attestations: write
id-token: write
@@ -135,3 +135,8 @@ jobs:
pnpm publish --no-git-checks --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: "docs: generated README"
file_pattern: "*.md"

View File

@@ -110,9 +110,9 @@ HTTP/200 OK
access-control-allow-credentials: true
access-control-allow-origin: *
connection: keep-alive
content-length: 559
content-length: 555
content-type: application/json
date: Sun, 18 May 2025 18:31:46 GMT
date: Sun, 18 May 2025 19:12:17 GMT
server: gunicorn/19.9.0
{
@@ -129,12 +129,12 @@ server: gunicorn/19.9.0
"Host": "httpbin.org",
"Sec-Fetch-Mode": "cors",
"User-Agent": "node",
"X-Amzn-Trace-Id": "Root=1-682a2792-7df702ce77a3b3696937eaeb"
"X-Amzn-Trace-Id": "Root=1-682a3111-131bcbff690b03fd64aa4617"
},
"json": {
"greeting": "Hello, http.md!"
},
"origin": "185.181.220.204",
"origin": "23.96.180.7",
"url": "https://httpbin.org/post"
}
@@ -264,14 +264,14 @@ Within your markdown document, the following variables are available in the Hand
**1. Using a value from a previous response in a new request:**
````markdown
```http id=createItem json
```http #createItem,json
POST https://httpbin.org/post
Content-Type: application/json
{"name": "My New Item"}
```
The new item ID is: {{responses.createItem.body.json.name}}
The new item ID is: {{response.body.json.name}}
Now, let's fetch the item using a (mocked) ID from the response:
@@ -283,6 +283,61 @@ GET https://httpbin.org/anything/{{responses.createItem.body.json.name}}
````
<details>
<summary>Output</summary>
````markdown
```http
POST https://httpbin.org/post
Content-Type: application/json
{"name": "My New Item"}
```
The new item ID is: My New Item
Now, let's fetch the item using a (mocked) ID from the response:
```http
GET https://httpbin.org/anything/My New Item
```
```
HTTP/200 OK
access-control-allow-credentials: true
access-control-allow-origin: *
connection: keep-alive
content-length: 451
content-type: application/json
date: Sun, 18 May 2025 19:12:18 GMT
server: gunicorn/19.9.0
{
"args": {},
"data": "",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "br, gzip, deflate",
"Accept-Language": "*",
"Host": "httpbin.org",
"Sec-Fetch-Mode": "cors",
"User-Agent": "node",
"X-Amzn-Trace-Id": "Root=1-682a3112-4bbb29111129c1556c487ca1"
},
"json": null,
"method": "GET",
"origin": "23.96.180.7",
"url": "https://httpbin.org/anything/My New Item"
}
```
````
</details>
*(Note: `httpbin.org/post` wraps the JSON sent in a "json" field in its response. If your API returns the ID directly at the root of the JSON body, you'd use `{{responses.createItem.body.id}}` assuming the `createItem` request had the `json` option.)*
**2. Displaying a status code in markdown text:**

View File

@@ -1,3 +1,4 @@
#!/usr/bin/env node
import 'dotenv/config.js';
import '../dist/cli/cli.js';

View File

@@ -176,6 +176,13 @@ Within your markdown document, the following variables are available in the Hand
::raw-md[./examples/with-template.md]
<details>
<summary>Output</summary>
::raw-md[./examples/with-template.md]{render}
</details>
_(Note: `httpbin.org/post` wraps the JSON sent in a "json" field in its response. If your API returns the ID directly at the root of the JSON body, you'd use `{{responses.createItem.body.id}}` assuming the `createItem` request had the `json` option.)_
**2. Displaying a status code in markdown text:**
@@ -202,34 +209,18 @@ The requests from the embedded document are processed, and their `request` and `
Assume `_shared_requests.md` contains:
````markdown
```http id=sharedGetRequest
GET https://httpbin.org/get
```
````
::raw-md[./examples/_shared_requests.md]
Then, in `main.md`:
````markdown
# Main Document
::raw-md[./examples/with-shared-requests.md]
Let's include some shared requests:
<details>
<summary>Output</summary>
::md[./_shared_requests.md]
::raw-md[./examples/with-shared-requests.md]{render}
The shared GET request returned: {{responses.sharedGetRequest.status}}
Now, a request specific to this document:
```http
POST https://httpbin.org/post
Content-Type: application/json
{"dataFromMain": "someValue", "sharedUrl": "{{requests.sharedGetRequest.url}}"}
```
::response
````
</details>
When `main.md` is processed, `_shared_requests.md` will be embedded, its `sharedGetRequest` will be executed, and its data will be available for templating.

View File

@@ -0,0 +1,3 @@
```http #sharedGetRequest
GET https://httpbin.org/get
```

View File

@@ -0,0 +1,18 @@
# Main Document
Let's include some shared requests:
::md[./_shared_requests.md]
The shared GET request returned: {{response.statusText}}
Now, a request specific to this document:
```http
POST https://httpbin.org/post
Content-Type: application/json
{"dataFromMain": "someValue", "sharedUrl": "{{requests.sharedGetRequest.url}}"}
```
::response

View File

@@ -1,11 +1,11 @@
```http id=createItem json
```http #createItem,json
POST https://httpbin.org/post
Content-Type: application/json
{"name": "My New Item"}
```
The new item ID is: {{responses.createItem.body.json.name}}
The new item ID is: {{response.body.json.name}}
Now, let's fetch the item using a (mocked) ID from the response:

View File

@@ -36,6 +36,7 @@
"dependencies": {
"blessed": "^0.1.81",
"commander": "^14.0.0",
"dotenv": "^16.5.0",
"eventemitter3": "^5.0.1",
"handlebars": "^4.7.8",
"hastscript": "^9.0.1",

9
pnpm-lock.yaml generated
View File

@@ -14,6 +14,9 @@ importers:
commander:
specifier: ^14.0.0
version: 14.0.0
dotenv:
specifier: ^16.5.0
version: 16.5.0
eventemitter3:
specifier: ^5.0.1
version: 5.0.1
@@ -657,6 +660,10 @@ packages:
devlop@1.1.0:
resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
dotenv@16.5.0:
resolution: {integrity: sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==}
engines: {node: '>=12'}
emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
@@ -2077,6 +2084,8 @@ snapshots:
dependencies:
dequal: 2.0.3
dotenv@16.5.0: {}
emoji-regex@8.0.0: {}
emojilib@2.4.0: {}

View File

@@ -10,6 +10,7 @@ type Response = {
statusText: string;
headers: Record<string, string>;
body?: string;
rawBody?: string;
};
type AddRequestOptios = {

View File

@@ -16,6 +16,11 @@ const httpHandler: ExecutionHandler = ({
return [key.trim(), value?.trim() || true];
})
);
let id = options.id?.toString();
const idPart = optionParts.find((option) => option.startsWith('#'));
if (idPart) {
id = idPart.slice(1);
}
addStep({
type: 'http',
@@ -28,7 +33,8 @@ const httpHandler: ExecutionHandler = ({
const content = template(context);
const [head, body] = content.split('\n\n');
const [top, ...headerItems] = head.split('\n');
const [method, url] = top.split(' ');
const [method, ...urlParts] = top.split(' ');
const url = urlParts.join(' ').trim();
const headers = Object.fromEntries(
headerItems.map((header) => {
@@ -53,7 +59,8 @@ const httpHandler: ExecutionHandler = ({
body
});
let responseText = await response.text();
const rawBody = await response.text();
let responseText = rawBody;
if (options.json) {
try {
responseText = JSON.parse(responseText);
@@ -66,7 +73,7 @@ const httpHandler: ExecutionHandler = ({
node.meta = undefined;
context.addRequest({
id: options.id?.toString(),
id,
request: {
method,
url,
@@ -78,6 +85,7 @@ const httpHandler: ExecutionHandler = ({
statusText: response.statusText,
headers: Object.fromEntries(response.headers.entries()),
body: responseText,
rawBody: rawBody,
},
});
},