@hulla/api-koa 0.0.0-stage → 2.0.1

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.
@@ -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": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "2.0.1",
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.1"
36
+ },
37
+ "devDependencies": {
38
+ "@hulla/api": "2.0.1",
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.1",
45
+ "@types/koa": "^3.0.0",
46
+ "@types/node": ">=22.0.0",
47
+ "koa": "^3.0.0"
48
+ }
49
+ }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.