@pipeup/share 0.6.0 → 0.7.0-beta.1

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/cli.js CHANGED
@@ -166,48 +166,82 @@ function n(e3) {
166
166
  }
167
167
  var a = /^[A-Za-z0-9_-]{43}$/;
168
168
  var s = /* @__PURE__ */ new Map();
169
- async function d() {
169
+ async function l() {
170
170
  let t2 = await crypto.subtle.generateKey({ name: "Ed25519" }, false, ["sign", "verify"]);
171
171
  return { publicKey: e(new Uint8Array(await crypto.subtle.exportKey("raw", t2.publicKey))), privateKey: t2.privateKey };
172
172
  }
173
- async function l(t2, r2) {
173
+ async function d(t2, r2) {
174
174
  let n2 = await crypto.subtle.sign({ name: "Ed25519" }, t2.privateKey, r2);
175
175
  return e(new Uint8Array(n2));
176
176
  }
177
- var u = 1e4;
178
- var h = 2 ** 40;
179
- var c = 2 ** 47;
180
- var p = 1e4;
181
- var f = /^[0-9a-f]{8}$/;
182
- var m = /^[A-Za-z0-9_-]{43}$/;
183
- var y = /^[A-Za-z0-9_-]{43}$/;
184
- var g = /* @__PURE__ */ new Set(["create", "reply", "edit", "delete", "resolve", "reopen", "addressed"]);
185
- async function w(e3, t2) {
177
+ var u = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
178
+ Uint8Array.from([48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32]);
179
+ var c = ["Otter", "Fox", "Owl", "Bear", "Rabbit"];
180
+ var h = ["Red", "Orange", "Yellow", "Green", "Teal", "Blue", "Purple", "Pink", "Brown", "Grey"];
181
+ function p(e3) {
182
+ let t2 = 2166136261;
183
+ for (let r2 = 0; r2 < e3.length; r2++) t2 = Math.imul(t2 ^ e3.charCodeAt(r2), 16777619);
184
+ return t2 = Math.imul(t2 ^ t2 >>> 16, 2246822507), t2 = Math.imul(t2 ^ t2 >>> 13, 3266489909), (t2 ^ t2 >>> 16) >>> 0;
185
+ }
186
+ var f = (e3) => `${h[((e4) => Math.floor(p(e4) / c.length) % h.length)(e3)]} ${c[((e4) => p(e4) % c.length)(e3)]}`;
187
+ var m = (e3) => {
188
+ let t2 = e3.name.replace(/^AI assistant · /, "") || f(e3.author);
189
+ return e3.alike ? `${t2} \xB7 ${(function(e4) {
190
+ let t3 = p(`id:${e4}`), r2 = "";
191
+ for (let e5 = 0; e5 < 4; e5++, t3 >>>= 5) r2 += "0123456789ABCDEFGHJKMNPQRSTVWXYZ"[31 & t3];
192
+ return r2;
193
+ })(e3.author)}` : t2;
194
+ };
195
+ var g = "AI assistant";
196
+ var y = (e3) => e3 === g || e3.startsWith(`${g} `);
197
+ var w = 1e4;
198
+ var b = 2 ** 40;
199
+ var x = 2 ** 47;
200
+ var v = 1e4;
201
+ var k = /^[0-9a-f]{8}$/;
202
+ var A = /^[A-Za-z0-9_-]{43}$/;
203
+ var E = /^(?:[^\s@<>"]{1,64}@[^\s@<>"]{1,190}\.[^\s@<>"]{2,24}|gh:[A-Za-z0-9-]{1,39})$/;
204
+ var $ = /^(?:[A-Za-z0-9_-]{43}|(?:host|gh):[A-Za-z0-9._@-]{1,64})$/;
205
+ var M = /^[A-Za-z0-9_-]{43}$/;
206
+ var S = /* @__PURE__ */ new Set(["create", "reply", "edit", "delete", "resolve", "reopen", "addressed", "react", "unreact", "seen", "contact", "uncontact"]);
207
+ var C = /^(?:\p{Regional_Indicator}{2}|[#*0-9]\uFE0F?\u20E3|\p{Extended_Pictographic}(?:\uFE0F|\p{Emoji_Modifier})?(?:\u200D\p{Extended_Pictographic}(?:\uFE0F|\p{Emoji_Modifier})?)*)$/u;
208
+ var N = (e3) => "string" == typeof e3 && C.test(e3) && [...e3].length <= 16;
209
+ async function O(e3, t2) {
186
210
  if (e3.author !== t2.publicKey) throw new Error("pipeup: an op must be signed by its author");
187
- return { body: e3, sig: await l(t2, r(n(e3))) };
211
+ return { body: e3, sig: await d(t2, r(n(e3))) };
188
212
  }
189
- var b = (e3, t2) => "string" == typeof e3 && e3.length <= t2;
190
- var x = (e3) => !!e3 && "object" == typeof e3;
191
- var v = (e3) => "number" == typeof e3 && Number.isFinite(e3) && e3 >= 0 && e3 <= 1;
192
- function A(e3) {
193
- if (!x(e3)) return false;
213
+ var P2 = (e3, t2) => "string" == typeof e3 && e3.length <= t2;
214
+ var q = (e3) => !!e3 && "object" == typeof e3;
215
+ var j = (e3) => "number" == typeof e3 && Number.isFinite(e3) && e3 >= 0 && e3 <= 1;
216
+ function T(e3) {
217
+ if (!q(e3)) return false;
194
218
  let { id: t2, path: r2, fingerprint: n2, snapshot: i, quote: o, point: a2, view: s2 } = e3;
195
- if (!(b(r2, 2e3) && b(i, 500) && "string" == typeof n2 && f.test(n2) && (void 0 === t2 || b(t2, 200) && "" !== t2) && (void 0 === o || x(o) && b(o.exact, p) && "" !== o.exact && b(o.prefix, 64) && b(o.suffix, 64)) && (void 0 === a2 || x(a2) && v(a2.x) && v(a2.y)))) return false;
219
+ if (!(P2(r2, 2e3) && P2(i, 500) && "string" == typeof n2 && k.test(n2) && (void 0 === t2 || P2(t2, 200) && "" !== t2) && (void 0 === o || q(o) && P2(o.exact, v) && "" !== o.exact && P2(o.prefix, 64) && P2(o.suffix, 64)) && (void 0 === a2 || q(a2) && j(a2.x) && j(a2.y)))) return false;
196
220
  if (void 0 !== s2) {
197
- if (!x(s2) || Array.isArray(s2)) return false;
221
+ if (!q(s2) || Array.isArray(s2)) return false;
198
222
  let e4 = Object.entries(s2);
199
- if (e4.length > 16 || !e4.every(([e5, t3]) => e5.length <= 64 && b(t3, 200))) return false;
223
+ if (e4.length > 16 || !e4.every(([e5, t3]) => e5.length <= 64 && P2(t3, 200))) return false;
200
224
  }
201
225
  return true;
202
226
  }
203
- function k(e3) {
204
- if (!x(e3)) return false;
227
+ function I(e3) {
228
+ if (!q(e3)) return false;
205
229
  let { body: t2, sig: r2 } = e3;
206
- if ("string" != typeof r2 || !x(t2) || JSON.stringify({ body: t2, sig: r2 }).length > 262144) return false;
230
+ if ("string" != typeof r2 || !q(t2) || JSON.stringify({ body: t2, sig: r2 }).length > 262144) return false;
207
231
  let n2 = t2;
208
- return !(1 !== n2.v || "string" != typeof n2.id || !m.test(n2.id) || !g.has(n2.kind) || !b(n2.doc, 64) || "" === n2.doc || "string" != typeof n2.thread || !m.test(n2.thread) || "string" != typeof n2.author || !y.test(n2.author) || "string" != typeof n2.name || n2.name.length > 80 || n2.name !== n2.name.trim() || "number" != typeof n2.clock || !Number.isSafeInteger(n2.clock) || n2.clock < 1 || n2.clock > h || "number" != typeof n2.at || !Number.isSafeInteger(n2.at) || n2.at < 0 || n2.at > c || void 0 !== n2.text && (!b(n2.text, u) || n2.text !== n2.text.trim()) || void 0 !== n2.target && ("string" != typeof n2.target || !m.test(n2.target)) || ("create" === n2.kind || "reply" === n2.kind || "edit" === n2.kind || "addressed" === n2.kind) && !n2.text || ("reply" === n2.kind || "edit" === n2.kind || "delete" === n2.kind) && !n2.target || "create" === n2.kind && (n2.id !== n2.thread || !A(n2.anchor)));
232
+ if (1 !== n2.v || "string" != typeof n2.id || !A.test(n2.id) || !S.has(n2.kind) || !P2(n2.doc, 64) || "" === n2.doc || "string" != typeof n2.thread || !A.test(n2.thread) || "string" != typeof n2.author || !M.test(n2.author) || "string" != typeof n2.name || n2.name.length > 80 || n2.name !== n2.name.trim() || "number" != typeof n2.clock || !Number.isSafeInteger(n2.clock) || n2.clock < 1 || n2.clock > b || "number" != typeof n2.at || !Number.isSafeInteger(n2.at) || n2.at < 0 || n2.at > x || void 0 !== n2.text && (!P2(n2.text, w) || n2.text !== n2.text.trim()) || void 0 !== n2.target && ("string" != typeof n2.target || !A.test(n2.target)) || ("create" === n2.kind || "reply" === n2.kind || "edit" === n2.kind || "addressed" === n2.kind) && !n2.text || ("reply" === n2.kind || "edit" === n2.kind || "delete" === n2.kind) && !n2.target) return false;
233
+ if (void 0 !== n2.mentions) {
234
+ if ("create" !== n2.kind && "reply" !== n2.kind && "edit" !== n2.kind || !Array.isArray(n2.mentions) || n2.mentions.length > 20) return false;
235
+ let e4 = n2.text?.length ?? 0;
236
+ for (let t3 of n2.mentions) {
237
+ if (!q(t3) || !Number.isSafeInteger(t3.start) || !Number.isSafeInteger(t3.end)) return false;
238
+ let { start: r3, end: n3, ref: i } = t3;
239
+ if (r3 < 0 || n3 <= r3 || n3 > e4 || "string" != typeof i || !$.test(i)) return false;
240
+ }
241
+ }
242
+ return !(!("contact" !== n2.kind || "string" == typeof n2.text && E.test(n2.text)) || "seen" === n2.kind && !n2.target || !("react" !== n2.kind && "unreact" !== n2.kind || n2.target && N(n2.text)) || !("create" !== n2.kind || n2.id === n2.thread && T(n2.anchor)));
209
243
  }
210
- function E(i) {
244
+ function F(i) {
211
245
  return (async function(r2, n2, i2) {
212
246
  if (!a.test(r2)) return false;
213
247
  try {
@@ -226,17 +260,18 @@ function E(i) {
226
260
  }
227
261
  })(i.body.author, r(n(i.body)), i.sig);
228
262
  }
229
- function $(e3, t2) {
263
+ function D(e3, t2) {
230
264
  return e3.body.clock !== t2.body.clock ? e3.body.clock - t2.body.clock : e3.body.at !== t2.body.at ? e3.body.at - t2.body.at : e3.body.author !== t2.body.author ? e3.body.author < t2.body.author ? -1 : 1 : e3.body.id !== t2.body.id ? e3.body.id < t2.body.id ? -1 : 1 : 0;
231
265
  }
232
- function M(e3, t2, r2, n2, i) {
233
- return { id: e3, author: t2, name: r2, text: n2, at: i, edited: false, deleted: false, replies: [] };
266
+ function R(e3, t2, r2, n2, i, o) {
267
+ let a2 = { id: e3, author: t2, name: r2, text: n2, at: i, edited: false, deleted: false, replies: [], reactions: [] };
268
+ return o?.length && (a2.mentions = o), a2;
234
269
  }
235
- async function S(t2) {
270
+ async function U(t2) {
236
271
  let i = { ...t2, id: void 0, thread: "create" === t2.kind ? void 0 : t2.thread }, o = await crypto.subtle.digest("SHA-256", r(n(i)));
237
272
  return e(new Uint8Array(o));
238
273
  }
239
- var C = class {
274
+ var K = class {
240
275
  constructor(e3) {
241
276
  this.doc = e3;
242
277
  }
@@ -253,85 +288,113 @@ var C = class {
253
288
  threads() {
254
289
  return (function(e3) {
255
290
  let t2 = /* @__PURE__ */ new Map(), r2 = /* @__PURE__ */ new Map(), n2 = /* @__PURE__ */ new Map();
256
- for (let { body: i } of [...e3].sort($)) {
257
- if (i.name && n2.set(i.author, i.name), "create" === i.kind) {
258
- if (t2.has(i.id) || !i.anchor || void 0 === i.text) continue;
259
- let e5 = M(i.id, i.author, i.name, i.text, i.at);
260
- t2.set(i.id, { id: i.id, anchor: i.anchor, root: e5, resolved: false }), r2.set(i.id, { comment: e5, thread: i.id });
291
+ for (let { body: i2 } of [...e3].sort(D)) {
292
+ if (i2.name && n2.set(i2.author, i2.name), "create" === i2.kind) {
293
+ if (t2.has(i2.id) || !i2.anchor || void 0 === i2.text) continue;
294
+ let e5 = R(i2.id, i2.author, i2.name, i2.text, i2.at, i2.mentions);
295
+ t2.set(i2.id, { id: i2.id, anchor: i2.anchor, root: e5, resolved: false }), r2.set(i2.id, { comment: e5, thread: i2.id });
261
296
  continue;
262
297
  }
263
- let e4 = t2.get(i.thread);
298
+ let e4 = t2.get(i2.thread);
264
299
  if (!e4) continue;
265
- if ("resolve" === i.kind || "reopen" === i.kind) {
266
- e4.resolved = "resolve" === i.kind, "reopen" === i.kind && delete e4.addressed;
300
+ if ("resolve" === i2.kind || "reopen" === i2.kind) {
301
+ e4.resolved = "resolve" === i2.kind, "reopen" === i2.kind && delete e4.addressed;
267
302
  continue;
268
303
  }
269
- if ("addressed" === i.kind) {
270
- void 0 !== i.text && (e4.addressed = { text: i.text, author: i.author, name: i.name, at: i.at });
304
+ if ("addressed" === i2.kind) {
305
+ void 0 !== i2.text && (e4.addressed = { text: i2.text, author: i2.author, name: i2.name, at: i2.at });
271
306
  continue;
272
307
  }
273
- let o = i.target ? r2.get(i.target) : void 0;
274
- if (o && o.thread === i.thread) {
275
- if ("reply" === i.kind) {
276
- if (r2.has(i.id) || void 0 === i.text) continue;
277
- let t3 = M(i.id, i.author, i.name, i.text, i.at);
278
- e4.root.replies.push(t3), r2.set(i.id, { comment: t3, thread: i.thread });
308
+ let o2 = i2.target ? r2.get(i2.target) : void 0;
309
+ if (o2 && o2.thread === i2.thread) {
310
+ if ("reply" === i2.kind) {
311
+ if (r2.has(i2.id) || void 0 === i2.text) continue;
312
+ let t3 = R(i2.id, i2.author, i2.name, i2.text, i2.at, i2.mentions);
313
+ e4.root.replies.push(t3), r2.set(i2.id, { comment: t3, thread: i2.thread });
314
+ continue;
315
+ }
316
+ if ("seen" === i2.kind) {
317
+ let e5 = o2.comment.seenBy ??= [];
318
+ e5.includes(i2.author) || e5.push(i2.author);
319
+ continue;
320
+ }
321
+ if ("react" === i2.kind || "unreact" === i2.kind) {
322
+ if (void 0 === i2.text || o2.comment.deleted) continue;
323
+ let e5 = o2.comment.reactions, t3 = e5.find((e6) => e6.emoji === i2.text);
324
+ if ("react" === i2.kind) {
325
+ let r3 = { author: i2.author, name: i2.name };
326
+ t3 ? t3.by.some((e6) => e6.author === i2.author) || t3.by.push(r3) : e5.length < 12 && e5.push({ emoji: i2.text, by: [r3] });
327
+ } else t3 && (t3.by = t3.by.filter((e6) => e6.author !== i2.author), t3.by.length || e5.splice(e5.indexOf(t3), 1));
279
328
  continue;
280
329
  }
281
- i.author !== o.comment.author || o.comment.deleted || ("edit" === i.kind && void 0 !== i.text && (o.comment.text = i.text, o.comment.edited = true), "delete" === i.kind && (o.comment.text = "", o.comment.deleted = true));
330
+ i2.author !== o2.comment.author || o2.comment.deleted || ("edit" === i2.kind && void 0 !== i2.text && (o2.comment.text = i2.text, o2.comment.edited = true, i2.mentions ? o2.comment.mentions = i2.mentions : delete o2.comment.mentions), "delete" === i2.kind && (o2.comment.text = "", o2.comment.deleted = true, o2.comment.reactions = [], delete o2.comment.mentions));
282
331
  }
283
332
  }
284
- for (let { comment: e4 } of r2.values()) e4.name || (e4.unnamed = true, e4.name = n2.get(e4.author) ?? "");
333
+ for (let { comment: e4 } of r2.values()) {
334
+ e4.name || (e4.unnamed = true, e4.name = n2.get(e4.author) ?? "");
335
+ for (let t3 of e4.reactions) for (let e5 of t3.by) e5.name ||= n2.get(e5.author) ?? "";
336
+ }
337
+ let i = /* @__PURE__ */ new Map(), o = (e4, t3) => {
338
+ let r3 = m({ author: e4, name: t3 }).toLowerCase();
339
+ (i.get(r3) ?? i.set(r3, /* @__PURE__ */ new Set()).get(r3)).add(e4);
340
+ };
341
+ for (let { comment: e4 } of r2.values()) {
342
+ o(e4.author, e4.name);
343
+ for (let t3 of e4.reactions) for (let e5 of t3.by) o(e5.author, e5.name);
344
+ }
345
+ let a2 = (e4, t3) => (i.get(m({ author: e4, name: t3 }).toLowerCase())?.size ?? 0) > 1;
346
+ for (let { comment: e4 } of r2.values()) {
347
+ a2(e4.author, e4.name) && (e4.alike = true);
348
+ for (let t3 of e4.reactions) for (let e5 of t3.by) a2(e5.author, e5.name) && (e5.alike = true);
349
+ }
285
350
  return [...t2.values()];
286
351
  })(this.ops.values());
287
352
  }
288
353
  async add(e3) {
289
354
  let t2 = [];
290
355
  for (let r2 of e3) {
291
- if (!k(r2) || r2.body.doc !== this.doc || this.ops.has(r2.body.id) || r2.body.id !== await S(r2.body) || !await E(r2)) continue;
356
+ if (!I(r2) || r2.body.doc !== this.doc || this.ops.has(r2.body.id) || r2.body.id !== await U(r2.body) || !await F(r2)) continue;
292
357
  let e4 = { body: r2.body, sig: r2.sig };
293
358
  this.ops.set(e4.body.id, e4), this.clock = Math.max(this.clock, e4.body.clock), "create" === e4.body.kind && this.createAt.set(e4.body.author, Math.max(this.createAt.get(e4.body.author) ?? 0, e4.body.at)), this.threadAt.set(e4.body.thread, Math.max(this.threadAt.get(e4.body.thread) ?? 0, e4.body.at)), t2.push(e4);
294
359
  }
295
360
  return t2;
296
361
  }
297
362
  async append(e3, t2, r2) {
298
- let n2 = { v: 1, ...r2, id: "", thread: "create" === r2.kind ? "" : r2.thread, doc: this.doc, author: e3.publicKey, name: t2, clock: Math.min(this.clock + 1, h), at: Math.min(Math.max(Date.now(), "create" === r2.kind ? (this.createAt.get(e3.publicKey) ?? 0) + 1 : (this.threadAt.get(r2.thread) ?? 0) + 1), c) };
299
- n2.id = await S(n2);
363
+ let n2 = { v: 1, ...r2, id: "", thread: "create" === r2.kind ? "" : r2.thread, doc: this.doc, author: e3.publicKey, name: t2, clock: Math.min(this.clock + 1, b), at: Math.min(Math.max(Date.now(), "create" === r2.kind ? (this.createAt.get(e3.publicKey) ?? 0) + 1 : (this.threadAt.get(r2.thread) ?? 0) + 1), x) };
364
+ n2.id = await U(n2);
300
365
  let i = this.ops.get(n2.id);
301
366
  if (i) return i;
302
367
  "create" === n2.kind && (n2.thread = n2.id);
303
- let [o] = await this.add([await w(n2, e3)]);
368
+ let [o] = await this.add([await O(n2, e3)]);
304
369
  if (!o) throw new Error("pipeup: could not record this change");
305
370
  return o;
306
371
  }
307
372
  };
308
- var j = "AI assistant";
309
- var I = (e3) => e3 === j || e3.startsWith(`${j} `);
310
- var D = /^([A-Za-z0-9_-]{16}):([A-Za-z0-9_-]{43})$/;
311
- function F() {
373
+ var H = /^([A-Za-z0-9_-]{16}):([A-Za-z0-9_-]{43})$/;
374
+ function L() {
312
375
  return `${(function(t2 = 16) {
313
376
  return e(crypto.getRandomValues(new Uint8Array(t2)));
314
377
  })(12)}:${e(crypto.getRandomValues(new Uint8Array(32)))}`;
315
378
  }
316
- async function U(e3) {
317
- let r2 = D.exec(e3.trim()), n2 = r2?.[1], i = r2?.[2];
379
+ async function _(e3) {
380
+ let r2 = H.exec(e3.trim()), n2 = r2?.[1], i = r2?.[2];
318
381
  if (!n2 || !i) throw new Error('pipeup: data-pipeup-doc must look like "<id>:<key>", as made by newDocumentAttribute()');
319
382
  return { id: n2, key: await crypto.subtle.importKey("raw", t(i), { name: "AES-GCM" }, false, ["encrypt", "decrypt"]) };
320
383
  }
321
- async function R(t2, n2, i) {
384
+ async function z(t2, n2, i) {
322
385
  let o = crypto.getRandomValues(new Uint8Array(12)), a2 = await crypto.subtle.encrypt({ name: "AES-GCM", iv: o, additionalData: r(i) }, t2, r(n2));
323
386
  return { iv: e(o), data: e(new Uint8Array(a2)) };
324
387
  }
325
- var H = (e3) => `pipeup-file:${e3}`;
326
- var L = /^[a-z][a-z0-9-]{1,23}\/[a-z][a-z0-9-]{0,31}$/;
327
- var z = class extends Error {
388
+ var B = (e3) => `pipeup-file:${e3}`;
389
+ var G = /^[a-z][a-z0-9-]{1,23}\/[a-z][a-z0-9-]{0,31}$/;
390
+ var Z = class extends Error {
328
391
  constructor(e3, t2, r2 = 1) {
329
392
  super("pipeup: shown here, but not saved yet"), this.id = e3, this.cause = t2, this.count = r2, this.name = "UnsavedChangeError";
330
393
  }
331
394
  };
332
- var K = class e2 {
395
+ var J = class e2 {
333
396
  constructor(e3, t2) {
334
- this.log = e3, this.options = t2, this.displayName = t2.name.trim() ? B(t2.name) : "";
397
+ this.log = e3, this.options = t2, this.displayName = t2.name.trim() ? Q(t2.name) : "";
335
398
  }
336
399
  listeners = /* @__PURE__ */ new Set();
337
400
  cache = [];
@@ -341,7 +404,7 @@ var K = class e2 {
341
404
  unsaved = [];
342
405
  static async open(t2) {
343
406
  if ("string" != typeof t2.doc || "" === t2.doc || t2.doc.length > 64) throw new Error("pipeup: the document id must be 1 to 64 characters");
344
- let r2 = new C(t2.doc), n2 = await t2.store.load(t2.doc), i = n2.length - (await r2.add(n2)).length;
407
+ let r2 = new K(t2.doc), n2 = await t2.store.load(t2.doc), i = n2.length - (await r2.add(n2)).length;
345
408
  i && console.warn(`pipeup: ${i} saved change${1 === i ? "" : "s"} on this page couldn't be read; kept as saved`);
346
409
  let o = new e2(r2, t2);
347
410
  return o.refresh(false), o;
@@ -356,7 +419,7 @@ var K = class e2 {
356
419
  return this.displayName;
357
420
  }
358
421
  set name(e3) {
359
- this.displayName = B(e3), this.refresh(true, [], "");
422
+ this.displayName = Q(e3), this.refresh(true, [], "");
360
423
  }
361
424
  threads() {
362
425
  return this.cache;
@@ -367,16 +430,16 @@ var K = class e2 {
367
430
  onChange(e3) {
368
431
  return this.listeners.add(e3), () => this.listeners.delete(e3);
369
432
  }
370
- comment(e3, t2) {
371
- return this.enqueue(() => this.write({ kind: "create", anchor: e3, text: _(t2) }));
433
+ comment(e3, t2, r2) {
434
+ return this.enqueue(() => this.write({ kind: "create", anchor: e3, text: V(t2), ...r2?.length ? { mentions: r2 } : {} }));
372
435
  }
373
- reply(e3, t2) {
374
- return this.enqueue(() => this.write({ kind: "reply", thread: this.find(e3).thread, target: e3, text: _(t2) }));
436
+ reply(e3, t2, r2) {
437
+ return this.enqueue(() => this.write({ kind: "reply", thread: this.find(e3).thread, target: e3, text: V(t2), ...r2?.length ? { mentions: r2 } : {} }));
375
438
  }
376
- edit(e3, t2) {
439
+ edit(e3, t2, r2) {
377
440
  return this.enqueue(async () => {
378
- let { thread: r2 } = this.mine(e3);
379
- await this.write({ kind: "edit", thread: r2, target: e3, text: _(t2) });
441
+ let { thread: n2 } = this.mine(e3);
442
+ await this.write({ kind: "edit", thread: n2, target: e3, text: V(t2), ...r2?.length ? { mentions: r2 } : {} });
380
443
  });
381
444
  }
382
445
  remove(e3) {
@@ -385,6 +448,64 @@ var K = class e2 {
385
448
  await this.write({ kind: "delete", thread: t2, target: e3 });
386
449
  });
387
450
  }
451
+ contact(e3) {
452
+ let t2 = e3.trim();
453
+ return E.test(t2) ? this.enqueue(async () => {
454
+ await this.write({ kind: "contact", thread: this.me, text: t2 });
455
+ }) : Promise.reject(new Error("pipeup: that isn't an email address or a GitHub name"));
456
+ }
457
+ uncontact() {
458
+ return this.enqueue(async () => {
459
+ await this.write({ kind: "uncontact", thread: this.me });
460
+ });
461
+ }
462
+ contacts() {
463
+ let e3 = /* @__PURE__ */ new Map();
464
+ for (let { body: t2 } of [...this.log.all()].sort(D)) "contact" === t2.kind && t2.text ? e3.set(t2.author, t2.text) : "uncontact" === t2.kind && e3.delete(t2.author);
465
+ return e3;
466
+ }
467
+ get portable() {
468
+ return this.options.identity.privateKey.extractable;
469
+ }
470
+ identityCode() {
471
+ return (async function(e3) {
472
+ if (!e3.privateKey.extractable) return null;
473
+ let r2 = await crypto.subtle.exportKey("jwk", e3.privateKey);
474
+ return r2.d ? (function(e4) {
475
+ let t2 = ((function(e5) {
476
+ let t3 = 0, r3 = 0, n2 = "";
477
+ for (let i of e5) for (r3 = r3 << 8 | i, t3 += 8; t3 >= 5; ) n2 += u[r3 >>> t3 - 5 & 31], t3 -= 5;
478
+ return t3 && (n2 += u[r3 << 5 - t3 & 31]), n2;
479
+ })(e4) + (function(e5) {
480
+ let t3 = 2166136261;
481
+ for (let r4 of e5) t3 = Math.imul(t3 ^ r4, 16777619);
482
+ t3 = Math.imul(t3 ^ t3 >>> 15, 739982445);
483
+ let r3 = "";
484
+ for (let e6 = 0; e6 < 4; e6++, t3 >>>= 5) r3 += u[31 & t3];
485
+ return r3;
486
+ })(e4)).match(/.{1,4}/g).join("-");
487
+ return `PU1-${t2}`;
488
+ })(t(r2.d)) : null;
489
+ })(this.options.identity);
490
+ }
491
+ seen(e3) {
492
+ return this.enqueue(async () => {
493
+ for (let t2 of e3) {
494
+ let e4 = this.comments.get(t2);
495
+ e4 && !e4.comment.seenBy?.includes(this.me) && await this.write({ kind: "seen", thread: e4.thread, target: t2 });
496
+ }
497
+ });
498
+ }
499
+ react(e3, t2) {
500
+ return this.enqueue(async () => {
501
+ await this.write({ kind: "react", thread: this.find(e3).thread, target: e3, text: W(t2) });
502
+ });
503
+ }
504
+ unreact(e3, t2) {
505
+ return this.enqueue(async () => {
506
+ await this.write({ kind: "unreact", thread: this.find(e3).thread, target: e3, text: W(t2) });
507
+ });
508
+ }
388
509
  resolve(e3) {
389
510
  return this.enqueue(async () => {
390
511
  await this.write({ kind: "resolve", thread: this.thread(e3) });
@@ -396,8 +517,8 @@ var K = class e2 {
396
517
  });
397
518
  }
398
519
  sign(e3, t2) {
399
- if (!L.test(e3)) throw new Error('pipeup: a signing purpose looks like "<add-on id>/<name>"');
400
- return l(this.options.identity, r(`pipeup:${e3}
520
+ if (!G.test(e3)) throw new Error('pipeup: a signing purpose looks like "<add-on id>/<name>"');
521
+ return d(this.options.identity, r(`pipeup:${e3}
401
522
  ${t2}`));
402
523
  }
403
524
  flush() {
@@ -405,7 +526,7 @@ ${t2}`));
405
526
  }
406
527
  exportFile() {
407
528
  return (async function(e3, t2, r2) {
408
- let n2 = r2 ? { pipeup: 1, doc: e3, sealed: true, ...await R(r2, JSON.stringify(t2), H(e3)) } : { pipeup: 1, doc: e3, sealed: false, ops: t2 };
529
+ let n2 = r2 ? { pipeup: 1, doc: e3, sealed: true, ...await z(r2, JSON.stringify(t2), B(e3)) } : { pipeup: 1, doc: e3, sealed: false, ops: t2 };
409
530
  return JSON.stringify(n2, null, 2);
410
531
  })(this.options.doc, this.log.all(), this.options.key);
411
532
  }
@@ -431,7 +552,7 @@ ${t2}`));
431
552
  } catch {
432
553
  throw new Error("pipeup: this feedback could not be opened with this document");
433
554
  }
434
- })(i, { iv: o.iv, data: o.data }, H(n2));
555
+ })(i, { iv: o.iv, data: o.data }, B(n2));
435
556
  } catch {
436
557
  throw new Error("pipeup: this feedback could not be opened with this document's key");
437
558
  }
@@ -450,7 +571,7 @@ ${t2}`));
450
571
  try {
451
572
  await this.persist();
452
573
  } catch (e4) {
453
- throw r2.length > 0 ? new z(r2[0].body.id, e4, r2.length) : e4;
574
+ throw r2.length > 0 ? new Z(r2[0].body.id, e4, r2.length) : e4;
454
575
  }
455
576
  return r2.length;
456
577
  });
@@ -466,7 +587,7 @@ ${t2}`));
466
587
  try {
467
588
  await this.persist();
468
589
  } catch (e4) {
469
- throw new z(t2.body.id, e4);
590
+ throw new Z(t2.body.id, e4);
470
591
  }
471
592
  return t2.body.id;
472
593
  }
@@ -503,19 +624,23 @@ ${t2}`));
503
624
  return e3;
504
625
  }
505
626
  };
506
- function _(e3) {
627
+ function V(e3) {
507
628
  let t2 = e3.trim();
508
629
  if (!t2) throw new Error("pipeup: a comment needs some words");
509
- if (t2.length > u) throw new Error(`pipeup: comments are limited to ${u} characters`);
630
+ if (t2.length > w) throw new Error(`pipeup: comments are limited to ${w} characters`);
510
631
  return t2;
511
632
  }
512
- function B(e3) {
633
+ function W(e3) {
634
+ if (!N(e3)) throw new Error("pipeup: a reaction is one emoji");
635
+ return e3;
636
+ }
637
+ function Q(e3) {
513
638
  let t2 = e3.replace(/\s+/g, " ").trim();
514
639
  if (!t2 || t2.length > 80) throw new Error("pipeup: choose a name of 1 to 80 characters");
515
- if (I(t2)) throw new Error("pipeup: that name is kept for AI replies; choose another");
640
+ if (y(t2)) throw new Error("pipeup: that name is kept for AI replies; choose another");
516
641
  return t2;
517
642
  }
518
- var G = class {
643
+ var X = class {
519
644
  docs = /* @__PURE__ */ new Map();
520
645
  profile = null;
521
646
  async load(e3) {
@@ -891,9 +1016,9 @@ function setAttribute(html, name, value) {
891
1016
  const old = attr(name).exec(tag);
892
1017
  let next;
893
1018
  if (old) {
894
- const q = old[3][0] === "'" ? "'" : '"';
895
- const v2 = q === "'" ? value.replace(/&/g, "&amp;").replace(/'/g, "&#39;") : escaped;
896
- next = tag.slice(0, old.index) + `${old[1]}${name}${old[2]}${q}${v2}${q}` + tag.slice(old.index + old[0].length);
1019
+ const q2 = old[3][0] === "'" ? "'" : '"';
1020
+ const v2 = q2 === "'" ? value.replace(/&/g, "&amp;").replace(/'/g, "&#39;") : escaped;
1021
+ next = tag.slice(0, old.index) + `${old[1]}${name}${old[2]}${q2}${v2}${q2}` + tag.slice(old.index + old[0].length);
897
1022
  } else {
898
1023
  const at = tag.length - 1;
899
1024
  next = `${tag.slice(0, at)} ${name}="${escaped}"${tag.slice(at)}`;
@@ -984,12 +1109,12 @@ function server(value, mailbox) {
984
1109
  return mailbox ? value.replace(/\/+$/, "") : u2.origin + u2.pathname;
985
1110
  }
986
1111
  async function feedbackOps(file, docAttr, io) {
987
- const { id, key } = await U(docAttr);
988
- const doc = await K.open({
1112
+ const { id, key } = await _(docAttr);
1113
+ const doc = await J.open({
989
1114
  doc: id,
990
1115
  key,
991
- store: new G(),
992
- identity: await d(),
1116
+ store: new X(),
1117
+ identity: await l(),
993
1118
  name: ""
994
1119
  });
995
1120
  try {
@@ -1015,11 +1140,11 @@ Run the same command with --new-doc and I will write a fresh one into the page.
1015
1140
  );
1016
1141
  throw new UsageError("the page needs a document identity");
1017
1142
  }
1018
- docAttr = F();
1143
+ docAttr = L();
1019
1144
  html = setAttribute(html, "data-pipeup-doc", docAttr);
1020
1145
  io.out("Gave the page a new document identity (data-pipeup-doc).");
1021
1146
  }
1022
- const { id } = await U(docAttr).catch(() => {
1147
+ const { id } = await _(docAttr).catch(() => {
1023
1148
  throw new UsageError("data-pipeup-doc in the page isn't a valid document identity");
1024
1149
  });
1025
1150
  const from = typeof flags.get("from") === "string" ? flags.get("from") : null;
@@ -1089,7 +1214,7 @@ async function addressOf(file, io) {
1089
1214
  const a2 = parseShare(readAttribute(html, "data-pipeup-share"), true);
1090
1215
  if (a2.kind === "off") throw new UsageError(`${file}: ${a2.reason}`);
1091
1216
  const docAttr = readAttribute(html, "data-pipeup-doc");
1092
- const doc = docAttr ? (await U(docAttr).catch(() => null))?.id : null;
1217
+ const doc = docAttr ? (await _(docAttr).catch(() => null))?.id : null;
1093
1218
  if (!doc) throw new UsageError(`${file} has no valid data-pipeup-doc`);
1094
1219
  return { html, a: a2, l: ladder(doc, a2.key) };
1095
1220
  }