@tienne/gestalt 0.108.0 → 0.110.0

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.
Files changed (113) hide show
  1. package/CLAUDE.md +1 -0
  2. package/dist/package.json +1 -1
  3. package/dist/src/cli/commands/peer.d.ts +49 -0
  4. package/dist/src/cli/commands/peer.d.ts.map +1 -0
  5. package/dist/src/cli/commands/peer.js +302 -0
  6. package/dist/src/cli/commands/peer.js.map +1 -0
  7. package/dist/src/cli/index.d.ts.map +1 -1
  8. package/dist/src/cli/index.js +81 -2
  9. package/dist/src/cli/index.js.map +1 -1
  10. package/dist/src/peer/client.d.ts +9 -0
  11. package/dist/src/peer/client.d.ts.map +1 -0
  12. package/dist/src/peer/client.js +51 -0
  13. package/dist/src/peer/client.js.map +1 -0
  14. package/dist/src/peer/daemon.d.ts +83 -0
  15. package/dist/src/peer/daemon.d.ts.map +1 -0
  16. package/dist/src/peer/daemon.js +516 -0
  17. package/dist/src/peer/daemon.js.map +1 -0
  18. package/dist/src/peer/errors.d.ts +2 -0
  19. package/dist/src/peer/errors.d.ts.map +1 -0
  20. package/dist/src/peer/errors.js +4 -0
  21. package/dist/src/peer/errors.js.map +1 -0
  22. package/dist/src/peer/feed.d.ts +103 -0
  23. package/dist/src/peer/feed.d.ts.map +1 -0
  24. package/dist/src/peer/feed.js +167 -0
  25. package/dist/src/peer/feed.js.map +1 -0
  26. package/dist/src/peer/index.d.ts +24 -0
  27. package/dist/src/peer/index.d.ts.map +1 -0
  28. package/dist/src/peer/index.js +14 -0
  29. package/dist/src/peer/index.js.map +1 -0
  30. package/dist/src/peer/keys.d.ts +26 -0
  31. package/dist/src/peer/keys.d.ts.map +1 -0
  32. package/dist/src/peer/keys.js +95 -0
  33. package/dist/src/peer/keys.js.map +1 -0
  34. package/dist/src/peer/locate.d.ts +44 -0
  35. package/dist/src/peer/locate.d.ts.map +1 -0
  36. package/dist/src/peer/locate.js +116 -0
  37. package/dist/src/peer/locate.js.map +1 -0
  38. package/dist/src/peer/lock.d.ts +32 -0
  39. package/dist/src/peer/lock.d.ts.map +1 -0
  40. package/dist/src/peer/lock.js +147 -0
  41. package/dist/src/peer/lock.js.map +1 -0
  42. package/dist/src/peer/mirror.d.ts +21 -0
  43. package/dist/src/peer/mirror.d.ts.map +1 -0
  44. package/dist/src/peer/mirror.js +68 -0
  45. package/dist/src/peer/mirror.js.map +1 -0
  46. package/dist/src/peer/network.d.ts +148 -0
  47. package/dist/src/peer/network.d.ts.map +1 -0
  48. package/dist/src/peer/network.js +809 -0
  49. package/dist/src/peer/network.js.map +1 -0
  50. package/dist/src/peer/paths.d.ts +23 -0
  51. package/dist/src/peer/paths.d.ts.map +1 -0
  52. package/dist/src/peer/paths.js +50 -0
  53. package/dist/src/peer/paths.js.map +1 -0
  54. package/dist/src/peer/protocol.d.ts +112 -0
  55. package/dist/src/peer/protocol.d.ts.map +1 -0
  56. package/dist/src/peer/protocol.js +8 -0
  57. package/dist/src/peer/protocol.js.map +1 -0
  58. package/dist/src/peer/redact.d.ts +30 -0
  59. package/dist/src/peer/redact.d.ts.map +1 -0
  60. package/dist/src/peer/redact.js +214 -0
  61. package/dist/src/peer/redact.js.map +1 -0
  62. package/dist/src/peer/relay.d.ts +70 -0
  63. package/dist/src/peer/relay.d.ts.map +1 -0
  64. package/dist/src/peer/relay.js +410 -0
  65. package/dist/src/peer/relay.js.map +1 -0
  66. package/dist/src/peer/roster.d.ts +89 -0
  67. package/dist/src/peer/roster.d.ts.map +1 -0
  68. package/dist/src/peer/roster.js +160 -0
  69. package/dist/src/peer/roster.js.map +1 -0
  70. package/dist/src/peer/seal.d.ts +37 -0
  71. package/dist/src/peer/seal.d.ts.map +1 -0
  72. package/dist/src/peer/seal.js +115 -0
  73. package/dist/src/peer/seal.js.map +1 -0
  74. package/dist/src/peer/share.d.ts +41 -0
  75. package/dist/src/peer/share.d.ts.map +1 -0
  76. package/dist/src/peer/share.js +70 -0
  77. package/dist/src/peer/share.js.map +1 -0
  78. package/dist/src/peer/state.d.ts +68 -0
  79. package/dist/src/peer/state.d.ts.map +1 -0
  80. package/dist/src/peer/state.js +161 -0
  81. package/dist/src/peer/state.js.map +1 -0
  82. package/dist/src/peer/tail.d.ts +55 -0
  83. package/dist/src/peer/tail.d.ts.map +1 -0
  84. package/dist/src/peer/tail.js +148 -0
  85. package/dist/src/peer/tail.js.map +1 -0
  86. package/dist/src/peer/transform.d.ts +79 -0
  87. package/dist/src/peer/transform.d.ts.map +1 -0
  88. package/dist/src/peer/transform.js +237 -0
  89. package/dist/src/peer/transform.js.map +1 -0
  90. package/dist/src/peer/transport.d.ts +89 -0
  91. package/dist/src/peer/transport.d.ts.map +1 -0
  92. package/dist/src/peer/transport.js +345 -0
  93. package/dist/src/peer/transport.js.map +1 -0
  94. package/dist/src/peer/viewer-html.d.ts +10 -0
  95. package/dist/src/peer/viewer-html.d.ts.map +1 -0
  96. package/dist/src/peer/viewer-html.js +165 -0
  97. package/dist/src/peer/viewer-html.js.map +1 -0
  98. package/dist/src/peer/viewer.d.ts +58 -0
  99. package/dist/src/peer/viewer.d.ts.map +1 -0
  100. package/dist/src/peer/viewer.js +249 -0
  101. package/dist/src/peer/viewer.js.map +1 -0
  102. package/dist/src/peer/wire.d.ts +90 -0
  103. package/dist/src/peer/wire.d.ts.map +1 -0
  104. package/dist/src/peer/wire.js +10 -0
  105. package/dist/src/peer/wire.js.map +1 -0
  106. package/dist/src/peer/ws.d.ts +47 -0
  107. package/dist/src/peer/ws.d.ts.map +1 -0
  108. package/dist/src/peer/ws.js +308 -0
  109. package/dist/src/peer/ws.js.map +1 -0
  110. package/package.json +1 -1
  111. package/plugin/.codex-plugin/plugin.json +1 -1
  112. package/plugin/.mcp.json +1 -1
  113. package/plugin/mcp.json +1 -1
@@ -0,0 +1,249 @@
1
+ import { randomBytes, timingSafeEqual } from 'node:crypto';
2
+ import { createServer } from 'node:http';
3
+ import { renderViewerHtml } from './viewer-html.js';
4
+ import { errMessage } from './errors.js';
5
+ const PAGE_PATH = /^\/s\/([A-Za-z0-9_-]{1,64})\/?$/;
6
+ const EVENTS_PATH = /^\/s\/([A-Za-z0-9_-]{1,64})\/events$/;
7
+ /** SSE 연결이 프록시나 OS에서 놀다 끊기지 않게 보내는 빈 주석 간격 */
8
+ const KEEPALIVE_MS = 15_000;
9
+ /**
10
+ * 공유 하나에 동시에 붙는 뷰어 상한. 넘으면 429. 뷰어 하나가 붙잡는 메모리는 소켓 버퍼로
11
+ * 묶여 있지만 연결마다 소켓과 타이머가 생기니 토큰을 아는 쪽이 끝없이 열지 못하게 한다.
12
+ * 사람이 보는 숫자로는 넉넉하다.
13
+ */
14
+ export const MAX_VIEWERS_PER_SHARE = 16;
15
+ /**
16
+ * 실시간 프레임을 쓰기 전에 소켓 버퍼에 이만큼 넘게 밀려 있으면 그 뷰어를 끊는다. 안 읽는
17
+ * 뷰어 하나가 데몬 메모리를 끝없이 붙잡지 못하게 한다. 이보다 큰 이벤트 하나는 들어가지만
18
+ * 소켓으로 다 빠지기 전에 다음 프레임이 오면 그때 끊는다. 앞부분은 소켓이 비는 만큼만 꺼내
19
+ * 보내서 여기에 안 쌓인다. 브라우저는 다시 붙어 Last-Event-ID부터 받는다.
20
+ */
21
+ export const MAX_LIVE_BACKLOG_BYTES = 8 * 1024 * 1024;
22
+ /** 앞부분을 공유에서 한 번에 꺼내는 양. 소켓이 차면 그 자리에서 멈추므로 대략이면 된다 */
23
+ export const REPLAY_CHUNK_BYTES = 256 * 1024;
24
+ /**
25
+ * 로컬 뷰어 HTTP 서버. `127.0.0.1`에만 붙는다.
26
+ *
27
+ * 같은 머신의 다른 프로세스나 브라우저에 열린 남의 웹페이지도 이 포트에 닿을 수 있다.
28
+ * 그래서 공유마다 토큰을 주소에 붙이고 없거나 틀리면 401이다. 모르는 공유 id도 401로
29
+ * 돌려준다 — 404와 401을 나누면 토큰 없이 공유 id가 살아 있는지 떠볼 수 있다.
30
+ *
31
+ * Routes:
32
+ * GET /s/:id?t=TOKEN → 뷰어 HTML
33
+ * GET /s/:id/events?t=TOKEN → SSE (line, trimmed, stats, state, closed). 뷰어 상한을 넘으면 429.
34
+ * closed의 missed는 따라잡는 중에 공유가 종료돼 못 보낸 이벤트 수
35
+ * * → 404
36
+ */
37
+ export class ViewerServer {
38
+ hooks;
39
+ server = null;
40
+ open = new Set();
41
+ viewers = new Map();
42
+ maxLiveBacklogBytes;
43
+ constructor(hooks, options = {}) {
44
+ this.hooks = hooks;
45
+ this.maxLiveBacklogBytes = options.maxLiveBacklogBytes ?? MAX_LIVE_BACKLOG_BYTES;
46
+ }
47
+ start(port = 0) {
48
+ return new Promise((resolve, reject) => {
49
+ this.server = createServer((req, res) => {
50
+ // 핸들러가 던지면 uncaughtException으로 데몬이 죽는다. 요청 대상이 `//[`처럼 URL로
51
+ // 안 읽히면 new URL이 던지는데 이 포트에 닿는 누구나 그런 요청을 보낼 수 있다
52
+ try {
53
+ this.handle(req, res);
54
+ }
55
+ catch (err) {
56
+ process.stderr.write(`[peer-viewer] request failed: ${errMessage(err)}\n`);
57
+ if (!res.headersSent)
58
+ res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });
59
+ res.end('Internal Server Error');
60
+ }
61
+ });
62
+ this.server.once('error', reject);
63
+ this.server.listen(port, '127.0.0.1', () => {
64
+ this.server.off('error', reject);
65
+ this.server.on('error', (err) => {
66
+ process.stderr.write(`[peer-viewer] server error: ${err.message}\n`);
67
+ });
68
+ resolve(this.port);
69
+ });
70
+ });
71
+ }
72
+ get port() {
73
+ const addr = this.server?.address();
74
+ return addr && typeof addr === 'object' ? addr.port : undefined;
75
+ }
76
+ stop() {
77
+ for (const res of this.open)
78
+ res.end();
79
+ this.open.clear();
80
+ return new Promise((resolve) => {
81
+ if (!this.server) {
82
+ resolve();
83
+ return;
84
+ }
85
+ this.server.closeAllConnections?.();
86
+ this.server.close(() => {
87
+ this.server = null;
88
+ resolve();
89
+ });
90
+ });
91
+ }
92
+ handle(req, res) {
93
+ if (req.method !== 'GET')
94
+ return plain(res, 405, 'Method Not Allowed');
95
+ // DNS 리바인딩으로 남의 도메인이 이 포트를 가리키게 해도 Host가 달라서 막힌다
96
+ if (!this.hostAllowed(req.headers.host))
97
+ return plain(res, 403, 'Forbidden');
98
+ const url = new URL(req.url ?? '/', 'http://127.0.0.1');
99
+ const page = PAGE_PATH.exec(url.pathname);
100
+ const events = page ? null : EVENTS_PATH.exec(url.pathname);
101
+ const id = page?.[1] ?? events?.[1];
102
+ if (!id)
103
+ return plain(res, 404, 'Not Found');
104
+ const share = this.hooks.lookup(id);
105
+ if (!share || !tokenMatches(url.searchParams.get('t'), share.token)) {
106
+ return plain(res, 401, 'Unauthorized');
107
+ }
108
+ if (page)
109
+ return this.page(res);
110
+ if ((this.viewers.get(id) ?? 0) >= MAX_VIEWERS_PER_SHARE) {
111
+ return plain(res, 429, 'Too Many Viewers');
112
+ }
113
+ return this.stream(req, res, share.stream);
114
+ }
115
+ hostAllowed(host) {
116
+ if (!host)
117
+ return false;
118
+ const port = this.port;
119
+ return host === `127.0.0.1:${port}` || host === `localhost:${port}`;
120
+ }
121
+ page(res) {
122
+ const nonce = randomBytes(16).toString('base64');
123
+ res.writeHead(200, {
124
+ 'Content-Type': 'text/html; charset=utf-8',
125
+ 'Content-Security-Policy': [
126
+ "default-src 'none'",
127
+ `script-src 'nonce-${nonce}'`,
128
+ `style-src 'nonce-${nonce}'`,
129
+ "connect-src 'self'",
130
+ "base-uri 'none'",
131
+ "form-action 'none'",
132
+ "frame-ancestors 'none'",
133
+ ].join('; '),
134
+ ...PRIVATE_HEADERS,
135
+ });
136
+ res.end(renderViewerHtml(nonce, MAX_VIEWERS_PER_SHARE));
137
+ }
138
+ stream(req, res, stream) {
139
+ const lastId = Number.parseInt(String(req.headers['last-event-id'] ?? '0'), 10);
140
+ res.writeHead(200, {
141
+ 'Content-Type': 'text/event-stream; charset=utf-8',
142
+ Connection: 'keep-alive',
143
+ ...PRIVATE_HEADERS,
144
+ });
145
+ res.write(': connected\n\n');
146
+ this.open.add(res);
147
+ this.viewers.set(stream.id, (this.viewers.get(stream.id) ?? 0) + 1);
148
+ this.hooks.onConnect?.(stream.id);
149
+ const keepalive = setInterval(() => res.write(': ping\n\n'), KEEPALIVE_MS);
150
+ keepalive.unref();
151
+ let detached = false;
152
+ let unsubscribe = () => { };
153
+ const detach = () => {
154
+ if (detached)
155
+ return;
156
+ detached = true;
157
+ clearInterval(keepalive);
158
+ unsubscribe();
159
+ this.open.delete(res);
160
+ const left = (this.viewers.get(stream.id) ?? 1) - 1;
161
+ if (left > 0)
162
+ this.viewers.set(stream.id, left);
163
+ else
164
+ this.viewers.delete(stream.id);
165
+ this.hooks.onDisconnect?.(stream.id);
166
+ };
167
+ req.on('close', detach);
168
+ res.on('close', detach);
169
+ const onFrame = (frame) => {
170
+ if (detached)
171
+ return;
172
+ if (res.writableLength > this.maxLiveBacklogBytes) {
173
+ res.destroy();
174
+ detach();
175
+ return;
176
+ }
177
+ res.write(sseFrame(frame));
178
+ if (frame.type === 'closed') {
179
+ res.end();
180
+ detach();
181
+ }
182
+ };
183
+ // 앞부분은 소켓이 비는 만큼씩 꺼내 쓴다. 다 따라잡으면 이벤트 루프로 돌아가지 않고 바로
184
+ // subscribe로 붙어서 그 사이에 poll이 끼지 않는다. subscribe가 상태와 통계를 다시 보내
185
+ // 따라잡는 동안 바뀐 것도 맞춘다. 기다리는 사이 공유가 종료되면 subscribe가 못 보낸 개수를
186
+ // closed에 실어 보낸다
187
+ let cursor = Number.isFinite(lastId) && lastId > 0 ? lastId : 0;
188
+ const pump = () => {
189
+ if (detached)
190
+ return;
191
+ while (!stream.isClosed) {
192
+ const { dropped, items } = stream.backlogAfter(cursor, REPLAY_CHUNK_BYTES);
193
+ if (dropped > 0) {
194
+ res.write(sseFrame({ type: 'trimmed', dropped }));
195
+ cursor += dropped;
196
+ }
197
+ if (items.length === 0)
198
+ break;
199
+ for (const item of items) {
200
+ cursor = item.seq;
201
+ if (!res.write(sseFrame({ type: 'event', ...item }))) {
202
+ res.once('drain', pump);
203
+ return;
204
+ }
205
+ }
206
+ }
207
+ unsubscribe = stream.subscribe(onFrame, cursor);
208
+ if (detached)
209
+ unsubscribe();
210
+ };
211
+ res.write(sseFrame({ type: 'state', status: stream.currentStatus }));
212
+ res.write(sseFrame({ type: 'stats', unknownLines: stream.unknownLines }));
213
+ pump();
214
+ }
215
+ }
216
+ /**
217
+ * 뷰어는 디스크에 아무것도 남기지 않아야 한다. 브라우저 캐시도 디스크다.
218
+ */
219
+ const PRIVATE_HEADERS = {
220
+ 'Cache-Control': 'no-store',
221
+ 'Referrer-Policy': 'no-referrer',
222
+ 'X-Content-Type-Options': 'nosniff',
223
+ };
224
+ function plain(res, status, body) {
225
+ res.writeHead(status, { 'Content-Type': 'text/plain; charset=utf-8', ...PRIVATE_HEADERS });
226
+ res.end(body);
227
+ }
228
+ function tokenMatches(given, expected) {
229
+ if (!given)
230
+ return false;
231
+ const a = Buffer.from(given);
232
+ const b = Buffer.from(expected);
233
+ return a.length === b.length && timingSafeEqual(a, b);
234
+ }
235
+ function sseFrame(frame) {
236
+ switch (frame.type) {
237
+ case 'event':
238
+ return `id: ${frame.seq}\nevent: line\ndata: ${frame.json}\n\n`;
239
+ case 'stats':
240
+ return `event: stats\ndata: ${JSON.stringify({ unknownLines: frame.unknownLines })}\n\n`;
241
+ case 'state':
242
+ return `event: state\ndata: ${JSON.stringify({ status: frame.status })}\n\n`;
243
+ case 'closed':
244
+ return `event: closed\ndata: ${JSON.stringify({ reason: frame.reason, missed: frame.missed })}\n\n`;
245
+ case 'trimmed':
246
+ return `event: trimmed\ndata: ${JSON.stringify({ dropped: frame.dropped })}\n\n`;
247
+ }
248
+ }
249
+ //# sourceMappingURL=viewer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viewer.js","sourceRoot":"","sources":["../../../src/peer/viewer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,90 @@
1
+ import type { SignedRoster } from './roster.js';
2
+ /**
3
+ * 데몬과 릴레이 사이 프레임. 한 WebSocket 글자 메시지가 JSON 하나다.
4
+ *
5
+ * 릴레이가 보는 건 보낸 기기, 받는 기기, 공유 id, 크기, 시각이다. `data`는 데몬끼리
6
+ * 봉인한 바이트를 base64url로 쓴 것이라 릴레이는 못 연다.
7
+ */
8
+ export type HelloMode = 'member' | 'join';
9
+ export type ClientFrame = {
10
+ t: 'hello';
11
+ team: string;
12
+ key: string;
13
+ mode: HelloMode;
14
+ sig: string;
15
+ roster?: SignedRoster;
16
+ }
17
+ /** ack가 false면 릴레이가 답을 안 보낸다. 공유 스트림 프레임처럼 자주 가는 것에 쓴다 */
18
+ | {
19
+ t: 'send';
20
+ id: number;
21
+ to: string;
22
+ data: string;
23
+ ack?: false;
24
+ } | {
25
+ t: 'resolve';
26
+ id: number;
27
+ share: string;
28
+ }
29
+ /** 이 기기가 지금 연 공유 id 전부. 받을 때마다 통째로 바꾼다 */
30
+ | {
31
+ t: 'announce';
32
+ shares: string[];
33
+ } | {
34
+ t: 'roster';
35
+ roster: SignedRoster;
36
+ }
37
+ /** join 모드 전용. 초대한 관리자에게 봉인한 요청 */
38
+ | {
39
+ t: 'join';
40
+ to: string;
41
+ data: string;
42
+ };
43
+ export type ServerFrame = {
44
+ t: 'challenge';
45
+ nonce: string;
46
+ } | {
47
+ t: 'welcome';
48
+ roster: SignedRoster;
49
+ online: string[];
50
+ }
51
+ /** join 모드로 붙어 명단에 오르기를 기다리는 중 */
52
+ | {
53
+ t: 'pending';
54
+ } | {
55
+ t: 'error';
56
+ code: RelayErrorCode;
57
+ message: string;
58
+ } | {
59
+ t: 'recv';
60
+ from: string;
61
+ data: string;
62
+ } | {
63
+ t: 'ack';
64
+ id: number;
65
+ ok: boolean;
66
+ error?: string;
67
+ } | {
68
+ t: 'resolved';
69
+ id: number;
70
+ owner: string | null;
71
+ } | {
72
+ t: 'roster';
73
+ roster: SignedRoster;
74
+ } | {
75
+ t: 'presence';
76
+ key: string;
77
+ online: boolean;
78
+ } | {
79
+ t: 'join';
80
+ from: string;
81
+ data: string;
82
+ };
83
+ export type RelayErrorCode = 'bad_frame' | 'bad_signature' | 'wrong_team' | 'not_member' | 'no_roster' | 'rate_limited';
84
+ /** 명단에서 빠진 기기를 끊을 때 쓰는 WebSocket close 코드 */
85
+ export declare const CLOSE_REMOVED = 4003;
86
+ /** 핸드셰이크 실패 */
87
+ export declare const CLOSE_REJECTED = 4001;
88
+ /** hello에 서명할 글자. 릴레이가 준 nonce를 넣어 다른 접속의 서명을 다시 못 쓰게 한다 */
89
+ export declare function helloSigned(team: string, nonce: string, mode: HelloMode): string;
90
+ //# sourceMappingURL=wire.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wire.d.ts","sourceRoot":"","sources":["../../../src/peer/wire.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD;;;;;GAKG;AAEH,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE1C,MAAM,MAAM,WAAW,GACnB;IAAE,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,SAAS,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,YAAY,CAAA;CAAE;AAChG,0DAA0D;GACxD;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,KAAK,CAAA;CAAE,GAChE;IAAE,CAAC,EAAE,SAAS,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE;AAC7C,0CAA0C;GACxC;IAAE,CAAC,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,GACnC;IAAE,CAAC,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,YAAY,CAAA;CAAE;AACvC,mCAAmC;GACjC;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5C,MAAM,MAAM,WAAW,GACnB;IAAE,CAAC,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,CAAC,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE;AAC1D,kCAAkC;GAChC;IAAE,CAAC,EAAE,SAAS,CAAA;CAAE,GAChB;IAAE,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACrD;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACzC;IAAE,CAAC,EAAE,KAAK,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GACrD;IAAE,CAAC,EAAE,UAAU,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GACnD;IAAE,CAAC,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,YAAY,CAAA;CAAE,GACrC;IAAE,CAAC,EAAE,UAAU,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,GAC/C;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9C,MAAM,MAAM,cAAc,GACtB,WAAW,GACX,eAAe,GACf,YAAY,GACZ,YAAY,GACZ,WAAW,GACX,cAAc,CAAC;AAEnB,6CAA6C;AAC7C,eAAO,MAAM,aAAa,OAAO,CAAC;AAClC,eAAe;AACf,eAAO,MAAM,cAAc,OAAO,CAAC;AAEnC,4DAA4D;AAC5D,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,MAAM,CAEhF"}
@@ -0,0 +1,10 @@
1
+ import { canonical } from './seal.js';
2
+ /** 명단에서 빠진 기기를 끊을 때 쓰는 WebSocket close 코드 */
3
+ export const CLOSE_REMOVED = 4003;
4
+ /** 핸드셰이크 실패 */
5
+ export const CLOSE_REJECTED = 4001;
6
+ /** hello에 서명할 글자. 릴레이가 준 nonce를 넣어 다른 접속의 서명을 다시 못 쓰게 한다 */
7
+ export function helloSigned(team, nonce, mode) {
8
+ return `gestalt-peer-relay-hello-v1\n${canonical({ team, nonce, mode })}`;
9
+ }
10
+ //# sourceMappingURL=wire.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wire.js","sourceRoot":"","sources":["../../../src/peer/wire.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AA2CtC,6CAA6C;AAC7C,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,CAAC;AAClC,eAAe;AACf,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,CAAC;AAEnC,4DAA4D;AAC5D,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,KAAa,EAAE,IAAe;IACtE,OAAO,gCAAgC,SAAS,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;AAC5E,CAAC"}
@@ -0,0 +1,47 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import type { IncomingMessage } from 'node:http';
3
+ import type { Duplex } from 'node:stream';
4
+ /** 메시지 하나의 상한. 넘으면 1009로 닫는다 */
5
+ export declare const DEFAULT_MAX_MESSAGE_BYTES: number;
6
+ export interface WsOptions {
7
+ /** 클라이언트는 보내는 프레임을 가리고(mask) 서버는 가린 프레임만 받는다 */
8
+ client: boolean;
9
+ maxMessageBytes?: number;
10
+ }
11
+ /**
12
+ * 연결 하나. 이벤트는 `message`(글자), `close`(코드, 이유), `pong`이다.
13
+ * `error`는 내보내지 않는다 — 소켓 오류는 1006 close로 바꿔 한 길로만 끝나게 한다.
14
+ */
15
+ export declare class WsConnection extends EventEmitter {
16
+ private readonly socket;
17
+ private readonly options;
18
+ private buf;
19
+ private fragments;
20
+ private fragmentBytes;
21
+ private closed;
22
+ private closeSent;
23
+ private readonly maxMessageBytes;
24
+ constructor(socket: Duplex, options: WsOptions, head?: Buffer);
25
+ /** 소켓 버퍼에 밀려 있는 바이트 */
26
+ get bufferedAmount(): number;
27
+ get isOpen(): boolean;
28
+ send(text: string): boolean;
29
+ ping(): void;
30
+ close(code?: number, reason?: string): void;
31
+ /** 상대에게 알리지 않고 끊는다. 안 읽는 상대를 떼어낼 때 쓴다 */
32
+ terminate(code?: number, reason?: string): void;
33
+ private finish;
34
+ private writeFrame;
35
+ private onData;
36
+ private readFrame;
37
+ private onFrame;
38
+ }
39
+ /**
40
+ * http(s) 서버의 `upgrade` 이벤트에서 부른다. 핸드셰이크가 틀리면 400으로 닫고 null.
41
+ */
42
+ export declare function acceptUpgrade(req: IncomingMessage, socket: Duplex, head: Buffer, options?: Omit<WsOptions, 'client'>): WsConnection | null;
43
+ /** `ws://` 또는 `wss://`로 붙는다 */
44
+ export declare function connectWs(url: string, options?: Omit<WsOptions, 'client'> & {
45
+ timeoutMs?: number;
46
+ }): Promise<WsConnection>;
47
+ //# sourceMappingURL=ws.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ws.d.ts","sourceRoot":"","sources":["../../../src/peer/ws.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAEjD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAY1C,gCAAgC;AAChC,eAAO,MAAM,yBAAyB,QAAkB,CAAC;AASzD,MAAM,WAAW,SAAS;IACxB,gDAAgD;IAChD,MAAM,EAAE,OAAO,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;GAGG;AACH,qBAAa,YAAa,SAAQ,YAAY;IAS1C,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAT1B,OAAO,CAAC,GAAG,CAA2B;IACtC,OAAO,CAAC,SAAS,CAAgB;IACjC,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;gBAGtB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,SAAS,EACnC,IAAI,CAAC,EAAE,MAAM;IAgBf,uBAAuB;IACvB,IAAI,cAAc,IAAI,MAAM,CAE3B;IAED,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAK3B,IAAI,IAAI,IAAI;IAIZ,KAAK,CAAC,IAAI,SAAO,EAAE,MAAM,SAAK,GAAG,IAAI;IAiBrC,yCAAyC;IACzC,SAAS,CAAC,IAAI,SAAO,EAAE,MAAM,SAAK,GAAG,IAAI;IAKzC,OAAO,CAAC,MAAM;IAQd,OAAO,CAAC,UAAU;IAuBlB,OAAO,CAAC,MAAM;IAUd,OAAO,CAAC,SAAS;IA8CjB,OAAO,CAAC,OAAO;CAiDhB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,eAAe,EACpB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAM,GACtC,YAAY,GAAG,IAAI,CAoBrB;AAED,+BAA+B;AAC/B,wBAAgB,SAAS,CACvB,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,GAAG;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAC/D,OAAO,CAAC,YAAY,CAAC,CA2CvB"}