@univerjs-pro/collaboration-transport-node 1.0.0-beta.2
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 +178 -0
- package/README.zh-CN.md +204 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +78 -0
- package/dist/index.mjs +1 -0
- package/package.json +21 -0
package/README.md
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# @univerjs-pro/collaboration-transport-node
|
|
2
|
+
|
|
3
|
+
English | [简体中文](./README.zh-CN.md)
|
|
4
|
+
|
|
5
|
+
The Node.js HTTP/WebSocket ingress for a Univer Collaboration server assembly.
|
|
6
|
+
|
|
7
|
+
Transport receives requests and WebSocket upgrades forwarded by the host Node HTTP server. Applications integrate authentication, logging, and other ingress concerns through HTTP middleware, then registered protocol Endpoints handle Collaboration requests. Transport is not normally used by itself:
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
Node HTTP server
|
|
11
|
+
→ Transport
|
|
12
|
+
├─→ HTTP middleware login authentication, CORS, trace, network logs
|
|
13
|
+
│ → Endpoint HTTP protocol routes; calls Service APIs as needed
|
|
14
|
+
│ → Service middleware / Database
|
|
15
|
+
└─→ Endpoint WebSocket Sessions, JOIN, Presence, ACKs, broadcasts
|
|
16
|
+
→ Endpoint middleware realtime-room ACL and Presence controls
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Server assembly
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pnpm add \
|
|
23
|
+
@univerjs-pro/collaboration-service \
|
|
24
|
+
@univerjs-pro/collaboration-endpoint \
|
|
25
|
+
@univerjs-pro/collaboration-transport-node \
|
|
26
|
+
@univerjs-pro/collaboration-database-memory
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { createServer } from 'node:http';
|
|
31
|
+
import { MemoryDatabaseAdapter } from '@univerjs-pro/collaboration-database-memory';
|
|
32
|
+
import { UniverCollabEndpoint } from '@univerjs-pro/collaboration-endpoint';
|
|
33
|
+
import { UniverCollabService } from '@univerjs-pro/collaboration-service';
|
|
34
|
+
import { createNodeTransport } from '@univerjs-pro/collaboration-transport-node';
|
|
35
|
+
|
|
36
|
+
const database = new MemoryDatabaseAdapter();
|
|
37
|
+
const collabService = new UniverCollabService({ dbAdapter: database });
|
|
38
|
+
const endpoint = new UniverCollabEndpoint(collabService);
|
|
39
|
+
const transport = createNodeTransport();
|
|
40
|
+
|
|
41
|
+
// use() handles HTTP requests only. Authenticate and attach request-scoped data here.
|
|
42
|
+
transport.use(async (ctx, next) => {
|
|
43
|
+
const user = await authenticate(ctx.incomingMessage);
|
|
44
|
+
if (!user) {
|
|
45
|
+
ctx.response.statusCode = 401;
|
|
46
|
+
ctx.response.end('Authentication required');
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Map the application's stable user primary key at the SDK boundary.
|
|
51
|
+
ctx.userID = user.userId;
|
|
52
|
+
ctx.customData.user = user;
|
|
53
|
+
ctx.customData.traceId = readTraceId(ctx.incomingMessage);
|
|
54
|
+
await next();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// useUpgrade() runs once per WebSocket handshake and can reject before upgrade.
|
|
58
|
+
transport.useUpgrade(async (ctx, next) => {
|
|
59
|
+
if (!isAllowedOrigin(ctx.incomingMessage.headers.origin)) {
|
|
60
|
+
ctx.reject(403, 'Origin rejected');
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
await next();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
endpoint.use('joinUnit', async (ctx, next) => {
|
|
67
|
+
// Transport ctx.userID → ctx.session.userID
|
|
68
|
+
// Transport ctx.customData → ctx.session.customData
|
|
69
|
+
console.log(ctx.session.userID, ctx.session.customData.user, ctx.session.customData.traceId);
|
|
70
|
+
await next();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
collabService.use('readUnitData', async (ctx, next) => {
|
|
74
|
+
// Transport ctx.userID → Service ctx.userID
|
|
75
|
+
// Transport ctx.customData → Service ctx.customData
|
|
76
|
+
console.log(ctx.userID, ctx.customData.user, ctx.customData.traceId);
|
|
77
|
+
await next();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
transport.register(endpoint);
|
|
81
|
+
|
|
82
|
+
const server = createServer((request, response) => {
|
|
83
|
+
transport.handleRequest(request, response);
|
|
84
|
+
});
|
|
85
|
+
server.on('upgrade', (request, socket, head) => {
|
|
86
|
+
transport.handleUpgrade(request, socket, head);
|
|
87
|
+
});
|
|
88
|
+
server.listen(3010);
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The Memory Adapter keeps this Transport example small; process exit loses all data. See
|
|
92
|
+
[Database Adapters](https://office.univer.ai/collaboration/database-adapters) for production
|
|
93
|
+
persistence choices.
|
|
94
|
+
|
|
95
|
+
Transport does not prescribe cookies, bearer tokens, user tables, or user types. The application validates its own credentials in HTTP middleware and explicitly writes the stable business identity to `ctx.userID`. On authentication failure, end the response without calling `next()`.
|
|
96
|
+
|
|
97
|
+
## How identity and customData reach Endpoint and Service
|
|
98
|
+
|
|
99
|
+
Transport creates a separate `ctx.customData` for every HTTP request. HTTP middleware may add the current request's user, tenant, trace, or ACL query objects.
|
|
100
|
+
|
|
101
|
+
```text
|
|
102
|
+
regular protocol HTTP request
|
|
103
|
+
→ Transport middleware sets userID/customData
|
|
104
|
+
→ Endpoint validates the protocol and calls Service
|
|
105
|
+
→ Service middleware reads the same userID/customData
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
WebSocket identity is inherited from a one-time ticket, not directly from the upgrade URL or client payload:
|
|
109
|
+
|
|
110
|
+
```text
|
|
111
|
+
userID/customData on the ticket HTTP request
|
|
112
|
+
→ Endpoint stores the association server-side and returns an opaque ticket string
|
|
113
|
+
→ WebSocket open consumes the association once
|
|
114
|
+
→ Endpoint creates Session { userID, memberID, customData }
|
|
115
|
+
→ Endpoint middleware reads ctx.session
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
The ticket string contains neither `userID` nor `customData`. Only the session-ticket HTTP route extends the current Transport context into a WebSocket Session; other HTTP routes do not.
|
|
119
|
+
|
|
120
|
+
Transport network-data lifetimes are:
|
|
121
|
+
|
|
122
|
+
- HTTP `ctx.customData` belongs only to the current request; every later HTTP request runs authentication middleware again.
|
|
123
|
+
- `EndpointSession.customData` comes from the HTTP request that issued the ticket and keeps the same reference for the current WebSocket Session.
|
|
124
|
+
- For regular HTTP protocol routes, Service middleware receives that HTTP request's `ctx.customData`, not Session `customData` automatically.
|
|
125
|
+
|
|
126
|
+
`customData` is only passed in memory. It is not automatically sent to browsers, persisted in the collaboration database, or logged.
|
|
127
|
+
|
|
128
|
+
## Mounting in a host application
|
|
129
|
+
|
|
130
|
+
Frameworks such as Express can continue handling application product routes while forwarding only Collaboration protocol paths to `handleRequest()`. The underlying HTTP server must also forward the corresponding WebSocket `upgrade`:
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
app.use('/universer-api', (request, response) => {
|
|
134
|
+
// Express rewrites request.url when mounted; Transport needs the full protocol path.
|
|
135
|
+
request.url = request.originalUrl;
|
|
136
|
+
transport.handleRequest(request, response);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
server.on('upgrade', (request, socket, head) => {
|
|
140
|
+
const url = new URL(request.url ?? '/', 'http://localhost');
|
|
141
|
+
if (url.pathname === '/universer-api/comb/connect') {
|
|
142
|
+
transport.handleUpgrade(request, socket, head);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Express must forward protocol paths before body parsers such as `express.json()` consume the request stream. Fastify, Nest, and other frameworks similarly need to forward the raw Node request, socket, and head.
|
|
148
|
+
|
|
149
|
+
Collaboration, History, Comment, and Worktree Endpoints can be registered on the same Transport and share the same HTTP authentication middleware. If an Endpoint does not match a request, it calls `next()`. If no registered Endpoint handles an HTTP request, Transport returns a plain-text 404; an unhandled WebSocket upgrade also receives HTTP 404 before connection establishment.
|
|
150
|
+
|
|
151
|
+
## API layers
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
transport.use(httpMiddleware);
|
|
155
|
+
transport.useUpgrade(upgradeMiddleware);
|
|
156
|
+
transport.register(endpoint);
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
- `use()`: regular HTTP requests only; callbacks directly receive `incomingMessage`, `response`, `userID`, and request-scoped `customData`.
|
|
160
|
+
- `useUpgrade()`: WebSocket handshakes only; read `incomingMessage` or call `reject(status, message)`.
|
|
161
|
+
- `register()`: register and own a protocol Endpoint that handles matching HTTP requests and WebSocket connections.
|
|
162
|
+
|
|
163
|
+
## Request limits and disposal
|
|
164
|
+
|
|
165
|
+
HTTP bodies and WebSocket messages both default to a 16 MiB limit. `readBody()` and `readJson()` enforce the limit and ensure the request stream is consumed only once.
|
|
166
|
+
|
|
167
|
+
Dispose from the network boundary inward during shutdown:
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
await transport.dispose();
|
|
171
|
+
await collabService.dispose();
|
|
172
|
+
await database.dispose();
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Transport closes active connections and disposes Endpoints it owns through `register()`. The registration returned by `register()` can also unregister and dispose that Endpoint early. Endpoint does not dispose Service, and Service does not dispose an externally injected Database Adapter.
|
|
176
|
+
|
|
177
|
+
See the [Collaboration documentation](https://office.univer.ai/collaboration/overview) for the
|
|
178
|
+
complete assembly, middleware, and production requirements.
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# @univerjs-pro/collaboration-transport-node
|
|
2
|
+
|
|
3
|
+
[English](./README.md) | [简体中文](./README.zh-CN.md)
|
|
4
|
+
|
|
5
|
+
Univer Collaboration 服务端组装中的 Node.js HTTP/WebSocket 入口。
|
|
6
|
+
|
|
7
|
+
Transport 接收宿主 Node HTTP server 转交的 request 和 WebSocket upgrade。应用通过 HTTP
|
|
8
|
+
middleware 接入认证、日志等入口逻辑,再由注册的协议 Endpoint 处理 Collaboration 请求。
|
|
9
|
+
它通常不单独使用:
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
Node HTTP server
|
|
13
|
+
→ Transport
|
|
14
|
+
├─→ HTTP middleware 登录认证、CORS、trace 和网络日志
|
|
15
|
+
│ → Endpoint HTTP 协议路由,必要时调用 Service API
|
|
16
|
+
│ → Service middleware / Database
|
|
17
|
+
└─→ Endpoint WebSocket Session、JOIN、Presence、ACK 和广播
|
|
18
|
+
→ Endpoint middleware 实时房间 ACL 和 Presence 控制
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 服务端组装
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pnpm add \
|
|
25
|
+
@univerjs-pro/collaboration-service \
|
|
26
|
+
@univerjs-pro/collaboration-endpoint \
|
|
27
|
+
@univerjs-pro/collaboration-transport-node \
|
|
28
|
+
@univerjs-pro/collaboration-database-memory
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { createServer } from 'node:http';
|
|
33
|
+
import { MemoryDatabaseAdapter } from '@univerjs-pro/collaboration-database-memory';
|
|
34
|
+
import { UniverCollabEndpoint } from '@univerjs-pro/collaboration-endpoint';
|
|
35
|
+
import { UniverCollabService } from '@univerjs-pro/collaboration-service';
|
|
36
|
+
import { createNodeTransport } from '@univerjs-pro/collaboration-transport-node';
|
|
37
|
+
|
|
38
|
+
const database = new MemoryDatabaseAdapter();
|
|
39
|
+
const collabService = new UniverCollabService({ dbAdapter: database });
|
|
40
|
+
const endpoint = new UniverCollabEndpoint(collabService);
|
|
41
|
+
const transport = createNodeTransport();
|
|
42
|
+
|
|
43
|
+
// use() 只接收 HTTP request;这里由应用认证并挂载身份和请求级数据。
|
|
44
|
+
transport.use(async (ctx, next) => {
|
|
45
|
+
const user = await authenticate(ctx.incomingMessage);
|
|
46
|
+
if (!user) {
|
|
47
|
+
ctx.response.statusCode = 401;
|
|
48
|
+
ctx.response.end('Authentication required');
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// 在进入 Collaboration SDK 时映射应用的稳定用户主键。
|
|
53
|
+
ctx.userID = user.userId;
|
|
54
|
+
ctx.customData.user = user;
|
|
55
|
+
ctx.customData.traceId = readTraceId(ctx.incomingMessage);
|
|
56
|
+
await next();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// useUpgrade() 每次 WebSocket handshake 运行一次,可在升级前拒绝。
|
|
60
|
+
transport.useUpgrade(async (ctx, next) => {
|
|
61
|
+
if (!isAllowedOrigin(ctx.incomingMessage.headers.origin)) {
|
|
62
|
+
ctx.reject(403, 'Origin rejected');
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
await next();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// WebSocket Session 发送 JOIN 消息、尚未加入 Unit 房间时,
|
|
69
|
+
// 会经过 joinUnit middleware;Session 数据继承自签发 ticket 的 HTTP 请求。
|
|
70
|
+
endpoint.use('joinUnit', async (ctx, next) => {
|
|
71
|
+
// Transport 挂载的 ctx.userID → ctx.session.userID
|
|
72
|
+
// Transport 挂载的 ctx.customData → ctx.session.customData
|
|
73
|
+
console.log(ctx.session.userID, ctx.session.customData.user, ctx.session.customData.traceId);
|
|
74
|
+
await next();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Endpoint 通过 HTTP 读取 Unit、changesets 或 Sheet block,
|
|
78
|
+
// Service 尚未访问 Database Adapter 时,会经过 readUnitData middleware。
|
|
79
|
+
collabService.use('readUnitData', async (ctx, next) => {
|
|
80
|
+
// Transport 挂载的 ctx.userID → Service ctx.userID
|
|
81
|
+
// Transport 挂载的 ctx.customData → Service ctx.customData
|
|
82
|
+
console.log(ctx.userID, ctx.customData.user, ctx.customData.traceId);
|
|
83
|
+
await next();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// Endpoint 通过独立入口注册,并由 Transport 管理生命周期。
|
|
87
|
+
transport.register(endpoint);
|
|
88
|
+
|
|
89
|
+
const server = createServer((request, response) => {
|
|
90
|
+
transport.handleRequest(request, response);
|
|
91
|
+
});
|
|
92
|
+
server.on('upgrade', (request, socket, head) => {
|
|
93
|
+
transport.handleUpgrade(request, socket, head);
|
|
94
|
+
});
|
|
95
|
+
server.listen(3010);
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
这里使用 Memory Adapter 只是为了让 Transport 示例保持最小;进程退出后数据会丢失。
|
|
99
|
+
生产环境的持久化选择见文档站的
|
|
100
|
+
[Database Adapter](https://office.univer.ai/zh-CN/collaboration/database-adapters)。
|
|
101
|
+
|
|
102
|
+
Transport 不规定 Cookie、Bearer token、用户表或用户类型。应用在 HTTP
|
|
103
|
+
middleware 中验证自己的凭据,再把稳定业务身份显式写入
|
|
104
|
+
`ctx.userID`。认证失败时直接结束 response,不要继续调用 `next()`。
|
|
105
|
+
|
|
106
|
+
## 身份与 customData 如何进入 Endpoint 和 Service
|
|
107
|
+
|
|
108
|
+
Transport 为每个 HTTP 请求创建独立的 `ctx.customData`。HTTP middleware 可以在
|
|
109
|
+
其中放入当前请求需要的用户、tenant、trace 或 ACL 查询对象。
|
|
110
|
+
|
|
111
|
+
```text
|
|
112
|
+
普通协议 HTTP 请求
|
|
113
|
+
→ Transport middleware 设置 userID/customData
|
|
114
|
+
→ Endpoint 验证协议并调用 Service
|
|
115
|
+
→ Service middleware 读取同一 userID/customData
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
WebSocket 身份不直接取自 upgrade URL 或客户端 payload,而是由 Endpoint 通过一次性
|
|
119
|
+
ticket 继承:
|
|
120
|
+
|
|
121
|
+
```text
|
|
122
|
+
ticket HTTP 请求上的 userID/customData
|
|
123
|
+
→ Endpoint 在服务端保存关联并返回 opaque ticket 字符串
|
|
124
|
+
→ WebSocket open 一次性消费关联记录
|
|
125
|
+
→ Endpoint 创建 Session { userID, memberID, customData }
|
|
126
|
+
→ Endpoint middleware 通过 ctx.session 读取
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
ticket 字符串本身不包含 `userID/customData`。只有 session-ticket HTTP 路由会把
|
|
130
|
+
当前 Transport context 延长到 WebSocket Session;其他 HTTP 路由不会。
|
|
131
|
+
|
|
132
|
+
Transport 的网络数据生命周期如下:
|
|
133
|
+
|
|
134
|
+
- HTTP `ctx.customData` 只属于当前请求;每个后续 HTTP 请求都会重新运行认证
|
|
135
|
+
middleware。
|
|
136
|
+
- `EndpointSession.customData` 来自签发 ticket 的那次 HTTP 请求,在当前
|
|
137
|
+
WebSocket Session 中保持同一引用。
|
|
138
|
+
- Endpoint 处理普通 HTTP 协议路由时,传给 Service middleware 的是该 HTTP 请求的
|
|
139
|
+
`ctx.customData`,不是自动取 Session `customData`。
|
|
140
|
+
|
|
141
|
+
`customData` 只在内存中传递,不会自动发送给浏览器、写入协同数据库或记录日志。
|
|
142
|
+
|
|
143
|
+
## 在宿主应用中挂载
|
|
144
|
+
|
|
145
|
+
Express 等框架可以继续处理应用自己的产品路由,只把 Collaboration 协议路径转交给
|
|
146
|
+
`handleRequest()`。底层 HTTP server 还必须转交对应的 WebSocket `upgrade`:
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
app.use('/universer-api', (request, response) => {
|
|
150
|
+
// Express 挂载会改写 request.url,Transport 需要看到完整协议路径。
|
|
151
|
+
request.url = request.originalUrl;
|
|
152
|
+
transport.handleRequest(request, response);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
server.on('upgrade', (request, socket, head) => {
|
|
156
|
+
const url = new URL(request.url ?? '/', 'http://localhost');
|
|
157
|
+
if (url.pathname === '/universer-api/comb/connect') {
|
|
158
|
+
transport.handleUpgrade(request, socket, head);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Express 集成必须在 `express.json()` 等 body parser 消费请求流之前把协议路径交给
|
|
164
|
+
Transport。Fastify、Nest 等框架同样需要转交原始 Node request、socket 和 head。
|
|
165
|
+
|
|
166
|
+
Collaboration、History、Comment 和 Worktree Endpoint 可以按路由前缀注册在同一个
|
|
167
|
+
Transport 上,共享同一组 HTTP 认证 middleware。Endpoint 没有匹配当前请求时会
|
|
168
|
+
继续调用 `next()`;所有已注册 Endpoint 都未处理的 HTTP 请求由 Transport 返回纯文本
|
|
169
|
+
404,未处理的 WebSocket upgrade 也在建立连接前返回 HTTP 404。
|
|
170
|
+
|
|
171
|
+
## API 分层
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
transport.use(httpMiddleware);
|
|
175
|
+
transport.useUpgrade(upgradeMiddleware);
|
|
176
|
+
transport.register(endpoint);
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
- `use()`:仅普通 HTTP request;回调直接获得 `incomingMessage`、`response`、
|
|
180
|
+
`userID` 和请求级 `customData`。
|
|
181
|
+
- `useUpgrade()`:仅 WebSocket handshake;可读取 `incomingMessage`,或调用
|
|
182
|
+
`reject(status, message)`。
|
|
183
|
+
- `register()`:注册并管理协议 Endpoint,由 Endpoint 处理匹配的 HTTP 请求和 WebSocket
|
|
184
|
+
连接。
|
|
185
|
+
|
|
186
|
+
## 请求限制与资源释放
|
|
187
|
+
|
|
188
|
+
HTTP body 和 WebSocket message 限制默认都是 16 MiB。`readBody()` 和 `readJson()` 会
|
|
189
|
+
执行大小限制,并保证请求流只消费一次。
|
|
190
|
+
|
|
191
|
+
停止时从网络入口向持久化层反向释放:
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
await transport.dispose();
|
|
195
|
+
await collabService.dispose();
|
|
196
|
+
await database.dispose();
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Transport 会终止活动连接并释放它通过 `register()` 拥有的 Endpoint;`register()` 返回的
|
|
200
|
+
registration 也可提前注销并释放该 Endpoint。Endpoint 不释放 Service,Service 也不释放
|
|
201
|
+
外部注入的 Database Adapter。
|
|
202
|
+
|
|
203
|
+
完整服务端装配、middleware 和生产环境要求见
|
|
204
|
+
[协同编辑文档](https://office.univer.ai/zh-CN/collaboration/overview)。
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object['defineProperty'](exports,Symbol['toStringTag'],{'value':'Module'});let e=require('node:crypto'),t=require('node:http'),n=require('ws');async function r(_0x4add8d,_0x3f1d20){let _0x431b5b=async _0x37e2b9=>{let _0xec7a57=_0x4add8d[_0x37e2b9];if(!_0xec7a57)return;let _0x18487e=!0x1,_0x435d60,_0x3da116=()=>{if(_0x18487e)throw Error('Transport\x20middleware\x20called\x20next()\x20more\x20than\x20once');return _0x18487e=!0x0,_0x435d60=_0x431b5b(_0x37e2b9+0x1),_0x435d60;};try{await _0xec7a57(_0x3f1d20,_0x3da116);}catch(_0x3d52f6){if(_0x435d60)try{await _0x435d60;}catch{}throw _0x3d52f6;}_0x435d60&&await _0x435d60;};await _0x431b5b(0x0);}async function i(_0x4f8024,_0x506397){await r(_0x4f8024['flatMap'](_0x352ea5=>_0x352ea5['disposed']?[]:_0x352ea5['kind']==='http'?[_0x352ea5['middleware']]:_0x352ea5['kind']==='endpoint'&&_0x352ea5['endpoint']['handleHttp']?[_0x352ea5['endpoint']['handleHttp']['bind'](_0x352ea5['endpoint'])]:[]),_0x506397);}async function a(_0x31ce76,_0x32f4f0){await r(_0x31ce76['flatMap'](_0xfe8ebc=>{if(_0xfe8ebc['disposed'])return[];if(_0xfe8ebc['kind']==='upgrade'){let _0x29e27a=_0xfe8ebc['middleware'];return[(_0x6aed08,_0x5a1be0)=>_0x29e27a(_0x6aed08,_0x5a1be0)];}return _0xfe8ebc['kind']==='endpoint'&&_0xfe8ebc['endpoint']['handleUpgrade']?[_0xfe8ebc['endpoint']['handleUpgrade']['bind'](_0xfe8ebc['endpoint'])]:[];}),_0x32f4f0);}var o=0x10*0x400*0x400,s=class{['socket'];['customData'];['id']=(0x0,e['randomUUID'])();['_closed']=!0x1;constructor(_0x3398a1,_0x2dc078){this['socket']=_0x3398a1,this['customData']=_0x2dc078;}['send'](_0x365761){return this['_closed']||this['socket']['readyState']!==this['socket']['OPEN']?Promise['reject'](Error('WebSocket\x20connection\x20is\x20not\x20open')):new Promise((_0x2d877b,_0x353469)=>{this['socket']['send'](_0x365761,_0x1aae42=>{_0x1aae42?_0x353469(_0x1aae42):_0x2d877b();});});}['close'](_0x3a14e9,_0xd9596e){this['_closed']||(this['_closed']=!0x0,(this['socket']['readyState']===this['socket']['OPEN']||this['socket']['readyState']===this['socket']['CONNECTING'])&&this['socket']['close'](_0x3a14e9,_0xd9596e));}['terminate'](){this['_closed']=!0x0,this['socket']['readyState']!==this['socket']['CLOSED']&&this['socket']['terminate']();}};function c(_0x510f2f,_0x29b4b2){if(_0x510f2f===void 0x0)return _0x29b4b2;if(!Number['isSafeInteger'](_0x510f2f)||_0x510f2f<=0x0)throw TypeError('Transport\x20byte\x20limits\x20must\x20be\x20positive\x20safe\x20integers');return _0x510f2f;}function l(_0x356200,_0x11e3ea){let _0x491b48;return()=>(_0x491b48??=new Promise((_0x5647ba,_0x336978)=>{let _0x219d2e=_0x356200['headers']['content-length'];if(_0x219d2e!==void 0x0&&Number['isFinite'](Number(_0x219d2e))&&Number(_0x219d2e)>_0x11e3ea){_0x356200['resume'](),_0x336978(new m('REQUEST_BODY_TOO_LARGE','Request\x20body\x20exceeds\x20'+_0x11e3ea+'\x20bytes'));return;}let _0x587020=[],_0x17e53e=0x0,_0x437b17=!0x1,_0x2e3e96=_0x1dc6ae=>{_0x437b17||(_0x437b17=!0x0,_0x336978(_0x1dc6ae));};_0x356200['on']('data',_0x2641dc=>{if(_0x437b17)return;let _0x18c8e7=typeof _0x2641dc=='string'?Buffer['from'](_0x2641dc):_0x2641dc;if(_0x17e53e+=_0x18c8e7['byteLength'],_0x17e53e>_0x11e3ea){_0x356200['resume'](),_0x2e3e96(new m('REQUEST_BODY_TOO_LARGE','Request\x20body\x20exceeds\x20'+_0x11e3ea+'\x20bytes'));return;}_0x587020['push'](_0x18c8e7);}),_0x356200['once']('end',()=>{_0x437b17||(_0x437b17=!0x0,_0x5647ba(Buffer['concat'](_0x587020,_0x17e53e)));}),_0x356200['once']('aborted',()=>{_0x2e3e96(new m('REQUEST_ABORTED','Request\x20was\x20aborted'));}),_0x356200['once']('error',_0x9e2c1f=>{_0x2e3e96(new m('REQUEST_ABORTED','Request\x20stream\x20failed',{'cause':_0x9e2c1f}));});}),_0x491b48);}function u(_0x2c3489){return async()=>{let _0x2b8bbb=await _0x2c3489();try{return JSON['parse'](Buffer['from'](_0x2b8bbb)['toString']('utf8'));}catch(_0x4bb115){throw new m('INVALID_JSON','Request\x20body\x20is\x20not\x20valid\x20JSON',{'cause':_0x4bb115});}};}function d(_0x2dd61c){return Array['isArray'](_0x2dd61c)?Buffer['concat'](_0x2dd61c):_0x2dd61c instanceof ArrayBuffer?Buffer['from'](_0x2dd61c):Buffer['from'](_0x2dd61c['buffer'],_0x2dd61c['byteOffset'],_0x2dd61c['byteLength']);}var f=class{['_entries']=[];['_webSocketServer'];['_connections']=new Set();['_eventQueues']=new Map();['_failedConnections']=new Set();['_connectionClosed']=new Map();['_maxRequestBodyBytes'];['_disposed']=!0x1;constructor(_0x490ee5={}){this['_maxRequestBodyBytes']=c(_0x490ee5['maxRequestBodyBytes'],o);let _0x102083=c(_0x490ee5['maxWebSocketMessageBytes'],this['_maxRequestBodyBytes']);this['_webSocketServer']=new n['WebSocketServer']({'noServer':!0x0,'maxPayload':_0x102083});}['use'](_0x9b3aa6){if(this['_assertRunning'](),typeof _0x9b3aa6!='function')throw TypeError('HTTP\x20Transport\x20middleware\x20must\x20be\x20a\x20function');let _0xe4893f={'kind':'http','middleware':_0x9b3aa6,'disposed':!0x1};return this['_entries']['push'](_0xe4893f),this['_registration'](_0xe4893f);}['useUpgrade'](_0x256f8f){if(this['_assertRunning'](),typeof _0x256f8f!='function')throw TypeError('WebSocket\x20upgrade\x20middleware\x20must\x20be\x20a\x20function');let _0x278ca5={'kind':'upgrade','middleware':_0x256f8f,'disposed':!0x1};return this['_entries']['push'](_0x278ca5),this['_registration'](_0x278ca5);}['register'](_0x398d30){if(this['_assertRunning'](),!_0x398d30||typeof _0x398d30!='object')throw TypeError('Transport\x20Endpoint\x20must\x20be\x20an\x20object');let _0x404a2d={'kind':'endpoint','endpoint':_0x398d30,'disposed':!0x1};return this['_entries']['push'](_0x404a2d),this['_registration'](_0x404a2d);}['handleRequest'](_0x5a348a,_0x532bb5){if(this['_disposed']){this['_writeHttpError'](_0x532bb5,new m('TRANSPORT_DISPOSED','Transport\x20is\x20disposed'));return;}let _0xd0a2df=l(_0x5a348a,this['_maxRequestBodyBytes']),_0x46063e={'incomingMessage':_0x5a348a,'response':_0x532bb5,'customData':Object['create'](null),'readBody':_0xd0a2df,'readJson':u(_0xd0a2df)};this['_dispatchHttp'](_0x46063e)['then'](()=>{this['_writeHttpNotFound'](_0x532bb5);})['catch'](_0x28e61c=>{this['_writeHttpError'](_0x532bb5,_0x28e61c);});}['handleUpgrade'](_0x1a61fe,_0x3e2e22,_0x40b499){if(this['_disposed']){_0x3e2e22['destroy']();return;}this['_handleUpgrade'](_0x1a61fe,_0x3e2e22,_0x40b499);}async['dispose'](){if(this['_disposed'])return;this['_disposed']=!0x0;let _0x1ff2ff=[...this['_connectionClosed']['values']()]['map'](_0x23e27d=>_0x23e27d['promise']);for(let _0x4978a6 of this['_connections'])_0x4978a6['terminate']();await Promise['allSettled'](_0x1ff2ff),await Promise['allSettled']([...this['_eventQueues']['values']()]);let _0x3e9c01=[...this['_entries']]['reverse']();for(let _0x6215f of _0x3e9c01)_0x6215f['disposed']||(_0x6215f['disposed']=!0x0,_0x6215f['kind']==='endpoint'&&await _0x6215f['endpoint']['dispose']?.());this['_entries']['length']=0x0;}async['_handleUpgrade'](_0xa912a8,_0x100feb,_0x316625){let _0x249584,_0x1dd2ef=Object['create'](null),_0x39729f=_0x3e68b1=>{if(_0x249584)throw Error('WebSocket\x20upgrade\x20was\x20already\x20accepted\x20or\x20rejected');_0x249584=_0x3e68b1;},_0x169bee={'incomingMessage':_0xa912a8,'customData':_0x1dd2ef,'reject':(_0x5c1301=0x193,_0x48b40b)=>{if(!Number['isSafeInteger'](_0x5c1301)||_0x5c1301<0x190||_0x5c1301>0x257)throw TypeError('WebSocket\x20rejection\x20status\x20must\x20be\x20an\x20integer\x20from\x20400\x20to\x20599');_0x39729f({'kind':'reject','statusCode':_0x5c1301,'message':_0x48b40b??t['STATUS_CODES'][_0x5c1301]??'Upgrade\x20rejected'});},'accept':_0x3751e7=>{if(!_0x3751e7||typeof _0x3751e7!='object')throw TypeError('WebSocket\x20handler\x20must\x20be\x20an\x20object');_0x39729f({'kind':'accept','handler':_0x3751e7});}};try{if(await a([...this['_entries']],_0x169bee),this['_disposed']||_0x100feb['destroyed']){_0x100feb['destroy']();return;}if(!_0x249584){this['_writeUpgradeResponse'](_0x100feb,0x194,'Not\x20Found');return;}if(_0x249584['kind']==='reject'){this['_writeUpgradeResponse'](_0x100feb,_0x249584['statusCode'],_0x249584['message']);return;}let _0x4f8378=_0x249584['handler'];this['_webSocketServer']['handleUpgrade'](_0xa912a8,_0x100feb,_0x316625,_0x10ff1a=>{if(this['_disposed']){_0x10ff1a['terminate']();return;}this['_acceptWebSocket'](_0x10ff1a,_0x1dd2ef,_0x4f8378);});}catch{this['_writeUpgradeResponse'](_0x100feb,0x1f4,'Internal\x20Server\x20Error');}}['_acceptWebSocket'](_0x5892e1,_0x3ea6fe,_0x3ed619){let _0x16bcb7=new s(_0x5892e1,_0x3ea6fe);this['_connections']['add'](_0x16bcb7),this['_eventQueues']['set'](_0x16bcb7,Promise['resolve']());let _0x5abcae,_0xc490fb=new Promise(_0x30ba2f=>{_0x5abcae=_0x30ba2f;});this['_connectionClosed']['set'](_0x16bcb7,{'promise':_0xc490fb,'resolve':_0x5abcae}),_0x5892e1['on']('message',(_0x3f4dba,_0x50d2e3)=>{let _0x37e5da=d(_0x3f4dba),_0x4e6630=_0x50d2e3?new Uint8Array(_0x37e5da):_0x37e5da['toString']('utf8'),_0x441ea7={'connection':_0x16bcb7,'data':_0x4e6630,'customData':Object['create'](null)};this['_enqueue'](_0x16bcb7,()=>_0x3ed619['message']?.(_0x441ea7));}),_0x5892e1['once']('close',(_0x414d02,_0x27545c)=>{let _0x487b3c={'connection':_0x16bcb7,'code':_0x414d02,'reason':_0x27545c['toString']('utf8')};this['_enqueue'](_0x16bcb7,()=>_0x3ed619['close']?.(_0x487b3c),!0x0);}),_0x5892e1['on']('error',()=>{});let _0xe9855={'connection':_0x16bcb7};this['_enqueue'](_0x16bcb7,()=>_0x3ed619['open']?.(_0xe9855));}['_enqueue'](_0x17c8f9,_0x3ec1c3,_0x1120c4=!0x1){let _0x32e455=(this['_eventQueues']['get'](_0x17c8f9)??Promise['resolve']())['catch'](()=>void 0x0)['then'](async()=>{this['_failedConnections']['has'](_0x17c8f9)&&!_0x1120c4||await _0x3ec1c3();})['catch'](()=>{this['_failedConnections']['add'](_0x17c8f9),_0x17c8f9['close'](0x3f3,'WebSocket\x20handler\x20failure');})['finally'](()=>{_0x1120c4&&(this['_connections']['delete'](_0x17c8f9),this['_eventQueues']['delete'](_0x17c8f9),this['_failedConnections']['delete'](_0x17c8f9),this['_connectionClosed']['get'](_0x17c8f9)?.['resolve'](),this['_connectionClosed']['delete'](_0x17c8f9));});this['_eventQueues']['set'](_0x17c8f9,_0x32e455);}async['_dispatchHttp'](_0x111716){if(this['_disposed'])throw new m('TRANSPORT_DISPOSED','Transport\x20is\x20disposed');await i([...this['_entries']],_0x111716);}['_writeHttpError'](_0xb121bc,_0x27ccfa){if(_0xb121bc['writableEnded']||_0xb121bc['destroyed'])return;let _0x5591f7=_0x27ccfa instanceof m?_0x27ccfa['code']==='REQUEST_BODY_TOO_LARGE'?0x19d:_0x27ccfa['code']==='INVALID_JSON'||_0x27ccfa['code']==='REQUEST_ABORTED'?0x190:0x1f7:0x1f4,_0x5aa0de=_0x5591f7===0x19d?'Payload\x20Too\x20Large':_0x5591f7===0x190?'Bad\x20Request':_0x5591f7===0x1f7?'Service\x20Unavailable':'Internal\x20Server\x20Error';_0xb121bc['headersSent']?_0xb121bc['destroy'](_0x27ccfa instanceof Error?_0x27ccfa:void 0x0):(_0xb121bc['statusCode']=_0x5591f7,_0xb121bc['setHeader']('content-type','text/plain;\x20charset=utf-8'),_0xb121bc['end'](_0x5aa0de));}['_writeHttpNotFound'](_0x4e8d5a){_0x4e8d5a['headersSent']||_0x4e8d5a['writableEnded']||_0x4e8d5a['destroyed']||(_0x4e8d5a['statusCode']=0x194,_0x4e8d5a['setHeader']('content-type','text/plain;\x20charset=utf-8'),_0x4e8d5a['end']('Not\x20Found'));}['_writeUpgradeResponse'](_0x201a1f,_0x82f6ca,_0xd14815){if(_0x201a1f['destroyed'])return;let _0x3cd54b=t['STATUS_CODES'][_0x82f6ca]??'Error',_0x39ed0b=Buffer['from'](_0xd14815,'utf8');_0x201a1f['end']('HTTP/1.1\x20'+_0x82f6ca+'\x20'+_0x3cd54b+'\x0d\x0aConnection:\x20close\x0d\x0aContent-Type:\x20text/plain;\x20charset=utf-8\x0d\x0aContent-Length:\x20'+_0x39ed0b['byteLength']+'\x0d\x0a\x0d\x0a'+_0x39ed0b['toString']('utf8'));}['_registration'](_0x2a9541){return{'dispose':async()=>{_0x2a9541['disposed']||(_0x2a9541['disposed']=!0x0,_0x2a9541['kind']==='endpoint'&&await _0x2a9541['endpoint']['dispose']?.());}};}['_assertRunning'](){if(this['_disposed'])throw new m('TRANSPORT_DISPOSED','Transport\x20is\x20disposed');}};function p(_0x3a09ba){return new f(_0x3a09ba);}var m=class extends Error{['code'];constructor(_0x229933,_0x1f76db,_0x2789bd){super(_0x1f76db,_0x2789bd),this['code']=_0x229933,this['name']='NodeTransportError';}};exports['NodeTransportError']=m,exports['createNodeTransport']=p;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
+
import { Duplex } from "node:stream";
|
|
3
|
+
|
|
4
|
+
//#region src/node-transport.d.ts
|
|
5
|
+
declare function createNodeTransport(options?: NodeTransportOptions): INodeTransport;
|
|
6
|
+
//#endregion
|
|
7
|
+
//#region src/transport.d.ts
|
|
8
|
+
type TransportCustomData = Record<string, unknown>;
|
|
9
|
+
interface TransportDisposable {
|
|
10
|
+
dispose(): void | Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
type NodeTransportErrorCode = "INVALID_JSON" | "REQUEST_BODY_TOO_LARGE" | "REQUEST_ABORTED" | "TRANSPORT_DISPOSED";
|
|
13
|
+
declare class NodeTransportError extends Error {
|
|
14
|
+
readonly code: NodeTransportErrorCode;
|
|
15
|
+
constructor(code: NodeTransportErrorCode, message: string, options?: ErrorOptions);
|
|
16
|
+
}
|
|
17
|
+
interface NodeTransportConnection {
|
|
18
|
+
readonly id: string;
|
|
19
|
+
readonly customData: TransportCustomData;
|
|
20
|
+
send(data: string | Uint8Array): void | Promise<void>;
|
|
21
|
+
close(code?: number, reason?: string): void;
|
|
22
|
+
}
|
|
23
|
+
interface NodeHttpTransportContext {
|
|
24
|
+
readonly incomingMessage: IncomingMessage;
|
|
25
|
+
readonly response: ServerResponse;
|
|
26
|
+
readonly customData: TransportCustomData;
|
|
27
|
+
userID?: string;
|
|
28
|
+
readBody(): Promise<Uint8Array>;
|
|
29
|
+
readJson<T = unknown>(): Promise<T>;
|
|
30
|
+
}
|
|
31
|
+
interface NodeWebSocketUpgradeContext {
|
|
32
|
+
readonly incomingMessage: IncomingMessage;
|
|
33
|
+
readonly customData: TransportCustomData;
|
|
34
|
+
reject(statusCode?: number, message?: string): void;
|
|
35
|
+
}
|
|
36
|
+
interface NodeWebSocketEndpointContext extends NodeWebSocketUpgradeContext {
|
|
37
|
+
accept(handler: NodeWebSocketHandler): void;
|
|
38
|
+
}
|
|
39
|
+
interface NodeWebSocketOpenContext {
|
|
40
|
+
readonly connection: NodeTransportConnection;
|
|
41
|
+
}
|
|
42
|
+
interface NodeWebSocketMessageContext {
|
|
43
|
+
readonly connection: NodeTransportConnection;
|
|
44
|
+
readonly data: string | Uint8Array;
|
|
45
|
+
readonly customData: TransportCustomData;
|
|
46
|
+
}
|
|
47
|
+
interface NodeWebSocketCloseContext {
|
|
48
|
+
readonly connection: NodeTransportConnection;
|
|
49
|
+
readonly code?: number;
|
|
50
|
+
readonly reason?: string;
|
|
51
|
+
}
|
|
52
|
+
interface NodeWebSocketHandler {
|
|
53
|
+
open?(ctx: NodeWebSocketOpenContext): void | Promise<void>;
|
|
54
|
+
message?(ctx: NodeWebSocketMessageContext): void | Promise<void>;
|
|
55
|
+
close?(ctx: NodeWebSocketCloseContext): void | Promise<void>;
|
|
56
|
+
}
|
|
57
|
+
type NodeTransportNext = () => Promise<void>;
|
|
58
|
+
type NodeHttpTransportMiddleware = (ctx: NodeHttpTransportContext, next: NodeTransportNext) => void | Promise<void>;
|
|
59
|
+
type NodeWebSocketUpgradeMiddleware = (ctx: NodeWebSocketUpgradeContext, next: NodeTransportNext) => void | Promise<void>;
|
|
60
|
+
interface NodeTransportEndpoint {
|
|
61
|
+
handleHttp?(ctx: NodeHttpTransportContext, next: NodeTransportNext): void | Promise<void>;
|
|
62
|
+
handleUpgrade?(ctx: NodeWebSocketEndpointContext, next: NodeTransportNext): void | Promise<void>;
|
|
63
|
+
dispose?(): void | Promise<void>;
|
|
64
|
+
}
|
|
65
|
+
interface NodeTransportOptions {
|
|
66
|
+
readonly maxRequestBodyBytes?: number;
|
|
67
|
+
readonly maxWebSocketMessageBytes?: number;
|
|
68
|
+
}
|
|
69
|
+
interface INodeTransport {
|
|
70
|
+
use(middleware: NodeHttpTransportMiddleware): TransportDisposable;
|
|
71
|
+
useUpgrade(middleware: NodeWebSocketUpgradeMiddleware): TransportDisposable;
|
|
72
|
+
register(endpoint: NodeTransportEndpoint): TransportDisposable;
|
|
73
|
+
handleRequest(request: IncomingMessage, response: ServerResponse): void;
|
|
74
|
+
handleUpgrade(request: IncomingMessage, socket: Duplex, head: Buffer): void;
|
|
75
|
+
dispose(): Promise<void>;
|
|
76
|
+
}
|
|
77
|
+
//#endregion
|
|
78
|
+
export { type INodeTransport, type NodeHttpTransportContext, type NodeHttpTransportMiddleware, type NodeTransportConnection, type NodeTransportEndpoint, NodeTransportError, type NodeTransportErrorCode, type NodeTransportNext, type NodeTransportOptions, type NodeWebSocketCloseContext, type NodeWebSocketEndpointContext, type NodeWebSocketHandler, type NodeWebSocketMessageContext, type NodeWebSocketOpenContext, type NodeWebSocketUpgradeContext, type NodeWebSocketUpgradeMiddleware, type TransportCustomData, type TransportDisposable, createNodeTransport };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const _0x30e403=_0x20a1;(function(_0x25841d,_0x537282){const _0x5891ff=_0x20a1,_0x35878d=_0x25841d();while(!![]){try{const _0x1ee9bd=parseInt(_0x5891ff(0xbd))/0x1*(-parseInt(_0x5891ff(0xb7))/0x2)+parseInt(_0x5891ff(0xb6))/0x3*(-parseInt(_0x5891ff(0xbc))/0x4)+-parseInt(_0x5891ff(0xb3))/0x5+parseInt(_0x5891ff(0xbe))/0x6*(-parseInt(_0x5891ff(0xba))/0x7)+-parseInt(_0x5891ff(0xb5))/0x8*(parseInt(_0x5891ff(0xb4))/0x9)+parseInt(_0x5891ff(0xbb))/0xa*(-parseInt(_0x5891ff(0xb2))/0xb)+parseInt(_0x5891ff(0xb1))/0xc*(parseInt(_0x5891ff(0xb9))/0xd);if(_0x1ee9bd===_0x537282)break;else _0x35878d['push'](_0x35878d['shift']());}catch(_0xe6e2b){_0x35878d['push'](_0x35878d['shift']());}}}(_0x5664,0xe5ee7));import _0x2513cb from'./index.cjs';const facadeExport0=_0x2513cb['NodeTransportError'],facadeExport1=_0x2513cb[_0x30e403(0xb8)];function _0x20a1(_0x31ae8b,_0x148019){_0x31ae8b=_0x31ae8b-0xb1;const _0x56647=_0x5664();let _0x20a12f=_0x56647[_0x31ae8b];return _0x20a12f;}export{facadeExport0 as NodeTransportError,facadeExport1 as createNodeTransport};function _0x5664(){const _0x56e034=['10ovIEwl','18HCyYWy','12OQRLUZ','22kNSzxs','8853035bKykdX','14639472zLKoUo','8qmgvAe','3DFHiBz','138614lVlIIN','createNodeTransport','88085491ixnuCR','2339827kejjnf','2854990UPXMYL','679768wlDTjP'];_0x5664=function(){return _0x56e034;};return _0x5664();}
|
package/package.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@univerjs-pro/collaboration-transport-node",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "1.0.0-beta.2",
|
|
5
|
+
"main": "./dist/index.cjs",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.cjs"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"ws": "^8.21.1"
|
|
20
|
+
}
|
|
21
|
+
}
|