{"_id":"@cikeyqi/queqiao-node-sdk","_rev":"4-74f51acdb8aa6c7619085d3184215500","name":"@cikeyqi/queqiao-node-sdk","dist-tags":{"latest":"0.1.3"},"versions":{"0.1.0":{"name":"@cikeyqi/queqiao-node-sdk","version":"0.1.0","_id":"@cikeyqi/queqiao-node-sdk@0.1.0","maintainers":[{"name":"cikeyqi","email":"cikeyqi@gmail.com"}],"dist":{"shasum":"297ed8af919b7413319cad8e004b0bc01a7cbc6b","tarball":"https://registry.npmjs.org/@cikeyqi/queqiao-node-sdk/-/queqiao-node-sdk-0.1.0.tgz","fileCount":32,"integrity":"sha512-Ic8F8Ev6q68L93AhMGfSmtLmumCfp964lHLENiwq2PchhFmgg/Y66QDl3ALf/arR3H4pEXfkszcM87uDXXbF3w==","signatures":[{"sig":"MEUCIQCphJSbXN6zTGunVaJTovYTjv1bYOfjr4Gnx4pmKLmT2gIgBccpFV1yyXH99UYMT4sRZ+eWfRN93G0/fl67rMB28vA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":70605},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"gitHead":"e4b9160680aba025f7e7829fce5f5d484ca8ad00","scripts":{"lint":"eslint . --ext .ts","build":"tsc -p tsconfig.json","format":"prettier -w .","prepublishOnly":"npm run build"},"_npmUser":{"name":"cikeyqi","email":"cikeyqi@gmail.com"},"_npmVersion":"11.6.2","description":"QueQiao Protocol V2 Node.js SDK","directories":{},"_nodeVersion":"24.13.0","dependencies":{"ws":"^8.16.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^8.57.0","prettier":"^3.3.3","@types/ws":"^8.5.10","typescript":"^5.4.5","@types/node":"^20.11.30","eslint-config-prettier":"^9.1.0","@typescript-eslint/parser":"^7.18.0","@typescript-eslint/eslint-plugin":"^7.18.0"},"_npmOperationalInternal":{"tmp":"tmp/queqiao-node-sdk_0.1.0_1770642910417_0.37861075964874136","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@cikeyqi/queqiao-node-sdk","version":"0.1.1","_id":"@cikeyqi/queqiao-node-sdk@0.1.1","maintainers":[{"name":"cikeyqi","email":"cikeyqi@gmail.com"}],"dist":{"shasum":"7c676c5e02434ab1e94688b4f3d980a358c5c861","tarball":"https://registry.npmjs.org/@cikeyqi/queqiao-node-sdk/-/queqiao-node-sdk-0.1.1.tgz","fileCount":32,"integrity":"sha512-5YujNpfAKKOh2OGBFFjBywPjrsEzqDEtazoRNnrIxynf1t0HZuV7SBofEV5m+UaI2hGRO7+CzW+yxcO1JQWwNw==","signatures":[{"sig":"MEUCIQDENPtOItZK30+n7fthJCtko5VF83SrAzt4h+1v4JgztgIgSEqVfjzfy1lHqaT+K9TSV0uKO/vdZczQZ9jlglKqe/0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":70363},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"gitHead":"d95786708c7f5d8792fffd9a4d226a03388654af","scripts":{"lint":"eslint . --ext .ts","build":"tsc -p tsconfig.json","format":"prettier -w .","prepublishOnly":"npm run build"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:a6395a9c-52ac-44bb-ac3c-e66ed4fd188b"}},"_npmVersion":"11.9.0","description":"QueQiao Protocol V2 Node.js SDK","directories":{},"_nodeVersion":"20.20.0","dependencies":{"ws":"^8.16.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^8.57.0","prettier":"^3.3.3","@types/ws":"^8.5.10","typescript":"^5.4.5","@types/node":"^20.11.30","eslint-config-prettier":"^9.1.0","@typescript-eslint/parser":"^7.18.0","@typescript-eslint/eslint-plugin":"^7.18.0"},"_npmOperationalInternal":{"tmp":"tmp/queqiao-node-sdk_0.1.1_1770643210081_0.04542301023763229","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@cikeyqi/queqiao-node-sdk","version":"0.1.2","_id":"@cikeyqi/queqiao-node-sdk@0.1.2","maintainers":[{"name":"cikeyqi","email":"cikeyqi@gmail.com"}],"homepage":"https://github.com/CikeyQi/queqiao-node-sdk#readme","bugs":{"url":"https://github.com/CikeyQi/queqiao-node-sdk/issues"},"dist":{"shasum":"b884c12a1d074acc8f516502b2bddebcf50a533c","tarball":"https://registry.npmjs.org/@cikeyqi/queqiao-node-sdk/-/queqiao-node-sdk-0.1.2.tgz","fileCount":32,"integrity":"sha512-Yyxdwx+c3WrAo6koupBXt/VW9sQ39yPic2QeUsv4OkFoKQNKnURsrwdQDPdzRD2qxx1vQR4drDX51r7bTIMqUA==","signatures":[{"sig":"MEUCIG+WAhBT1KN2EtdxdEtzCRafRDgGwVOYleu5Agq+gAMmAiEA0ODjJEM6APejIdsHabDoopK0o9E04iGvbTLNeGtq1FE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@cikeyqi%2fqueqiao-node-sdk@0.1.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":79124},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=18.18"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"gitHead":"bf7a1520431f99c2137d436c573fd6ca3aa37a33","scripts":{"lint":"eslint . --ext .ts","test":"npm run build && node --test --test-concurrency=1","build":"tsc -p tsconfig.json","check":"npm run lint && npm test","format":"prettier -w .","test:strict":"npm run build && node --test","prepublishOnly":"npm run check"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:e8b93887-991d-4c02-a023-4137cb4a7edb"}},"repository":{"url":"git+https://github.com/CikeyQi/queqiao-node-sdk.git","type":"git"},"_npmVersion":"11.9.0","description":"QueQiao Protocol V2 Node.js SDK","directories":{},"_nodeVersion":"24.13.0","dependencies":{"ws":"^8.16.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^8.57.0","prettier":"^3.3.3","@types/ws":"^8.5.10","typescript":"^5.4.5","@types/node":"^20.11.30","eslint-config-prettier":"^9.1.0","@typescript-eslint/parser":"^7.18.0","@typescript-eslint/eslint-plugin":"^7.18.0"},"_npmOperationalInternal":{"tmp":"tmp/queqiao-node-sdk_0.1.2_1770718087227_0.7970700640449484","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"@cikeyqi/queqiao-node-sdk","version":"0.1.3","description":"QueQiao Protocol V2 Node.js SDK","repository":{"type":"git","url":"git+https://github.com/CikeyQi/queqiao-node-sdk.git"},"homepage":"https://github.com/CikeyQi/queqiao-node-sdk#readme","bugs":{"url":"https://github.com/CikeyQi/queqiao-node-sdk/issues"},"type":"module","main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"scripts":{"build":"tsc -p tsconfig.json","test":"npm run build && node --test --test-concurrency=1","test:strict":"npm run build && node --test","check":"npm run lint && npm test","lint":"eslint . --ext .ts","format":"prettier -w .","prepublishOnly":"npm run check"},"engines":{"node":">=18.18"},"dependencies":{"ws":"^8.16.0"},"devDependencies":{"@types/node":"^20.11.30","@types/ws":"^8.5.10","@typescript-eslint/eslint-plugin":"^7.18.0","@typescript-eslint/parser":"^7.18.0","eslint":"^8.57.0","eslint-config-prettier":"^9.1.0","prettier":"^3.3.3","typescript":"^5.4.5"},"publishConfig":{"access":"public"},"gitHead":"4596b736693a602b03ede74f71b63c9d793457c7","_id":"@cikeyqi/queqiao-node-sdk@0.1.3","_nodeVersion":"24.13.0","_npmVersion":"11.10.0","dist":{"integrity":"sha512-rJHfwiGaYEqBEYQlQBzRLz7zd6rxlW9/nxw6/EM9MX/K14J9yH4LEK3kIv9Nr+bn3XztuuzooT7SaiWiLN3HBg==","shasum":"885015ececd7f94b90e11991d0ae486cd5fc7cb8","tarball":"https://registry.npmjs.org/@cikeyqi/queqiao-node-sdk/-/queqiao-node-sdk-0.1.3.tgz","fileCount":32,"unpackedSize":84253,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@cikeyqi%2fqueqiao-node-sdk@0.1.3","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIH64H1PdthseV5YndlP6ZTQKlLCcQehEVgUIYBjLB4QgAiAcUH99887SogjHCjPZKLE19MN6CQm6IhJAOulB99Tqlg=="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:e8b93887-991d-4c02-a023-4137cb4a7edb"}},"directories":{},"maintainers":[{"name":"cikeyqi","email":"cikeyqi@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/queqiao-node-sdk_0.1.3_1770896846599_0.7045004690145948"},"_hasShrinkwrap":false}},"time":{"created":"2026-02-09T13:15:10.363Z","modified":"2026-02-12T11:47:27.080Z","0.1.0":"2026-02-09T13:15:10.561Z","0.1.1":"2026-02-09T13:20:10.229Z","0.1.2":"2026-02-10T10:08:07.390Z","0.1.3":"2026-02-12T11:47:26.764Z"},"bugs":{"url":"https://github.com/CikeyQi/queqiao-node-sdk/issues"},"homepage":"https://github.com/CikeyQi/queqiao-node-sdk#readme","repository":{"type":"git","url":"git+https://github.com/CikeyQi/queqiao-node-sdk.git"},"description":"QueQiao Protocol V2 Node.js SDK","maintainers":[{"name":"cikeyqi","email":"cikeyqi@gmail.com"}],"readme":"﻿# QueQiao Node SDK\n\n面向 Node.js / TypeScript 的 QueQiao Protocol V2 SDK。  \n用于把你的应用接入 `QueQiao`，统一处理 WebSocket 连接、API 调用、事件分发与回声匹配。\n\n## 适用范围\n\n- 协议：`QueQiao Protocol V2`\n- API 文档对应：QueQiao `v0.2.11+`\n- 事件文档对应：QueQiao `v0.3.0+`\n\n## 安装与环境\n\n```bash\nnpm install @cikeyqi/queqiao-node-sdk\n```\n\n- Node.js `>= 18.18`\n- 包为 ESM（CommonJS 请使用动态导入）\n\n```js\nconst { createClient } = await import('@cikeyqi/queqiao-node-sdk');\n```\n\n## 先配置 QueQiao 服务端\n\n以正向连接（SDK 主动连 QueQiao）为例，QueQiao 侧至少确认这些配置：\n\n```yaml\nserver_name: \"Server\"\naccess_token: \"your-token\" # 可留空，留空表示不校验鉴权\n\nwebsocket_server:\n  enable: true\n  host: \"127.0.0.1\"\n  port: 8080\n```\n\n对应到握手 Header：\n- `x-self-name` 必须与 `server_name` 一致\n- `Authorization: Bearer <token>` 对应 `access_token`\n- `x-client-origin` 建议填写来源；本 SDK 会固定注入为 `queqiao-node-sdk`\n\n## 连接模式选择\n\n- `forward`：SDK 主动连接 QueQiao 的 WebSocket Server（最常见）\n- `reverse`：SDK 启动 WebSocket Server，等待 QueQiao 反连\n\n## 快速开始（forward 单连接）\n\n```ts\nimport { createClient } from '@cikeyqi/queqiao-node-sdk';\n\nconst client = createClient('ws://127.0.0.1:8080', {\n  selfName: 'Server',\n  accessToken: 'your-token',\n});\n\nclient.on('open', () => {\n  console.log('connected');\n});\n\nclient.on('player_chat', async (event) => {\n  console.log(`[${event.server_name}] ${event.player.nickname}: ${event.message}`);\n});\n\nconst resp = await client.request('broadcast', {\n  message: [{ text: 'Hello from Node SDK', color: 'green' }],\n});\n\nif (resp.status !== 'SUCCESS') {\n  console.error('broadcast failed:', resp);\n}\n```\n\n说明：\n- `request` 默认会自动触发连接（`autoConnect: true`）\n- 若你希望启动阶段就显式建立连接，调用 `await client.connect()`\n\n## 多服务器接入（forward 多连接）\n\n```ts\nimport { createClient } from '@cikeyqi/queqiao-node-sdk';\n\nconst client = createClient([\n  { url: 'ws://127.0.0.1:8080', selfName: 'Survival', accessToken: 'token-a' },\n  { url: 'ws://127.0.0.1:8081', selfName: 'Lobby' },\n]);\n\nawait client.connect();\n\nawait client.request(\n  'send_rcon_command',\n  { command: 'list' },\n  { selfName: 'Survival', timeoutMs: 5000 },\n);\n```\n\n注意：\n- 多连接请求时必须传 `options.selfName`\n\n## 反向连接（reverse）\n\n当 QueQiao 作为 WebSocket Client 反连时使用。  \nQueQiao 侧需启用 `websocket_client` 并指向你的 SDK 地址。\n\n```ts\nimport { createReverseClient } from '@cikeyqi/queqiao-node-sdk';\n\nconst client = createReverseClient(\n  { host: '0.0.0.0', port: 6700, path: '/minecraft/ws' },\n  { accessToken: 'reverse-token' },\n);\n\nclient.on('connection_open', (selfName) => {\n  console.log('reverse connected:', selfName);\n});\n\nawait client.connect();\nawait client.request(\n  'broadcast',\n  { message: [{ text: 'reverse hello' }] },\n  { selfName: 'ServerA' },\n);\n```\n\n## API 调用\n\n统一入口：\n\n```ts\nclient.request(api, data, options?)\n```\n\n`RequestOptions`：\n\n```ts\ninterface RequestOptions {\n  echo?: string;\n  timeoutMs?: number;\n  selfName?: string;\n}\n```\n\n已内置类型映射（`KnownApi`）：\n\n| API | data |\n| --- | --- |\n| `broadcast` | `{ message: MinecraftTextComponent }` |\n| `send_private_msg` | `{ uuid?: string \\| null; nickname?: string \\| null; message: MinecraftTextComponent }` |\n| `send_actionbar` | `{ message: MinecraftTextComponent }` |\n| `send_title` | `{ title?: MinecraftTextComponent; subtitle?: MinecraftTextComponent; fade_in?: number; stay?: number; fade_out?: number }` |\n| `send_rcon_command` | `{ command: string }` |\n\n重点行为：\n- SDK 只负责请求/响应匹配，`status === 'FAILED'` 不会自动抛错\n- 业务层请自行检查返回的 `code` / `status` / `message`\n\n## 事件监听\n\n事件会以三种事件名分发：\n\n- `event`：总线事件（通配）\n- `event_name`：如 `PlayerChatEvent`\n- `sub_type`：如 `player_chat`\n\n示例：\n\n```ts\nclient.on('event', (e) => {\n  console.log('all events:', e.event_name);\n});\n\nclient.on('PlayerJoinEvent', (e) => {\n  console.log('join:', e.player.nickname);\n});\n\nclient.on('player_command', (e) => {\n  console.log('command:', e.command);\n});\n```\n\n## 连接事件\n\n- `open`\n- `close`\n- `reconnect`\n- `error`\n- `connection_open`\n- `connection_close`\n- `connection_reconnect`\n- `connection_error`\n\n建议：\n- 建议始终监听 `error` 与 `connection_error`，便于记录协议解析异常与连接异常。\n\n## 连接管理\n\n除 `request` 外，客户端还提供以下连接管理方法：\n\n```ts\nawait client.connect({ selfName? });\nawait client.close({ code?, reason?, selfName? });\nclient.isOpen({ selfName? });\nclient.list();\nclient.status();\nclient.add(config); // 仅 forward 模式\nawait client.remove({ selfName, code?, reason? });\n```\n\n行为说明：\n- `list()`：返回当前连接名列表（forward 为已配置连接；reverse 为当前在线反连）\n- `status()`：返回 `{ selfName, open }[]`，用于统一查看每个连接状态\n- `isOpen()`：不传 `selfName` 时表示“是否至少有一个连接处于 open”\n- `add()`：仅 forward 可用；reverse 调用会抛错\n- `remove()`：forward 会移除并关闭该连接；reverse 会关闭该 `selfName` 当前连接\n- `connect` / `close` / `isOpen` / `remove` 统一使用对象参数，避免位置参数歧义\n- 多连接场景下，`request` 仍需传 `options.selfName`\n\n## 模式参数约束\n\n- `forward` 可用：`url`、`connections`、`headers`、`selfName`、`accessToken`、`reconnect*`\n- `reverse` 可用：`server`、`accessToken`、`strictHeaders`、`rejectDuplicateOrigin`\n- `reverse` 不接受：`url`、`connections`、`headers`、`selfName`、`reconnect*`\n- forward 多连接（`connections`）时，不接受全局 `selfName` / `accessToken`，请逐连接设置\n\n## 常用配置项\n\n| 字段 | 默认值 | 说明 |\n| --- | --- | --- |\n| `mode` | `forward` | 连接模式 |\n| `reconnect` | `true` | forward 断线自动重连 |\n| `reconnectIntervalMs` | `1000` | 重连初始间隔 |\n| `reconnectMaxIntervalMs` | `30000` | 重连最大间隔 |\n| `connectTimeoutMs` | `10000` | 连接超时 |\n| `heartbeatIntervalMs` | `0` | 心跳间隔，`0` 为关闭 |\n| `heartbeatTimeoutMs` | `0` | 心跳超时；当开启心跳且此项为 `0` 时自动取 `2 * heartbeatIntervalMs` |\n| `requestTimeoutMs` | `15000` | 请求超时 |\n| `maxPendingRequests` | `1000` | 最大待响应请求数，`0` 为不限制 |\n| `maxPayloadBytes` | `0` | WebSocket 最大消息体，`0` 为不限制 |\n| `autoConnect` | `true` | 请求前自动尝试建连 |\n| `strictHeaders` | `true` | reverse 模式下严格校验握手 Header（关闭后若配置了 `accessToken`，仍会校验 `Authorization`） |\n| `rejectDuplicateOrigin` | `true` | reverse 模式下拒绝重复来源连接 |\n\n## 工厂方法\n\n- `createClient(url, overrides?)`\n- `createClient(connections, overrides?)`\n- `connectClient(url, overrides?)`\n- `connectClient(connections, overrides?)`\n- `createReverseClient(server, overrides?)`\n- `connectReverseClient(server, overrides?)`\n\n`connect*` 会在创建后自动执行 `connect()`。\n\n## 导出项\n\n```ts\nimport {\n  QueQiaoClient,\n  createClient,\n  connectClient,\n  createReverseClient,\n  connectReverseClient,\n} from '@cikeyqi/queqiao-node-sdk';\n```\n\n完整类型（事件、请求、响应、配置）都可从包根导入。\n","readmeFilename":"README.md"}