@pipeup/share 0.8.2 → 0.9.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/README.md +1 -1
- package/dist/cli.js +163 -156
- package/dist/pipeup+share.min.js +2 -2
- package/dist/share.esm.js +1 -1
- package/dist/share.headless.js +1 -1
- package/dist/share.min.js +1 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -164,7 +164,7 @@ function n(e3) {
|
|
|
164
164
|
let t2 = e3;
|
|
165
165
|
return "{" + Object.keys(t2).filter((e4) => void 0 !== t2[e4]).sort().map((e4) => JSON.stringify(e4) + ":" + n(t2[e4])).join(",") + "}";
|
|
166
166
|
}
|
|
167
|
-
var
|
|
167
|
+
var o = /^[A-Za-z0-9_-]{43}$/;
|
|
168
168
|
var s = /* @__PURE__ */ new Map();
|
|
169
169
|
async function d() {
|
|
170
170
|
let t2 = await crypto.subtle.generateKey({ name: "Ed25519" }, false, ["sign", "verify"]);
|
|
@@ -192,84 +192,85 @@ var f = (e3) => {
|
|
|
192
192
|
};
|
|
193
193
|
var m = "AI assistant";
|
|
194
194
|
var g = (e3) => e3 === m || e3.startsWith(`${m} `);
|
|
195
|
-
var y =
|
|
196
|
-
var
|
|
197
|
-
var
|
|
198
|
-
var
|
|
199
|
-
var
|
|
200
|
-
var
|
|
201
|
-
var
|
|
202
|
-
var
|
|
203
|
-
var
|
|
204
|
-
var
|
|
205
|
-
var
|
|
206
|
-
var
|
|
207
|
-
|
|
195
|
+
var y = (e3) => `${m} \xB7 ${e3.replace(/\s+/g, " ").trim() || "Agent"}`.slice(0, 80).trim();
|
|
196
|
+
var b = 1e4;
|
|
197
|
+
var x = 2 ** 40;
|
|
198
|
+
var v = 2 ** 47;
|
|
199
|
+
var k = 1e4;
|
|
200
|
+
var A = /^[0-9a-f]{8}$/;
|
|
201
|
+
var E = /^[A-Za-z0-9_-]{43}$/;
|
|
202
|
+
var $ = /^(?:[^\s@<>"]{1,64}@[^\s@<>"]{1,190}\.[^\s@<>"]{2,24}|gh:[A-Za-z0-9-]{1,39})$/;
|
|
203
|
+
var S = /^(?:[A-Za-z0-9_-]{43}|(?:host|gh):[A-Za-z0-9._@-]{1,64})$/;
|
|
204
|
+
var M = /^[A-Za-z0-9_-]{43}$/;
|
|
205
|
+
var C = /* @__PURE__ */ new Set(["create", "reply", "edit", "delete", "resolve", "reopen", "addressed", "react", "unreact", "seen", "contact", "uncontact"]);
|
|
206
|
+
var N = /^(?:\p{Regional_Indicator}{2}|[#*0-9]\uFE0F?\u20E3|\p{Extended_Pictographic}(?:\uFE0F|\p{Emoji_Modifier})?(?:\u200D\p{Extended_Pictographic}(?:\uFE0F|\p{Emoji_Modifier})?)*)$/u;
|
|
207
|
+
var O = (e3) => "string" == typeof e3 && N.test(e3) && [...e3].length <= 16;
|
|
208
|
+
async function q(e3, t2) {
|
|
208
209
|
if (e3.author !== t2.publicKey) throw new Error("pipeup: an op must be signed by its author");
|
|
209
210
|
return { body: e3, sig: await l(t2, r(n(e3))) };
|
|
210
211
|
}
|
|
211
|
-
var
|
|
212
|
-
var
|
|
213
|
-
var
|
|
214
|
-
function
|
|
215
|
-
if (!
|
|
216
|
-
let { id: t2, path: r2, fingerprint: n2, snapshot: i, quote:
|
|
217
|
-
if (!(
|
|
212
|
+
var j = (e3, t2) => "string" == typeof e3 && e3.length <= t2;
|
|
213
|
+
var P2 = (e3) => !!e3 && "object" == typeof e3;
|
|
214
|
+
var T = (e3) => "number" == typeof e3 && Number.isFinite(e3) && e3 >= 0 && e3 <= 1;
|
|
215
|
+
function I(e3) {
|
|
216
|
+
if (!P2(e3)) return false;
|
|
217
|
+
let { id: t2, path: r2, fingerprint: n2, snapshot: i, quote: a, point: o2, view: s2 } = e3;
|
|
218
|
+
if (!(j(r2, 2e3) && j(i, 500) && "string" == typeof n2 && A.test(n2) && (void 0 === t2 || j(t2, 200) && "" !== t2) && (void 0 === a || P2(a) && j(a.exact, k) && "" !== a.exact && j(a.prefix, 64) && j(a.suffix, 64)) && (void 0 === o2 || P2(o2) && T(o2.x) && T(o2.y)))) return false;
|
|
218
219
|
if (void 0 !== s2) {
|
|
219
|
-
if (!
|
|
220
|
+
if (!P2(s2) || Array.isArray(s2)) return false;
|
|
220
221
|
let e4 = Object.entries(s2);
|
|
221
|
-
if (e4.length > 16 || !e4.every(([e5, t3]) => e5.length <= 64 &&
|
|
222
|
+
if (e4.length > 16 || !e4.every(([e5, t3]) => e5.length <= 64 && j(t3, 200))) return false;
|
|
222
223
|
}
|
|
223
224
|
return true;
|
|
224
225
|
}
|
|
225
|
-
function
|
|
226
|
-
if (!
|
|
226
|
+
function F(e3) {
|
|
227
|
+
if (!P2(e3)) return false;
|
|
227
228
|
let { body: t2, sig: r2 } = e3;
|
|
228
|
-
if ("string" != typeof r2 || !
|
|
229
|
+
if ("string" != typeof r2 || !P2(t2) || JSON.stringify({ body: t2, sig: r2 }).length > 262144) return false;
|
|
229
230
|
let n2 = t2;
|
|
230
|
-
if (1 !== n2.v || "string" != typeof n2.id || !
|
|
231
|
+
if (1 !== n2.v || "string" != typeof n2.id || !E.test(n2.id) || !C.has(n2.kind) || !j(n2.doc, 64) || "" === n2.doc || "string" != typeof n2.thread || !E.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 > x || "number" != typeof n2.at || !Number.isSafeInteger(n2.at) || n2.at < 0 || n2.at > v || void 0 !== n2.text && (!j(n2.text, b) || n2.text !== n2.text.trim()) || void 0 !== n2.target && ("string" != typeof n2.target || !E.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;
|
|
231
232
|
if (void 0 !== n2.mentions) {
|
|
232
233
|
if ("create" !== n2.kind && "reply" !== n2.kind && "edit" !== n2.kind || !Array.isArray(n2.mentions) || n2.mentions.length > 20) return false;
|
|
233
234
|
let e4 = n2.text?.length ?? 0;
|
|
234
235
|
for (let t3 of n2.mentions) {
|
|
235
|
-
if (!
|
|
236
|
+
if (!P2(t3) || !Number.isSafeInteger(t3.start) || !Number.isSafeInteger(t3.end)) return false;
|
|
236
237
|
let { start: r3, end: n3, ref: i } = t3;
|
|
237
|
-
if (r3 < 0 || n3 <= r3 || n3 > e4 || "string" != typeof i || !
|
|
238
|
+
if (r3 < 0 || n3 <= r3 || n3 > e4 || "string" != typeof i || !S.test(i)) return false;
|
|
238
239
|
}
|
|
239
240
|
}
|
|
240
|
-
return !(!("contact" !== n2.kind || "string" == typeof n2.text &&
|
|
241
|
+
return !(!("contact" !== n2.kind || "string" == typeof n2.text && $.test(n2.text)) || "seen" === n2.kind && !n2.target || !("react" !== n2.kind && "unreact" !== n2.kind || n2.target && O(n2.text)) || !("create" !== n2.kind || n2.id === n2.thread && I(n2.anchor)));
|
|
241
242
|
}
|
|
242
|
-
function
|
|
243
|
+
function L(i) {
|
|
243
244
|
return (async function(r2, n2, i2) {
|
|
244
|
-
if (!
|
|
245
|
+
if (!o.test(r2)) return false;
|
|
245
246
|
try {
|
|
246
247
|
if (e(t(r2)) !== r2) return false;
|
|
247
|
-
let
|
|
248
|
-
if (!
|
|
249
|
-
s.size >= 512 && s.delete(s.keys().next().value),
|
|
250
|
-
let e3 =
|
|
248
|
+
let a = s.get(r2);
|
|
249
|
+
if (!a) {
|
|
250
|
+
s.size >= 512 && s.delete(s.keys().next().value), a = crypto.subtle.importKey("raw", t(r2), { name: "Ed25519" }, true, ["verify"]), s.set(r2, a);
|
|
251
|
+
let e3 = a;
|
|
251
252
|
e3.catch(() => {
|
|
252
253
|
s.get(r2) === e3 && s.delete(r2);
|
|
253
254
|
});
|
|
254
255
|
}
|
|
255
|
-
return await crypto.subtle.verify({ name: "Ed25519" }, await
|
|
256
|
+
return await crypto.subtle.verify({ name: "Ed25519" }, await a, t(i2), n2);
|
|
256
257
|
} catch {
|
|
257
258
|
return false;
|
|
258
259
|
}
|
|
259
260
|
})(i.body.author, r(n(i.body)), i.sig);
|
|
260
261
|
}
|
|
261
|
-
function
|
|
262
|
+
function D(e3, t2) {
|
|
262
263
|
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;
|
|
263
264
|
}
|
|
264
|
-
function
|
|
265
|
-
let
|
|
266
|
-
return
|
|
265
|
+
function U(e3, t2, r2, n2, i, a) {
|
|
266
|
+
let o2 = { id: e3, author: t2, name: r2, text: n2, at: i, edited: false, deleted: false, replies: [], reactions: [] };
|
|
267
|
+
return a?.length && (o2.mentions = a), o2;
|
|
267
268
|
}
|
|
268
|
-
async function
|
|
269
|
-
let i = { ...t2, id: void 0, thread: "create" === t2.kind ? void 0 : t2.thread },
|
|
270
|
-
return e(new Uint8Array(
|
|
269
|
+
async function B(t2) {
|
|
270
|
+
let i = { ...t2, id: void 0, thread: "create" === t2.kind ? void 0 : t2.thread }, a = await crypto.subtle.digest("SHA-256", r(n(i)));
|
|
271
|
+
return e(new Uint8Array(a));
|
|
271
272
|
}
|
|
272
|
-
var
|
|
273
|
+
var _ = class {
|
|
273
274
|
constructor(e3) {
|
|
274
275
|
this.doc = e3;
|
|
275
276
|
}
|
|
@@ -286,10 +287,10 @@ var D = class {
|
|
|
286
287
|
threads() {
|
|
287
288
|
return (function(e3) {
|
|
288
289
|
let t2 = /* @__PURE__ */ new Map(), r2 = /* @__PURE__ */ new Map(), n2 = /* @__PURE__ */ new Map();
|
|
289
|
-
for (let { body: i2 } of [...e3].sort(
|
|
290
|
-
if (i2.name && n2.set(i2.author, i2.name), "create" === i2.kind) {
|
|
290
|
+
for (let { body: i2 } of [...e3].sort(D)) {
|
|
291
|
+
if (i2.name && !g(i2.name) && n2.set(i2.author, i2.name), "create" === i2.kind) {
|
|
291
292
|
if (t2.has(i2.id) || !i2.anchor || void 0 === i2.text) continue;
|
|
292
|
-
let e5 =
|
|
293
|
+
let e5 = U(i2.id, i2.author, i2.name, i2.text, i2.at, i2.mentions);
|
|
293
294
|
t2.set(i2.id, { id: i2.id, anchor: i2.anchor, root: e5, resolved: false }), r2.set(i2.id, { comment: e5, thread: i2.id });
|
|
294
295
|
continue;
|
|
295
296
|
}
|
|
@@ -303,47 +304,47 @@ var D = class {
|
|
|
303
304
|
void 0 !== i2.text && (e4.addressed = { text: i2.text, author: i2.author, name: i2.name, at: i2.at });
|
|
304
305
|
continue;
|
|
305
306
|
}
|
|
306
|
-
let
|
|
307
|
-
if (
|
|
307
|
+
let a2 = i2.target ? r2.get(i2.target) : void 0;
|
|
308
|
+
if (a2 && a2.thread === i2.thread) {
|
|
308
309
|
if ("reply" === i2.kind) {
|
|
309
310
|
if (r2.has(i2.id) || void 0 === i2.text) continue;
|
|
310
|
-
let t3 =
|
|
311
|
+
let t3 = U(i2.id, i2.author, i2.name, i2.text, i2.at, i2.mentions);
|
|
311
312
|
e4.root.replies.push(t3), r2.set(i2.id, { comment: t3, thread: i2.thread });
|
|
312
313
|
continue;
|
|
313
314
|
}
|
|
314
315
|
if ("seen" === i2.kind) {
|
|
315
|
-
let e5 =
|
|
316
|
+
let e5 = a2.comment.seenBy ??= [];
|
|
316
317
|
e5.includes(i2.author) || e5.push(i2.author);
|
|
317
318
|
continue;
|
|
318
319
|
}
|
|
319
320
|
if ("react" === i2.kind || "unreact" === i2.kind) {
|
|
320
|
-
if (void 0 === i2.text ||
|
|
321
|
-
let e5 =
|
|
321
|
+
if (void 0 === i2.text || a2.comment.deleted) continue;
|
|
322
|
+
let e5 = a2.comment.reactions, t3 = e5.find((e6) => e6.emoji === i2.text);
|
|
322
323
|
if ("react" === i2.kind) {
|
|
323
324
|
let r3 = { author: i2.author, name: i2.name };
|
|
324
325
|
t3 ? t3.by.some((e6) => e6.author === i2.author) || t3.by.push(r3) : e5.length < 12 && e5.push({ emoji: i2.text, by: [r3] });
|
|
325
326
|
} else t3 && (t3.by = t3.by.filter((e6) => e6.author !== i2.author), t3.by.length || e5.splice(e5.indexOf(t3), 1));
|
|
326
327
|
continue;
|
|
327
328
|
}
|
|
328
|
-
i2.author !==
|
|
329
|
+
i2.author !== a2.comment.author || a2.comment.deleted || ("edit" === i2.kind && void 0 !== i2.text && (a2.comment.text = i2.text, a2.comment.edited = true, i2.mentions ? a2.comment.mentions = i2.mentions : delete a2.comment.mentions), "delete" === i2.kind && (a2.comment.text = "", a2.comment.deleted = true, a2.comment.reactions = [], delete a2.comment.mentions));
|
|
329
330
|
}
|
|
330
331
|
}
|
|
331
332
|
for (let { comment: e4 } of r2.values()) {
|
|
332
333
|
e4.name || (e4.unnamed = true, e4.name = n2.get(e4.author) ?? "");
|
|
333
334
|
for (let t3 of e4.reactions) for (let e5 of t3.by) e5.name ||= n2.get(e5.author) ?? "";
|
|
334
335
|
}
|
|
335
|
-
let i = /* @__PURE__ */ new Map(),
|
|
336
|
+
let i = /* @__PURE__ */ new Map(), a = (e4, t3) => {
|
|
336
337
|
let r3 = f({ author: e4, name: t3 }).toLowerCase();
|
|
337
338
|
(i.get(r3) ?? i.set(r3, /* @__PURE__ */ new Set()).get(r3)).add(e4);
|
|
338
339
|
};
|
|
339
340
|
for (let { comment: e4 } of r2.values()) {
|
|
340
|
-
|
|
341
|
-
for (let t3 of e4.reactions) for (let e5 of t3.by)
|
|
341
|
+
a(e4.author, e4.name);
|
|
342
|
+
for (let t3 of e4.reactions) for (let e5 of t3.by) a(e5.author, e5.name);
|
|
342
343
|
}
|
|
343
|
-
let
|
|
344
|
+
let o2 = (e4, t3) => !g(t3) && (i.get(f({ author: e4, name: t3 }).toLowerCase())?.size ?? 0) > 1;
|
|
344
345
|
for (let { comment: e4 } of r2.values()) {
|
|
345
|
-
|
|
346
|
-
for (let t3 of e4.reactions) for (let e5 of t3.by)
|
|
346
|
+
o2(e4.author, e4.name) && (e4.alike = true);
|
|
347
|
+
for (let t3 of e4.reactions) for (let e5 of t3.by) o2(e5.author, e5.name) && (e5.alike = true);
|
|
347
348
|
}
|
|
348
349
|
return [...t2.values()];
|
|
349
350
|
})(this.ops.values());
|
|
@@ -351,80 +352,80 @@ var D = class {
|
|
|
351
352
|
async add(e3) {
|
|
352
353
|
let t2 = [];
|
|
353
354
|
for (let r2 of e3) {
|
|
354
|
-
if (!
|
|
355
|
+
if (!F(r2) || r2.body.doc !== this.doc || this.ops.has(r2.body.id) || r2.body.id !== await B(r2.body) || !await L(r2)) continue;
|
|
355
356
|
let e4 = { body: r2.body, sig: r2.sig };
|
|
356
357
|
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);
|
|
357
358
|
}
|
|
358
359
|
return t2;
|
|
359
360
|
}
|
|
360
361
|
async append(e3, t2, r2) {
|
|
361
|
-
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,
|
|
362
|
-
n2.id = await
|
|
362
|
+
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, x), at: Math.min(Math.max(Date.now(), "create" === r2.kind ? (this.createAt.get(e3.publicKey) ?? 0) + 1 : (this.threadAt.get(r2.thread) ?? 0) + 1), v) };
|
|
363
|
+
n2.id = await B(n2);
|
|
363
364
|
let i = this.ops.get(n2.id);
|
|
364
365
|
if (i) return i;
|
|
365
366
|
"create" === n2.kind && (n2.thread = n2.id);
|
|
366
|
-
let [
|
|
367
|
-
if (!
|
|
368
|
-
return
|
|
367
|
+
let [a] = await this.add([await q(n2, e3)]);
|
|
368
|
+
if (!a) throw new Error("pipeup: could not record this change");
|
|
369
|
+
return a;
|
|
369
370
|
}
|
|
370
371
|
};
|
|
371
|
-
var
|
|
372
|
-
function
|
|
372
|
+
var z = /^([A-Za-z0-9_-]{16}):([A-Za-z0-9_-]{43})$/;
|
|
373
|
+
function H() {
|
|
373
374
|
return `${(function(t2 = 16) {
|
|
374
375
|
return e(crypto.getRandomValues(new Uint8Array(t2)));
|
|
375
376
|
})(12)}:${e(crypto.getRandomValues(new Uint8Array(32)))}`;
|
|
376
377
|
}
|
|
377
|
-
async function
|
|
378
|
-
let r2 =
|
|
378
|
+
async function K(e3) {
|
|
379
|
+
let r2 = z.exec(e3.trim()), n2 = r2?.[1], i = r2?.[2];
|
|
379
380
|
if (!n2 || !i) throw new Error('pipeup: data-pipeup-doc must look like "<id>:<key>", as made by newDocumentAttribute()');
|
|
380
381
|
return { id: n2, key: await crypto.subtle.importKey("raw", t(i), { name: "AES-GCM" }, false, ["encrypt", "decrypt"]) };
|
|
381
382
|
}
|
|
382
|
-
async function
|
|
383
|
-
let
|
|
384
|
-
return { iv: e(
|
|
383
|
+
async function G(t2, n2, i) {
|
|
384
|
+
let a = crypto.getRandomValues(new Uint8Array(12)), o2 = await crypto.subtle.encrypt({ name: "AES-GCM", iv: a, additionalData: r(i) }, t2, r(n2));
|
|
385
|
+
return { iv: e(a), data: e(new Uint8Array(o2)) };
|
|
385
386
|
}
|
|
386
|
-
var
|
|
387
|
-
async function
|
|
387
|
+
var Z = (e3) => `pipeup-file:${e3}`;
|
|
388
|
+
async function J(e3, n2, i) {
|
|
388
389
|
if (e3.length > 5e6) throw new Error("pipeup: this feedback file is too large");
|
|
389
|
-
let
|
|
390
|
+
let a, o2, s2;
|
|
390
391
|
try {
|
|
391
|
-
|
|
392
|
+
a = JSON.parse(e3);
|
|
392
393
|
} catch {
|
|
393
394
|
throw new Error("pipeup: this is not an Pipeup feedback file");
|
|
394
395
|
}
|
|
395
|
-
if (!
|
|
396
|
-
if (
|
|
397
|
-
if (!
|
|
396
|
+
if (!a || "object" != typeof a || 1 !== a.pipeup || "string" != typeof a.doc) throw new Error("pipeup: this is not an Pipeup feedback file");
|
|
397
|
+
if (a.doc !== n2) throw new Error("pipeup: this feedback is for a different document");
|
|
398
|
+
if (!a.sealed) return Array.isArray(a.ops) ? a.ops : [];
|
|
398
399
|
if (!i) throw new Error("pipeup: this feedback is sealed, and this page has no document key");
|
|
399
|
-
if ("string" != typeof
|
|
400
|
+
if ("string" != typeof a.iv || "string" != typeof a.data) throw new Error("pipeup: this is not an Pipeup feedback file");
|
|
400
401
|
try {
|
|
401
|
-
|
|
402
|
+
o2 = await (async function(e4, n3, i2) {
|
|
402
403
|
try {
|
|
403
|
-
let
|
|
404
|
-
return new TextDecoder().decode(
|
|
404
|
+
let a2 = await crypto.subtle.decrypt({ name: "AES-GCM", iv: t(n3.iv), additionalData: r(i2) }, e4, t(n3.data));
|
|
405
|
+
return new TextDecoder().decode(a2);
|
|
405
406
|
} catch {
|
|
406
407
|
throw new Error("pipeup: this feedback could not be opened with this document");
|
|
407
408
|
}
|
|
408
|
-
})(i, { iv:
|
|
409
|
+
})(i, { iv: a.iv, data: a.data }, Z(n2));
|
|
409
410
|
} catch {
|
|
410
411
|
throw new Error("pipeup: this feedback could not be opened with this document's key");
|
|
411
412
|
}
|
|
412
413
|
try {
|
|
413
|
-
s2 = JSON.parse(
|
|
414
|
+
s2 = JSON.parse(o2);
|
|
414
415
|
} catch {
|
|
415
416
|
throw new Error("pipeup: this is not an Pipeup feedback file");
|
|
416
417
|
}
|
|
417
418
|
return Array.isArray(s2) ? s2 : [];
|
|
418
419
|
}
|
|
419
|
-
var
|
|
420
|
-
var
|
|
420
|
+
var V = /^[a-z][a-z0-9-]{1,23}\/[a-z][a-z0-9-]{0,31}$/;
|
|
421
|
+
var W = class extends Error {
|
|
421
422
|
constructor(e3, t2, r2 = 1) {
|
|
422
423
|
super("pipeup: shown here, but not saved yet"), this.id = e3, this.cause = t2, this.count = r2, this.name = "UnsavedChangeError";
|
|
423
424
|
}
|
|
424
425
|
};
|
|
425
|
-
var
|
|
426
|
+
var Q = class e2 {
|
|
426
427
|
constructor(e3, t2) {
|
|
427
|
-
this.log = e3, this.options = t2, this.displayName = t2.name.trim() ?
|
|
428
|
+
this.log = e3, this.options = t2, this.displayName = t2.name.trim() ? ee(t2.name) : "";
|
|
428
429
|
}
|
|
429
430
|
listeners = /* @__PURE__ */ new Set();
|
|
430
431
|
cache = [];
|
|
@@ -434,10 +435,10 @@ var J = class e2 {
|
|
|
434
435
|
unsaved = [];
|
|
435
436
|
static async open(t2) {
|
|
436
437
|
if ("string" != typeof t2.doc || "" === t2.doc || t2.doc.length > 64) throw new Error("pipeup: the document id must be 1 to 64 characters");
|
|
437
|
-
let r2 = new
|
|
438
|
+
let r2 = new _(t2.doc), n2 = await t2.store.load(t2.doc), i = n2.length - (await r2.add(n2)).length;
|
|
438
439
|
i && console.warn(`pipeup: ${i} saved change${1 === i ? "" : "s"} on this page couldn't be read; kept as saved`);
|
|
439
|
-
let
|
|
440
|
-
return
|
|
440
|
+
let a = new e2(r2, t2);
|
|
441
|
+
return a.refresh(false), a;
|
|
441
442
|
}
|
|
442
443
|
get id() {
|
|
443
444
|
return this.options.doc;
|
|
@@ -449,7 +450,7 @@ var J = class e2 {
|
|
|
449
450
|
return this.displayName;
|
|
450
451
|
}
|
|
451
452
|
set name(e3) {
|
|
452
|
-
this.displayName =
|
|
453
|
+
this.displayName = ee(e3), this.refresh(true, [], "");
|
|
453
454
|
}
|
|
454
455
|
threads() {
|
|
455
456
|
return this.cache;
|
|
@@ -461,15 +462,15 @@ var J = class e2 {
|
|
|
461
462
|
return this.listeners.add(e3), () => this.listeners.delete(e3);
|
|
462
463
|
}
|
|
463
464
|
comment(e3, t2, r2) {
|
|
464
|
-
return this.enqueue(() => this.write({ kind: "create", anchor: e3, text:
|
|
465
|
+
return this.enqueue(() => this.write({ kind: "create", anchor: e3, text: X(t2), ...r2?.length ? { mentions: r2 } : {} }));
|
|
465
466
|
}
|
|
466
467
|
reply(e3, t2, r2) {
|
|
467
|
-
return this.enqueue(() => this.write({ kind: "reply", thread: this.find(e3).thread, target: e3, text:
|
|
468
|
+
return this.enqueue(() => this.write({ kind: "reply", thread: this.find(e3).thread, target: e3, text: X(t2), ...r2?.length ? { mentions: r2 } : {} }));
|
|
468
469
|
}
|
|
469
470
|
edit(e3, t2, r2) {
|
|
470
471
|
return this.enqueue(async () => {
|
|
471
472
|
let { thread: n2 } = this.mine(e3);
|
|
472
|
-
await this.write({ kind: "edit", thread: n2, target: e3, text:
|
|
473
|
+
await this.write({ kind: "edit", thread: n2, target: e3, text: X(t2), ...r2?.length ? { mentions: r2 } : {} });
|
|
473
474
|
});
|
|
474
475
|
}
|
|
475
476
|
remove(e3) {
|
|
@@ -480,7 +481,7 @@ var J = class e2 {
|
|
|
480
481
|
}
|
|
481
482
|
contact(e3) {
|
|
482
483
|
let t2 = e3.trim();
|
|
483
|
-
return
|
|
484
|
+
return $.test(t2) ? this.enqueue(async () => {
|
|
484
485
|
await this.write({ kind: "contact", thread: this.me, text: t2 });
|
|
485
486
|
}) : Promise.reject(new Error("pipeup: that isn't an email address or a GitHub name"));
|
|
486
487
|
}
|
|
@@ -491,7 +492,7 @@ var J = class e2 {
|
|
|
491
492
|
}
|
|
492
493
|
contacts() {
|
|
493
494
|
let e3 = /* @__PURE__ */ new Map();
|
|
494
|
-
for (let { body: t2 } of [...this.log.all()].sort(
|
|
495
|
+
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);
|
|
495
496
|
return e3;
|
|
496
497
|
}
|
|
497
498
|
get portable() {
|
|
@@ -513,12 +514,12 @@ var J = class e2 {
|
|
|
513
514
|
}
|
|
514
515
|
react(e3, t2) {
|
|
515
516
|
return this.enqueue(async () => {
|
|
516
|
-
await this.write({ kind: "react", thread: this.find(e3).thread, target: e3, text:
|
|
517
|
+
await this.write({ kind: "react", thread: this.find(e3).thread, target: e3, text: Y(t2) });
|
|
517
518
|
});
|
|
518
519
|
}
|
|
519
520
|
unreact(e3, t2) {
|
|
520
521
|
return this.enqueue(async () => {
|
|
521
|
-
await this.write({ kind: "unreact", thread: this.find(e3).thread, target: e3, text:
|
|
522
|
+
await this.write({ kind: "unreact", thread: this.find(e3).thread, target: e3, text: Y(t2) });
|
|
522
523
|
});
|
|
523
524
|
}
|
|
524
525
|
resolve(e3) {
|
|
@@ -531,8 +532,14 @@ var J = class e2 {
|
|
|
531
532
|
await this.write({ kind: "reopen", thread: this.thread(e3) });
|
|
532
533
|
});
|
|
533
534
|
}
|
|
535
|
+
paste(e3, t2, r2, n2) {
|
|
536
|
+
return this.enqueue(async () => {
|
|
537
|
+
let i = this.thread(e3), a = y(t2), o2 = this.cache.find((e4) => e4.id === i).root.id;
|
|
538
|
+
await this.write({ kind: "reply", thread: i, target: o2, text: X(r2) }, a), n2?.trim() && await this.write({ kind: "addressed", thread: i, text: X(n2) }, a);
|
|
539
|
+
});
|
|
540
|
+
}
|
|
534
541
|
sign(e3, t2) {
|
|
535
|
-
if (!
|
|
542
|
+
if (!V.test(e3)) throw new Error('pipeup: a signing purpose looks like "<add-on id>/<name>"');
|
|
536
543
|
return l(this.options.identity, r(`pipeup:${e3}
|
|
537
544
|
${t2}`));
|
|
538
545
|
}
|
|
@@ -541,12 +548,12 @@ ${t2}`));
|
|
|
541
548
|
}
|
|
542
549
|
exportFile() {
|
|
543
550
|
return (async function(e3, t2, r2) {
|
|
544
|
-
let n2 = r2 ? { pipeup: 1, doc: e3, sealed: true, ...await
|
|
551
|
+
let n2 = r2 ? { pipeup: 1, doc: e3, sealed: true, ...await G(r2, JSON.stringify(t2), Z(e3)) } : { pipeup: 1, doc: e3, sealed: false, ops: t2 };
|
|
545
552
|
return JSON.stringify(n2, null, 2);
|
|
546
553
|
})(this.options.doc, this.log.all(), this.options.key);
|
|
547
554
|
}
|
|
548
555
|
async importFile(e3) {
|
|
549
|
-
return this.merge(await
|
|
556
|
+
return this.merge(await J(e3, this.options.doc, this.options.key), "file");
|
|
550
557
|
}
|
|
551
558
|
merge(e3, t2) {
|
|
552
559
|
return e3.length > 5e3 ? Promise.reject(new RangeError("pipeup: too many changes to merge at once")) : this.enqueue(async () => {
|
|
@@ -555,7 +562,7 @@ ${t2}`));
|
|
|
555
562
|
try {
|
|
556
563
|
await this.persist();
|
|
557
564
|
} catch (e4) {
|
|
558
|
-
throw r2.length > 0 ? new
|
|
565
|
+
throw r2.length > 0 ? new W(r2[0].body.id, e4, r2.length) : e4;
|
|
559
566
|
}
|
|
560
567
|
return r2.length;
|
|
561
568
|
});
|
|
@@ -565,15 +572,15 @@ ${t2}`));
|
|
|
565
572
|
return this.e = t2.catch(() => {
|
|
566
573
|
}), t2;
|
|
567
574
|
}
|
|
568
|
-
async write(e3) {
|
|
569
|
-
let
|
|
570
|
-
this.unsaved.push(
|
|
575
|
+
async write(e3, t2 = this.displayName) {
|
|
576
|
+
let r2 = await this.log.append(this.options.identity, t2, e3);
|
|
577
|
+
this.unsaved.push(r2), this.refresh(true, [r2], "local");
|
|
571
578
|
try {
|
|
572
579
|
await this.persist();
|
|
573
580
|
} catch (e4) {
|
|
574
|
-
throw new
|
|
581
|
+
throw new W(r2.body.id, e4);
|
|
575
582
|
}
|
|
576
|
-
return
|
|
583
|
+
return r2.body.id;
|
|
577
584
|
}
|
|
578
585
|
async persist() {
|
|
579
586
|
if (0 === this.unsaved.length) return;
|
|
@@ -608,23 +615,23 @@ ${t2}`));
|
|
|
608
615
|
return e3;
|
|
609
616
|
}
|
|
610
617
|
};
|
|
611
|
-
function
|
|
618
|
+
function X(e3) {
|
|
612
619
|
let t2 = e3.trim();
|
|
613
620
|
if (!t2) throw new Error("pipeup: a comment needs some words");
|
|
614
|
-
if (t2.length >
|
|
621
|
+
if (t2.length > b) throw new Error(`pipeup: comments are limited to ${b} characters`);
|
|
615
622
|
return t2;
|
|
616
623
|
}
|
|
617
|
-
function
|
|
618
|
-
if (!
|
|
624
|
+
function Y(e3) {
|
|
625
|
+
if (!O(e3)) throw new Error("pipeup: a reaction is one emoji");
|
|
619
626
|
return e3;
|
|
620
627
|
}
|
|
621
|
-
function
|
|
628
|
+
function ee(e3) {
|
|
622
629
|
let t2 = e3.replace(/\s+/g, " ").trim();
|
|
623
630
|
if (!t2 || t2.length > 80) throw new Error("pipeup: choose a name of 1 to 80 characters");
|
|
624
631
|
if (g(t2)) throw new Error("pipeup: that name is kept for AI replies; choose another");
|
|
625
632
|
return t2;
|
|
626
633
|
}
|
|
627
|
-
var
|
|
634
|
+
var te = class {
|
|
628
635
|
docs = /* @__PURE__ */ new Map();
|
|
629
636
|
profile = null;
|
|
630
637
|
async load(e3) {
|
|
@@ -731,15 +738,15 @@ var retryAfter = (res, fallback = 10) => {
|
|
|
731
738
|
const n2 = Number(res.headers.get("Retry-After"));
|
|
732
739
|
return n2 > 0 ? Math.min(n2, 3600) : fallback;
|
|
733
740
|
};
|
|
734
|
-
var idsOf = (ops) => ops.map((
|
|
741
|
+
var idsOf = (ops) => ops.map((o2) => o2?.body?.id).filter((i) => typeof i === "string");
|
|
735
742
|
|
|
736
743
|
// share/src/mailbox.ts
|
|
737
744
|
var MAX_BODY = 524288;
|
|
738
745
|
var MAX_PAGES = 1e3;
|
|
739
746
|
var batchId = async (ct) => toB64u(await sha256(utf8(ct))).slice(0, 22);
|
|
740
747
|
var Mailbox = class {
|
|
741
|
-
constructor(
|
|
742
|
-
this.a =
|
|
748
|
+
constructor(a, l2, f2, signal) {
|
|
749
|
+
this.a = a;
|
|
743
750
|
this.l = l2;
|
|
744
751
|
this.f = f2;
|
|
745
752
|
this.signal = signal;
|
|
@@ -769,10 +776,10 @@ var Mailbox = class {
|
|
|
769
776
|
if (typeof b2?.id !== "string" || typeof b2.ct !== "string" || this.seen.has(b2.id)) continue;
|
|
770
777
|
this.seen.add(b2.id);
|
|
771
778
|
const bytes = fromText(b2.ct);
|
|
772
|
-
const
|
|
773
|
-
if (!
|
|
774
|
-
out.push(
|
|
775
|
-
for (const id of idsOf(
|
|
779
|
+
const o2 = bytes && await open(this.l, "share", bytes);
|
|
780
|
+
if (!o2) continue;
|
|
781
|
+
out.push(o2);
|
|
782
|
+
for (const id of idsOf(o2.ops)) this.ids.add(id);
|
|
776
783
|
}
|
|
777
784
|
if (typeof j2.next === "string") this.cursor = j2.next;
|
|
778
785
|
this.expires = typeof j2.expires === "string" ? Date.parse(j2.expires) || null : null;
|
|
@@ -782,7 +789,7 @@ var Mailbox = class {
|
|
|
782
789
|
}
|
|
783
790
|
/** The POST for these ops. A retry of the same ops reuses its sealed text and id, so the server collapses it. */
|
|
784
791
|
async req(ops) {
|
|
785
|
-
const key = ops.map((
|
|
792
|
+
const key = ops.map((o2) => o2.body.id).join();
|
|
786
793
|
if (this.memo?.key !== key) {
|
|
787
794
|
const ct2 = toText(await seal(this.l, "share", ops));
|
|
788
795
|
this.memo = { key, ct: ct2, id: await batchId(ct2) };
|
|
@@ -858,15 +865,15 @@ async function readPaste(f2, base, id, signal) {
|
|
|
858
865
|
return { j: j2, size, ttl: typeof ttl === "number" && ttl > 0 ? ttl * 1e3 : null };
|
|
859
866
|
}
|
|
860
867
|
var PrivateBin = class {
|
|
861
|
-
constructor(
|
|
868
|
+
constructor(a, l2, f2, signal, gen, onGen = () => {
|
|
862
869
|
}) {
|
|
863
|
-
this.a =
|
|
870
|
+
this.a = a;
|
|
864
871
|
this.l = l2;
|
|
865
872
|
this.f = f2;
|
|
866
873
|
this.signal = signal;
|
|
867
874
|
this.onGen = onGen;
|
|
868
|
-
this.head = gen ||
|
|
869
|
-
this.trail.push(
|
|
875
|
+
this.head = gen || a.paste;
|
|
876
|
+
this.trail.push(a.paste);
|
|
870
877
|
}
|
|
871
878
|
ids = /* @__PURE__ */ new Set();
|
|
872
879
|
expires = null;
|
|
@@ -1034,17 +1041,17 @@ function parseArgs(argv) {
|
|
|
1034
1041
|
const flags = /* @__PURE__ */ new Map();
|
|
1035
1042
|
const rest = [];
|
|
1036
1043
|
for (let i = 0; i < argv.length; i++) {
|
|
1037
|
-
const
|
|
1038
|
-
if (
|
|
1039
|
-
else if (
|
|
1040
|
-
const [name, inline] =
|
|
1044
|
+
const a = argv[i];
|
|
1045
|
+
if (a === "-h") flags.set("help", true);
|
|
1046
|
+
else if (a.startsWith("--")) {
|
|
1047
|
+
const [name, inline] = a.slice(2).split(/=(.*)/s, 2);
|
|
1041
1048
|
if (WITH_VALUE.has(name)) {
|
|
1042
1049
|
const v2 = inline ?? argv[++i];
|
|
1043
1050
|
if (v2 === void 0 || v2.startsWith("--")) throw new UsageError(`--${name} needs a value`);
|
|
1044
1051
|
flags.set(name, v2);
|
|
1045
1052
|
} else if (["mailbox", "new-doc", "help"].includes(name)) flags.set(name, true);
|
|
1046
1053
|
else throw new UsageError(`I don't know the option --${name}`);
|
|
1047
|
-
} else rest.push(
|
|
1054
|
+
} else rest.push(a);
|
|
1048
1055
|
}
|
|
1049
1056
|
return { command: rest[0], file: rest[1], flags };
|
|
1050
1057
|
}
|
|
@@ -1083,21 +1090,21 @@ async function patiently(io, run2) {
|
|
|
1083
1090
|
}
|
|
1084
1091
|
function server(value, mailbox) {
|
|
1085
1092
|
if (typeof value !== "string") throw new UsageError("--server needs the address of the service");
|
|
1086
|
-
const
|
|
1093
|
+
const a = parseShare(
|
|
1087
1094
|
mailbox ? `${value.replace(/\/+$/, "")}/m/${"x".repeat(22)}#pm1.${"x".repeat(43)}` : `${value}?${"x".repeat(16)}#${"x".repeat(43)}`,
|
|
1088
1095
|
true
|
|
1089
1096
|
);
|
|
1090
|
-
if (
|
|
1097
|
+
if (a.kind === "off")
|
|
1091
1098
|
throw new UsageError("--server must be an https: address (http://localhost is allowed for development)");
|
|
1092
1099
|
const u2 = new URL(value);
|
|
1093
1100
|
return mailbox ? value.replace(/\/+$/, "") : u2.origin + u2.pathname;
|
|
1094
1101
|
}
|
|
1095
1102
|
async function feedbackOps(file, docAttr, io) {
|
|
1096
|
-
const { id, key } = await
|
|
1097
|
-
const doc = await
|
|
1103
|
+
const { id, key } = await K(docAttr);
|
|
1104
|
+
const doc = await Q.open({
|
|
1098
1105
|
doc: id,
|
|
1099
1106
|
key,
|
|
1100
|
-
store: new
|
|
1107
|
+
store: new te(),
|
|
1101
1108
|
identity: await d(),
|
|
1102
1109
|
name: ""
|
|
1103
1110
|
});
|
|
@@ -1124,11 +1131,11 @@ Run the same command with --new-doc and I will write a fresh one into the page.
|
|
|
1124
1131
|
);
|
|
1125
1132
|
throw new UsageError("the page needs a document identity");
|
|
1126
1133
|
}
|
|
1127
|
-
docAttr =
|
|
1134
|
+
docAttr = H();
|
|
1128
1135
|
html = setAttribute(html, "data-pipeup-doc", docAttr);
|
|
1129
1136
|
io.out("Gave the page a new document identity (data-pipeup-doc).");
|
|
1130
1137
|
}
|
|
1131
|
-
const { id } = await
|
|
1138
|
+
const { id } = await K(docAttr).catch(() => {
|
|
1132
1139
|
throw new UsageError("data-pipeup-doc in the page isn't a valid document identity");
|
|
1133
1140
|
});
|
|
1134
1141
|
const from = typeof flags.get("from") === "string" ? flags.get("from") : null;
|
|
@@ -1195,32 +1202,32 @@ Run the same command with --new-doc and I will write a fresh one into the page.
|
|
|
1195
1202
|
}
|
|
1196
1203
|
async function addressOf(file, io) {
|
|
1197
1204
|
const html = await io.read(file);
|
|
1198
|
-
const
|
|
1199
|
-
if (
|
|
1205
|
+
const a = parseShare(readAttribute(html, "data-pipeup-share"), true);
|
|
1206
|
+
if (a.kind === "off") throw new UsageError(`${file}: ${a.reason}`);
|
|
1200
1207
|
const docAttr = readAttribute(html, "data-pipeup-doc");
|
|
1201
|
-
const doc = docAttr ? (await
|
|
1208
|
+
const doc = docAttr ? (await K(docAttr).catch(() => null))?.id : null;
|
|
1202
1209
|
if (!doc) throw new UsageError(`${file} has no valid data-pipeup-doc`);
|
|
1203
|
-
return { html, a
|
|
1210
|
+
return { html, a, l: ladder(doc, a.key) };
|
|
1204
1211
|
}
|
|
1205
1212
|
async function stop(file, flags, io) {
|
|
1206
1213
|
const key = flags.get("key");
|
|
1207
1214
|
if (typeof key !== "string") throw new UsageError("stop needs --key with the stop key that create printed");
|
|
1208
|
-
const { a
|
|
1209
|
-
if (
|
|
1210
|
-
const res = await io.fetch(
|
|
1215
|
+
const { a, l: l2 } = await addressOf(file, io);
|
|
1216
|
+
if (a.kind === "mailbox") {
|
|
1217
|
+
const res = await io.fetch(a.mailbox, { method: "DELETE", headers: { Authorization: `Bearer ${key}` } });
|
|
1211
1218
|
if (res.status === 403) throw new Error("That isn't this mailbox's stop key, so nothing was deleted.");
|
|
1212
1219
|
if (res.status === 404) throw new Error("The shared copy is already gone.");
|
|
1213
1220
|
if (res.status !== 204 && res.status !== 200)
|
|
1214
1221
|
throw new Error(`The server answered ${res.status}, so I can't say it was deleted.`);
|
|
1215
1222
|
io.out("Deleted the shared copy. Everyone keeps the comments they already have.");
|
|
1216
1223
|
} else {
|
|
1217
|
-
const b2 = new PrivateBin(
|
|
1224
|
+
const b2 = new PrivateBin(a, l2, io.fetch);
|
|
1218
1225
|
const later = [];
|
|
1219
1226
|
await b2.read().catch(() => {
|
|
1220
1227
|
});
|
|
1221
1228
|
for (const id of b2.trail.slice(1))
|
|
1222
|
-
later.push({ id, ttl: (await readPaste(io.fetch,
|
|
1223
|
-
const res = await deletePaste(io.fetch,
|
|
1229
|
+
later.push({ id, ttl: (await readPaste(io.fetch, a.base, id).catch(() => null))?.ttl ?? null });
|
|
1230
|
+
const res = await deletePaste(io.fetch, a.base, a.paste, key);
|
|
1224
1231
|
const j2 = await res.json().catch(() => ({}));
|
|
1225
1232
|
if (j2.status !== 0)
|
|
1226
1233
|
throw new Error(
|
|
@@ -1244,17 +1251,17 @@ async function stop(file, flags, io) {
|
|
|
1244
1251
|
);
|
|
1245
1252
|
}
|
|
1246
1253
|
async function update(file, io) {
|
|
1247
|
-
const { html, a
|
|
1248
|
-
if (
|
|
1249
|
-
const b2 = new PrivateBin(
|
|
1254
|
+
const { html, a, l: l2 } = await addressOf(file, io);
|
|
1255
|
+
if (a.kind === "mailbox") return io.out("A mailbox has no newer copies, so there is nothing to update.");
|
|
1256
|
+
const b2 = new PrivateBin(a, l2, io.fetch);
|
|
1250
1257
|
await b2.read().catch((e3) => {
|
|
1251
1258
|
throw new Error(
|
|
1252
1259
|
`I couldn't read the shared copy: ${e3.message === "gone" ? "it is gone" : e3.message}`
|
|
1253
1260
|
);
|
|
1254
1261
|
});
|
|
1255
|
-
if (b2.head ===
|
|
1262
|
+
if (b2.head === a.paste) return io.out("The page already points at the newest copy.");
|
|
1256
1263
|
const fragment = new URL(readAttribute(html, "data-pipeup-share")).hash;
|
|
1257
|
-
await io.write(file, setAttribute(html, "data-pipeup-share", `${
|
|
1264
|
+
await io.write(file, setAttribute(html, "data-pipeup-share", `${a.base}?${b2.head}${fragment}`));
|
|
1258
1265
|
io.out(`Pointed ${file} at the newest copy, so new reviewers don't have to walk through the older ones.`);
|
|
1259
1266
|
}
|
|
1260
1267
|
async function run(argv, io) {
|