@canmi/seam-adapter-hono 0.5.31 → 0.5.38
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/README.md +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -12
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -19,3 +19,4 @@ Exports a single `seam()` function that returns a Hono `MiddlewareHandler`. Wrap
|
|
|
19
19
|
|
|
20
20
|
- Peer dependencies: `@canmi/seam-server`, `hono` ^4.0.0
|
|
21
21
|
- Designed for use with Hono's `app.use()` middleware registration
|
|
22
|
+
- `SeamHonoOptions.publicDir` for public file serving; auto-reads from `router.publicDir` when omitted
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;AA2BA;;;KAAY,gBAAA,IACX,OAAA,GAAU,CAAA;EACT,MAAA,IAAU,GAAA,OAAU,EAAA;EACpB,SAAA,IAAa,GAAA,OAAU,EAAA;EACvB,OAAA,IAAW,GAAA,OAAU,EAAA;AAAA,MAElB,iBAAA;AAAA,UAIY,eAAA;EAChB,SAAA;EACA,SAAA;EACA,QAAA,GAAW,WAAA;EACX,UAAA,GAAa,UAAA;EACb,gBAAA,GAAmB,gBAAA;EACnB,SAAA,GAAY,gBAAA;EACZ,UAAA,GAAa,UAAA;AAAA;;iBAgBE,IAAA,WAAe,aAAA,CAAA,CAC9B,MAAA,EAAQ,MAAA,CAAO,CAAA,GACf,IAAA,GAAO,eAAA,GACL,iBAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,21 +1,37 @@
|
|
|
1
|
-
import { createHttpHandler, startChannelWs, toWebResponse } from "@canmi/seam-server";
|
|
2
|
-
|
|
1
|
+
import { buildMultipartFields, createHttpHandler, startChannelWs, toWebResponse } from "@canmi/seam-server";
|
|
3
2
|
//#region src/index.ts
|
|
4
3
|
const SEAM_PREFIX = "/_seam/";
|
|
5
4
|
const PROCEDURE_PREFIX = "/_seam/procedure/";
|
|
6
5
|
const EVENTS_SUFFIX = ".events";
|
|
7
6
|
const wsSessions = /* @__PURE__ */ new WeakMap();
|
|
7
|
+
function resolveRouterPublicDir(router) {
|
|
8
|
+
const publicDir = router.publicDir;
|
|
9
|
+
return typeof publicDir === "string" ? publicDir : void 0;
|
|
10
|
+
}
|
|
8
11
|
/** Hono middleware that handles all /_seam/* routes via the seam router */
|
|
9
12
|
function seam(router, opts) {
|
|
13
|
+
const effectivePublicDir = opts?.publicDir ?? resolveRouterPublicDir(router);
|
|
10
14
|
const handlerOpts = {};
|
|
11
15
|
if (opts?.staticDir) handlerOpts.staticDir = opts.staticDir;
|
|
16
|
+
if (effectivePublicDir) handlerOpts.publicDir = effectivePublicDir;
|
|
12
17
|
if (opts?.fallback) handlerOpts.fallback = opts.fallback;
|
|
13
18
|
if (opts?.rpcHashMap) handlerOpts.rpcHashMap = opts.rpcHashMap;
|
|
14
19
|
if (opts?.sseOptions) handlerOpts.sseOptions = opts.sseOptions;
|
|
15
20
|
const handler = createHttpHandler(router, handlerOpts);
|
|
16
21
|
return async (c, next) => {
|
|
17
22
|
const url = new URL(c.req.url);
|
|
18
|
-
if (!url.pathname.startsWith(SEAM_PREFIX))
|
|
23
|
+
if (!url.pathname.startsWith(SEAM_PREFIX)) {
|
|
24
|
+
if (!effectivePublicDir || c.req.method !== "GET" && c.req.method !== "HEAD") return next();
|
|
25
|
+
const raw = c.req.raw;
|
|
26
|
+
const result = await handler({
|
|
27
|
+
method: raw.method,
|
|
28
|
+
url: raw.url,
|
|
29
|
+
body: () => Promise.reject(/* @__PURE__ */ new Error("no body")),
|
|
30
|
+
header: (name) => raw.headers.get(name)
|
|
31
|
+
});
|
|
32
|
+
if (result.status === 404 && !("stream" in result)) return next();
|
|
33
|
+
return toWebResponse(result);
|
|
34
|
+
}
|
|
19
35
|
if (opts?.upgradeWebSocket && c.req.header("upgrade") === "websocket" && url.pathname.startsWith(PROCEDURE_PREFIX) && url.pathname.endsWith(EVENTS_SUFFIX)) {
|
|
20
36
|
const channelName = url.pathname.slice(17, -7);
|
|
21
37
|
const rawInput = url.searchParams.get("input");
|
|
@@ -43,22 +59,17 @@ function seam(router, opts) {
|
|
|
43
59
|
}))(c, next);
|
|
44
60
|
}
|
|
45
61
|
const raw = c.req.raw;
|
|
46
|
-
const
|
|
47
|
-
let formDataCache;
|
|
48
|
-
const getFormData = async () => formDataCache ??= await raw.formData();
|
|
62
|
+
const { body, file } = buildMultipartFields(raw);
|
|
49
63
|
return toWebResponse(await handler({
|
|
50
64
|
method: raw.method,
|
|
51
65
|
url: raw.url,
|
|
52
|
-
body
|
|
66
|
+
body,
|
|
53
67
|
header: (name) => raw.headers.get(name),
|
|
54
|
-
file
|
|
55
|
-
const f = (await getFormData()).get("file");
|
|
56
|
-
return f ? { stream: () => f.stream() } : null;
|
|
57
|
-
} : void 0
|
|
68
|
+
file
|
|
58
69
|
}));
|
|
59
70
|
};
|
|
60
71
|
}
|
|
61
|
-
|
|
62
72
|
//#endregion
|
|
63
73
|
export { seam };
|
|
74
|
+
|
|
64
75
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/* src/server/adapter/hono/src/index.ts */\n\nimport {
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/* src/server/adapter/hono/src/index.ts */\n\nimport {\n\tcreateHttpHandler,\n\ttoWebResponse,\n\tstartChannelWs,\n\tbuildMultipartFields,\n} from '@canmi/seam-server'\nimport type {\n\tDefinitionMap,\n\tRouter,\n\tHttpHandler,\n\tHttpHandlerOptions,\n\tRpcHashMap,\n\tChannelWsOptions,\n\tChannelWsSession,\n\tSseOptions,\n} from '@canmi/seam-server'\nimport type { MiddlewareHandler } from 'hono'\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\n/**\n * Hono-compatible upgradeWebSocket factory.\n * Runtimes (Deno, Bun, Cloudflare) provide their own implementation;\n * the user injects it via options so the adapter stays runtime-agnostic.\n */\nexport type UpgradeWebSocket = (\n\thandler: (c: any) => {\n\t\tonOpen?: (evt: any, ws: any) => void\n\t\tonMessage?: (evt: any, ws: any) => void\n\t\tonClose?: (evt: any, ws: any) => void\n\t},\n) => MiddlewareHandler\n\n/* eslint-enable @typescript-eslint/no-explicit-any */\n\nexport interface SeamHonoOptions {\n\tstaticDir?: string\n\tpublicDir?: string\n\tfallback?: HttpHandler\n\trpcHashMap?: RpcHashMap\n\tupgradeWebSocket?: UpgradeWebSocket\n\twsOptions?: ChannelWsOptions\n\tsseOptions?: SseOptions\n}\n\nconst SEAM_PREFIX = '/_seam/'\nconst PROCEDURE_PREFIX = '/_seam/procedure/'\nconst EVENTS_SUFFIX = '.events'\n\n// Track WS sessions without patching the ws object\nconst wsSessions = new WeakMap<object, ChannelWsSession>()\n\nfunction resolveRouterPublicDir<T extends DefinitionMap>(router: Router<T>): string | undefined {\n\tconst publicDir = (router as unknown as { publicDir?: string }).publicDir\n\treturn typeof publicDir === 'string' ? publicDir : undefined\n}\n\n/** Hono middleware that handles all /_seam/* routes via the seam router */\nexport function seam<T extends DefinitionMap>(\n\trouter: Router<T>,\n\topts?: SeamHonoOptions,\n): MiddlewareHandler {\n\tconst effectivePublicDir = opts?.publicDir ?? resolveRouterPublicDir(router)\n\tconst handlerOpts: HttpHandlerOptions = {}\n\tif (opts?.staticDir) handlerOpts.staticDir = opts.staticDir\n\tif (effectivePublicDir) handlerOpts.publicDir = effectivePublicDir\n\tif (opts?.fallback) handlerOpts.fallback = opts.fallback\n\tif (opts?.rpcHashMap) handlerOpts.rpcHashMap = opts.rpcHashMap\n\tif (opts?.sseOptions) handlerOpts.sseOptions = opts.sseOptions\n\n\tconst handler = createHttpHandler(router, handlerOpts)\n\n\treturn async (c, next) => {\n\t\tconst url = new URL(c.req.url)\n\n\t\tif (!url.pathname.startsWith(SEAM_PREFIX)) {\n\t\t\tif (!effectivePublicDir || (c.req.method !== 'GET' && c.req.method !== 'HEAD')) {\n\t\t\t\treturn next()\n\t\t\t}\n\t\t\t// Public file: let handler check the file, fall through to next() on miss\n\t\t\tconst raw = c.req.raw\n\t\t\tconst result = await handler({\n\t\t\t\tmethod: raw.method,\n\t\t\t\turl: raw.url,\n\t\t\t\tbody: () => Promise.reject(new Error('no body')),\n\t\t\t\theader: (name) => raw.headers.get(name),\n\t\t\t})\n\t\t\tif (result.status === 404 && !('stream' in result)) return next()\n\t\t\treturn toWebResponse(result)\n\t\t}\n\n\t\t// WebSocket upgrade for channel paths\n\t\tif (\n\t\t\topts?.upgradeWebSocket &&\n\t\t\tc.req.header('upgrade') === 'websocket' &&\n\t\t\turl.pathname.startsWith(PROCEDURE_PREFIX) &&\n\t\t\turl.pathname.endsWith(EVENTS_SUFFIX)\n\t\t) {\n\t\t\tconst channelName = url.pathname.slice(PROCEDURE_PREFIX.length, -EVENTS_SUFFIX.length)\n\t\t\tconst rawInput = url.searchParams.get('input')\n\t\t\tlet channelInput: unknown\n\t\t\ttry {\n\t\t\t\tchannelInput = rawInput ? JSON.parse(rawInput) : {}\n\t\t\t} catch {\n\t\t\t\treturn c.text('Invalid input query parameter', 400)\n\t\t\t}\n\n\t\t\tconst wsHandler = opts.upgradeWebSocket(() => ({\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\t\t\tonOpen(_evt: any, ws: any) {\n\t\t\t\t\tconst wsObj = ws as object\n\t\t\t\t\tconst session = startChannelWs(\n\t\t\t\t\t\trouter,\n\t\t\t\t\t\tchannelName,\n\t\t\t\t\t\tchannelInput,\n\t\t\t\t\t\t{ send: (data: string) => (ws as { send: (d: string) => void }).send(data) },\n\t\t\t\t\t\topts.wsOptions,\n\t\t\t\t\t)\n\t\t\t\t\twsSessions.set(wsObj, session)\n\t\t\t\t},\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\t\t\tonMessage(evt: any, ws: any) {\n\t\t\t\t\tconst session = wsSessions.get(ws as object)\n\t\t\t\t\tconst text =\n\t\t\t\t\t\ttypeof (evt as { data: unknown }).data === 'string'\n\t\t\t\t\t\t\t? (evt as { data: string }).data\n\t\t\t\t\t\t\t: String((evt as { data: unknown }).data)\n\t\t\t\t\tsession?.onMessage(text)\n\t\t\t\t},\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\t\t\tonClose(_evt: any, ws: any) {\n\t\t\t\t\tconst session = wsSessions.get(ws as object)\n\t\t\t\t\tsession?.close()\n\t\t\t\t\twsSessions.delete(ws as object)\n\t\t\t\t},\n\t\t\t}))\n\t\t\treturn wsHandler(c, next)\n\t\t}\n\n\t\tconst raw = c.req.raw\n\t\tconst { body, file } = buildMultipartFields(raw)\n\n\t\tconst result = await handler({\n\t\t\tmethod: raw.method,\n\t\t\turl: raw.url,\n\t\t\tbody,\n\t\t\theader: (name) => raw.headers.get(name),\n\t\t\tfile,\n\t\t})\n\n\t\treturn toWebResponse(result)\n\t}\n}\n"],"mappings":";;AA+CA,MAAM,cAAc;AACpB,MAAM,mBAAmB;AACzB,MAAM,gBAAgB;AAGtB,MAAM,6BAAa,IAAI,SAAmC;AAE1D,SAAS,uBAAgD,QAAuC;CAC/F,MAAM,YAAa,OAA6C;AAChE,QAAO,OAAO,cAAc,WAAW,YAAY,KAAA;;;AAIpD,SAAgB,KACf,QACA,MACoB;CACpB,MAAM,qBAAqB,MAAM,aAAa,uBAAuB,OAAO;CAC5E,MAAM,cAAkC,EAAE;AAC1C,KAAI,MAAM,UAAW,aAAY,YAAY,KAAK;AAClD,KAAI,mBAAoB,aAAY,YAAY;AAChD,KAAI,MAAM,SAAU,aAAY,WAAW,KAAK;AAChD,KAAI,MAAM,WAAY,aAAY,aAAa,KAAK;AACpD,KAAI,MAAM,WAAY,aAAY,aAAa,KAAK;CAEpD,MAAM,UAAU,kBAAkB,QAAQ,YAAY;AAEtD,QAAO,OAAO,GAAG,SAAS;EACzB,MAAM,MAAM,IAAI,IAAI,EAAE,IAAI,IAAI;AAE9B,MAAI,CAAC,IAAI,SAAS,WAAW,YAAY,EAAE;AAC1C,OAAI,CAAC,sBAAuB,EAAE,IAAI,WAAW,SAAS,EAAE,IAAI,WAAW,OACtE,QAAO,MAAM;GAGd,MAAM,MAAM,EAAE,IAAI;GAClB,MAAM,SAAS,MAAM,QAAQ;IAC5B,QAAQ,IAAI;IACZ,KAAK,IAAI;IACT,YAAY,QAAQ,uBAAO,IAAI,MAAM,UAAU,CAAC;IAChD,SAAS,SAAS,IAAI,QAAQ,IAAI,KAAK;IACvC,CAAC;AACF,OAAI,OAAO,WAAW,OAAO,EAAE,YAAY,QAAS,QAAO,MAAM;AACjE,UAAO,cAAc,OAAO;;AAI7B,MACC,MAAM,oBACN,EAAE,IAAI,OAAO,UAAU,KAAK,eAC5B,IAAI,SAAS,WAAW,iBAAiB,IACzC,IAAI,SAAS,SAAS,cAAc,EACnC;GACD,MAAM,cAAc,IAAI,SAAS,MAAM,IAAyB,GAAsB;GACtF,MAAM,WAAW,IAAI,aAAa,IAAI,QAAQ;GAC9C,IAAI;AACJ,OAAI;AACH,mBAAe,WAAW,KAAK,MAAM,SAAS,GAAG,EAAE;WAC5C;AACP,WAAO,EAAE,KAAK,iCAAiC,IAAI;;AAgCpD,UA7BkB,KAAK,wBAAwB;IAE9C,OAAO,MAAW,IAAS;KAC1B,MAAM,QAAQ;KACd,MAAM,UAAU,eACf,QACA,aACA,cACA,EAAE,OAAO,SAAkB,GAAqC,KAAK,KAAK,EAAE,EAC5E,KAAK,UACL;AACD,gBAAW,IAAI,OAAO,QAAQ;;IAG/B,UAAU,KAAU,IAAS;KAC5B,MAAM,UAAU,WAAW,IAAI,GAAa;KAC5C,MAAM,OACL,OAAQ,IAA0B,SAAS,WACvC,IAAyB,OAC1B,OAAQ,IAA0B,KAAK;AAC3C,cAAS,UAAU,KAAK;;IAGzB,QAAQ,MAAW,IAAS;AACX,gBAAW,IAAI,GAAa,EACnC,OAAO;AAChB,gBAAW,OAAO,GAAa;;IAEhC,EAAE,CACc,GAAG,KAAK;;EAG1B,MAAM,MAAM,EAAE,IAAI;EAClB,MAAM,EAAE,MAAM,SAAS,qBAAqB,IAAI;AAUhD,SAAO,cARQ,MAAM,QAAQ;GAC5B,QAAQ,IAAI;GACZ,KAAK,IAAI;GACT;GACA,SAAS,SAAS,IAAI,QAAQ,IAAI,KAAK;GACvC;GACA,CAAC,CAE0B"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canmi/seam-adapter-hono",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.38",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist"
|
|
@@ -17,14 +17,14 @@
|
|
|
17
17
|
"test": "vitest run"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
|
-
"@canmi/seam-server": "0.5.
|
|
21
|
-
"hono": "
|
|
22
|
-
"tsdown": "
|
|
23
|
-
"typescript": "
|
|
24
|
-
"vitest": "
|
|
20
|
+
"@canmi/seam-server": "0.5.38",
|
|
21
|
+
"hono": "4",
|
|
22
|
+
"tsdown": "0.21",
|
|
23
|
+
"typescript": "6",
|
|
24
|
+
"vitest": "4"
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
27
|
-
"@canmi/seam-server": "0.5.
|
|
28
|
-
"hono": "
|
|
27
|
+
"@canmi/seam-server": "0.5.38",
|
|
28
|
+
"hono": "4"
|
|
29
29
|
}
|
|
30
30
|
}
|