@pipeup/share 0.5.4 → 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
@@ -170,44 +170,78 @@ 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 u(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 d = 1e4;
178
- var c = 2 ** 40;
179
- var h = 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"]);
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 u(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 E(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 > c || "number" != typeof n2.at || !Number.isSafeInteger(n2.at) || n2.at < 0 || n2.at > h || void 0 !== n2.text && (!b(n2.text, d) || 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) && !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 k(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 k(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,81 +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;
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
- let o = i.target ? r2.get(i.target) : void 0;
270
- if (o && o.thread === i.thread) {
271
- if ("reply" === i.kind) {
272
- if (r2.has(i.id) || void 0 === i.text) continue;
273
- let t3 = M(i.id, i.author, i.name, i.text, i.at);
274
- e4.root.replies.push(t3), r2.set(i.id, { comment: t3, thread: i.thread });
304
+ if ("addressed" === i2.kind) {
305
+ void 0 !== i2.text && (e4.addressed = { text: i2.text, author: i2.author, name: i2.name, at: i2.at });
306
+ continue;
307
+ }
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);
275
319
  continue;
276
320
  }
277
- 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));
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));
328
+ continue;
329
+ }
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));
278
331
  }
279
332
  }
280
- 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
+ }
281
350
  return [...t2.values()];
282
351
  })(this.ops.values());
283
352
  }
284
353
  async add(e3) {
285
354
  let t2 = [];
286
355
  for (let r2 of e3) {
287
- if (!E(r2) || r2.body.doc !== this.doc || this.ops.has(r2.body.id) || r2.body.id !== await S(r2.body) || !await k(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;
288
357
  let e4 = { body: r2.body, sig: r2.sig };
289
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);
290
359
  }
291
360
  return t2;
292
361
  }
293
362
  async append(e3, t2, r2) {
294
- 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, c), at: Math.min(Math.max(Date.now(), "create" === r2.kind ? (this.createAt.get(e3.publicKey) ?? 0) + 1 : (this.threadAt.get(r2.thread) ?? 0) + 1), h) };
295
- 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);
296
365
  let i = this.ops.get(n2.id);
297
366
  if (i) return i;
298
367
  "create" === n2.kind && (n2.thread = n2.id);
299
- let [o] = await this.add([await w(n2, e3)]);
368
+ let [o] = await this.add([await O(n2, e3)]);
300
369
  if (!o) throw new Error("pipeup: could not record this change");
301
370
  return o;
302
371
  }
303
372
  };
304
- var j = "AI assistant";
305
- var I = (e3) => e3 === j || e3.startsWith(`${j} `);
306
- var D = /^([A-Za-z0-9_-]{16}):([A-Za-z0-9_-]{43})$/;
307
- function F() {
373
+ var H = /^([A-Za-z0-9_-]{16}):([A-Za-z0-9_-]{43})$/;
374
+ function L() {
308
375
  return `${(function(t2 = 16) {
309
376
  return e(crypto.getRandomValues(new Uint8Array(t2)));
310
377
  })(12)}:${e(crypto.getRandomValues(new Uint8Array(32)))}`;
311
378
  }
312
- async function U(e3) {
313
- 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];
314
381
  if (!n2 || !i) throw new Error('pipeup: data-pipeup-doc must look like "<id>:<key>", as made by newDocumentAttribute()');
315
382
  return { id: n2, key: await crypto.subtle.importKey("raw", t(i), { name: "AES-GCM" }, false, ["encrypt", "decrypt"]) };
316
383
  }
317
- async function R(t2, n2, i) {
384
+ async function z(t2, n2, i) {
318
385
  let o = crypto.getRandomValues(new Uint8Array(12)), a2 = await crypto.subtle.encrypt({ name: "AES-GCM", iv: o, additionalData: r(i) }, t2, r(n2));
319
386
  return { iv: e(o), data: e(new Uint8Array(a2)) };
320
387
  }
321
- var H = (e3) => `pipeup-file:${e3}`;
322
- var L = /^[a-z][a-z0-9-]{1,23}\/[a-z][a-z0-9-]{0,31}$/;
323
- 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 {
324
391
  constructor(e3, t2, r2 = 1) {
325
392
  super("pipeup: shown here, but not saved yet"), this.id = e3, this.cause = t2, this.count = r2, this.name = "UnsavedChangeError";
326
393
  }
327
394
  };
328
- var K = class e2 {
395
+ var J = class e2 {
329
396
  constructor(e3, t2) {
330
- 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) : "";
331
398
  }
332
399
  listeners = /* @__PURE__ */ new Set();
333
400
  cache = [];
@@ -337,7 +404,7 @@ var K = class e2 {
337
404
  unsaved = [];
338
405
  static async open(t2) {
339
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");
340
- 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;
341
408
  i && console.warn(`pipeup: ${i} saved change${1 === i ? "" : "s"} on this page couldn't be read; kept as saved`);
342
409
  let o = new e2(r2, t2);
343
410
  return o.refresh(false), o;
@@ -352,7 +419,7 @@ var K = class e2 {
352
419
  return this.displayName;
353
420
  }
354
421
  set name(e3) {
355
- this.displayName = B(e3), this.refresh(true, [], "");
422
+ this.displayName = Q(e3), this.refresh(true, [], "");
356
423
  }
357
424
  threads() {
358
425
  return this.cache;
@@ -363,16 +430,16 @@ var K = class e2 {
363
430
  onChange(e3) {
364
431
  return this.listeners.add(e3), () => this.listeners.delete(e3);
365
432
  }
366
- comment(e3, t2) {
367
- 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 } : {} }));
368
435
  }
369
- reply(e3, t2) {
370
- 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 } : {} }));
371
438
  }
372
- edit(e3, t2) {
439
+ edit(e3, t2, r2) {
373
440
  return this.enqueue(async () => {
374
- let { thread: r2 } = this.mine(e3);
375
- 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 } : {} });
376
443
  });
377
444
  }
378
445
  remove(e3) {
@@ -381,6 +448,64 @@ var K = class e2 {
381
448
  await this.write({ kind: "delete", thread: t2, target: e3 });
382
449
  });
383
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
+ }
384
509
  resolve(e3) {
385
510
  return this.enqueue(async () => {
386
511
  await this.write({ kind: "resolve", thread: this.thread(e3) });
@@ -392,8 +517,8 @@ var K = class e2 {
392
517
  });
393
518
  }
394
519
  sign(e3, t2) {
395
- if (!L.test(e3)) throw new Error('pipeup: a signing purpose looks like "<add-on id>/<name>"');
396
- return u(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}
397
522
  ${t2}`));
398
523
  }
399
524
  flush() {
@@ -401,7 +526,7 @@ ${t2}`));
401
526
  }
402
527
  exportFile() {
403
528
  return (async function(e3, t2, r2) {
404
- 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 };
405
530
  return JSON.stringify(n2, null, 2);
406
531
  })(this.options.doc, this.log.all(), this.options.key);
407
532
  }
@@ -427,7 +552,7 @@ ${t2}`));
427
552
  } catch {
428
553
  throw new Error("pipeup: this feedback could not be opened with this document");
429
554
  }
430
- })(i, { iv: o.iv, data: o.data }, H(n2));
555
+ })(i, { iv: o.iv, data: o.data }, B(n2));
431
556
  } catch {
432
557
  throw new Error("pipeup: this feedback could not be opened with this document's key");
433
558
  }
@@ -446,7 +571,7 @@ ${t2}`));
446
571
  try {
447
572
  await this.persist();
448
573
  } catch (e4) {
449
- 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;
450
575
  }
451
576
  return r2.length;
452
577
  });
@@ -462,7 +587,7 @@ ${t2}`));
462
587
  try {
463
588
  await this.persist();
464
589
  } catch (e4) {
465
- throw new z(t2.body.id, e4);
590
+ throw new Z(t2.body.id, e4);
466
591
  }
467
592
  return t2.body.id;
468
593
  }
@@ -499,19 +624,23 @@ ${t2}`));
499
624
  return e3;
500
625
  }
501
626
  };
502
- function _(e3) {
627
+ function V(e3) {
503
628
  let t2 = e3.trim();
504
629
  if (!t2) throw new Error("pipeup: a comment needs some words");
505
- if (t2.length > d) throw new Error(`pipeup: comments are limited to ${d} characters`);
630
+ if (t2.length > w) throw new Error(`pipeup: comments are limited to ${w} characters`);
506
631
  return t2;
507
632
  }
508
- 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) {
509
638
  let t2 = e3.replace(/\s+/g, " ").trim();
510
639
  if (!t2 || t2.length > 80) throw new Error("pipeup: choose a name of 1 to 80 characters");
511
- 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");
512
641
  return t2;
513
642
  }
514
- var G = class {
643
+ var X = class {
515
644
  docs = /* @__PURE__ */ new Map();
516
645
  profile = null;
517
646
  async load(e3) {
@@ -887,9 +1016,9 @@ function setAttribute(html, name, value) {
887
1016
  const old = attr(name).exec(tag);
888
1017
  let next;
889
1018
  if (old) {
890
- const q = old[3][0] === "'" ? "'" : '"';
891
- const v2 = q === "'" ? value.replace(/&/g, "&amp;").replace(/'/g, "&#39;") : escaped;
892
- 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);
893
1022
  } else {
894
1023
  const at = tag.length - 1;
895
1024
  next = `${tag.slice(0, at)} ${name}="${escaped}"${tag.slice(at)}`;
@@ -980,11 +1109,11 @@ function server(value, mailbox) {
980
1109
  return mailbox ? value.replace(/\/+$/, "") : u2.origin + u2.pathname;
981
1110
  }
982
1111
  async function feedbackOps(file, docAttr, io) {
983
- const { id, key } = await U(docAttr);
984
- const doc = await K.open({
1112
+ const { id, key } = await _(docAttr);
1113
+ const doc = await J.open({
985
1114
  doc: id,
986
1115
  key,
987
- store: new G(),
1116
+ store: new X(),
988
1117
  identity: await l(),
989
1118
  name: ""
990
1119
  });
@@ -1011,11 +1140,11 @@ Run the same command with --new-doc and I will write a fresh one into the page.
1011
1140
  );
1012
1141
  throw new UsageError("the page needs a document identity");
1013
1142
  }
1014
- docAttr = F();
1143
+ docAttr = L();
1015
1144
  html = setAttribute(html, "data-pipeup-doc", docAttr);
1016
1145
  io.out("Gave the page a new document identity (data-pipeup-doc).");
1017
1146
  }
1018
- const { id } = await U(docAttr).catch(() => {
1147
+ const { id } = await _(docAttr).catch(() => {
1019
1148
  throw new UsageError("data-pipeup-doc in the page isn't a valid document identity");
1020
1149
  });
1021
1150
  const from = typeof flags.get("from") === "string" ? flags.get("from") : null;
@@ -1085,7 +1214,7 @@ async function addressOf(file, io) {
1085
1214
  const a2 = parseShare(readAttribute(html, "data-pipeup-share"), true);
1086
1215
  if (a2.kind === "off") throw new UsageError(`${file}: ${a2.reason}`);
1087
1216
  const docAttr = readAttribute(html, "data-pipeup-doc");
1088
- const doc = docAttr ? (await U(docAttr).catch(() => null))?.id : null;
1217
+ const doc = docAttr ? (await _(docAttr).catch(() => null))?.id : null;
1089
1218
  if (!doc) throw new UsageError(`${file} has no valid data-pipeup-doc`);
1090
1219
  return { html, a: a2, l: ladder(doc, a2.key) };
1091
1220
  }