Skip to content

Commit d3e9df9

Browse files
authored
Release/0.14.0 (#43)
* v0.14.0 - see CHANGELOG for details
1 parent 994c5de commit d3e9df9

18 files changed

+586
-40
lines changed

CHANGELOG.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,27 @@
1+
## [0.14.0] - 2025-02-04
2+
3+
## Changed
4+
5+
- multiple paths can now be registered on the same HTTP verb, and if the paths
6+
are exactly the same, or if the "static" and the "parameter" portions of the
7+
paths somehow overlap, then the handler is invoked **once for each registered
8+
path**; for example if we register `@Get("/foo/:bar")` and `@Get("/foo/bar")`
9+
(in that order) on the same handler function, then for every request to
10+
`/foo/bar`, that handler is invoked **twice**, the first time having no
11+
"param" at all, and the 2nd time having the param `bar` with the value
12+
`"bar"`. This behavior follows TC39 decorator specs where all decorators to a
13+
function are applied "from inside out" aka: the last declared decorator gets
14+
applied first, and so on
15+
16+
## Added
17+
18+
- `ctx.state._oakRoutingCtrl_regPath` is available as a pointer to the
19+
registered path that matches the URL request currently being handled; this is
20+
helpful in rare situations where multiple overlapping paths are registered on
21+
the same handler function, causing it to be invoked multiple times, and so we
22+
may benefit from a mechanism to control when to write to the response body (as
23+
this operation can only be done once)
24+
125
## [0.13.0] - 2025-02-01
226

327
### Added

deno.jsonc

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@dklab/oak-routing-ctrl",
3-
"version": "0.13.0",
3+
"version": "0.14.0",
44
"exports": {
55
".": "./mod.ts",
66
"./mod": "./mod.ts"
@@ -15,8 +15,8 @@
1515
},
1616
"tasks": {
1717
"pretty": "deno lint --ignore=docs && deno check . && deno fmt",
18-
"test": "deno test -RE",
19-
"check-doc": "deno check --doc .",
18+
"test": "deno test -RE -I=jspm.dev,jsr.io,deno.land -N=0.0.0.0,127.0.0.1",
19+
"check-doc": "deno check -I=jspm.dev,jsr.io,deno.land --doc .",
2020
"doc": "deno doc --html mod.ts"
2121
},
2222
"imports": {
@@ -26,7 +26,8 @@
2626
"@std/io": "jsr:@std/io@^0.225.2",
2727
"@std/path": "jsr:@std/path@^1.0.8",
2828
"@std/testing": "jsr:@std/testing@^1.0.9",
29-
"zod": "npm:zod@^3.24.1"
29+
"zod": "npm:zod@^3.24.1",
30+
"superoak": "https://deno.land/x/[email protected]/mod.ts"
3031
},
3132
"fmt": {
3233
"useTabs": false,

deno.lock

Lines changed: 96 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

mod.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,11 @@ export {
1818
z,
1919
type zInfer,
2020
} from "./src/utils/schema_utils.ts";
21+
22+
export type {
23+
/**
24+
* re-exporting from oak for convenient uses
25+
* @ignore
26+
*/
27+
Context,
28+
} from "@oak/oak";

src/Controller.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,29 @@ type ClassDecorator = (
2626
*/
2727
export const Controller =
2828
(pathPrefix: string = ""): ClassDecorator => (target, context): void => {
29+
const ctrlClassName = target.name;
2930
debug(
30-
`invoking ControllerDecorator for ${target.name} -`,
31+
`invoking ControllerDecorator for ${ctrlClassName} -`,
3132
"runtime provides context:",
3233
context,
3334
);
3435
const fnNames: string[] = Object.getOwnPropertyNames(target.prototype);
3536
for (const fnName of fnNames) {
3637
const pair = store.get(fnName);
3738
if (!pair) continue;
38-
pair.forEach((path, verb, p) => {
39+
const patchedPair = new Map();
40+
pair.forEach((verb, path) => {
3941
const fullPath = join(pathPrefix, path);
40-
p.set(verb, fullPath);
41-
patchOasPath(fnName, verb, fullPath);
42+
patchedPair.set(fullPath, verb);
43+
debug(
44+
`[${ctrlClassName}] @Controller: patched [${verb}] ${path} to ${fullPath}`,
45+
);
46+
// @TODO consider throwing if we discover 2 (or more) Controllers
47+
// sharing the exact same set of path, fnName, and method
48+
patchOasPath(ctrlClassName, fnName, verb, fullPath);
4249
});
50+
store.delete(fnName);
51+
const fqFnName = `${ctrlClassName}.${fnName}`;
52+
store.set(fqFnName, patchedPair);
4353
}
4454
};

src/Delete_test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,5 @@ Deno.test("@Delete decorator", () => {
2323
assertSpyCall(Delete, 0, { args: ["/bar"] });
2424
assertInstanceOf(Delete.calls[0].returned, Function);
2525
assertSpyCalls(Delete, 1);
26-
assertEquals(store.get("doSomething")?.get("delete"), "/bar");
26+
assertEquals(store.get("doSomething")?.get("/bar"), "delete");
2727
});

src/Get_test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,5 @@ Deno.test("@Get decorator", () => {
2323
assertSpyCall(Get, 0, { args: ["/bar"] });
2424
assertInstanceOf(Get.calls[0].returned, Function);
2525
assertSpyCalls(Get, 1);
26-
assertEquals(store.get("doSomething")?.get("get"), "/bar");
26+
assertEquals(store.get("doSomething")?.get("/bar"), "get");
2727
});

src/Head_test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,5 @@ Deno.test("@Head decorator", () => {
2323
assertSpyCall(Head, 0, { args: ["/bar"] });
2424
assertInstanceOf(Head.calls[0].returned, Function);
2525
assertSpyCalls(Head, 1);
26-
assertEquals(store.get("doSomething")?.get("head"), "/bar");
26+
assertEquals(store.get("doSomething")?.get("/bar"), "head");
2727
});

src/Options_test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,5 @@ Deno.test("@Options decorator", () => {
2323
assertSpyCall(Options, 0, { args: ["/bar"] });
2424
assertInstanceOf(Options.calls[0].returned, Function);
2525
assertSpyCalls(Options, 1);
26-
assertEquals(store.get("doSomething")?.get("options"), "/bar");
26+
assertEquals(store.get("doSomething")?.get("/bar"), "options");
2727
});

src/Patch_test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,5 @@ Deno.test("@Patch decorator", () => {
2323
assertSpyCall(Patch, 0, { args: ["/bar"] });
2424
assertInstanceOf(Patch.calls[0].returned, Function);
2525
assertSpyCalls(Patch, 1);
26-
assertEquals(store.get("doSomething")?.get("patch"), "/bar");
26+
assertEquals(store.get("doSomething")?.get("/bar"), "patch");
2727
});

0 commit comments

Comments
 (0)