运行时接入
NASDK 是为跨平台、跨运行时设计的。
NASDK 将具体的物理传输方式拆成单独包,这意味着NASDK可以按需导入传输适配器,以在任何JSRuntime上完成通讯。
Web Runtime
| 包 | 可用 | 说明 |
|---|---|---|
websocket-client | ✅ | 主动连接 WebSocket |
websocket-server | ❌ | 浏览器不能监听端口 |
streamable-http-client | ✅ | 使用 Fetch |
streamable-http-server | ❌ | 浏览器不能监听端口 |
tcp-client | ❌ | 浏览器不提供原始 Socket |
tcp-server | ❌ | |
unix-client | ❌ | |
unix-server | ❌ |
Web 环境目前没有可用的Server包,仅可以使用websocket与streamable-http。
ts
import NApp from '@nyirusu/nasdk'
import WebSocketClientProvider from '@nyirusu/nact-websocket-client'
import StreamableHttpClientProvider from '@nyirusu/nact-streamable-http-client'
const app = new NApp({ id: 'web' })
app.nact.use(new WebSocketClientProvider())
app.nact.use(new StreamableHttpClientProvider())Node.js Runtime
| 包 | 可用 | 说明 |
|---|---|---|
websocket-client | ✅ | Nodejs环境均可使用 |
websocket-server | ✅ | |
streamable-http-client | ✅ | |
streamable-http-server | ✅ | |
tcp-client | ✅ | |
tcp-server | ✅ | |
unix-client | ✅(仅POSIX) | |
unix-server | ✅(仅POSIX) |
Bun Runtime
使用 Node.js 兼容接口时沿用 Node.js 配置
若要使用原生 Bun.serve() ,可接入 provider.fetch() 完成StreamableHTTP附着。
暂时没有WebSocket附着形式。
ts
import NApp from '@nyirusu/nasdk'
import StreamableHTTPServerProvider from '@nyirusu/nact-streamable-http-server'
const streamProvider = new StreamableHTTPServerProvider()
const app = new NApp({
id: 'BunHttpApp',
server: [{ type: 'streamable-http', provider: { noServer: true } }],
})
app.nact.use(streamProvider)
await app.start()
Bun.serve({
port: 18900,
fetch(request) {
if (new URL(request.url).pathname === '/nact@http')
return streamProvider.fetch(request)
return new Response(null, { status: 404 })
},
})Express
| 包 | 可用 | 说明 |
|---|---|---|
websocket-client | ✅ | 主动连接 |
websocket-server | ✅ | 附着到 HTTP Server |
streamable-http-client | ✅ | 使用 Fetch |
streamable-http-server | ✅ | 附着到 Express 路由 |
tcp-client | ✅(需要底层Runtime支持) | |
tcp-server | ✅(需要底层Runtime支持) | |
unix-client | ✅(仅 POSIX) | |
unix-server | ✅(仅 POSIX) |
ts
import express from 'express'
import http from 'node:http'
import NApp from '@nyirusu/nasdk'
import WebSocketServerProvider from '@nyirusu/nact-websocket-server'
import StreamableHTTPServerProvider, { toNodeHandler } from '@nyirusu/nact-streamable-http-server'
const web = express()
const httpServer = http.createServer(web)
const streamProvider = new StreamableHTTPServerProvider()
const wsProvider = new WebSocketServerProvider()
const app = new NApp({
id: 'ExpressApp',
server: [
{
type: 'websocket',
provider: { noServer: true },
},
{
type: 'streamable-http',
provider: { noServer: true },
}]
})
app.nact.use(streamProvider)
app.nact.use(wsProvider)
await app.start()
//附着到WebSocket端点
wsProvider.attach(httpServer, { path: '/nact@ws' })
//附着到HTTP端点
web.all('/nact@http', toNodeHandler(streamProvider.fetch))
web.use(express.json())
httpServer.listen(18900) Next.js
| 包 | 可用 | 说明 |
|---|---|---|
websocket-client | ✅ | 主动连接 |
websocket-server | ✅ | 独立端口监听 |
streamable-http-client | ✅ | 使用 Fetch |
streamable-http-server | ✅ | Route Handler |
tcp-client | ✅ | 需要底层 Runtime 支持 |
tcp-server | ✅ | |
unix-client | ✅ | 仅 POSIX |
unix-server | ✅ |
Streamable HTTP
ts
import NApp from '@nyirusu/nasdk'
import StreamableHTTPServerProvider from '@nyirusu/nact-streamable-http-server'
export const runtime = 'nodejs'
const streamProvider = new StreamableHTTPServerProvider()
const app = new NApp({
id: 'NextHttpApp',
server: [{ type: 'streamable-http', provider: { noServer: true } }],
})
app.nact.use(streamProvider)
const started = app.start()
async function handle(request: Request) {
await started
return streamProvider.fetch(request)
}
export { handle as GET, handle as POST } WebSocket
TIP
NextJS WebSocket路由较为特殊,既没有直接开放HTTPServer供我们Upgrade,也没有提供足够的钩子实现WebSocketServer监听。
因此,目前NextJS只支持以服务器而非附着器形式的WebSocket Server。
ts
export async function register() {
if (process.env.NEXT_RUNTIME !== 'nodejs') return
const { default: NApp } = await import('@nyirusu/nasdk')
const { default: WebSocketServerProvider } = await import('@nyirusu/nact-websocket-server')
const app = new NApp({
id: 'NextWsApp',
server: [{
type: 'websocket',
provider: { noServer: false, host: '127.0.0.1', port: 18901, path: '/nact@ws' },
}],
})
app.nact.use(new WebSocketServerProvider())
await app.start()
}Nuxt
| 包 | 可用 | 说明 |
|---|---|---|
websocket-client | ✅ | 主动连接 |
websocket-server | ✅ | 独立端口监听 |
streamable-http-client | ✅ | 使用 Fetch |
streamable-http-server | ✅ | H3 路由 |
tcp-client | ✅ | 需要底层 Runtime 支持 |
tcp-server | ✅ | |
unix-client | ✅ | 仅 POSIX |
unix-server | ✅ | |
websocket-nuxtjs-server | ✅ | Nitro专用包,通过原生 WS 路由实现 |
Streamable HTTP
ts
import { defineEventHandler, toWebRequest, sendWebResponse } from 'h3'
import NApp from '@nyirusu/nasdk'
import StreamableHTTPServerProvider from '@nyirusu/nact-streamable-http-server'
const streamProvider = new StreamableHTTPServerProvider()
const app = new NApp({
id: 'NuxtHttpApp',
server: [{ type: 'streamable-http', provider: { noServer: true } }],
})
app.nact.use(streamProvider)
const started = app.start()
export default defineEventHandler(async event => {
await started
return sendWebResponse(event, await streamProvider.fetch(toWebRequest(event)))
})WebSocket
专用包 @nyirusu/nact-websocket-nuxtjs-server 可以让 Nitro 负责托管现有WS路由。
ts
export default defineNuxtConfig({
nitro: { experimental: { websocket: true } }
})ts
import NApp from '@nyirusu/nasdk'
import NuxtWebSocketServerProvider from '@nyirusu/nact-websocket-nuxtjs-server'
export default defineNitroPlugin(async nitroApp => {
const provider = new NuxtWebSocketServerProvider()
const app = new NApp({
id: 'NuxtWsApp',
server: [{ type: 'websocket-nuxtjs' }],
})
app.nact.use(provider)
await app.start()
Object.assign(nitroApp, { nactWs: provider })
nitroApp.hooks.hook('close', () => app.terminate())
})ts
import { defineWebSocketHandler } from 'h3'
import type NuxtWebSocketServerProvider from '@nyirusu/nact-websocket-nuxtjs-server'
function hooks() {
return (useNitroApp() as ReturnType<typeof useNitroApp> & {
nactWs: NuxtWebSocketServerProvider
}).nactWs.hooks
}
export default defineWebSocketHandler({
upgrade: request => hooks().upgrade(request),
open: peer => hooks().open(peer),
message: (peer, message) => hooks().message(peer, message),
close: (peer, details) => hooks().close(peer, details),
error: (peer, reason) => hooks().error(peer, reason),
})当然,你也可以让标准WebSocketServer作为插件服务器,在nuxt启动时自主监听。
ts
import NApp from '@nyirusu/nasdk'
import WebSocketServerProvider from '@nyirusu/nact-websocket-server'
export default defineNitroPlugin(async nitroApp => {
const app = new NApp({
id: 'NuxtWsApp',
server: [{
type: 'websocket',
provider: { noServer: false, host: '127.0.0.1', port: 18901, path: '/nact@ws' }
}],
})
app.nact.use(new WebSocketServerProvider())
await app.start()
nitroApp.hooks.hook('close', () => app.terminate())
})CloudFlare Worker
| 包 | 可用 | 说明 |
|---|---|---|
websocket-client | ✅ | 主动连接 WebSocket |
websocket-server | ❌ | 需要自主监听端口 |
streamable-http-client | ✅ | 使用 Fetch |
streamable-http-server | ❌ | 需要自主监听端口 |
tcp-client | ✅ | Worker可以正常出站TCP链接 |
tcp-server | ❌ | 不支持入站 TCP 监听 |
unix-client | ❌ | 不提供 Unix Socket |
unix-server | ❌ | |
websocket-cloudflare-worker-server | ✅ | 平台限定,通过附着到 fetch 路由完成通讯 |
TIP
NASDK目前没有打算为CloudFlareWorker提供对应的StreamableHTTP支持,原因是NASDK需要使用Durable Object实现状态保存。
WebSocket不需要DO,CFWorker的状态保存会随着链接维持。
下面是一个简单的 echo Worker,返回原始输入和该连接的累计调用次数:
ts
import NApp, { NACAB } from '@nyirusu/nasdk'
import WebSocketServerProvider from '@nyirusu/nact-websocket-cloudflare-worker-server'
let globalCount = 0
export default {
async fetch(request: Request): Promise<Response> {
//前置路由处理
if (new URL(request.url).pathname !== '/nact@ws') return new Response('Not Found', { status: 404 })
if (request.headers.get('Upgrade')?.toLowerCase() !== 'websocket') return new Response(null, { status: 426, headers: { Upgrade: 'websocket' } })
//绑定物理传输
const provider = new WebSocketServerProvider()
const app = new NApp({
id: 'CloudFlareWebSocketEcho',
server: [{ type: provider.type }],
})
app.nact.use(provider)
//编写简单累加逻辑
let connectionCount = 0
const abilities = new NACAB()
abilities.register({
name: 'echo',
description: '返回输入',
execute: input => ({ echo: input, cCount: ++connectionCount, gCount: ++globalCount })
})
app.bindProcessor('ability', abilities.nacpAdaptor)
//启动
await app.start()
//返回由Provider包装好后升级的Response句柄
return provider.fetch(request)
}
}Vercel Runtime
| 包 | 可用 | 说明 |
|---|---|---|
websocket-client | ✅ | 主动连接 WebSocket |
websocket-server | ✅ | 本Provider可以附着到宿主 HTTP Server,导出给 Vercel |
streamable-http-client | ✅ | 使用 Fetch |
streamable-http-server | ❌ | Vercel内存会话无法保证 GET 与 POST 跨实例路由后正常通信 |
tcp-client | ✅ | 使用 Node.js 出站 TCP |
tcp-server | ❌ | 不提供公网入站 TCP 监听 |
unix-client | ❌ | Vercel不提供 Unix Socket |
unix-server | ❌ |
ts
import http from 'node:http'
import NApp from '@nyirusu/nasdk'
import WebSocketServerProvider from '@nyirusu/nact-websocket-server'
const server = http.createServer()
const wsProvider = new WebSocketServerProvider()
const app = new NApp({
id: 'VercelWebSocketEcho',
server: [{
type: 'websocket',
provider: { noServer: true },
}],
})
app.nact.use(wsProvider)
await app.start()
wsProvider.attach(server, { path: '/nact@ws' })
export default server 
