@deepfates/lync 0.3.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.
- package/LICENSE +21 -0
- package/README.md +374 -0
- package/bin/lync.js +2 -0
- package/dist/cli/bin.d.ts +3 -0
- package/dist/cli/bin.d.ts.map +1 -0
- package/dist/cli/bin.js +5 -0
- package/dist/cli/bin.js.map +1 -0
- package/dist/cli/index.d.ts +11 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +404 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/sync.d.ts +42 -0
- package/dist/cli/sync.d.ts.map +1 -0
- package/dist/cli/sync.js +232 -0
- package/dist/cli/sync.js.map +1 -0
- package/dist/client/create.d.ts +10 -0
- package/dist/client/create.d.ts.map +1 -0
- package/dist/client/create.js +50 -0
- package/dist/client/create.js.map +1 -0
- package/dist/client/index.d.ts +3 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +3 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/testing.d.ts +14 -0
- package/dist/client/testing.d.ts.map +1 -0
- package/dist/client/testing.js +19 -0
- package/dist/client/testing.js.map +1 -0
- package/dist/client/types.d.ts +49 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/client/types.js +2 -0
- package/dist/client/types.js.map +1 -0
- package/dist/errors.d.ts +16 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +39 -0
- package/dist/errors.js.map +1 -0
- package/dist/events.d.ts +119 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +686 -0
- package/dist/events.js.map +1 -0
- package/dist/file-log.d.ts +25 -0
- package/dist/file-log.d.ts.map +1 -0
- package/dist/file-log.js +97 -0
- package/dist/file-log.js.map +1 -0
- package/dist/idb-log.d.ts +21 -0
- package/dist/idb-log.d.ts.map +1 -0
- package/dist/idb-log.js +114 -0
- package/dist/idb-log.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/indexes/entries.d.ts +6 -0
- package/dist/indexes/entries.d.ts.map +1 -0
- package/dist/indexes/entries.js +7 -0
- package/dist/indexes/entries.js.map +1 -0
- package/dist/indexes/index.d.ts +3 -0
- package/dist/indexes/index.d.ts.map +1 -0
- package/dist/indexes/index.js +3 -0
- package/dist/indexes/index.js.map +1 -0
- package/dist/indexes/memory.d.ts +4 -0
- package/dist/indexes/memory.d.ts.map +1 -0
- package/dist/indexes/memory.js +193 -0
- package/dist/indexes/memory.js.map +1 -0
- package/dist/indexes/types.d.ts +70 -0
- package/dist/indexes/types.d.ts.map +1 -0
- package/dist/indexes/types.js +2 -0
- package/dist/indexes/types.js.map +1 -0
- package/dist/json.d.ts +3 -0
- package/dist/json.d.ts.map +1 -0
- package/dist/json.js +15 -0
- package/dist/json.js.map +1 -0
- package/dist/looms.d.ts +25 -0
- package/dist/looms.d.ts.map +1 -0
- package/dist/looms.js +359 -0
- package/dist/looms.js.map +1 -0
- package/dist/memory-log.d.ts +5 -0
- package/dist/memory-log.d.ts.map +1 -0
- package/dist/memory-log.js +7 -0
- package/dist/memory-log.js.map +1 -0
- package/dist/memory.d.ts +4 -0
- package/dist/memory.d.ts.map +1 -0
- package/dist/memory.js +255 -0
- package/dist/memory.js.map +1 -0
- package/dist/profiles/text-story.d.ts +26 -0
- package/dist/profiles/text-story.d.ts.map +1 -0
- package/dist/profiles/text-story.js +55 -0
- package/dist/profiles/text-story.js.map +1 -0
- package/dist/references.d.ts +14 -0
- package/dist/references.d.ts.map +1 -0
- package/dist/references.js +109 -0
- package/dist/references.js.map +1 -0
- package/dist/relay/attach.d.ts +24 -0
- package/dist/relay/attach.d.ts.map +1 -0
- package/dist/relay/attach.js +54 -0
- package/dist/relay/attach.js.map +1 -0
- package/dist/relay/index.d.ts +4 -0
- package/dist/relay/index.d.ts.map +1 -0
- package/dist/relay/index.js +4 -0
- package/dist/relay/index.js.map +1 -0
- package/dist/relay/relay.d.ts +80 -0
- package/dist/relay/relay.d.ts.map +1 -0
- package/dist/relay/relay.js +329 -0
- package/dist/relay/relay.js.map +1 -0
- package/dist/relay/serve.d.ts +18 -0
- package/dist/relay/serve.d.ts.map +1 -0
- package/dist/relay/serve.js +37 -0
- package/dist/relay/serve.js.map +1 -0
- package/dist/sha256.d.ts +2 -0
- package/dist/sha256.d.ts.map +1 -0
- package/dist/sha256.js +84 -0
- package/dist/sha256.js.map +1 -0
- package/dist/store.d.ts +97 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +250 -0
- package/dist/store.js.map +1 -0
- package/dist/sync-protocol.d.ts +80 -0
- package/dist/sync-protocol.d.ts.map +1 -0
- package/dist/sync-protocol.js +135 -0
- package/dist/sync-protocol.js.map +1 -0
- package/dist/synced-store.d.ts +68 -0
- package/dist/synced-store.d.ts.map +1 -0
- package/dist/synced-store.js +340 -0
- package/dist/synced-store.js.map +1 -0
- package/dist/types.d.ts +78 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/uuid.d.ts +9 -0
- package/dist/uuid.d.ts.map +1 -0
- package/dist/uuid.js +25 -0
- package/dist/uuid.js.map +1 -0
- package/dist/views.d.ts +78 -0
- package/dist/views.d.ts.map +1 -0
- package/dist/views.js +192 -0
- package/dist/views.js.map +1 -0
- package/package.json +183 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { IncomingMessage } from "node:http";
|
|
2
|
+
import type { Duplex } from "node:stream";
|
|
3
|
+
/**
|
|
4
|
+
* The lync line-sync relay — a dumb event-union relay, mountable on any Node
|
|
5
|
+
* HTTP server or run standalone.
|
|
6
|
+
*
|
|
7
|
+
* One append-only `<root>.lync` file per root. `seq` is the per-root count of
|
|
8
|
+
* stored lines: a resume cursor, nothing more. The relay never parses a line
|
|
9
|
+
* beyond extracting its id. Accepted events fan out to every subscriber of the
|
|
10
|
+
* root, sender included — echoes are duplicate no-ops under union. Same-id,
|
|
11
|
+
* different-body is never resolved: both sides keep their bytes, the variant
|
|
12
|
+
* goes to a `<root>.conflicts` sidecar, and an err frame goes to everyone.
|
|
13
|
+
* Presence is relayed, never stored. Nothing fails invisibly.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* The members of a `ws` WebSocket the relay actually touches, as a structural
|
|
17
|
+
* type. `ws` ships no type declarations, so the relay's public surface must
|
|
18
|
+
* never name its types: a consumer without `ws` installed still typechecks.
|
|
19
|
+
*/
|
|
20
|
+
export interface LyncRelaySocket {
|
|
21
|
+
readyState: number;
|
|
22
|
+
readonly OPEN: number;
|
|
23
|
+
send(data: string): void;
|
|
24
|
+
ping(): void;
|
|
25
|
+
terminate(): void;
|
|
26
|
+
on(event: "message", listener: (data: {
|
|
27
|
+
toString(): string;
|
|
28
|
+
}) => void): this;
|
|
29
|
+
on(event: "close", listener: () => void): this;
|
|
30
|
+
on(event: "error", listener: (error: unknown) => void): this;
|
|
31
|
+
}
|
|
32
|
+
export interface LyncRelayOptions {
|
|
33
|
+
/** Directory of per-root append-only files. Created if absent. */
|
|
34
|
+
dir: string;
|
|
35
|
+
/** If set, upgrades require `Authorization: Bearer <token>`. */
|
|
36
|
+
token?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Per-upgrade authorization. Return false to reject. Runs after the token
|
|
39
|
+
* check (if any). Use for cookie/session auth on an embedded relay.
|
|
40
|
+
*/
|
|
41
|
+
authenticate?: (request: IncomingMessage) => boolean | Promise<boolean>;
|
|
42
|
+
/** Called with each wired socket; for connection counting and keepalive. */
|
|
43
|
+
onConnection?: (socket: LyncRelaySocket) => void;
|
|
44
|
+
log?: (message: string) => void;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* A read-only snapshot of one live room, as `status()` reports it. Every field
|
|
48
|
+
* is a copy of existing room state — reading it mutates nothing.
|
|
49
|
+
*/
|
|
50
|
+
export interface LyncRoomStatus {
|
|
51
|
+
/** The room's root name (its `<root>.lync` file). */
|
|
52
|
+
root: string;
|
|
53
|
+
/** Log generation: fresh per recovery, carried on ev/live frames. */
|
|
54
|
+
generation: string;
|
|
55
|
+
/** Per-root arrival counter: the count of accepted lines, resume cursor. */
|
|
56
|
+
seq: number;
|
|
57
|
+
/** Live socket count subscribed to this room right now. */
|
|
58
|
+
subscribers: number;
|
|
59
|
+
/**
|
|
60
|
+
* Lines accepted into memory and broadcast but NOT yet on disk (a prior
|
|
61
|
+
* append failed) — the durability lag. 0 when the log is fully persisted.
|
|
62
|
+
*/
|
|
63
|
+
pendingUnpersisted: number;
|
|
64
|
+
}
|
|
65
|
+
export interface LyncRelay {
|
|
66
|
+
/** Handle an HTTP upgrade: authorize, upgrade, and wire the socket. */
|
|
67
|
+
handleUpgrade(request: IncomingMessage, socket: Duplex, head: Buffer): void;
|
|
68
|
+
/** Wire a socket you upgraded yourself. */
|
|
69
|
+
handleConnection(socket: LyncRelaySocket): void;
|
|
70
|
+
/**
|
|
71
|
+
* A read-only snapshot of every live room — its seq, live subscriber count,
|
|
72
|
+
* and durability lag (pending-unpersisted lines). Strictly observational:
|
|
73
|
+
* it reads existing room state and mutates nothing.
|
|
74
|
+
*/
|
|
75
|
+
status(): LyncRoomStatus[];
|
|
76
|
+
/** Close all sockets and flush every pending append. */
|
|
77
|
+
close(): Promise<void>;
|
|
78
|
+
}
|
|
79
|
+
export declare function createLyncRelay(options: LyncRelayOptions): LyncRelay;
|
|
80
|
+
//# sourceMappingURL=relay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relay.d.ts","sourceRoot":"","sources":["../../src/relay/relay.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAG1C;;;;;;;;;;;GAWG;AAEH;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,IAAI,IAAI,IAAI,CAAC;IACb,SAAS,IAAI,IAAI,CAAC;IAClB,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE;QAAE,QAAQ,IAAI,MAAM,CAAA;KAAE,KAAK,IAAI,GAAG,IAAI,CAAC;IAC7E,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IAC/C,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI,CAAC;CAC9D;AA4BD,MAAM,WAAW,gBAAgB;IAC/B,kEAAkE;IAClE,GAAG,EAAE,MAAM,CAAC;IACZ,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACxE,4EAA4E;IAC5E,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;IACjD,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,qDAAqD;IACrD,IAAI,EAAE,MAAM,CAAC;IACb,qEAAqE;IACrE,UAAU,EAAE,MAAM,CAAC;IACnB,4EAA4E;IAC5E,GAAG,EAAE,MAAM,CAAC;IACZ,2DAA2D;IAC3D,WAAW,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,SAAS;IACxB,uEAAuE;IACvE,aAAa,CAAC,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5E,2CAA2C;IAC3C,gBAAgB,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,CAAC;IAChD;;;;OAIG;IACH,MAAM,IAAI,cAAc,EAAE,CAAC;IAC3B,wDAAwD;IACxD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AA8BD,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,SAAS,CAiTpE"}
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
import { appendFile, mkdir, readFile } from "node:fs/promises";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
4
|
+
import { createRequire } from "node:module";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { decodeFrame, encodeFrame, extractLineId } from "../sync-protocol.js";
|
|
7
|
+
/**
|
|
8
|
+
* `ws` is acquired lazily, so the package truthfully has zero dependencies:
|
|
9
|
+
* only running a relay needs it, and the factory stays synchronous. Bundler
|
|
10
|
+
* note: this dynamic require is opaque to esbuild/webpack — mark "ws" as
|
|
11
|
+
* external when bundling a server that runs the relay.
|
|
12
|
+
*/
|
|
13
|
+
function acquireWebSocketServer() {
|
|
14
|
+
try {
|
|
15
|
+
const require = createRequire(import.meta.url);
|
|
16
|
+
const ws = require("ws");
|
|
17
|
+
return ws.WebSocketServer;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
throw new Error("running a relay requires ws: npm install ws");
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const ROOT_NAME = /^[A-Za-z0-9._-]+$/;
|
|
24
|
+
export function createLyncRelay(options) {
|
|
25
|
+
const log = options.log ?? ((message) => process.stderr.write(`${message}\n`));
|
|
26
|
+
const dirReady = mkdir(options.dir, { recursive: true }).then(() => undefined);
|
|
27
|
+
const rooms = new Map();
|
|
28
|
+
// Resolved rooms, for the read-only status() surface only. A room lands here
|
|
29
|
+
// once recovery settles; never read on the write, sync, union, or close path.
|
|
30
|
+
const ready = new Map();
|
|
31
|
+
const sockets = new Set();
|
|
32
|
+
const WebSocketServer = acquireWebSocketServer();
|
|
33
|
+
const wss = new WebSocketServer({ noServer: true });
|
|
34
|
+
function handleUpgrade(request, socket, head) {
|
|
35
|
+
if (options.token && request.headers.authorization !== `Bearer ${options.token}`) {
|
|
36
|
+
reject(socket, "bad or missing token");
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (!options.authenticate) {
|
|
40
|
+
wss.handleUpgrade(request, socket, head, (ws) => handleConnection(ws));
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
Promise.resolve(options.authenticate(request)).then((ok) => {
|
|
44
|
+
if (ok)
|
|
45
|
+
wss.handleUpgrade(request, socket, head, (ws) => handleConnection(ws));
|
|
46
|
+
else
|
|
47
|
+
reject(socket, "authenticate() returned false");
|
|
48
|
+
}, (error) => reject(socket, `authenticate() threw: ${String(error)}`));
|
|
49
|
+
}
|
|
50
|
+
function reject(socket, why) {
|
|
51
|
+
log(`[lync relay] rejected upgrade: ${why}`);
|
|
52
|
+
socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n");
|
|
53
|
+
socket.destroy();
|
|
54
|
+
}
|
|
55
|
+
function handleConnection(socket) {
|
|
56
|
+
sockets.add(socket);
|
|
57
|
+
options.onConnection?.(socket);
|
|
58
|
+
const subscribed = new Set();
|
|
59
|
+
// Frames from one socket are handled strictly in arrival order, so a
|
|
60
|
+
// client that pushes then subscribes sees any errors before its backlog.
|
|
61
|
+
let frameChain = Promise.resolve();
|
|
62
|
+
socket.on("message", (raw) => {
|
|
63
|
+
const frame = decodeFrame(raw.toString());
|
|
64
|
+
frameChain = frameChain.then(() => handleFrame(socket, subscribed, frame));
|
|
65
|
+
});
|
|
66
|
+
socket.on("close", () => {
|
|
67
|
+
sockets.delete(socket);
|
|
68
|
+
void detach(socket, subscribed);
|
|
69
|
+
});
|
|
70
|
+
socket.on("error", (error) => log(`[lync relay] socket error: ${String(error)}`));
|
|
71
|
+
}
|
|
72
|
+
async function handleFrame(socket, subscribed, frame) {
|
|
73
|
+
try {
|
|
74
|
+
switch (frame.t) {
|
|
75
|
+
case "err":
|
|
76
|
+
send(socket, frame.reason === "malformed-frame" || frame.reason === "unknown-frame-kind" ? frame : { t: "err", reason: "client-error-received", detail: frame.reason });
|
|
77
|
+
return;
|
|
78
|
+
case "sub": {
|
|
79
|
+
const room = await openRoom(frame.root);
|
|
80
|
+
room.subscribers.add(socket);
|
|
81
|
+
subscribed.add(room.root);
|
|
82
|
+
if (room.recoveryNote) {
|
|
83
|
+
send(socket, { t: "err", root: room.root, reason: "recovered-damaged-tail", detail: room.recoveryNote });
|
|
84
|
+
}
|
|
85
|
+
for (let index = frame.since; index < room.lines.length; index += 1) {
|
|
86
|
+
send(socket, { t: "ev", root: room.root, seq: index + 1, line: room.lines[index], gen: room.generation });
|
|
87
|
+
}
|
|
88
|
+
send(socket, { t: "live", root: room.root, seq: room.seq, gen: room.generation });
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
case "ev": {
|
|
92
|
+
const room = await openRoom(frame.root);
|
|
93
|
+
const id = extractLineId(frame.line);
|
|
94
|
+
if (id === undefined) {
|
|
95
|
+
send(socket, { t: "err", root: room.root, reason: "line-without-id", detail: truncate(frame.line) });
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const existing = room.byId.get(id);
|
|
99
|
+
if (existing !== undefined) {
|
|
100
|
+
if (existing === frame.line) {
|
|
101
|
+
// Duplicate under union — normally a pure no-op. But if this line
|
|
102
|
+
// is still not on disk (a prior append failed), the re-push is our
|
|
103
|
+
// chance to heal without a restart: retry it (and any earlier
|
|
104
|
+
// pending line, in order). Nothing fails invisibly — a still-dead
|
|
105
|
+
// disk re-surfaces persist-failed.
|
|
106
|
+
if (room.unpersisted.has(id)) {
|
|
107
|
+
const { failedId } = await persistPending(room);
|
|
108
|
+
if (failedId !== undefined) {
|
|
109
|
+
broadcast(room, { t: "err", root: room.root, reason: "persist-failed", detail: failedId });
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const kept = await appendSerialized(room, join(options.dir, `${room.root}.conflicts`), frame.line);
|
|
115
|
+
broadcast(room, { t: "err", root: room.root, reason: "same-id-conflict", detail: id }, socket);
|
|
116
|
+
send(socket, { t: "err", root: room.root, reason: "same-id-conflict", detail: id });
|
|
117
|
+
if (!kept.ok) {
|
|
118
|
+
// The variant bytes were NOT retained — clients must not believe
|
|
119
|
+
// the sidecar promise was kept. Loud, to everyone, exactly once.
|
|
120
|
+
broadcast(room, { t: "err", root: room.root, reason: "conflict-persist-failed", detail: id }, socket);
|
|
121
|
+
send(socket, { t: "err", root: room.root, reason: "conflict-persist-failed", detail: id });
|
|
122
|
+
}
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
room.byId.set(id, frame.line);
|
|
126
|
+
room.lines.push(frame.line);
|
|
127
|
+
room.seq += 1;
|
|
128
|
+
const seq = room.seq;
|
|
129
|
+
// Before writing this line, first drain any earlier lines that failed
|
|
130
|
+
// to persist — the on-disk log converges here, in order, with no
|
|
131
|
+
// restart. If an earlier line is still unwritable the disk write for
|
|
132
|
+
// this one is deferred too (it must not jump ahead), and this line
|
|
133
|
+
// joins the pending set to be retried on the next activity or re-push.
|
|
134
|
+
const { failedId } = await persistPending(room, { id, line: frame.line });
|
|
135
|
+
// Live delivery is the relay's primary job: fan out even if the disk
|
|
136
|
+
// write failed. A durability failure is surfaced loudly, never hidden.
|
|
137
|
+
broadcast(room, { t: "ev", root: room.root, seq, line: frame.line, gen: room.generation });
|
|
138
|
+
if (failedId !== undefined) {
|
|
139
|
+
broadcast(room, { t: "err", root: room.root, reason: "persist-failed", detail: failedId });
|
|
140
|
+
}
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
case "presence": {
|
|
144
|
+
const room = await openRoom(frame.root);
|
|
145
|
+
broadcast(room, frame, socket);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
case "live":
|
|
149
|
+
send(socket, { t: "err", root: frame.root, reason: "unexpected-live-from-client" });
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
log(`[lync relay] frame handling failed: ${String(error)}`);
|
|
155
|
+
send(socket, { t: "err", reason: "server-error", detail: String(error) });
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function openRoom(root) {
|
|
159
|
+
if (!ROOT_NAME.test(root)) {
|
|
160
|
+
return Promise.reject(new Error(`invalid root name: ${truncate(root)}`));
|
|
161
|
+
}
|
|
162
|
+
let pending = rooms.get(root);
|
|
163
|
+
if (!pending) {
|
|
164
|
+
pending = dirReady.then(() => recoverRoom(root));
|
|
165
|
+
rooms.set(root, pending);
|
|
166
|
+
// Record the resolved room for status() to read. Purely observational:
|
|
167
|
+
// a recovery failure is left to the caller's rejection, never masked.
|
|
168
|
+
void pending.then((room) => ready.set(root, room), () => { });
|
|
169
|
+
}
|
|
170
|
+
return pending;
|
|
171
|
+
}
|
|
172
|
+
async function recoverRoom(root) {
|
|
173
|
+
const room = { root, generation: randomUUID(), seq: 0, lines: [], byId: new Map(), unpersisted: new Map(), subscribers: new Set(), writeChain: Promise.resolve() };
|
|
174
|
+
const path = join(options.dir, `${root}.lync`);
|
|
175
|
+
if (!existsSync(path))
|
|
176
|
+
return room;
|
|
177
|
+
const text = await readFile(path, "utf8");
|
|
178
|
+
const endsClean = text.length === 0 || text.endsWith("\n");
|
|
179
|
+
const lines = text.split("\n");
|
|
180
|
+
if (lines.at(-1) === "")
|
|
181
|
+
lines.pop();
|
|
182
|
+
if (!endsClean && lines.length > 0) {
|
|
183
|
+
const tail = lines.at(-1) ?? "";
|
|
184
|
+
room.recoveryNote = `sealed truncated final line (${tail.length} bytes) as damaged`;
|
|
185
|
+
log(`[lync relay] ${root}: ${room.recoveryNote}`);
|
|
186
|
+
await appendFile(path, "\n");
|
|
187
|
+
}
|
|
188
|
+
for (const line of lines) {
|
|
189
|
+
const id = extractLineId(line);
|
|
190
|
+
// A damaged or sealed-truncated line stays on disk (never eaten) but is
|
|
191
|
+
// not replayed to subscribers — it isn't a real event.
|
|
192
|
+
if (id === undefined)
|
|
193
|
+
continue;
|
|
194
|
+
room.lines.push(line);
|
|
195
|
+
room.seq += 1;
|
|
196
|
+
if (!room.byId.has(id))
|
|
197
|
+
room.byId.set(id, line);
|
|
198
|
+
}
|
|
199
|
+
return room;
|
|
200
|
+
}
|
|
201
|
+
// Serialize appends per room. A write failure must never wedge the room or
|
|
202
|
+
// vanish silently: clear any prior rejection so the next write still runs,
|
|
203
|
+
// keep the chain resolved so the room recovers, and report ok/failure to the
|
|
204
|
+
// caller so a persistence error can be surfaced loudly.
|
|
205
|
+
function appendSerialized(room, path, line) {
|
|
206
|
+
const attempt = room.writeChain
|
|
207
|
+
.catch(() => undefined)
|
|
208
|
+
.then(() => appendFile(path, `${line}\n`))
|
|
209
|
+
.then(() => ({ ok: true }), (error) => {
|
|
210
|
+
log(`[lync relay] persist failed for ${path}: ${String(error)}`);
|
|
211
|
+
return { ok: false };
|
|
212
|
+
});
|
|
213
|
+
room.writeChain = attempt.then(() => undefined);
|
|
214
|
+
return attempt;
|
|
215
|
+
}
|
|
216
|
+
// Converge the room's on-disk log with no restart. As ONE serialized unit
|
|
217
|
+
// on the room's writeChain, write every currently-unpersisted line in append
|
|
218
|
+
// order, then `tail` (a freshly accepted line, if any). Each line that lands
|
|
219
|
+
// clears from `unpersisted`; the first failure stops the run and leaves that
|
|
220
|
+
// line and every LATER one pending, in order — a later line is never written
|
|
221
|
+
// ahead of an earlier one for the same room. Lines already on disk are never
|
|
222
|
+
// re-written (only the pending set and the new tail are touched), and each
|
|
223
|
+
// append writes one whole `line\n`, so a partial failure never corrupts the
|
|
224
|
+
// file. Returns the id of the first line that still could not persist, or
|
|
225
|
+
// undefined if everything (including tail) reached disk.
|
|
226
|
+
function persistPending(room, tail) {
|
|
227
|
+
const path = join(options.dir, `${room.root}.lync`);
|
|
228
|
+
const attempt = room.writeChain.catch(() => undefined).then(async () => {
|
|
229
|
+
const queue = [...room.unpersisted];
|
|
230
|
+
if (tail)
|
|
231
|
+
queue.push([tail.id, tail.line]);
|
|
232
|
+
for (let index = 0; index < queue.length; index += 1) {
|
|
233
|
+
const [id, line] = queue[index];
|
|
234
|
+
try {
|
|
235
|
+
await appendFile(path, `${line}\n`);
|
|
236
|
+
room.unpersisted.delete(id);
|
|
237
|
+
}
|
|
238
|
+
catch (error) {
|
|
239
|
+
log(`[lync relay] persist failed for ${path}: ${String(error)}`);
|
|
240
|
+
// This line and every later one stay pending, in append order, so
|
|
241
|
+
// the next activity retries from here without reordering the log.
|
|
242
|
+
for (let rest = index; rest < queue.length; rest += 1) {
|
|
243
|
+
const [pendingId, pendingLine] = queue[rest];
|
|
244
|
+
if (!room.unpersisted.has(pendingId))
|
|
245
|
+
room.unpersisted.set(pendingId, pendingLine);
|
|
246
|
+
}
|
|
247
|
+
return { failedId: id };
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return {};
|
|
251
|
+
});
|
|
252
|
+
room.writeChain = attempt.then(() => undefined, () => undefined);
|
|
253
|
+
return attempt;
|
|
254
|
+
}
|
|
255
|
+
function broadcast(room, frame, except) {
|
|
256
|
+
const encoded = encodeFrame(frame);
|
|
257
|
+
for (const subscriber of room.subscribers) {
|
|
258
|
+
if (subscriber === except)
|
|
259
|
+
continue;
|
|
260
|
+
if (subscriber.readyState === subscriber.OPEN)
|
|
261
|
+
subscriber.send(encoded);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
function send(socket, frame) {
|
|
265
|
+
if (socket.readyState === socket.OPEN)
|
|
266
|
+
socket.send(encodeFrame(frame));
|
|
267
|
+
}
|
|
268
|
+
async function detach(socket, subscribed) {
|
|
269
|
+
for (const root of subscribed) {
|
|
270
|
+
const room = await rooms.get(root);
|
|
271
|
+
room?.subscribers.delete(socket);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
// A fresh array of plain records built from current room state — no handle
|
|
275
|
+
// into the room's mutable maps escapes, so a caller can never disturb it.
|
|
276
|
+
function status() {
|
|
277
|
+
return [...ready.values()].map((room) => ({
|
|
278
|
+
root: room.root,
|
|
279
|
+
generation: room.generation,
|
|
280
|
+
seq: room.seq,
|
|
281
|
+
subscribers: room.subscribers.size,
|
|
282
|
+
pendingUnpersisted: room.unpersisted.size,
|
|
283
|
+
}));
|
|
284
|
+
}
|
|
285
|
+
return {
|
|
286
|
+
handleUpgrade,
|
|
287
|
+
handleConnection,
|
|
288
|
+
status,
|
|
289
|
+
close: async () => {
|
|
290
|
+
// Flush every pending append first — writeChains are kept resolved (never
|
|
291
|
+
// rejected) by appendSerialized, so this settles promptly and no accepted
|
|
292
|
+
// write is dropped. This honors the durability promise in the interface.
|
|
293
|
+
for (const pending of rooms.values()) {
|
|
294
|
+
try {
|
|
295
|
+
const room = await pending;
|
|
296
|
+
await room.writeChain;
|
|
297
|
+
}
|
|
298
|
+
catch {
|
|
299
|
+
// A room that never recovered has no pending writes worth waiting on.
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
// Then tear down sockets and the server under a hard cap: on some ws
|
|
303
|
+
// builds (notably bun) socket teardown and wss.close() can block
|
|
304
|
+
// indefinitely, so shutdown must never hang.
|
|
305
|
+
for (const socket of sockets) {
|
|
306
|
+
try {
|
|
307
|
+
socket.terminate();
|
|
308
|
+
}
|
|
309
|
+
catch {
|
|
310
|
+
// Already gone.
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
await Promise.race([
|
|
314
|
+
new Promise((resolve) => {
|
|
315
|
+
const timer = setTimeout(resolve, 300);
|
|
316
|
+
wss.close(() => {
|
|
317
|
+
clearTimeout(timer);
|
|
318
|
+
resolve();
|
|
319
|
+
});
|
|
320
|
+
}),
|
|
321
|
+
new Promise((resolve) => setTimeout(resolve, 1500)),
|
|
322
|
+
]);
|
|
323
|
+
},
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
function truncate(text) {
|
|
327
|
+
return text.length > 80 ? `${text.slice(0, 77)}...` : text;
|
|
328
|
+
}
|
|
329
|
+
//# sourceMappingURL=relay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relay.js","sourceRoot":"","sources":["../../src/relay/relay.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAkB,MAAM,qBAAqB,CAAC;AAyC9F;;;;;GAKG;AACH,SAAS,sBAAsB;IAC7B,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC/C,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAkF,CAAC;QAC1G,OAAO,EAAE,CAAC,eAAe,CAAC;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;IACjE,CAAC;AACH,CAAC;AA8ED,MAAM,SAAS,GAAG,mBAAmB,CAAC;AAEtC,MAAM,UAAU,eAAe,CAAC,OAAyB;IACvD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,OAAe,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,IAAI,CAAC,CAAC,CAAC;IACvF,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IAC/E,MAAM,KAAK,GAAG,IAAI,GAAG,EAAyB,CAAC;IAC/C,6EAA6E;IAC7E,8EAA8E;IAC9E,MAAM,KAAK,GAAG,IAAI,GAAG,EAAgB,CAAC;IACtC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAmB,CAAC;IAC3C,MAAM,eAAe,GAAG,sBAAsB,EAAE,CAAC;IACjD,MAAM,GAAG,GAAG,IAAI,eAAe,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IAEpD,SAAS,aAAa,CAAC,OAAwB,EAAE,MAAc,EAAE,IAAY;QAC3E,IAAI,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,OAAO,CAAC,aAAa,KAAK,UAAU,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;YACjF,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;YACvC,OAAO;QACT,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC;YAC1B,GAAG,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC,CAAC;YACvE,OAAO;QACT,CAAC;QACD,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CACjD,CAAC,EAAE,EAAE,EAAE;YACL,IAAI,EAAE;gBAAE,GAAG,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC,CAAC;;gBAC1E,MAAM,CAAC,MAAM,EAAE,+BAA+B,CAAC,CAAC;QACvD,CAAC,EACD,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,yBAAyB,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CACpE,CAAC;IACJ,CAAC;IAED,SAAS,MAAM,CAAC,MAAc,EAAE,GAAW;QACzC,GAAG,CAAC,kCAAkC,GAAG,EAAE,CAAC,CAAC;QAC7C,MAAM,CAAC,KAAK,CAAC,mCAAmC,CAAC,CAAC;QAClD,MAAM,CAAC,OAAO,EAAE,CAAC;IACnB,CAAC;IAED,SAAS,gBAAgB,CAAC,MAAuB;QAC/C,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACpB,OAAO,CAAC,YAAY,EAAE,CAAC,MAAM,CAAC,CAAC;QAC/B,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;QACrC,qEAAqE;QACrE,yEAAyE;QACzE,IAAI,UAAU,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;QACnC,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,EAAE;YAC3B,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;YAC1C,UAAU,GAAG,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,CAAC,CAAC,CAAC;QAC7E,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACtB,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACvB,KAAK,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,CAAC,8BAA8B,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;IACpF,CAAC;IAED,KAAK,UAAU,WAAW,CAAC,MAAuB,EAAE,UAAuB,EAAE,KAAgB;QAC3F,IAAI,CAAC;YACH,QAAQ,KAAK,CAAC,CAAC,EAAE,CAAC;gBAChB,KAAK,KAAK;oBACR,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,KAAK,iBAAiB,IAAI,KAAK,CAAC,MAAM,KAAK,oBAAoB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,uBAAuB,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;oBACxK,OAAO;gBACT,KAAK,KAAK,CAAC,CAAC,CAAC;oBACX,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;oBACxC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;oBAC7B,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;oBAC1B,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;wBACtB,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,wBAAwB,EAAE,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC;oBAC3G,CAAC;oBACD,KAAK,IAAI,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;wBACpE,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;oBAC5G,CAAC;oBACD,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;oBAClF,OAAO;gBACT,CAAC;gBACD,KAAK,IAAI,CAAC,CAAC,CAAC;oBACV,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;oBACxC,MAAM,EAAE,GAAG,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;oBACrC,IAAI,EAAE,KAAK,SAAS,EAAE,CAAC;wBACrB,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;wBACrG,OAAO;oBACT,CAAC;oBACD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;oBACnC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;wBAC3B,IAAI,QAAQ,KAAK,KAAK,CAAC,IAAI,EAAE,CAAC;4BAC5B,kEAAkE;4BAClE,mEAAmE;4BACnE,8DAA8D;4BAC9D,kEAAkE;4BAClE,mCAAmC;4BACnC,IAAI,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;gCAC7B,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC,CAAC;gCAChD,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;oCAC3B,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;gCAC7F,CAAC;4BACH,CAAC;4BACD,OAAO;wBACT,CAAC;wBACD,MAAM,IAAI,GAAG,MAAM,gBAAgB,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,IAAI,YAAY,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;wBACnG,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC;wBAC/F,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC;wBACpF,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;4BACb,iEAAiE;4BACjE,iEAAiE;4BACjE,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,yBAAyB,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC;4BACtG,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,yBAAyB,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC;wBAC7F,CAAC;wBACD,OAAO;oBACT,CAAC;oBACD,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;oBAC9B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;oBAC5B,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;oBACd,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC;oBACrB,sEAAsE;oBACtE,iEAAiE;oBACjE,qEAAqE;oBACrE,mEAAmE;oBACnE,uEAAuE;oBACvE,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,cAAc,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;oBAC1E,qEAAqE;oBACrE,uEAAuE;oBACvE,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;oBAC3F,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;wBAC3B,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;oBAC7F,CAAC;oBACD,OAAO;gBACT,CAAC;gBACD,KAAK,UAAU,CAAC,CAAC,CAAC;oBAChB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;oBACxC,SAAS,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;oBAC/B,OAAO;gBACT,CAAC;gBACD,KAAK,MAAM;oBACT,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,6BAA6B,EAAE,CAAC,CAAC;oBACpF,OAAO;YACX,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,CAAC,uCAAuC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YAC5D,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC5E,CAAC;IACH,CAAC;IAED,SAAS,QAAQ,CAAC,IAAY;QAC5B,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1B,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,sBAAsB,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;QAC3E,CAAC;QACD,IAAI,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC9B,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;YACjD,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YACzB,uEAAuE;YACvE,sEAAsE;YACtE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAC/D,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,KAAK,UAAU,WAAW,CAAC,IAAY;QACrC,MAAM,IAAI,GAAS,EAAE,IAAI,EAAE,UAAU,EAAE,UAAU,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,GAAG,EAAE,EAAE,WAAW,EAAE,IAAI,GAAG,EAAE,EAAE,WAAW,EAAE,IAAI,GAAG,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;QACzK,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,IAAI,OAAO,CAAC,CAAC;QAC/C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;QACnC,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC1C,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC3D,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC/B,IAAI,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE;YAAE,KAAK,CAAC,GAAG,EAAE,CAAC;QACrC,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACnC,MAAM,IAAI,GAAG,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAChC,IAAI,CAAC,YAAY,GAAG,gCAAgC,IAAI,CAAC,MAAM,oBAAoB,CAAC;YACpF,GAAG,CAAC,gBAAgB,IAAI,KAAK,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC;YAClD,MAAM,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC/B,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,EAAE,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;YAC/B,wEAAwE;YACxE,uDAAuD;YACvD,IAAI,EAAE,KAAK,SAAS;gBAAE,SAAS;YAC/B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACtB,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;YACd,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QAClD,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,2EAA2E;IAC3E,2EAA2E;IAC3E,6EAA6E;IAC7E,wDAAwD;IACxD,SAAS,gBAAgB,CAAC,IAAU,EAAE,IAAY,EAAE,IAAY;QAC9D,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU;aAC5B,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC;aACtB,IAAI,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC;aACzC,IAAI,CACH,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,EACpB,CAAC,KAAK,EAAE,EAAE;YACR,GAAG,CAAC,mCAAmC,IAAI,KAAK,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACjE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC;QACvB,CAAC,CACF,CAAC;QACJ,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAChD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,0EAA0E;IAC1E,6EAA6E;IAC7E,6EAA6E;IAC7E,6EAA6E;IAC7E,6EAA6E;IAC7E,6EAA6E;IAC7E,2EAA2E;IAC3E,4EAA4E;IAC5E,0EAA0E;IAC1E,yDAAyD;IACzD,SAAS,cAAc,CAAC,IAAU,EAAE,IAAmC;QACrE,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,IAAI,OAAO,CAAC,CAAC;QACpD,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,KAAK,IAAoC,EAAE;YACrG,MAAM,KAAK,GAA4B,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC;YAC7D,IAAI,IAAI;gBAAE,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;YAC3C,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;gBACrD,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;gBAChC,IAAI,CAAC;oBACH,MAAM,UAAU,CAAC,IAAI,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC;oBACpC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBAC9B,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,GAAG,CAAC,mCAAmC,IAAI,KAAK,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;oBACjE,kEAAkE;oBAClE,kEAAkE;oBAClE,KAAK,IAAI,IAAI,GAAG,KAAK,EAAE,IAAI,GAAG,KAAK,CAAC,MAAM,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC;wBACtD,MAAM,CAAC,SAAS,EAAE,WAAW,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;wBAC7C,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC;4BAAE,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;oBACrF,CAAC;oBACD,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;gBAC1B,CAAC;YACH,CAAC;YACD,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QACjE,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,SAAS,SAAS,CAAC,IAAU,EAAE,KAAgB,EAAE,MAAwB;QACvE,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;QACnC,KAAK,MAAM,UAAU,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YAC1C,IAAI,UAAU,KAAK,MAAM;gBAAE,SAAS;YACpC,IAAI,UAAU,CAAC,UAAU,KAAK,UAAU,CAAC,IAAI;gBAAE,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC1E,CAAC;IACH,CAAC;IAED,SAAS,IAAI,CAAC,MAAuB,EAAE,KAAgB;QACrD,IAAI,MAAM,CAAC,UAAU,KAAK,MAAM,CAAC,IAAI;YAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC;IACzE,CAAC;IAED,KAAK,UAAU,MAAM,CAAC,MAAuB,EAAE,UAAuB;QACpE,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;YAC9B,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACnC,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;IAED,2EAA2E;IAC3E,0EAA0E;IAC1E,SAAS,MAAM;QACb,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACxC,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,WAAW,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI;YAClC,kBAAkB,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI;SAC1C,CAAC,CAAC,CAAC;IACN,CAAC;IAED,OAAO;QACL,aAAa;QACb,gBAAgB;QAChB,MAAM;QACN,KAAK,EAAE,KAAK,IAAI,EAAE;YAChB,0EAA0E;YAC1E,0EAA0E;YAC1E,yEAAyE;YACzE,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;gBACrC,IAAI,CAAC;oBACH,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC;oBAC3B,MAAM,IAAI,CAAC,UAAU,CAAC;gBACxB,CAAC;gBAAC,MAAM,CAAC;oBACP,sEAAsE;gBACxE,CAAC;YACH,CAAC;YACD,qEAAqE;YACrE,iEAAiE;YACjE,6CAA6C;YAC7C,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;gBAC7B,IAAI,CAAC;oBACH,MAAM,CAAC,SAAS,EAAE,CAAC;gBACrB,CAAC;gBAAC,MAAM,CAAC;oBACP,gBAAgB;gBAClB,CAAC;YACH,CAAC;YACD,MAAM,OAAO,CAAC,IAAI,CAAC;gBACjB,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;oBAC5B,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;oBACvC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE;wBACb,YAAY,CAAC,KAAK,CAAC,CAAC;wBACpB,OAAO,EAAE,CAAC;oBACZ,CAAC,CAAC,CAAC;gBACL,CAAC,CAAC;gBACF,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;aAC1D,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY;IAC5B,OAAO,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7D,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type LyncRelayOptions, type LyncRoomStatus } from "./relay.js";
|
|
2
|
+
/**
|
|
3
|
+
* Run the relay standalone on its own HTTP server. For embedding in an
|
|
4
|
+
* existing server, use `createLyncRelay` and call `handleUpgrade` from your
|
|
5
|
+
* own `upgrade` listener.
|
|
6
|
+
*/
|
|
7
|
+
export interface LyncServeOptions extends LyncRelayOptions {
|
|
8
|
+
/** Port to listen on. 0 (default) picks a free port. */
|
|
9
|
+
port?: number;
|
|
10
|
+
}
|
|
11
|
+
export interface LyncSyncServer {
|
|
12
|
+
port: number;
|
|
13
|
+
/** Read-only snapshot of the relay's live rooms. See `LyncRelay.status`. */
|
|
14
|
+
status: () => LyncRoomStatus[];
|
|
15
|
+
close: () => Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
export declare function startLyncServe(options: LyncServeOptions): Promise<LyncSyncServer>;
|
|
18
|
+
//# sourceMappingURL=serve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/relay/serve.ts"],"names":[],"mappings":"AACA,OAAO,EAAmB,KAAK,gBAAgB,EAAE,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AAEzF;;;;GAIG;AAEH,MAAM,WAAW,gBAAiB,SAAQ,gBAAgB;IACxD,wDAAwD;IACxD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,MAAM,EAAE,MAAM,cAAc,EAAE,CAAC;IAC/B,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAED,wBAAsB,cAAc,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,cAAc,CAAC,CAmCvF"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { createServer } from "node:http";
|
|
2
|
+
import { createLyncRelay } from "./relay.js";
|
|
3
|
+
export async function startLyncServe(options) {
|
|
4
|
+
const relay = createLyncRelay(options);
|
|
5
|
+
const httpServer = createServer((_request, response) => {
|
|
6
|
+
response.writeHead(404).end();
|
|
7
|
+
});
|
|
8
|
+
httpServer.on("upgrade", (request, socket, head) => relay.handleUpgrade(request, socket, head));
|
|
9
|
+
await new Promise((resolve, reject) => {
|
|
10
|
+
httpServer.once("error", reject);
|
|
11
|
+
httpServer.listen(options.port ?? 0, () => resolve());
|
|
12
|
+
});
|
|
13
|
+
const address = httpServer.address();
|
|
14
|
+
if (address === null || typeof address === "string") {
|
|
15
|
+
throw new Error("lync serve: could not determine listening port");
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
port: address.port,
|
|
19
|
+
status: () => relay.status(),
|
|
20
|
+
close: async () => {
|
|
21
|
+
await relay.close();
|
|
22
|
+
// Drop any lingering connections and release the listen handle. The
|
|
23
|
+
// close callback is unreliable under some runtimes (bun), so cap the
|
|
24
|
+
// wait and unref the server so it can never keep the loop alive.
|
|
25
|
+
httpServer.closeAllConnections?.();
|
|
26
|
+
httpServer.unref();
|
|
27
|
+
await new Promise((resolve) => {
|
|
28
|
+
const timer = setTimeout(resolve, 1000);
|
|
29
|
+
httpServer.close(() => {
|
|
30
|
+
clearTimeout(timer);
|
|
31
|
+
resolve();
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=serve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve.js","sourceRoot":"","sources":["../../src/relay/serve.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAe,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,eAAe,EAA8C,MAAM,YAAY,CAAC;AAoBzF,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAAyB;IAC5D,MAAM,KAAK,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;IACvC,MAAM,UAAU,GAAW,YAAY,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE;QAC7D,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;IAChC,CAAC,CAAC,CAAC;IACH,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;IAEhG,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC1C,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACjC,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,EAAE,CAAC;IACrC,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QACpD,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;IACpE,CAAC;IAED,OAAO;QACL,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,MAAM,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE;QAC5B,KAAK,EAAE,KAAK,IAAI,EAAE;YAChB,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC;YACpB,oEAAoE;YACpE,qEAAqE;YACrE,iEAAiE;YAChE,UAAmD,CAAC,mBAAmB,EAAE,EAAE,CAAC;YAC7E,UAAU,CAAC,KAAK,EAAE,CAAC;YACnB,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;gBAClC,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;gBACxC,UAAU,CAAC,KAAK,CAAC,GAAG,EAAE;oBACpB,YAAY,CAAC,KAAK,CAAC,CAAC;oBACpB,OAAO,EAAE,CAAC;gBACZ,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/dist/sha256.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sha256.d.ts","sourceRoot":"","sources":["../src/sha256.ts"],"names":[],"mappings":"AAiBA,wBAAgB,SAAS,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAmEnD"}
|
package/dist/sha256.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// SHA-256 in pure TypeScript: synchronous, dependency-free, and byte-for-byte
|
|
2
|
+
// identical in Node and the browser. Lync hashing runs on the same code path
|
|
3
|
+
// everywhere, so this file never reaches for node:crypto and the browser
|
|
4
|
+
// bundle stays free of node builtins. WebCrypto's subtle.digest is async and
|
|
5
|
+
// cannot back the synchronous line parser, which is why we implement it here.
|
|
6
|
+
const K = new Uint32Array([
|
|
7
|
+
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
|
8
|
+
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
|
9
|
+
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
|
10
|
+
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
|
11
|
+
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
|
12
|
+
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
|
13
|
+
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
|
14
|
+
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
|
|
15
|
+
]);
|
|
16
|
+
export function sha256Hex(bytes) {
|
|
17
|
+
let h0 = 0x6a09e667;
|
|
18
|
+
let h1 = 0xbb67ae85;
|
|
19
|
+
let h2 = 0x3c6ef372;
|
|
20
|
+
let h3 = 0xa54ff53a;
|
|
21
|
+
let h4 = 0x510e527f;
|
|
22
|
+
let h5 = 0x9b05688c;
|
|
23
|
+
let h6 = 0x1f83d9ab;
|
|
24
|
+
let h7 = 0x5be0cd19;
|
|
25
|
+
const bitLen = bytes.length * 8;
|
|
26
|
+
const withOne = bytes.length + 1;
|
|
27
|
+
const total = withOne + ((56 - (withOne % 64) + 64) % 64) + 8;
|
|
28
|
+
const msg = new Uint8Array(total);
|
|
29
|
+
msg.set(bytes);
|
|
30
|
+
msg[bytes.length] = 0x80;
|
|
31
|
+
const view = new DataView(msg.buffer);
|
|
32
|
+
view.setUint32(total - 8, Math.floor(bitLen / 0x100000000));
|
|
33
|
+
view.setUint32(total - 4, bitLen >>> 0);
|
|
34
|
+
const w = new Uint32Array(64);
|
|
35
|
+
for (let off = 0; off < total; off += 64) {
|
|
36
|
+
for (let i = 0; i < 16; i++)
|
|
37
|
+
w[i] = view.getUint32(off + i * 4);
|
|
38
|
+
for (let i = 16; i < 64; i++) {
|
|
39
|
+
const s0 = rotr(w[i - 15], 7) ^ rotr(w[i - 15], 18) ^ (w[i - 15] >>> 3);
|
|
40
|
+
const s1 = rotr(w[i - 2], 17) ^ rotr(w[i - 2], 19) ^ (w[i - 2] >>> 10);
|
|
41
|
+
w[i] = (w[i - 16] + s0 + w[i - 7] + s1) | 0;
|
|
42
|
+
}
|
|
43
|
+
let a = h0;
|
|
44
|
+
let b = h1;
|
|
45
|
+
let c = h2;
|
|
46
|
+
let d = h3;
|
|
47
|
+
let e = h4;
|
|
48
|
+
let f = h5;
|
|
49
|
+
let g = h6;
|
|
50
|
+
let h = h7;
|
|
51
|
+
for (let i = 0; i < 64; i++) {
|
|
52
|
+
const s1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);
|
|
53
|
+
const ch = (e & f) ^ (~e & g);
|
|
54
|
+
const t1 = (h + s1 + ch + K[i] + w[i]) | 0;
|
|
55
|
+
const s0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);
|
|
56
|
+
const maj = (a & b) ^ (a & c) ^ (b & c);
|
|
57
|
+
const t2 = (s0 + maj) | 0;
|
|
58
|
+
h = g;
|
|
59
|
+
g = f;
|
|
60
|
+
f = e;
|
|
61
|
+
e = (d + t1) | 0;
|
|
62
|
+
d = c;
|
|
63
|
+
c = b;
|
|
64
|
+
b = a;
|
|
65
|
+
a = (t1 + t2) | 0;
|
|
66
|
+
}
|
|
67
|
+
h0 = (h0 + a) | 0;
|
|
68
|
+
h1 = (h1 + b) | 0;
|
|
69
|
+
h2 = (h2 + c) | 0;
|
|
70
|
+
h3 = (h3 + d) | 0;
|
|
71
|
+
h4 = (h4 + e) | 0;
|
|
72
|
+
h5 = (h5 + f) | 0;
|
|
73
|
+
h6 = (h6 + g) | 0;
|
|
74
|
+
h7 = (h7 + h) | 0;
|
|
75
|
+
}
|
|
76
|
+
return toHex8(h0) + toHex8(h1) + toHex8(h2) + toHex8(h3) + toHex8(h4) + toHex8(h5) + toHex8(h6) + toHex8(h7);
|
|
77
|
+
}
|
|
78
|
+
function rotr(x, n) {
|
|
79
|
+
return (x >>> n) | (x << (32 - n));
|
|
80
|
+
}
|
|
81
|
+
function toHex8(x) {
|
|
82
|
+
return (x >>> 0).toString(16).padStart(8, "0");
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=sha256.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sha256.js","sourceRoot":"","sources":["../src/sha256.ts"],"names":[],"mappings":"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"}
|