@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/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
- import { ChatMessageError as e, MAX_ENTRY_BYTES as t, MAX_MESSAGE_BYTES as n, MAX_NAME_CHARACTERS as r, MAX_TEXT_CHARACTERS as i, MAX_TOPIC_CHARACTERS as a, MESSAGE_VERSION as o, MessageType as s, chatMessageSchema as c, checkChatMessage as l, countCharacters as u, createChatMessage as d, encodeChatMessage as f, feedEntrySchema as p, hasValidSignature as m, historyFileSchema as h, historyLinkSchema as g, historyRowSchema as _, newMessageId as v, parseChatMessage as y, parseFeedEntry as b, parseHistoryFile as x, signedBytes as S } from "./message.js";
2
- import { Bee as C, BeeResponseError as w, Identifier as T, PrivateKey as E, Topic as D } from "@ethersphere/bee-js";
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 O = {
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 k(e) {
17
- return j(e, -1, e.ts);
16
+ function L(e) {
17
+ return z(e, -1, e.ts);
18
18
  }
19
- function A(e) {
20
- return j(e.msg, e.seq, e.at);
19
+ function R(e) {
20
+ return z(e.msg, e.seq, e.at);
21
21
  }
22
- function j(e, t, n) {
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 M = class {
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
- }, N = class extends Error {
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
- }, P = class extends Error {
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 F(e) {
64
- return e instanceof w && (e.status === 404 || e.status === 500);
63
+ function U(e) {
64
+ return e instanceof M && (e.status === 404 || e.status === 500);
65
65
  }
66
- async function I(e, t) {
66
+ async function W(e, t) {
67
67
  try {
68
68
  return await t();
69
69
  } catch (t) {
70
- if (F(t)) return null;
71
- throw t instanceof w ? t : new P(e, { cause: t });
70
+ if (U(t)) return null;
71
+ throw t instanceof M ? t : new H(e, { cause: t });
72
72
  }
73
73
  }
74
- function L(e, t, n, r) {
75
- let i = D.fromString(n), a = (n) => e.feed.makeReader(i, t, R(n));
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
- readSlot: (e) => I(e, async () => (await a(r.slotReadMs).downloadPayload({ index: e })).payload.toUint8Array()),
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 = R(r.feedReadMs);
80
+ let n = K(i.feedReadMs);
80
81
  try {
81
- let { payload: r, feedIndex: a } = await e.feed.makeReader(i, t, n).downloadPayload();
82
+ let { payload: r, feedIndex: i } = await e.feed.makeReader(o, t, n).downloadPayload();
82
83
  return {
83
- index: Number(a.toBigInt()),
84
+ index: Number(i.toBigInt()),
84
85
  payload: r.toUint8Array()
85
86
  };
86
87
  } catch (e) {
87
- if (F(e)) return null;
88
- throw n.signal?.aborted ? new N({ cause: e }) : e;
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, R(r.feedReadMs))).toUint8Array();
93
+ return (await e.data.download(t, void 0, K(i.feedReadMs))).toUint8Array();
93
94
  }
94
95
  };
95
96
  }
96
- function R(e) {
97
+ function K(e) {
97
98
  return { signal: AbortSignal.timeout(e) };
98
99
  }
99
- var z = "0123456789abcdef".repeat(4);
100
- function B(e, t, n, r, i) {
101
- let a = new E(n), o = T.fromString(r);
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 }, R(i.writeMs));
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 V = {
109
+ var J = {
109
110
  LIVE: "live",
110
111
  RECONNECTING: "reconnecting",
111
112
  STALLED: "stalled"
112
- }, H = {
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
- }, U = class extends Error {
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
- }, W = class {
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
- ...H,
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
- start(e) {
149
- this.stop(), this.next = e, this.resetRefusals(), this.unreadablePolls = 0, this.gatewayFailures = 0, this.missed.clear(), this.status = null, this.running = !0, this.schedule(0);
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.settings.pollIntervalMs);
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 P) return "unreadable";
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, r = await this.read(n);
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 U(n);
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 = b(t, e, this.topic);
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(V.RECONNECTING), this.events.error(e);
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 ? V.STALLED : V.LIVE);
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
- }, G = 16, K = class {
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.readHead();
274
- if (e === null) return {
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
- let t = await this.newestLink(e.index, e.payload);
279
- if (t === null) return {
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 n = await this.readFile(t);
284
- return n === "refused" ? {
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: t.toSeq + 1,
289
- rows: n ?? []
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 N) return this.events.error(e), null;
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 = x(t, this.topic, e);
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 = b(t, e, this.topic);
317
- if (n.ok) return n.value.history;
318
- for (let t = e - 1; t >= Math.max(0, e - G); t--) {
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 P) return null;
364
+ if (e instanceof H) return null;
321
365
  throw e;
322
366
  });
323
367
  if (e === null) continue;
324
- let n = b(e, t, this.topic);
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 = x(await this.source.readFile(e.ref), this.topic, e);
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
- }, q = {
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
- }, J = class {
346
- constructor(e, t, n, r, i = q) {
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 } = d(this.key, {
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: s.REACTION,
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
- }, Y = [
548
+ }, fe = [
433
549
  1e3,
434
550
  2e3,
435
551
  4e3,
436
552
  8e3
437
- ], X = 3;
438
- function Z(e) {
553
+ ], pe = 3;
554
+ function $(e) {
439
555
  return (e.startsWith("0x") ? e.slice(2) : e).toLowerCase();
440
556
  }
441
- var Q = class {
557
+ var me = class {
442
558
  constructor(e, t = {}) {
443
- this.emitter = new M(), this.seen = /* @__PURE__ */ new Set(), this.status = null, this.generation = 0, this.running = !1, this.starting = null, this.openTimer = null, this.wakeOpen = null;
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 E(Z(n.privateKey)), this.topic = r.chatTopic, this.nickname = n.nickname, this.openRetryMs = t.openRetryMs ?? Y, this.senderSettings = {
446
- ...q,
447
- isLive: () => this.status === V.LIVE,
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 ?? L(new C(r.beeUrl), Z(r.chatAddress), r.chatTopic, i), this.write = t.write ?? B(new C(r.writeUrl ?? r.beeUrl), r.stamp ? Z(r.stamp) : z, Z(r.gsocResourceId), r.gsocTopic, i), this.follower = new W(this.source, this.topic, {
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(O.MESSAGE_SKIPPED, {
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(O.ERROR, e)
464
- }, {
465
- pollIntervalMs: r.pollingInterval ?? 1e3,
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(O.ERROR, e)
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 === s.REACTION) {
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 k(this.sender.send({
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(O.LOADING_PREVIOUS_MESSAGES, !0);
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(O.LOADING_PREVIOUS_MESSAGES, !1);
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(O.LOADING_INIT, !0);
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(O.LOADING_INIT, !1);
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(O.ERROR, n), this.setStatus(V.RECONNECTING), t === X && this.emitter.emit(O.CRITICAL_ERROR, n), await this.waitToOpen(this.openRetryMs[Math.min(t, this.openRetryMs.length) - 1] ?? 8e3);
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 J(this.key, this.nickname, this.write, {
550
- pending: (e) => this.emitter.emit(O.MESSAGE_REQUEST_INITIATED, k(e)),
551
- written: (e) => this.emitter.emit(O.MESSAGE_REQUEST_UPLOADED, k(e)),
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(O.ERROR, t), this.emitter.emit(O.MESSAGE_REQUEST_ERROR, k(e));
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(O.MESSAGE_RECEIVED, A(e)));
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 = A(n);
567
- t.push(e), this.emitter.emit(O.MESSAGE_RECEIVED, e);
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(O.STATUS, e));
695
+ e !== this.status && (this.status = e, this.emitter.emit(I.STATUS, e));
573
696
  }
574
697
  };
575
698
  //#endregion
576
- export { M as ChatEmitter, e as ChatMessageError, O as EVENTS, V as FeedStatus, N as HeadLookupTimeoutError, t as MAX_ENTRY_BYTES, n as MAX_MESSAGE_BYTES, r as MAX_NAME_CHARACTERS, i as MAX_TEXT_CHARACTERS, a as MAX_TOPIC_CHARACTERS, o as MESSAGE_VERSION, s as MessageType, Q as SwarmChat, U as UnreadableGatewayError, P as UnreadableSlotError, c as chatMessageSchema, l as checkChatMessage, u as countCharacters, d as createChatMessage, f as encodeChatMessage, p as feedEntrySchema, m as hasValidSignature, h as historyFileSchema, g as historyLinkSchema, _ as historyRowSchema, v as newMessageId, y as parseChatMessage, b as parseFeedEntry, x as parseHistoryFile, S as signedBytes };
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 };
@@ -1,2 +1,3 @@
1
1
  export * from './message.js';
2
2
  export * from './entry.js';
3
+ export * from './note.js';
@@ -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;