1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250 | /**
* Hand-authored declarations for the "." export of ardegazu-rooms-kit — the
* compiled dist/index.js (ClojureScript). Mirrors the v1.0.0 TypeScript API
* surface exactly; wire, storage and log shapes are unchanged.
*
* `Identity` is RE-EXPORTED from ardegazu-id-kit, never redeclared, and that
* stays true even though the runtime value is no longer id-kit's dist: this kit
* now compiles id-kit's sha-pinned ClojureScript sources into its own build
* (deps.edn), so the class OBJECT is ours while the implementation is id-kit's,
* unchanged. A hand-written declaration here would be a second description of
* that surface, free to drift — exactly the failure src/vendor/id was retired
* for. One declaration, owned upstream, keeps the two type-identical; the
* runtime interop is proved cryptographically in test/api.test.mjs.
*/
/** Bytes always backed by a plain ArrayBuffer (what WebCrypto's types demand). */
export type Bytes = Uint8Array<ArrayBuffer>;
export { Identity } from "ardegazu-id-kit";
export type { Fingerprint } from "ardegazu-id-kit";
import type { Identity as SuiteIdentity, Fingerprint } from "ardegazu-id-kit";
// ---- relay descriptor (lib/descriptor) -------------------------------------
export interface MailboxConfig {
baseUrl: string;
credsUrl: string;
maxMessageKb: number;
}
export interface RelayConfig {
relayMultiaddr: string;
turnCredsUrl: string;
discoveryTopic: string;
/** Offline store-and-forward mailbox; absent ⇒ feature off (PROTOCOL.md §9). */
mailbox?: MailboxConfig;
}
// ---- the replicated log (lib/protocol, lib/log) ----------------------------
/** Per-peer connection state surfaced to the app. */
export type PeerConnState = "connecting" | "direct" | "relayed" | "disconnected";
/** `lid` carries a legacy v1 "<peerId>:<seq>" id during one-shot migration. */
export interface LegacyMsg {
lid: string;
from: string;
name: string;
ts: number;
text?: string;
thread?: string;
rx?: [string, string[]][];
}
/**
* v2 replicated-log operations (post-decryption payloads of OrbitDB entries).
* Message ids are entry hashes (CIDs); `thread`/`target` reference them.
*/
export type LogOp =
| { t: "chat"; ts: number; name: string; text: string; thread?: string }
| {
t: "img";
ts: number;
name: string;
cid: string;
mime: string;
bytes: number;
w: number;
h: number;
thread?: string;
}
| { t: "react"; ts: number; name: string; target: string; emoji: string; op: "add" | "remove" }
| { t: "name"; ts: number; name: string }
| { t: "call"; ts: number; name: string }
| { t: "legacy"; ts: number; msgs: LegacyMsg[] };
export interface LogEntryMeta {
hash: string;
/**
* Verified author id: base64url Ed25519 pub for sueta identities, a stable
* per-device key for OrbitDB's default "publickey" identities, "" when the
* entry's signing key doesn't match its referenced identity.
*/
from: string;
/** Lamport time, used only for folding reaction ops deterministically. */
clock: number;
op: LogOp;
}
/** Outcome of feeding one mailbox-delivered entry into the log. */
export type IngestResult = "joined" | "duplicate" | "deferred" | "rejected";
/** The @orbitdb/core oplog entry, as far as this kit touches it. */
export interface OrbitEntry {
hash: string;
id: string;
payload: unknown;
next: string[];
refs: string[];
clock: { id: string; time: number };
v: number;
key?: string;
identity?: string;
sig?: string;
}
/** A multiformats CID, structurally — the concrete class comes from helia. */
export interface CIDLike {
code: number;
toString(base?: unknown): string;
}
/**
* The room's replicated, encrypted, append-only log (an OrbitDB events DB on
* Helia). Reachable as `ChatClient#log` / `BoardClient#log`.
*/
export declare class RoomLog {
get address(): string;
get myAuthorId(): string;
/** Live + replicated entries, deduped by hash. Wire these via RoomLog.open. */
onEntry: (e: LogEntryMeta) => void;
onError: (err: Error) => void;
/** Mailbox hooks (default no-ops). */
onAppended: (hash: string) => void;
onImageStored: (rootCid: string) => void;
/** When set, putImage chunks at this size so blocks fit one mailbox frame. */
imageChunkBytes: number | null;
static open(
helia: unknown,
rc: unknown,
identity: SuiteIdentity | null,
hooks?: {
onEntry?: (e: LogEntryMeta) => void;
onError?: (err: Error) => void;
directory?: string;
},
): Promise<RoomLog>;
append(op: LogOp): Promise<string>;
/** Replay the newest `amount` entries from local storage (offline-capable). */
loadTail(amount: number): Promise<number>;
emitUnseen(): Promise<void>;
putImage(data: Bytes): Promise<{ cid: string; bytes: number }>;
getImage(cidStr: string, signal?: AbortSignal): Promise<Bytes | null>;
pruneImages(keep: Set<string>, dropped: string[]): Promise<void>;
sealedEntryBytes(hash: string): Promise<Uint8Array | null>;
myIdentityBlock(): { cid: CIDLike; bytes: Uint8Array } | null;
rawBlock(cid: CIDLike): Promise<Uint8Array | null>;
imageDagBlocks(rootCid: string): Promise<{ cid: CIDLike; bytes: Uint8Array }[]>;
decodeSealedEntry(bytes: Uint8Array): Promise<OrbitEntry | null>;
putEntryBlock(hash: string, bytes: Uint8Array): Promise<void>;
putRawBlock(cid: CIDLike, bytes: Uint8Array): Promise<void>;
putIdentityBlock(cid: CIDLike, bytes: Uint8Array): Promise<void>;
hasEntry(hash: string): Promise<boolean>;
ingestEntry(entry: OrbitEntry): Promise<IngestResult>;
entryMeta(entry: OrbitEntry): Promise<LogEntryMeta | null>;
pinImageIfLocal(rootCid: string): Promise<boolean>;
close(): Promise<void>;
/** Forget-room: drop the log's local data (entries, index, heads). */
drop(): Promise<void>;
private constructor();
}
// ---- the libp2p transport (lib/net) ----------------------------------------
export interface NetEvents {
peerState(peer: string, state: PeerConnState, name: string | undefined): void;
peerGone(peer: string): void;
message(from: string, payload: unknown): void;
binary(from: string, data: Uint8Array): void;
peerReady(peer: string): void;
status(up: boolean): void;
}
/**
* One libp2p node per session: relay websocket, pubsub discovery, direct WebRTC
* upgrades, and the sealed presence/membership machine. Reachable as
* `ChatClient#net` / `BoardClient#net`.
*/
export declare class Net {
readonly libp2p: unknown;
get myId(): string;
get relayUp(): boolean;
static create(opts: {
privateKey: unknown;
crypto: unknown;
ice: unknown;
relayMultiaddr: string;
discoveryTopic: string;
myName: () => string;
events: NetEvents;
/** Node port: browser Origin for the managed relay's allowlist. */
wsOrigin?: string;
}): Promise<Net>;
handleFrames(protocol: string, onFrame: (from: string, frame: Uint8Array) => void): Promise<void>;
sendFrame(protocol: string, peer: string, frame: Uint8Array): Promise<boolean>;
/** Directed JSON payload; resolves false if the peer is unreachable. */
sendTo(peer: string, payload: unknown): Promise<boolean>;
sendBinaryTo(peer: string, data: Bytes): Promise<void>;
/** Seal once, deliver to every connected member via the room topic. */
broadcast(payload: unknown): Promise<void>;
broadcastBinary(data: Bytes): Promise<void>;
openPeers(): string[];
debugState(): Record<string, unknown>[];
close(): Promise<void>;
private constructor();
}
// ---- chat ------------------------------------------------------------------
export type { ChatClient, ChatOptions, ChatMessage, ChatPeer, ChatEvents } from "./chat/index.js";
export { CHAT_SALT } from "./chat/index.js";
// ---- board -----------------------------------------------------------------
export type { BoardClient, BoardOptions, BoardElement, BoardEvents, StrokeSpec } from "./board/index.js";
export { BOARD_SALT } from "./board/index.js";
// ---- shared ----------------------------------------------------------------
/** 32 random bytes, base64url unpadded (43 chars) — the room capability. */
export declare function newRoomSecret(): string;
/**
* Install the Node origin shims (WebSocket + fetch) for the given origin — it
* must be on the relay app's allowlist. Idempotent; throws if called again with
* a DIFFERENT origin. `ChatClient.join`/`BoardClient.join` call it for you.
*/
export declare function installOrigin(origin: string, hosts?: RegExp[]): void;
/**
* Minimal browser globals (`window` timers, a "visible" document, a file-backed
* `localStorage`) for modules written against the DOM.
*/
export declare function installBrowserGlobals(storageFile?: string): void;
/** Re-exported so `new ChatClient(...)`-style typing has the identity handy. */
export type { Identity as IdentityType } from "ardegazu-id-kit";
export type { Fingerprint as FingerprintType } from "ardegazu-id-kit";
// re-exported above; kept so `Fingerprint` is usable without a second import
export type ChatIdent = { pub: string; fp: Fingerprint };
|