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 | /**
* The p2p offline mailbox: deposit an encrypted payload for a room and let
* a peer that shows up later replay it. This is the catch-up path for peers
* that are never online at the same time — for js-libp2p/Helia/OrbitDB
* apps, where encrypted log entries and head CIDs go, keyed by the database
* address.
*
* Unlike the rest of this client these calls do not go to api.ird.ro: they
* go to the app's own node at `https://<hostname>/mailbox/v1`,
* authenticated with a short-lived bearer token. Mint one with
* `credentials` (which wraps `p2p.mintMailboxCredentials`) and pass the
* returned value as the first argument to `deposit` and `replay`; it
* carries both the `endpoint` and the `token`. The app needs
* `mailbox_enabled` — see `p2p.updateApp`.
*
* The node stores exactly the bytes you send and holds no keys: encrypt
* before depositing. Payloads are capped by the app's
* `mailbox_max_message_kb` (default 64), retained for `mailbox_ttl_hours`
* (default 24) and bounded by a per-app `mailbox_max_mb` ring buffer — so
* replayed history can be truncated under you; see `isHistoryGap`.
*/
import type { Client } from './client.js';
import type { Body, Res } from './types.js';
export type MailboxCredentials = Res<'/p2p/apps/{id}/mailbox-credentials', 'post'>['credentials'];
export type MailboxOptions = Readonly<{
fetch?: typeof fetch;
timeoutMs?: number;
}>;
export type MailboxPayload = Uint8Array | string | Blob | ReadableStream<Uint8Array>;
export type MailboxMessage = Readonly<{
/** Opaque ordered token; a string that must stay one (see `isHistoryGap`). */
seq: string;
id: string;
ts: number;
/** Standard base64; decode with `payloadBytes`. */
payload: string;
}>;
export type ReplayPage = Readonly<{
messages: readonly MailboxMessage[];
has_more: boolean;
oldest_seq?: string | null;
latest_seq?: string | null;
}>;
export type DepositResult = Readonly<{
seq: string;
id: string;
expires_at: number;
deduped: boolean;
}>;
export type ReplayOptions = MailboxOptions & Readonly<{
/** Replay forward from this seq, exclusive (the usual cursor). */
after?: string;
/** Scroll back from this seq, exclusive. */
before?: string;
/** Page size, default 100, capped at 500. */
limit?: number;
}>;
export type ReplayAllOptions = MailboxOptions & Readonly<{
after?: string;
limit?: number;
}>;
export type ReplayAllResult = Readonly<{
messages: readonly MailboxMessage[];
/** The seq to resume from next time (the last one seen, or the `after` you passed). */
cursor: string | undefined;
/** Whether history was truncated before your cursor (checked on the first page). */
gap: boolean;
}>;
/**
* Mint a mailbox token for APP-ID and return the credentials directly —
* `{ token, ttl, expires_at, endpoint, room? }` — ready to pass to
* `deposit` and `replay`. BODY may scope the token to one `room` and set
* `ttl_sec` (default 3600, capped at 86400). Mint short and reuse one
* across calls: every mint is a charged request.
*/
export declare const credentials: (client: Client, appId: string, body?: Body<"/p2p/apps/{id}/mailbox-credentials", "post">) => Promise<MailboxCredentials>;
/**
* POST /mailbox/v1/rooms/{room}/messages — Deposit PAYLOAD into ROOM for
* peers that arrive later. Identical payloads deduplicate per room within
* the retention window (`deduped: true`), so retries are safe and free.
* Throws an api error whose `code` is the node's, notably
* "message_too_large" and "node_storage_full".
*/
export declare const deposit: (creds: MailboxCredentials, room: string, payload: MailboxPayload, options?: MailboxOptions) => Promise<DepositResult>;
/**
* GET /mailbox/v1/rooms/{room}/messages — Replay ROOM, oldest first. Every
* `seq` is a string and must stay one: it packs a millisecond clock into
* its high bits, far past what a JSON number survives. There is no
* server-side ack: the cursor is yours to keep; check it against
* `oldest_seq` with `isHistoryGap` on every replay.
*/
export declare const replay: (creds: MailboxCredentials, room: string, options?: ReplayOptions) => Promise<ReplayPage>;
/** Decode one replayed message's base64 payload into bytes. */
export declare const payloadBytes: (message: Pick<MailboxMessage, "payload">) => Uint8Array;
/** Decode one replayed message's payload as UTF-8 text (only meaningful for text deposits). */
export declare const payloadString: (message: Pick<MailboxMessage, "payload">) => string;
/**
* True when CURSOR — the seq you last saw — is older than PAGE's
* `oldest_seq`, meaning messages were evicted (TTL or the app's ring
* buffer) between your visits. Report that as truncated history, not as a
* quiet room. An absent/blank cursor (a first visit) is never a gap.
*/
export declare const isHistoryGap: (cursor: string | null | undefined, page: Pick<ReplayPage, "oldest_seq">) => boolean;
/**
* Replay ROOM forward from `after` until `has_more` is exhausted,
* collecting every message. Every page is a charged request, so pass the
* largest `limit` you can use.
*/
export declare const replayAll: (creds: MailboxCredentials, room: string, options?: ReplayAllOptions) => Promise<ReplayAllResult>;
|