@johnhenry/letterpress 0.0.2 → 0.0.3
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/CHANGELOG.md +26 -0
- package/package.json +1 -1
- package/test/types/fixture.ts +31 -2
- package/types.d.ts +16 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,32 @@ 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.0.3] - 2026-09-26
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- 🐛 **Types**: `RouterExtension.endpoint` (added by the `#9` fix) was
|
|
14
|
+
declared to return `Router` directly, i.e. callable only through
|
|
15
|
+
`Route`'s `(request: Request) => Response` signature. But at runtime
|
|
16
|
+
(and per the README's own documented examples) `router.endpoint` is
|
|
17
|
+
curried: the first tagged-template call defines the match pattern
|
|
18
|
+
(method/path/headers), and the *second* call — on the value the first
|
|
19
|
+
call returns — is either another tagged template supplying a raw HTTP
|
|
20
|
+
response literal (e.g.
|
|
21
|
+
`` router.endpoint`GET /x`` `HTTP/1.1 200 OK\n\n...`` ``) or a plain
|
|
22
|
+
handler function (`(request) => Response`), both registering the route
|
|
23
|
+
and returning the same `Router`. The old declaration didn't model this
|
|
24
|
+
curried, dual-mode second call at all, forcing consumers to fall back to
|
|
25
|
+
`@ts-expect-error`/`any`. Added `RouterEndpointResponder`, a callable
|
|
26
|
+
type with two overload signatures (tagged-template and single-handler),
|
|
27
|
+
and changed `RouterExtension.endpoint`'s return type from `Router` to
|
|
28
|
+
`RouterEndpointResponder`. Extended `test/types/fixture.ts` with both
|
|
29
|
+
curried forms (verified to fail to type-check against the pre-fix
|
|
30
|
+
declaration, and to pass cleanly against the fix) so a regression here
|
|
31
|
+
is caught by `npm test`/`npm run typecheck` instead of relying on
|
|
32
|
+
someone eyeballing `types.d.ts`
|
|
33
|
+
([#11](https://github.com/johnhenry/letterpress/issues/11))
|
|
34
|
+
|
|
9
35
|
## [0.0.2] - 2026-09-26
|
|
10
36
|
|
|
11
37
|
### Fixed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@johnhenry/letterpress",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
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/test/types/fixture.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Type-only regression fixture for letterpress #9.
|
|
2
|
+
* Type-only regression fixture for letterpress #9 and #11.
|
|
3
3
|
*
|
|
4
4
|
* Not executed -- test/types.test.mjs spawns `tsc --noEmit` over this file
|
|
5
|
-
* (via the root tsconfig.json). It exists to catch
|
|
5
|
+
* (via the root tsconfig.json). It exists to catch regressions
|
|
6
6
|
* automatically instead of relying on someone eyeballing types.d.ts/fs.d.ts:
|
|
7
7
|
*
|
|
8
8
|
* 1. Importing "@johnhenry/letterpress" (and its "/fs" subpath) by their
|
|
@@ -19,6 +19,14 @@
|
|
|
19
19
|
* `import { createRouter } from '@johnhenry/letterpress'` type-checked
|
|
20
20
|
* the module specifier but `createRouter` itself was still `any`/
|
|
21
21
|
* missing (TS2305 "has no exported member").
|
|
22
|
+
* 3. `RouterExtension.endpoint`'s declared type didn't model that it's
|
|
23
|
+
* curried at runtime (#11): the first tagged-template call returns a
|
|
24
|
+
* responder that must be called a *second* time, either as another
|
|
25
|
+
* tagged template (a raw HTTP response literal) or with a single
|
|
26
|
+
* handler function, both returning `Router`. Before the fix, the first
|
|
27
|
+
* call's return type was `Router` itself (callable only via `Route`'s
|
|
28
|
+
* `(request: Request) => Response` signature), so both curried forms
|
|
29
|
+
* below failed to type-check.
|
|
22
30
|
*/
|
|
23
31
|
import {
|
|
24
32
|
createRouter,
|
|
@@ -37,6 +45,27 @@ const router = createRouter({ baseUrl: "http://localhost" });
|
|
|
37
45
|
const extended = router.endpoint`GET /ping`;
|
|
38
46
|
void extended;
|
|
39
47
|
|
|
48
|
+
// `.endpoint` is curried (letterpress #11): the first tagged-template call
|
|
49
|
+
// (above) defines the match pattern and returns a responder that must be
|
|
50
|
+
// called a *second* time, either as another tagged template (a raw HTTP
|
|
51
|
+
// response literal) or with a single handler function -- both forms
|
|
52
|
+
// register the route and return the same Router. Neither form below should
|
|
53
|
+
// require @ts-expect-error/@ts-ignore.
|
|
54
|
+
const routerAfterTemplateResponse = router.endpoint`GET /protected [Authorization: Bearer *]`
|
|
55
|
+
`HTTP/1.1 200 OK
|
|
56
|
+
Content-Type: text/plain
|
|
57
|
+
|
|
58
|
+
This is a protected resource
|
|
59
|
+
`;
|
|
60
|
+
const routerAfterHandlerResponse = router.endpoint`GET /api/data`(
|
|
61
|
+
async (request: Request) => {
|
|
62
|
+
void request;
|
|
63
|
+
return new Response("ok");
|
|
64
|
+
}
|
|
65
|
+
);
|
|
66
|
+
void routerAfterTemplateResponse;
|
|
67
|
+
void routerAfterHandlerResponse;
|
|
68
|
+
|
|
40
69
|
async function checkRouter() {
|
|
41
70
|
const response = await router(new Request("http://localhost/ping"));
|
|
42
71
|
return response instanceof Response;
|
package/types.d.ts
CHANGED
|
@@ -4,12 +4,27 @@ export type Route = (
|
|
|
4
4
|
context?: Record<string, any>
|
|
5
5
|
) => Response | Promise<Response>;
|
|
6
6
|
|
|
7
|
+
// The value returned by the *first* `router.endpoint` tagged-template call
|
|
8
|
+
// (the method/path/header match pattern, e.g.
|
|
9
|
+
// `` router.endpoint`GET /protected [Authorization: Bearer *]` ``). That
|
|
10
|
+
// return value is itself curried: it must be called a *second* time,
|
|
11
|
+
// either as another tagged template supplying a raw HTTP response literal
|
|
12
|
+
// (e.g. `` `HTTP/1.1 200 OK\n\n...` ``, parsed the same way createResponse
|
|
13
|
+
// parses one) or with a single plain handler function
|
|
14
|
+
// (`(request: Request) => Response`), per the README's documented usage
|
|
15
|
+
// and create-router.mjs's actual implementation. Both forms register the
|
|
16
|
+
// route and return the same `Router` so calls can be chained.
|
|
17
|
+
export type RouterEndpointResponder = {
|
|
18
|
+
(template: TemplateStringsArray, ...substitutions: any[]): Router;
|
|
19
|
+
(handler: Route): Router;
|
|
20
|
+
};
|
|
21
|
+
|
|
7
22
|
// Extension for the Router to add endpoints
|
|
8
23
|
export type RouterExtension = {
|
|
9
24
|
endpoint: (
|
|
10
25
|
template: TemplateStringsArray,
|
|
11
26
|
...substitutions: any[]
|
|
12
|
-
) =>
|
|
27
|
+
) => RouterEndpointResponder;
|
|
13
28
|
};
|
|
14
29
|
|
|
15
30
|
// Combines Route and RouterExtension
|