@solarpunkltd/swarm-chat-js 7.1.0 → 7.2.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/README.md +50 -8
- package/dist/chat.d.ts +7 -0
- package/dist/follower.d.ts +37 -6
- package/dist/history.d.ts +20 -6
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +232 -109
- package/dist/message/index.d.ts +1 -0
- package/dist/message/note.d.ts +38 -0
- package/dist/message.cjs +1 -1
- package/dist/message.js +139 -95
- package/dist/notes.d.ts +81 -0
- package/dist/swarm.d.ts +8 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { ChatMessageError as e,
|
|
2
|
-
import { Bee as
|
|
1
|
+
import { ChatMessageError as e, DEFAULT_NOTE_HEARTBEAT_MS as t, DEFAULT_NOTE_SLOT_MS as n, MAX_ENTRY_BYTES as r, MAX_MESSAGE_BYTES as i, MAX_NAME_CHARACTERS as a, MAX_NOTE_BYTES as o, MAX_TEXT_CHARACTERS as s, MAX_TOPIC_CHARACTERS as c, MESSAGE_VERSION as l, MessageType as u, NOTE_VERSION as d, chatMessageSchema as f, checkChatMessage as p, countCharacters as m, createChatMessage as h, encodeChatMessage as g, encodeSlotNote as _, feedEntrySchema as v, hasValidSignature as ee, historyFileSchema as te, historyLinkSchema as y, historyRowSchema as ne, newMessageId as b, noteAddress as x, noteIdentifier as S, noteIdentifierText as C, noteSlotEnd as w, noteSlotOf as T, parseChatMessage as E, parseFeedEntry as D, parseHistoryFile as O, parseSlotNote as k, signedBytes as A, slotNoteSchema as re } from "./message.js";
|
|
2
|
+
import { Bee as j, BeeResponseError as M, Identifier as N, PrivateKey as P, Topic as F } from "@ethersphere/bee-js";
|
|
3
3
|
//#region src/events.ts
|
|
4
|
-
var
|
|
4
|
+
var I = {
|
|
5
5
|
LOADING_INIT: "loadingInit",
|
|
6
6
|
LOADING_PREVIOUS_MESSAGES: "loadingPreviousMessages",
|
|
7
7
|
MESSAGE_RECEIVED: "messageReceived",
|
|
@@ -13,13 +13,13 @@ var O = {
|
|
|
13
13
|
MESSAGE_SKIPPED: "messageSkipped",
|
|
14
14
|
ERROR: "error"
|
|
15
15
|
};
|
|
16
|
-
function
|
|
17
|
-
return
|
|
16
|
+
function L(e) {
|
|
17
|
+
return z(e, -1, e.ts);
|
|
18
18
|
}
|
|
19
|
-
function
|
|
20
|
-
return
|
|
19
|
+
function R(e) {
|
|
20
|
+
return z(e.msg, e.seq, e.at);
|
|
21
21
|
}
|
|
22
|
-
function
|
|
22
|
+
function z(e, t, n) {
|
|
23
23
|
return {
|
|
24
24
|
id: e.id,
|
|
25
25
|
type: e.type,
|
|
@@ -34,7 +34,7 @@ function j(e, t, n) {
|
|
|
34
34
|
sentAt: e.ts
|
|
35
35
|
};
|
|
36
36
|
}
|
|
37
|
-
var
|
|
37
|
+
var B = class {
|
|
38
38
|
constructor() {
|
|
39
39
|
this.listeners = /* @__PURE__ */ new Map(), this.on = (e, t) => {
|
|
40
40
|
this.listeners.set(e, [...this.listeners.get(e) ?? [], t]);
|
|
@@ -51,65 +51,66 @@ var M = class {
|
|
|
51
51
|
console.error(`a listener for ${e} threw`, t);
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
|
-
},
|
|
54
|
+
}, V = class extends Error {
|
|
55
55
|
constructor(e) {
|
|
56
56
|
super("the feed head lookup did not answer in time", e), this.name = "HeadLookupTimeoutError";
|
|
57
57
|
}
|
|
58
|
-
},
|
|
58
|
+
}, H = class extends Error {
|
|
59
59
|
constructor(e, t) {
|
|
60
60
|
super(`feed slot ${e} was served but could not be read`, t), this.index = e, this.name = "UnreadableSlotError";
|
|
61
61
|
}
|
|
62
62
|
};
|
|
63
|
-
function
|
|
64
|
-
return e instanceof
|
|
63
|
+
function U(e) {
|
|
64
|
+
return e instanceof M && (e.status === 404 || e.status === 500);
|
|
65
65
|
}
|
|
66
|
-
async function
|
|
66
|
+
async function W(e, t) {
|
|
67
67
|
try {
|
|
68
68
|
return await t();
|
|
69
69
|
} catch (t) {
|
|
70
|
-
if (
|
|
71
|
-
throw t instanceof
|
|
70
|
+
if (U(t)) return null;
|
|
71
|
+
throw t instanceof M ? t : new H(e, { cause: t });
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
|
-
function
|
|
75
|
-
let
|
|
74
|
+
function G(e, t, r, i, a = n) {
|
|
75
|
+
let o = F.fromString(r), s = (n) => e.feed.makeReader(o, t, K(n));
|
|
76
76
|
return {
|
|
77
|
-
|
|
77
|
+
readNote: (n) => W(n, async () => (await e.soc.makeReader(t, K(i.slotReadMs)).download(S(r, a, n))).payload.toUint8Array()),
|
|
78
|
+
readSlot: (e) => W(e, async () => (await s(i.slotReadMs).downloadPayload({ index: e })).payload.toUint8Array()),
|
|
78
79
|
async readHead() {
|
|
79
|
-
let n =
|
|
80
|
+
let n = K(i.feedReadMs);
|
|
80
81
|
try {
|
|
81
|
-
let { payload: r, feedIndex:
|
|
82
|
+
let { payload: r, feedIndex: i } = await e.feed.makeReader(o, t, n).downloadPayload();
|
|
82
83
|
return {
|
|
83
|
-
index: Number(
|
|
84
|
+
index: Number(i.toBigInt()),
|
|
84
85
|
payload: r.toUint8Array()
|
|
85
86
|
};
|
|
86
87
|
} catch (e) {
|
|
87
|
-
if (
|
|
88
|
-
throw n.signal?.aborted ? new
|
|
88
|
+
if (U(e)) return null;
|
|
89
|
+
throw n.signal?.aborted ? new V({ cause: e }) : e;
|
|
89
90
|
}
|
|
90
91
|
},
|
|
91
92
|
async readFile(t) {
|
|
92
|
-
return (await e.data.download(t, void 0,
|
|
93
|
+
return (await e.data.download(t, void 0, K(i.feedReadMs))).toUint8Array();
|
|
93
94
|
}
|
|
94
95
|
};
|
|
95
96
|
}
|
|
96
|
-
function
|
|
97
|
+
function K(e) {
|
|
97
98
|
return { signal: AbortSignal.timeout(e) };
|
|
98
99
|
}
|
|
99
|
-
var
|
|
100
|
-
function
|
|
101
|
-
let a = new
|
|
100
|
+
var ie = "0123456789abcdef".repeat(4);
|
|
101
|
+
function q(e, t, n, r, i) {
|
|
102
|
+
let a = new P(n), o = N.fromString(r);
|
|
102
103
|
return async (n) => {
|
|
103
|
-
await e.messaging.gsocSend(t, a, o, n, { deferred: !1 },
|
|
104
|
+
await e.messaging.gsocSend(t, a, o, n, { deferred: !1 }, K(i.writeMs));
|
|
104
105
|
};
|
|
105
106
|
}
|
|
106
107
|
//#endregion
|
|
107
108
|
//#region src/follower.ts
|
|
108
|
-
var
|
|
109
|
+
var J = {
|
|
109
110
|
LIVE: "live",
|
|
110
111
|
RECONNECTING: "reconnecting",
|
|
111
112
|
STALLED: "stalled"
|
|
112
|
-
},
|
|
113
|
+
}, Y = {
|
|
113
114
|
pollIntervalMs: 1e3,
|
|
114
115
|
maxSlotsPerPoll: 16,
|
|
115
116
|
unservedPollsBeforeProbe: 3,
|
|
@@ -128,14 +129,14 @@ var V = {
|
|
|
128
129
|
stallAfterMs: 8e3,
|
|
129
130
|
random: Math.random,
|
|
130
131
|
now: Date.now
|
|
131
|
-
},
|
|
132
|
+
}, X = class extends Error {
|
|
132
133
|
constructor(e) {
|
|
133
134
|
super(`the gateway keeps answering feed slot ${e} with something that is not a chunk`), this.index = e, this.name = "UnreadableGatewayError";
|
|
134
135
|
}
|
|
135
|
-
},
|
|
136
|
-
constructor(e, t, n, r = {}) {
|
|
137
|
-
this.source = e, this.topic = t, this.events = n, this.next = 0, this.unservedPolls = 0, this.nextProbeAt = 0, this.unreadablePolls = 0, this.gatewayFailures = 0, this.missed = /* @__PURE__ */ new Map(), this.timer = null, this.generation = 0, this.running = !1, this.status = null, this.settings = {
|
|
138
|
-
...
|
|
136
|
+
}, ae = class {
|
|
137
|
+
constructor(e, t, n, r = {}, i = null) {
|
|
138
|
+
this.source = e, this.topic = t, this.events = n, this.notes = i, this.next = 0, this.ceiling = null, this.nextNoteSlot = 0, this.lastNoteFoundAt = 0, this.unservedPolls = 0, this.nextProbeAt = 0, this.unreadablePolls = 0, this.gatewayFailures = 0, this.missed = /* @__PURE__ */ new Map(), this.timer = null, this.generation = 0, this.running = !1, this.status = null, this.settings = {
|
|
139
|
+
...Y,
|
|
139
140
|
...r
|
|
140
141
|
};
|
|
141
142
|
}
|
|
@@ -145,8 +146,11 @@ var V = {
|
|
|
145
146
|
get missedIndices() {
|
|
146
147
|
return [...this.missed.keys()];
|
|
147
148
|
}
|
|
148
|
-
|
|
149
|
-
|
|
149
|
+
get ledByNotes() {
|
|
150
|
+
return this.ceiling !== null;
|
|
151
|
+
}
|
|
152
|
+
start(e, t = null) {
|
|
153
|
+
this.stop(), this.next = e, this.ceiling = t?.newest ?? null, this.nextNoteSlot = t?.nextNoteSlot ?? (this.notes ? this.notes.lastReadableSlot() + 1 : 0), this.lastNoteFoundAt = this.settings.now(), this.resetRefusals(), this.unreadablePolls = 0, this.gatewayFailures = 0, this.missed.clear(), this.status = null, this.running = !0, this.schedule(0);
|
|
150
154
|
}
|
|
151
155
|
stop() {
|
|
152
156
|
this.running = !1, this.generation++, this.timer &&= (clearTimeout(this.timer), null);
|
|
@@ -160,7 +164,7 @@ var V = {
|
|
|
160
164
|
async poll(e) {
|
|
161
165
|
let t = 0, n = !1;
|
|
162
166
|
try {
|
|
163
|
-
t = await this.walk(e), await this.readMissed(e);
|
|
167
|
+
this.notes !== null && await this.readDueNotes(this.notes, e), t = await this.walk(e), await this.readMissed(e);
|
|
164
168
|
} catch (t) {
|
|
165
169
|
n = !0, e === this.generation && this.gatewayFailed(t);
|
|
166
170
|
}
|
|
@@ -169,28 +173,49 @@ var V = {
|
|
|
169
173
|
this.schedule(this.backoffMs());
|
|
170
174
|
return;
|
|
171
175
|
}
|
|
172
|
-
this.gatewayAnswered(), this.schedule(t >= this.settings.maxSlotsPerPoll ? 0 : this.
|
|
176
|
+
this.gatewayAnswered(), this.schedule(t >= this.settings.maxSlotsPerPoll ? 0 : this.untilNextRead());
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
untilNextRead() {
|
|
180
|
+
return this.ceiling === null || this.notes === null ? this.settings.pollIntervalMs : Math.max(0, this.notes.readableAt(this.nextNoteSlot) - this.settings.now());
|
|
181
|
+
}
|
|
182
|
+
async readDueNotes(e, t) {
|
|
183
|
+
let n = this.settings.now(), r = e.lastReadableSlot(n);
|
|
184
|
+
if (r < this.nextNoteSlot) return;
|
|
185
|
+
let i = Math.max(this.nextNoteSlot, r - e.lookBackSlots + 1), a = await e.scan(r, i);
|
|
186
|
+
if (t === this.generation) {
|
|
187
|
+
if (this.nextNoteSlot = r + 1, a) {
|
|
188
|
+
this.lastNoteFoundAt = n, this.ceiling = Math.max(this.ceiling ?? -1, a.note.newest, this.next - 1);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
n - this.lastNoteFoundAt >= e.silenceMs && (this.lastNoteFoundAt = n, e.clock.distrust(), this.nextNoteSlot = Math.min(this.nextNoteSlot, e.lastReadableSlot(n) + 1));
|
|
173
192
|
}
|
|
174
193
|
}
|
|
175
194
|
async read(e) {
|
|
176
195
|
try {
|
|
177
196
|
return await this.source.readSlot(e);
|
|
178
197
|
} catch (e) {
|
|
179
|
-
if (e instanceof
|
|
198
|
+
if (e instanceof H) return "unreadable";
|
|
180
199
|
throw e;
|
|
181
200
|
}
|
|
182
201
|
}
|
|
183
202
|
async walk(e) {
|
|
184
203
|
for (let t = 0; t < this.settings.maxSlotsPerPoll; t++) {
|
|
185
|
-
let n = this.next
|
|
204
|
+
let n = this.next;
|
|
205
|
+
if (this.ceiling !== null && n > this.ceiling) return t;
|
|
206
|
+
let r = await this.read(n);
|
|
186
207
|
if (e !== this.generation) return t;
|
|
208
|
+
if (this.ceiling !== null && (r === null || r === "unreadable")) {
|
|
209
|
+
this.remember(n), this.next = n + 1;
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
187
212
|
if (r === null || r === "unreadable") {
|
|
188
213
|
if (t === 0) {
|
|
189
214
|
if (this.unservedPolls++, this.unreadablePolls = r === "unreadable" ? this.unreadablePolls + 1 : 0, this.shouldProbe() && await this.probePast(n, e)) {
|
|
190
215
|
this.unreadablePolls = 0;
|
|
191
216
|
continue;
|
|
192
217
|
}
|
|
193
|
-
if (this.unreadablePolls >= this.settings.unreadablePollsBeforeFailure) throw new
|
|
218
|
+
if (this.unreadablePolls >= this.settings.unreadablePollsBeforeFailure) throw new X(n);
|
|
194
219
|
}
|
|
195
220
|
return t;
|
|
196
221
|
}
|
|
@@ -246,16 +271,16 @@ var V = {
|
|
|
246
271
|
}
|
|
247
272
|
}
|
|
248
273
|
accept(e, t) {
|
|
249
|
-
let n =
|
|
250
|
-
n.ok ? this.events.entry(n.value) : this.events.skipped(e, n.reason, n.detail);
|
|
274
|
+
let n = D(t, e, this.topic);
|
|
275
|
+
n.ok ? (this.notes?.clock.observe(n.value.at, this.settings.now()), this.events.entry(n.value)) : this.events.skipped(e, n.reason, n.detail);
|
|
251
276
|
}
|
|
252
277
|
gatewayFailed(e) {
|
|
253
|
-
this.gatewayFailures++, this.setStatus(
|
|
278
|
+
this.gatewayFailures++, this.setStatus(J.RECONNECTING), this.events.error(e);
|
|
254
279
|
}
|
|
255
280
|
gatewayAnswered() {
|
|
256
281
|
this.gatewayFailures = 0;
|
|
257
282
|
let e = this.settings.now(), t = [...this.missed.values()].some((t) => e - t >= this.settings.stallAfterMs);
|
|
258
|
-
this.setStatus(t ?
|
|
283
|
+
this.setStatus(t ? J.STALLED : J.LIVE);
|
|
259
284
|
}
|
|
260
285
|
backoffMs() {
|
|
261
286
|
let { pollIntervalMs: e, backoffCapMs: t, random: n } = this.settings, r = Math.min(t, e * 2 ** this.gatewayFailures);
|
|
@@ -264,36 +289,52 @@ var V = {
|
|
|
264
289
|
setStatus(e) {
|
|
265
290
|
e !== this.status && (this.status = e, this.events.status(e));
|
|
266
291
|
}
|
|
267
|
-
},
|
|
268
|
-
constructor(e, t, n) {
|
|
269
|
-
this.source = e, this.topic = t, this.events = n, this.older = null, this.loading = null;
|
|
292
|
+
}, oe = 16, se = class {
|
|
293
|
+
constructor(e, t, n, r = null) {
|
|
294
|
+
this.source = e, this.topic = t, this.events = n, this.notes = r, this.older = null, this.loading = null;
|
|
270
295
|
}
|
|
271
296
|
async open() {
|
|
272
297
|
this.older = null;
|
|
273
|
-
let e = await this.
|
|
274
|
-
if (e
|
|
298
|
+
let e = await this.newestNote();
|
|
299
|
+
if (e !== null) return this.openAt(e.newest, e);
|
|
300
|
+
let t = await this.readHead();
|
|
301
|
+
return t === null ? {
|
|
275
302
|
startAt: 0,
|
|
276
|
-
rows: []
|
|
303
|
+
rows: [],
|
|
304
|
+
notes: null
|
|
305
|
+
} : this.openAt(t.index, null, t.payload);
|
|
306
|
+
}
|
|
307
|
+
async newestNote() {
|
|
308
|
+
if (this.notes === null) return null;
|
|
309
|
+
let e = this.notes.lastReadableSlot(), t = await this.notes.scan(e, e - this.notes.lookBackSlots + 1);
|
|
310
|
+
return t === null ? null : {
|
|
311
|
+
newest: t.note.newest,
|
|
312
|
+
nextNoteSlot: e + 1
|
|
277
313
|
};
|
|
278
|
-
|
|
279
|
-
|
|
314
|
+
}
|
|
315
|
+
async openAt(e, t, n) {
|
|
316
|
+
let r = e < 0 ? null : await this.newestLink(e, n);
|
|
317
|
+
if (r === null) return {
|
|
280
318
|
startAt: 0,
|
|
281
|
-
rows: []
|
|
319
|
+
rows: [],
|
|
320
|
+
notes: t
|
|
282
321
|
};
|
|
283
|
-
let
|
|
284
|
-
return
|
|
322
|
+
let i = await this.readFile(r);
|
|
323
|
+
return i === "refused" ? {
|
|
285
324
|
startAt: 0,
|
|
286
|
-
rows: []
|
|
325
|
+
rows: [],
|
|
326
|
+
notes: t
|
|
287
327
|
} : {
|
|
288
|
-
startAt:
|
|
289
|
-
rows:
|
|
328
|
+
startAt: r.toSeq + 1,
|
|
329
|
+
rows: i ?? [],
|
|
330
|
+
notes: t
|
|
290
331
|
};
|
|
291
332
|
}
|
|
292
333
|
async readHead() {
|
|
293
334
|
try {
|
|
294
335
|
return await this.source.readHead();
|
|
295
336
|
} catch (e) {
|
|
296
|
-
if (e instanceof
|
|
337
|
+
if (e instanceof V) return this.events.error(e), null;
|
|
297
338
|
throw e;
|
|
298
339
|
}
|
|
299
340
|
}
|
|
@@ -308,27 +349,30 @@ var V = {
|
|
|
308
349
|
async loadOlderOnce() {
|
|
309
350
|
let e = this.older;
|
|
310
351
|
if (e === null) return [];
|
|
311
|
-
let t = await this.source.readFile(e.ref), n =
|
|
352
|
+
let t = await this.source.readFile(e.ref), n = O(t, this.topic, e);
|
|
312
353
|
if (!n.ok) throw this.older = null, Error(`history file ${e.ref} refused (${n.reason}): ${n.detail}`);
|
|
313
354
|
return this.report(n.value.skipped), this.older = n.value.prev, n.value.rows;
|
|
314
355
|
}
|
|
315
356
|
async newestLink(e, t) {
|
|
316
|
-
let n =
|
|
317
|
-
if (
|
|
318
|
-
|
|
357
|
+
let n = t === void 0 ? e : e - 1;
|
|
358
|
+
if (t !== void 0) {
|
|
359
|
+
let n = D(t, e, this.topic);
|
|
360
|
+
if (n.ok) return n.value.history;
|
|
361
|
+
}
|
|
362
|
+
for (let t = n; t >= Math.max(0, e - oe); t--) {
|
|
319
363
|
let e = await this.source.readSlot(t).catch((e) => {
|
|
320
|
-
if (e instanceof
|
|
364
|
+
if (e instanceof H) return null;
|
|
321
365
|
throw e;
|
|
322
366
|
});
|
|
323
367
|
if (e === null) continue;
|
|
324
|
-
let n =
|
|
368
|
+
let n = D(e, t, this.topic);
|
|
325
369
|
if (n.ok) return n.value.history;
|
|
326
370
|
}
|
|
327
371
|
return null;
|
|
328
372
|
}
|
|
329
373
|
async readFile(e) {
|
|
330
374
|
try {
|
|
331
|
-
let t =
|
|
375
|
+
let t = O(await this.source.readFile(e.ref), this.topic, e);
|
|
332
376
|
return t.ok ? (this.report(t.value.skipped), this.older = t.value.prev, t.value.rows) : (this.events.error(/* @__PURE__ */ Error(`history file ${e.ref} refused (${t.reason}): ${t.detail}`)), "refused");
|
|
333
377
|
} catch (t) {
|
|
334
378
|
return this.events.error(t), this.older = e, null;
|
|
@@ -337,20 +381,92 @@ var V = {
|
|
|
337
381
|
report(e) {
|
|
338
382
|
e > 0 && this.events.skipped(e);
|
|
339
383
|
}
|
|
340
|
-
},
|
|
384
|
+
}, ce = {
|
|
385
|
+
slotMs: n,
|
|
386
|
+
heartbeatMs: t,
|
|
387
|
+
marginMs: 1e3,
|
|
388
|
+
scanBatch: 4,
|
|
389
|
+
clockLimitMs: 3e5
|
|
390
|
+
};
|
|
391
|
+
function le(e) {
|
|
392
|
+
return typeof e.readNote == "function";
|
|
393
|
+
}
|
|
394
|
+
var ue = class {
|
|
395
|
+
constructor(e) {
|
|
396
|
+
this.limitMs = e, this.aheadAtMost = Infinity, this.trusted = !0;
|
|
397
|
+
}
|
|
398
|
+
observe(e, t) {
|
|
399
|
+
this.aheadAtMost = Math.min(this.aheadAtMost, t - e);
|
|
400
|
+
}
|
|
401
|
+
distrust() {
|
|
402
|
+
this.trusted = !1;
|
|
403
|
+
}
|
|
404
|
+
get isTrusted() {
|
|
405
|
+
return this.trusted;
|
|
406
|
+
}
|
|
407
|
+
get ms() {
|
|
408
|
+
let e = Number.isFinite(this.aheadAtMost), t = this.trusted || !e ? Math.min(0, this.aheadAtMost) : this.aheadAtMost;
|
|
409
|
+
return Math.max(-this.limitMs, Math.min(this.limitMs, t));
|
|
410
|
+
}
|
|
411
|
+
}, Z = class {
|
|
412
|
+
constructor(e, t, n) {
|
|
413
|
+
this.source = e, this.now = n, this.counts = {
|
|
414
|
+
found: 0,
|
|
415
|
+
empty: 0
|
|
416
|
+
}, this.settings = {
|
|
417
|
+
...ce,
|
|
418
|
+
...t
|
|
419
|
+
}, this.clock = new ue(this.settings.clockLimitMs);
|
|
420
|
+
}
|
|
421
|
+
lastReadableSlot(e = this.now()) {
|
|
422
|
+
return T(e - this.settings.marginMs - this.clock.ms, this.settings.slotMs) - 1;
|
|
423
|
+
}
|
|
424
|
+
readableAt(e) {
|
|
425
|
+
return w(e, this.settings.slotMs) + this.settings.marginMs + this.clock.ms;
|
|
426
|
+
}
|
|
427
|
+
get lookBackSlots() {
|
|
428
|
+
return Math.ceil(this.settings.heartbeatMs / this.settings.slotMs) + 2;
|
|
429
|
+
}
|
|
430
|
+
get silenceMs() {
|
|
431
|
+
return this.settings.heartbeatMs + 2 * this.settings.slotMs + this.settings.marginMs;
|
|
432
|
+
}
|
|
433
|
+
async read(e) {
|
|
434
|
+
let t;
|
|
435
|
+
try {
|
|
436
|
+
t = await this.source.readNote(e);
|
|
437
|
+
} catch (e) {
|
|
438
|
+
if (!(e instanceof H)) throw e;
|
|
439
|
+
t = null;
|
|
440
|
+
}
|
|
441
|
+
let n = t === null ? null : k(t);
|
|
442
|
+
return n === null ? (this.counts.empty++, null) : (this.counts.found++, this.clock.observe(n.writtenAt, this.now()), n);
|
|
443
|
+
}
|
|
444
|
+
async scan(e, t) {
|
|
445
|
+
for (let n = e; n >= t; n -= this.settings.scanBatch) {
|
|
446
|
+
let e = [];
|
|
447
|
+
for (let r = n; r > n - this.settings.scanBatch && r >= t; r--) e.push(r);
|
|
448
|
+
let r = await Promise.all(e.map((e) => this.read(e))), i = r.findIndex((e) => e !== null);
|
|
449
|
+
if (i >= 0) return {
|
|
450
|
+
slot: e[i],
|
|
451
|
+
note: r[i]
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
}, Q = {
|
|
341
457
|
resendAfterMs: 1e4,
|
|
342
458
|
maxResends: 5,
|
|
343
459
|
reactionWindowMs: 1e3,
|
|
344
460
|
isLive: () => !0
|
|
345
|
-
},
|
|
346
|
-
constructor(e, t, n, r, i =
|
|
461
|
+
}, de = class {
|
|
462
|
+
constructor(e, t, n, r, i = Q) {
|
|
347
463
|
this.key = e, this.name = t, this.write = n, this.events = r, this.settings = i, this.outgoing = /* @__PURE__ */ new Map(), this.reactions = /* @__PURE__ */ new Map(), this.stopped = !1;
|
|
348
464
|
}
|
|
349
465
|
get address() {
|
|
350
466
|
return this.key.publicKey().address().toHex();
|
|
351
467
|
}
|
|
352
468
|
send(e) {
|
|
353
|
-
let { message: t, bytes: n } =
|
|
469
|
+
let { message: t, bytes: n } = h(this.key, {
|
|
354
470
|
...e,
|
|
355
471
|
name: this.name
|
|
356
472
|
}), r = {
|
|
@@ -378,7 +494,7 @@ var V = {
|
|
|
378
494
|
let i = this.reactions.get(r);
|
|
379
495
|
this.reactions.delete(r), i && i.taps % 2 == 1 && !this.stopped && this.send({
|
|
380
496
|
topic: e,
|
|
381
|
-
type:
|
|
497
|
+
type: u.REACTION,
|
|
382
498
|
target: t,
|
|
383
499
|
text: n
|
|
384
500
|
});
|
|
@@ -429,49 +545,56 @@ var V = {
|
|
|
429
545
|
clearTimer(e) {
|
|
430
546
|
e.timer &&= (clearTimeout(e.timer), null);
|
|
431
547
|
}
|
|
432
|
-
},
|
|
548
|
+
}, fe = [
|
|
433
549
|
1e3,
|
|
434
550
|
2e3,
|
|
435
551
|
4e3,
|
|
436
552
|
8e3
|
|
437
|
-
],
|
|
438
|
-
function
|
|
553
|
+
], pe = 3;
|
|
554
|
+
function $(e) {
|
|
439
555
|
return (e.startsWith("0x") ? e.slice(2) : e).toLowerCase();
|
|
440
556
|
}
|
|
441
|
-
var
|
|
557
|
+
var me = class {
|
|
442
558
|
constructor(e, t = {}) {
|
|
443
|
-
this.emitter = new
|
|
559
|
+
this.emitter = new B(), this.seen = /* @__PURE__ */ new Set(), this.status = null, this.generation = 0, this.running = !1, this.starting = null, this.openTimer = null, this.wakeOpen = null;
|
|
444
560
|
let { user: n, infra: r } = e;
|
|
445
|
-
this.key = new
|
|
446
|
-
...
|
|
447
|
-
isLive: () => this.status ===
|
|
561
|
+
this.key = new P($(n.privateKey)), this.topic = r.chatTopic, this.nickname = n.nickname, this.openRetryMs = t.openRetryMs ?? fe, this.senderSettings = {
|
|
562
|
+
...Q,
|
|
563
|
+
isLive: () => this.status === J.LIVE,
|
|
448
564
|
...t.sender
|
|
449
565
|
};
|
|
450
566
|
let i = {
|
|
451
567
|
slotReadMs: r.socReadTimeout ?? 5e3,
|
|
452
568
|
feedReadMs: r.feedReadTimeout ?? 15e3,
|
|
453
569
|
writeMs: r.gsocWriteTimeout ?? 1e4
|
|
570
|
+
}, a = {
|
|
571
|
+
...r.noteSlotMs === void 0 ? {} : { slotMs: r.noteSlotMs },
|
|
572
|
+
...r.noteHeartbeatMs === void 0 ? {} : { heartbeatMs: r.noteHeartbeatMs },
|
|
573
|
+
...t.notes
|
|
454
574
|
};
|
|
455
|
-
this.source = t.source ??
|
|
575
|
+
this.source = t.source ?? G(new j(r.beeUrl), $(r.chatAddress), r.chatTopic, i, a.slotMs), this.write = t.write ?? q(new j(r.writeUrl ?? r.beeUrl), r.stamp ? $(r.stamp) : ie, $(r.gsocResourceId), r.gsocTopic, i);
|
|
576
|
+
let o = t.follower?.now ?? (() => Date.now()), s = {
|
|
577
|
+
pollIntervalMs: r.pollingInterval ?? 1e3,
|
|
578
|
+
...t.follower,
|
|
579
|
+
now: o
|
|
580
|
+
}, c = this.source;
|
|
581
|
+
this.notes = le(c) ? new Z(c, a, o) : null, this.follower = new ae(this.source, this.topic, {
|
|
456
582
|
entry: (e) => this.show(e),
|
|
457
|
-
skipped: (e, t, n) => this.emitter.emit(
|
|
583
|
+
skipped: (e, t, n) => this.emitter.emit(I.MESSAGE_SKIPPED, {
|
|
458
584
|
index: e,
|
|
459
585
|
reason: t,
|
|
460
586
|
detail: n
|
|
461
587
|
}),
|
|
462
588
|
status: (e) => this.setStatus(e),
|
|
463
|
-
error: (e) => this.emitter.emit(
|
|
464
|
-
}, {
|
|
465
|
-
|
|
466
|
-
...t.follower
|
|
467
|
-
}), this.history = new K(this.source, this.topic, {
|
|
468
|
-
skipped: (e) => this.emitter.emit(O.MESSAGE_SKIPPED, {
|
|
589
|
+
error: (e) => this.emitter.emit(I.ERROR, e)
|
|
590
|
+
}, s, this.notes), this.history = new se(this.source, this.topic, {
|
|
591
|
+
skipped: (e) => this.emitter.emit(I.MESSAGE_SKIPPED, {
|
|
469
592
|
index: -1,
|
|
470
593
|
reason: "history-row",
|
|
471
594
|
detail: `${e} rows of a history file`
|
|
472
595
|
}),
|
|
473
|
-
error: (e) => this.emitter.emit(
|
|
474
|
-
}), this.sender = this.makeSender();
|
|
596
|
+
error: (e) => this.emitter.emit(I.ERROR, e)
|
|
597
|
+
}, this.notes), this.sender = this.makeSender();
|
|
475
598
|
}
|
|
476
599
|
getEmitter() {
|
|
477
600
|
return this.emitter;
|
|
@@ -494,11 +617,11 @@ var Q = class {
|
|
|
494
617
|
this.running = !1, this.generation++, this.openTimer &&= (clearTimeout(this.openTimer), null), this.wakeOpen?.(), this.follower.stop(), this.sender.stop();
|
|
495
618
|
}
|
|
496
619
|
async sendMessage(e, t, n) {
|
|
497
|
-
if (t ===
|
|
620
|
+
if (t === u.REACTION) {
|
|
498
621
|
if (!n) throw Error("a reaction names the message it reacts to");
|
|
499
622
|
return this.sender.react(this.topic, n, e), null;
|
|
500
623
|
}
|
|
501
|
-
return
|
|
624
|
+
return L(this.sender.send({
|
|
502
625
|
topic: this.topic,
|
|
503
626
|
type: t,
|
|
504
627
|
target: n ?? "",
|
|
@@ -509,12 +632,12 @@ var Q = class {
|
|
|
509
632
|
return this.sender.retry(typeof e == "string" ? e : e.id);
|
|
510
633
|
}
|
|
511
634
|
async fetchPreviousMessages() {
|
|
512
|
-
this.emitter.emit(
|
|
635
|
+
this.emitter.emit(I.LOADING_PREVIOUS_MESSAGES, !0);
|
|
513
636
|
try {
|
|
514
637
|
let e = this.generation, t = await this.history.loadOlder();
|
|
515
638
|
return e === this.generation ? this.showRows(t) : [];
|
|
516
639
|
} finally {
|
|
517
|
-
this.emitter.emit(
|
|
640
|
+
this.emitter.emit(I.LOADING_PREVIOUS_MESSAGES, !1);
|
|
518
641
|
}
|
|
519
642
|
}
|
|
520
643
|
hasPreviousMessages() {
|
|
@@ -524,16 +647,16 @@ var Q = class {
|
|
|
524
647
|
return [...e].sort((e, t) => e.index >= 0 && t.index >= 0 ? e.index - t.index : e.index >= 0 || t.index >= 0 ? e.index >= 0 ? -1 : 1 : e.timestamp - t.timestamp);
|
|
525
648
|
}
|
|
526
649
|
async open(e) {
|
|
527
|
-
this.emitter.emit(
|
|
650
|
+
this.emitter.emit(I.LOADING_INIT, !0);
|
|
528
651
|
let t = 0;
|
|
529
652
|
for (; e === this.generation;) try {
|
|
530
653
|
let t = await this.history.open();
|
|
531
654
|
if (e !== this.generation) return;
|
|
532
|
-
this.showRows(t.rows), this.follower.start(t.startAt), this.emitter.emit(
|
|
655
|
+
this.showRows(t.rows), this.follower.start(t.startAt, t.notes), this.emitter.emit(I.LOADING_INIT, !1);
|
|
533
656
|
return;
|
|
534
657
|
} catch (n) {
|
|
535
658
|
if (e !== this.generation) return;
|
|
536
|
-
t++, this.emitter.emit(
|
|
659
|
+
t++, this.emitter.emit(I.ERROR, n), this.setStatus(J.RECONNECTING), t === pe && this.emitter.emit(I.CRITICAL_ERROR, n), await this.waitToOpen(this.openRetryMs[Math.min(t, this.openRetryMs.length) - 1] ?? 8e3);
|
|
537
660
|
}
|
|
538
661
|
}
|
|
539
662
|
waitToOpen(e) {
|
|
@@ -546,31 +669,31 @@ var Q = class {
|
|
|
546
669
|
});
|
|
547
670
|
}
|
|
548
671
|
makeSender() {
|
|
549
|
-
return new
|
|
550
|
-
pending: (e) => this.emitter.emit(
|
|
551
|
-
written: (e) => this.emitter.emit(
|
|
672
|
+
return new de(this.key, this.nickname, this.write, {
|
|
673
|
+
pending: (e) => this.emitter.emit(I.MESSAGE_REQUEST_INITIATED, L(e)),
|
|
674
|
+
written: (e) => this.emitter.emit(I.MESSAGE_REQUEST_UPLOADED, L(e)),
|
|
552
675
|
failed: (e, t) => {
|
|
553
|
-
this.emitter.emit(
|
|
676
|
+
this.emitter.emit(I.ERROR, t), this.emitter.emit(I.MESSAGE_REQUEST_ERROR, L(e));
|
|
554
677
|
},
|
|
555
678
|
confirmed: () => {}
|
|
556
679
|
}, this.senderSettings);
|
|
557
680
|
}
|
|
558
681
|
show(e) {
|
|
559
|
-
this.seen.has(e.seq) || (this.seen.add(e.seq), this.sender.confirm(e.msg), this.emitter.emit(
|
|
682
|
+
this.seen.has(e.seq) || (this.seen.add(e.seq), this.sender.confirm(e.msg), this.emitter.emit(I.MESSAGE_RECEIVED, R(e)));
|
|
560
683
|
}
|
|
561
684
|
showRows(e) {
|
|
562
685
|
let t = [];
|
|
563
686
|
for (let n of e) {
|
|
564
687
|
if (this.seen.has(n.seq)) continue;
|
|
565
688
|
this.seen.add(n.seq), this.sender.confirm(n.msg);
|
|
566
|
-
let e =
|
|
567
|
-
t.push(e), this.emitter.emit(
|
|
689
|
+
let e = R(n);
|
|
690
|
+
t.push(e), this.emitter.emit(I.MESSAGE_RECEIVED, e);
|
|
568
691
|
}
|
|
569
692
|
return t;
|
|
570
693
|
}
|
|
571
694
|
setStatus(e) {
|
|
572
|
-
e !== this.status && (this.status = e, this.emitter.emit(
|
|
695
|
+
e !== this.status && (this.status = e, this.emitter.emit(I.STATUS, e));
|
|
573
696
|
}
|
|
574
697
|
};
|
|
575
698
|
//#endregion
|
|
576
|
-
export {
|
|
699
|
+
export { B as ChatEmitter, e as ChatMessageError, t as DEFAULT_NOTE_HEARTBEAT_MS, n as DEFAULT_NOTE_SLOT_MS, I as EVENTS, J as FeedStatus, V as HeadLookupTimeoutError, r as MAX_ENTRY_BYTES, i as MAX_MESSAGE_BYTES, a as MAX_NAME_CHARACTERS, o as MAX_NOTE_BYTES, s as MAX_TEXT_CHARACTERS, c as MAX_TOPIC_CHARACTERS, l as MESSAGE_VERSION, u as MessageType, d as NOTE_VERSION, me as SwarmChat, X as UnreadableGatewayError, H as UnreadableSlotError, f as chatMessageSchema, p as checkChatMessage, m as countCharacters, h as createChatMessage, g as encodeChatMessage, _ as encodeSlotNote, v as feedEntrySchema, ee as hasValidSignature, te as historyFileSchema, y as historyLinkSchema, ne as historyRowSchema, b as newMessageId, x as noteAddress, S as noteIdentifier, C as noteIdentifierText, w as noteSlotEnd, T as noteSlotOf, E as parseChatMessage, D as parseFeedEntry, O as parseHistoryFile, k as parseSlotNote, A as signedBytes, re as slotNoteSchema };
|
package/dist/message/index.d.ts
CHANGED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Identifier, Reference } from '@ethersphere/bee-js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
/**
|
|
4
|
+
* Slot notes: the chat server's announcement, after a stretch of wall-clock time in which messages landed, of the
|
|
5
|
+
* newest feed slot it has written. A reader asks for a note only once its time slot is over, and asks for a feed slot
|
|
6
|
+
* only once a note has named it, so it never asks Bee for an address that is not written yet. Bee answers a chunk it
|
|
7
|
+
* could not find by skipping every peer it asked for that address for a minute, which is what made new messages
|
|
8
|
+
* arrive about forty seconds late while readers polled the next slot.
|
|
9
|
+
*
|
|
10
|
+
* Time slot `s` covers `[s * slotMs, (s + 1) * slotMs)` in Unix milliseconds. Its note is a single owner chunk signed
|
|
11
|
+
* by the chat feed's key, at the identifier keccak256(`<topic>/note/<slotMs>/<s>`).
|
|
12
|
+
*/
|
|
13
|
+
export declare const NOTE_VERSION = 1;
|
|
14
|
+
export declare const DEFAULT_NOTE_SLOT_MS = 2000;
|
|
15
|
+
export declare const DEFAULT_NOTE_HEARTBEAT_MS = 30000;
|
|
16
|
+
/** A note is far below one chunk, so it is a single owner chunk with its payload inline. */
|
|
17
|
+
export declare const MAX_NOTE_BYTES = 256;
|
|
18
|
+
export declare const slotNoteSchema: z.ZodObject<{
|
|
19
|
+
v: z.ZodLiteral<1>;
|
|
20
|
+
newest: z.ZodNumber;
|
|
21
|
+
writtenAt: z.ZodNumber;
|
|
22
|
+
}, z.core.$strict>;
|
|
23
|
+
export type SlotNote = z.infer<typeof slotNoteSchema>;
|
|
24
|
+
/** The time slot that holds the instant `timeMs`. */
|
|
25
|
+
export declare function noteSlotOf(timeMs: number, slotMs: number): number;
|
|
26
|
+
/** The instant time slot `slot` ends, which is the first instant of the next one. */
|
|
27
|
+
export declare function noteSlotEnd(slot: number, slotMs: number): number;
|
|
28
|
+
export declare function noteIdentifierText(topic: string, slotMs: number, slot: number): string;
|
|
29
|
+
export declare function noteIdentifier(topic: string, slotMs: number, slot: number): Identifier;
|
|
30
|
+
/**
|
|
31
|
+
* Where a note lives, the address `GET /chunks/<address>` reads it from: keccak256 of the identifier and the owner,
|
|
32
|
+
* the single owner chunk rule.
|
|
33
|
+
*/
|
|
34
|
+
export declare function noteAddress(topic: string, slotMs: number, slot: number, owner: string): Reference;
|
|
35
|
+
/** The payload, with its fields in this order, so two encodings of one note are the same bytes. */
|
|
36
|
+
export declare function encodeSlotNote(note: Omit<SlotNote, 'v'>): Uint8Array;
|
|
37
|
+
/** A note, or null for anything that fails the strict check, which a reader treats as no note at all. */
|
|
38
|
+
export declare function parseSlotNote(payload: Uint8Array): SlotNote | null;
|