Skip to content

运行时接入 ​

NASDK 是为跨平台、跨运行时设计的。

NASDK 将具体的物理传输方式拆成单独包,这意味着NASDK可以按需导入传输适配器,以在任何JSRuntime上完成通讯。

TIP

没有被安装和导入的 Provider 不会进入构建产物。

具体传输列表与接入方式详见传输 Provider。

下列表格中的包名省略 @nyirusu/nact- 前缀。

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 

Released under the MIT License.