@johnhenry/letterpress 0.2.1 → 0.2.2
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/.github/workflows/ci.yml +3 -0
- package/.github/workflows/publish.yml +10 -13
- package/CHANGELOG.md +14 -4
- package/README.md +1 -8
- package/package.json +1 -1
- package/rewrite.mjs +1 -1
- package/test/tag-req-res.mjs +32 -1
- package/utility/parse-http-text.mjs +1 -0
package/.github/workflows/ci.yml
CHANGED
|
@@ -1,25 +1,22 @@
|
|
|
1
1
|
name: Publish to npm
|
|
2
2
|
|
|
3
|
+
# Publish model: main is the release branch. A push to main publishes only if
|
|
4
|
+
# package.json's version isn't on npm yet (otherwise a clean no-op), then tags
|
|
5
|
+
# v<version> and creates a GitHub Release. See johnhenry/workflows' README.
|
|
3
6
|
on:
|
|
4
|
-
release:
|
|
5
|
-
types: [published]
|
|
6
7
|
push:
|
|
7
|
-
|
|
8
|
-
# publish.yml / johnhenry/workflows' README for the full incident
|
|
9
|
-
# writeup: creating a release very soon after a push/merge to the
|
|
10
|
-
# same commit can silently drop the release event for a job whose
|
|
11
|
-
# body is uses: <external-repo>/...@v1. This gives a second,
|
|
12
|
-
# idempotency-safe chance to fire.
|
|
13
|
-
tags:
|
|
14
|
-
- "v*"
|
|
8
|
+
branches: [main]
|
|
15
9
|
workflow_dispatch: {}
|
|
16
10
|
|
|
11
|
+
concurrency:
|
|
12
|
+
group: publish-${{ github.ref }}
|
|
13
|
+
cancel-in-progress: false
|
|
14
|
+
|
|
17
15
|
jobs:
|
|
18
16
|
publish:
|
|
19
17
|
permissions:
|
|
20
|
-
contents:
|
|
21
|
-
id-token: write #
|
|
22
|
-
# see johnhenry/workflows' README for why this must be declared here too.
|
|
18
|
+
contents: write # tag + GitHub Release by-product of npm-publish.yml
|
|
19
|
+
id-token: write # npm provenance
|
|
23
20
|
uses: johnhenry/workflows/.github/workflows/npm-publish.yml@v1
|
|
24
21
|
with:
|
|
25
22
|
node-version: "26"
|
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|
|
6
6
|
|
|
7
7
|
This project will adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) once it reaches 1.0.0.
|
|
8
8
|
|
|
9
|
+
## [0.2.2] - 2026-10-14
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- 🐛 **Multi-line bodies duplicated earlier lines** (#20). In
|
|
14
|
+
`utility/parse-http-text.mjs`, the line buffer was not cleared after each
|
|
15
|
+
committed body line, so a body of `a\nb\nc` came out as `a\nab\nabc`.
|
|
16
|
+
This affected `createResponse`/`createRoute`/`tagRequest`/`tagResponse`
|
|
17
|
+
(a regression since `0.2.0`; `0.0.3` was correct).
|
|
18
|
+
|
|
9
19
|
## [0.2.1] - 2026-10-01
|
|
10
20
|
|
|
11
21
|
### Fixed
|
|
@@ -117,7 +127,7 @@ already behaved) is the one `create-route.mjs` was always meant to have.
|
|
|
117
127
|
carried over during the leroute -> `@johnhenry/letterpress` rename --
|
|
118
128
|
it simply wasn't in the initial `@johnhenry/letterpress` source, a gap
|
|
119
129
|
in that migration rather than an intentional removal. It was discovered
|
|
120
|
-
while porting a downstream consumer
|
|
130
|
+
while porting a private downstream consumer (a live HTTP request
|
|
121
131
|
inspector/proxy), whose proxy mode depends on it for rewriting proxied
|
|
122
132
|
requests/responses. Ported unchanged in behavior as `rewrite.mjs`
|
|
123
133
|
(`createRewriter`, plus `rewriteRequest`/`rewriteResponse`/`addRule`/
|
|
@@ -133,7 +143,7 @@ already behaved) is the one `create-route.mjs` was always meant to have.
|
|
|
133
143
|
|
|
134
144
|
- ✨ **`router.mount(prefix, subHandler)`**: another `leroute` capability
|
|
135
145
|
(`create-lerouter.mjs`'s `router.mount`) dropped during the same rename,
|
|
136
|
-
found via the same
|
|
146
|
+
found via the same port -- that app's own top-level router uses it
|
|
137
147
|
to mount its dashboard sub-router under `/inspect`. Delegates every
|
|
138
148
|
request under `prefix` to `subHandler` with the prefix stripped from the
|
|
139
149
|
forwarded request's path; `subHandler` may be a plain `(Request) =>
|
|
@@ -155,9 +165,9 @@ already behaved) is the one `create-route.mjs` was always meant to have.
|
|
|
155
165
|
and forwarded to `defaultHandler` too. This was also present in `leroute`
|
|
156
166
|
(`router(request, ctx = {})`) and dropped during the rename; brought back
|
|
157
167
|
specifically so `mount()` can hand a mounted sub-router its `mountPrefix`
|
|
158
|
-
(used by
|
|
168
|
+
(used by that app to reconstruct the full request path for display in its
|
|
159
169
|
captured-request feed, purely cosmetic but a real fidelity gap otherwise)
|
|
160
|
-
-- caught by testing `mount()` end-to-end against
|
|
170
|
+
-- caught by testing `mount()` end-to-end against that app itself, not by
|
|
161
171
|
a direct comparison against the old source this time. Existing single-arg
|
|
162
172
|
`router(request)` callers are unaffected (`ctx` defaults to `{}`).
|
|
163
173
|
|
package/README.md
CHANGED
|
@@ -265,7 +265,7 @@ rewriter.addRule({ match: { path: "/teapot" }, action: { setStatus: 418 } });
|
|
|
265
265
|
> This existed in `leroute` (the package letterpress was renamed from --
|
|
266
266
|
> see the provenance note near the top of this README) but was dropped
|
|
267
267
|
> during that rename. It's ported here, discovered missing while porting a
|
|
268
|
-
> downstream consumer (
|
|
268
|
+
> downstream consumer (a private request inspector/proxy) that relies on
|
|
269
269
|
> it for proxy-mode rewriting -- see CHANGELOG.md.
|
|
270
270
|
|
|
271
271
|
### `deconstruct`, `cook`
|
|
@@ -318,13 +318,6 @@ is designed to pair with a sibling package that does.
|
|
|
318
318
|
`Route`-shaped function works too; `leserve` is the tested, documented
|
|
319
319
|
pairing, not a hard dependency (it's a `devDependency` here, used only in
|
|
320
320
|
the demo scripts).
|
|
321
|
-
- **[`@johnhenry/prism`](https://github.com/johnhenry/prism)** -- a live
|
|
322
|
-
HTTP request inspector/proxy that uses `createRouter()` as its top-level
|
|
323
|
-
router. Porting it surfaced two real gaps left over from letterpress's
|
|
324
|
-
own rename from `leroute`: `router.mount()` (with the `ctx`-forwarding
|
|
325
|
-
its `mountPrefix` depends on) and `createRewriter`
|
|
326
|
-
(`@johnhenry/letterpress/rewrite`) -- both closed as part of that port,
|
|
327
|
-
see CHANGELOG.md.
|
|
328
321
|
|
|
329
322
|
## 🤝 Contributing
|
|
330
323
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@johnhenry/letterpress",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "A tagged-template-string HTTP routing library for defining and matching request handlers with plain JavaScript syntax.",
|
|
5
5
|
"main": "index.mjs",
|
|
6
6
|
"types": "types.d.ts",
|
package/rewrite.mjs
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* see the README's provenance note) as `createRewriter`, but was dropped
|
|
9
9
|
* during the leroute -> @johnhenry/letterpress rename. It's ported here
|
|
10
10
|
* unchanged in behavior, discovered missing while porting a downstream
|
|
11
|
-
* consumer (
|
|
11
|
+
* consumer (a private request inspector/proxy) that relies on it for
|
|
12
12
|
* proxy-mode rewriting.
|
|
13
13
|
*
|
|
14
14
|
* Rules format:
|
package/test/tag-req-res.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { test } from "node:test";
|
|
2
2
|
import { strict as assert } from "node:assert";
|
|
3
|
-
import { tagRequest, tagResponse } from "../index.mjs";
|
|
3
|
+
import { tagRequest, tagResponse, createResponse } from "../index.mjs";
|
|
4
4
|
|
|
5
5
|
const createReadableStream = (data) =>
|
|
6
6
|
new ReadableStream({
|
|
@@ -251,3 +251,34 @@ hello`;
|
|
|
251
251
|
assert.equal(auto.headers.get("content-length"), "5");
|
|
252
252
|
});
|
|
253
253
|
});
|
|
254
|
+
|
|
255
|
+
test("multi-line bodies are not duplicated (issue #20)", async (t) => {
|
|
256
|
+
await t.test("tagResponse", async () => {
|
|
257
|
+
const response = await tagResponse`HTTP/1.1 200 OK
|
|
258
|
+
|
|
259
|
+
a
|
|
260
|
+
b
|
|
261
|
+
c`;
|
|
262
|
+
assert.equal(await response.text(), "a\nb\nc");
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
await t.test("tagRequest", async () => {
|
|
266
|
+
const request = await tagRequest()`POST /x HTTP/1.1
|
|
267
|
+
|
|
268
|
+
a
|
|
269
|
+
b
|
|
270
|
+
c`;
|
|
271
|
+
assert.equal(await request.text(), "a\nb\nc");
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
await t.test("createResponse with substitution in a later line", async () => {
|
|
275
|
+
const name = "x";
|
|
276
|
+
const response = await createResponse`HTTP/1.1 200 OK
|
|
277
|
+
Content-Type: text/plain
|
|
278
|
+
|
|
279
|
+
line1
|
|
280
|
+
line2 ${name}
|
|
281
|
+
line3`;
|
|
282
|
+
assert.equal(await response.text(), "line1\nline2 x\nline3");
|
|
283
|
+
});
|
|
284
|
+
});
|