@hulla/api-koa 0.0.0-stage → 2.0.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/dist/index.d.ts +22 -0
- package/dist/index.js +92 -0
- package/package.json +47 -4
- package/README.md +0 -3
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { AdapterErrorInput, AdapterResponse, AdapterRuntimeOptions } from "@hulla/api/adapters";
|
|
2
|
+
import { Awaitable, ServerAdapter, ServerContextInput, ServerExecutableFor } from "@hulla/api/server";
|
|
3
|
+
import { Contract } from "@hulla/api";
|
|
4
|
+
import Koa from "koa";
|
|
5
|
+
//#region src/index.d.ts
|
|
6
|
+
type KoaAdapterContext<State extends object = Koa.DefaultState> = {
|
|
7
|
+
readonly ctx: Koa.ParameterizedContext<State>;
|
|
8
|
+
readonly state: State;
|
|
9
|
+
};
|
|
10
|
+
type KoaContextInput<C extends Contract = Contract, State extends object = Koa.DefaultState> = ServerContextInput<C> & KoaAdapterContext<State>;
|
|
11
|
+
type KoaServerErrorInput<State extends object = Koa.DefaultState> = Omit<AdapterErrorInput, 'hostContext'> & KoaAdapterContext<State>;
|
|
12
|
+
type KoaServerOptions<State extends object = Koa.DefaultState> = Omit<AdapterRuntimeOptions, 'onError'> & {
|
|
13
|
+
readonly maxBodyBytes?: number;
|
|
14
|
+
readonly onError?: ((input: KoaServerErrorInput<State>) => Awaitable<AdapterResponse | undefined | void>) | undefined;
|
|
15
|
+
};
|
|
16
|
+
type KoaAdapter<State extends object = Koa.DefaultState> = ServerAdapter<'koa', KoaAdapterContext<State>> & {
|
|
17
|
+
readonly mount: <const C extends Contract, const Context extends object>(implementation: ServerExecutableFor<C, Context, 'koa', KoaAdapterContext<State>>, options?: KoaServerOptions<State>) => Koa.Middleware<State>;
|
|
18
|
+
};
|
|
19
|
+
/** Mount as terminal middleware. Scope paths with the application's router when composing APIs. */
|
|
20
|
+
declare function koaAdapter<State extends object = Koa.DefaultState>(defaults?: KoaServerOptions<State>): KoaAdapter<State>;
|
|
21
|
+
//#endregion
|
|
22
|
+
export { KoaAdapter, KoaAdapterContext, KoaContextInput, KoaServerErrorInput, KoaServerOptions, koaAdapter };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { Readable } from "node:stream";
|
|
2
|
+
import { createNodeBodyReader, nodeRequestHeader, nodeRequestHeaders, nodeRequestLifetime } from "@hulla/api-node";
|
|
3
|
+
import { bodyLimit, createAdapterHandler, errorResponse, toFetchResponse } from "@hulla/api/adapters";
|
|
4
|
+
import { assertAdapterContext, createServerAdapter, serverContextAdapterId } from "@hulla/api/server";
|
|
5
|
+
|
|
6
|
+
//#region src/index.ts
|
|
7
|
+
function readHeader(name) {
|
|
8
|
+
return nodeRequestHeader(this.request.headers, name);
|
|
9
|
+
}
|
|
10
|
+
/** Mount as terminal middleware. Scope paths with the application's router when composing APIs. */
|
|
11
|
+
function koaAdapter(defaults = {}) {
|
|
12
|
+
const configuredDefaults = { ...defaults };
|
|
13
|
+
return createServerAdapter("koa", { mount: (implementation, overrides) => {
|
|
14
|
+
assertAdapterContext(implementation.context, "koa");
|
|
15
|
+
const options = {
|
|
16
|
+
...configuredDefaults,
|
|
17
|
+
...overrides
|
|
18
|
+
};
|
|
19
|
+
const limit = bodyLimit(options.maxBodyBytes);
|
|
20
|
+
const native = serverContextAdapterId(implementation.context) !== void 0;
|
|
21
|
+
const dispatch = createAdapterHandler(implementation, options.onError === void 0 ? {} : { onError: ({ hostContext, ...input }) => options.onError?.({
|
|
22
|
+
...input,
|
|
23
|
+
...hostContext
|
|
24
|
+
}) });
|
|
25
|
+
return async (ctx) => {
|
|
26
|
+
const lifetime = nodeRequestLifetime(ctx.req, ctx.res);
|
|
27
|
+
const host = {
|
|
28
|
+
ctx,
|
|
29
|
+
state: ctx.state
|
|
30
|
+
};
|
|
31
|
+
const observe = async (error) => {
|
|
32
|
+
const fallback = errorResponse(error, "transport");
|
|
33
|
+
try {
|
|
34
|
+
return await options.onError?.({
|
|
35
|
+
...host,
|
|
36
|
+
request: ctx.req,
|
|
37
|
+
error,
|
|
38
|
+
phase: "transport",
|
|
39
|
+
defaultResponse: fallback
|
|
40
|
+
}) ?? fallback;
|
|
41
|
+
} catch {
|
|
42
|
+
return fallback;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
let response;
|
|
46
|
+
try {
|
|
47
|
+
const parsed = ctx.request.body;
|
|
48
|
+
const result = await dispatch({
|
|
49
|
+
request: ctx.req,
|
|
50
|
+
signal: lifetime.signal,
|
|
51
|
+
method: ctx.method === "HEAD" ? "GET" : ctx.method,
|
|
52
|
+
pathname: ctx.path,
|
|
53
|
+
query: new URLSearchParams(ctx.querystring),
|
|
54
|
+
readHeaders: () => nodeRequestHeaders(ctx.req.headers),
|
|
55
|
+
readHeader,
|
|
56
|
+
readBody: createNodeBodyReader(ctx.req, limit, "Koa"),
|
|
57
|
+
...parsed === void 0 ? {} : { body: { value: parsed } },
|
|
58
|
+
...native ? { contextInput: host } : {},
|
|
59
|
+
...options.onError === void 0 ? {} : { hostContext: host }
|
|
60
|
+
});
|
|
61
|
+
response = toFetchResponse(result, async (error) => {
|
|
62
|
+
await observe(error);
|
|
63
|
+
});
|
|
64
|
+
} catch (error) {
|
|
65
|
+
try {
|
|
66
|
+
response = toFetchResponse(await observe(error));
|
|
67
|
+
} catch (failure) {
|
|
68
|
+
lifetime.dispose();
|
|
69
|
+
throw failure;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
if (ctx.method === "HEAD" || response.body === null) {
|
|
74
|
+
await response.body?.cancel();
|
|
75
|
+
ctx.body = null;
|
|
76
|
+
} else ctx.body = Readable.fromWeb(response.body);
|
|
77
|
+
response.headers.forEach((value, name) => {
|
|
78
|
+
if (name !== "set-cookie") ctx.set(name, value);
|
|
79
|
+
});
|
|
80
|
+
const cookies = response.headers.getSetCookie();
|
|
81
|
+
if (cookies.length > 0) ctx.set("set-cookie", cookies);
|
|
82
|
+
ctx.status = response.status;
|
|
83
|
+
} catch (error) {
|
|
84
|
+
lifetime.dispose();
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
} });
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
//#endregion
|
|
92
|
+
export { koaAdapter };
|
package/package.json
CHANGED
|
@@ -1,6 +1,49 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hulla/api-koa",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Koa middleware adapter for @hulla/api",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/hulladev/api.git",
|
|
9
|
+
"directory": "packages/koa"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist"
|
|
13
|
+
],
|
|
14
|
+
"type": "module",
|
|
15
|
+
"sideEffects": false,
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"import": "./dist/index.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public",
|
|
24
|
+
"registry": "https://registry.npmjs.org/"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsdown",
|
|
28
|
+
"clean": "rm -rf dist coverage",
|
|
29
|
+
"test": "vitest run",
|
|
30
|
+
"test:coverage": "vitest run --coverage",
|
|
31
|
+
"test:watch": "vitest",
|
|
32
|
+
"typecheck": "tsc --noEmit"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@hulla/api-node": "^2.0.0"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@hulla/api": "2.0.0",
|
|
39
|
+
"@types/koa": "^3.0.0",
|
|
40
|
+
"@types/node": "^26.2.0",
|
|
41
|
+
"koa": "^3.0.0"
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"@hulla/api": "^2.0.0",
|
|
45
|
+
"@types/koa": "^3.0.0",
|
|
46
|
+
"@types/node": ">=22.0.0",
|
|
47
|
+
"koa": "^3.0.0"
|
|
48
|
+
}
|
|
49
|
+
}
|
package/README.md
DELETED