@vielzeug/archive 26.11.4 → 26.11.6

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.
@@ -1,14 +1,14 @@
1
1
  import { ARCHIVE_LIMITS as e, SESSION_MAX_AGE_SECONDS as t, archiveAliasSchema as n, archiveCollectionMetaSchema as r, archiveHealthSchema as i, archiveIdentitySchema as a, archiveLikeStateSchema as o, archiveLikedIdsSchema as s, archiveLikesPageSchema as c, archiveModerationLogSchema as l, archiveOwnReportsSchema as u, archiveReportsSchema as d, archiveSortSchema as f, isWellFormedText as p, uuidSchema as m } from "../contract.js";
2
- import { ApiError as h, badRequest as g, conflict as _, forbidden as v, invalid as y, json as b, noStore as x, notFound as S, queryParams as C, readJsonBody as w } from "./http.js";
3
- import { SQL as T, likedPublicationsSql as E, likesPageSql as D, listPublicationsSql as O, moderationLogSql as k, reportsPageSql as A } from "./sql.js";
4
- import { forwardPage as j, forwardPageParams as M, nextCursor as N } from "./_pagination.js";
2
+ import { forwardPage as h, forwardPageParams as g, nextCursor as _ } from "./_pagination.js";
3
+ import { ApiError as v, badRequest as y, conflict as b, forbidden as x, invalid as S, json as C, noStore as w, notFound as T, queryParams as E, readJsonBody as D } from "./http.js";
4
+ import { SQL as O, likedPublicationsSql as k, likesPageSql as A, listPublicationsSql as j, moderationLogSql as M, reportsPageSql as N } from "./sql.js";
5
5
  import { s as P } from "@vielzeug/spell";
6
- //#region src/worker/publications.ts
6
+ // src/worker/publications.ts
7
7
  async function databaseCall(e) {
8
8
  try {
9
9
  return await e();
10
10
  } catch (e) {
11
- throw new h(500, "internal", "The service is not available.", {
11
+ throw new v(500, "internal", "The service is not available.", {
12
12
  cause: e,
13
13
  diagnostic: { kind: "database" }
14
14
  });
@@ -18,7 +18,7 @@ var first = (e, t, ...n) => databaseCall(() => e.DB.prepare(t).bind(...n).first(
18
18
  let r = await e.DB.prepare(t).bind(...n).all();
19
19
  if (!Array.isArray(r.results)) throw Error("Missing database query results");
20
20
  return r.results;
21
- }), run = (e, t, ...n) => databaseCall(() => e.DB.prepare(t).bind(...n).run()), uuid = () => crypto.randomUUID(), now = () => (/* @__PURE__ */ new Date()).toISOString(), recordUnavailable = () => new h(503, "record-unavailable", "The record is not available.", { diagnostic: { kind: "record" } }), F = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, rowToEntry = (e, t) => {
21
+ }), run = (e, t, ...n) => databaseCall(() => e.DB.prepare(t).bind(...n).run()), uuid = () => crypto.randomUUID(), now = () => (/* @__PURE__ */ new Date()).toISOString(), recordUnavailable = () => new v(503, "record-unavailable", "The record is not available.", { diagnostic: { kind: "record" } }), F = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, rowToEntry = (e, t) => {
22
22
  let n;
23
23
  try {
24
24
  n = JSON.parse(t.payload_json);
@@ -51,11 +51,11 @@ var first = (e, t, ...n) => databaseCall(() => e.DB.prepare(t).bind(...n).first(
51
51
  hidden: t.hidden_at !== null
52
52
  };
53
53
  }, entryById = async (e, t, n) => {
54
- let r = await first(t, T.publication, n, e.name);
54
+ let r = await first(t, O.publication, n, e.name);
55
55
  return r === null ? null : rowToEntry(e, r);
56
56
  }, searchMatchFor = (t) => {
57
57
  if (t === null) return;
58
- if (t.length > e.searchChars) throw g();
58
+ if (t.length > e.searchChars) throw y();
59
59
  let n = (t.match(/[\p{L}\p{N}]+/gu) ?? []).slice(0, e.searchTerms);
60
60
  if (n.length !== 0) return n.map((e) => `"${e}"*`).join(" ");
61
61
  }, entryCursorContext = (e) => JSON.stringify([
@@ -71,13 +71,13 @@ var first = (e, t, ...n) => databaseCall(() => e.DB.prepare(t).bind(...n).first(
71
71
  likeCount: e.like_count
72
72
  }), I = 16, vocabValues = (e, t, n) => {
73
73
  let r = e.getAll(t);
74
- if (r.length > I) throw g();
74
+ if (r.length > I) throw y();
75
75
  let i = [...new Set(r)].sort();
76
- if (i.some((e) => !n.includes(e))) throw g();
76
+ if (i.some((e) => !n.includes(e))) throw y();
77
77
  return i;
78
78
  };
79
79
  async function listEntries(e, t, n) {
80
- let r = new URL(t.url), i = C(r, [
80
+ let r = new URL(t.url), i = E(r, [
81
81
  "author",
82
82
  "cursor",
83
83
  "filter",
@@ -86,9 +86,9 @@ async function listEntries(e, t, n) {
86
86
  "sort",
87
87
  "tag"
88
88
  ], ["filter", "tag"]), a = i.get("sort") ?? "recent", o = f.safeParse(a);
89
- if (!o.success) throw g();
89
+ if (!o.success) throw y();
90
90
  let s = vocabValues(i, "filter", e.filters), c = vocabValues(i, "tag", e.tags), l = i.get("author");
91
- if (l !== null && !F.test(l)) throw g();
91
+ if (l !== null && !F.test(l)) throw y();
92
92
  let u = searchMatchFor(i.get("search")), d = entryCursorContext({
93
93
  author: l ?? void 0,
94
94
  collection: e.name,
@@ -96,7 +96,7 @@ async function listEntries(e, t, n) {
96
96
  searchMatch: u,
97
97
  sort: o.data,
98
98
  tags: c
99
- }), { cursor: p, limit: m } = M(i, d), h = {
99
+ }), { cursor: p, limit: m } = g(i, d), h = {
100
100
  author: l ?? void 0,
101
101
  collection: e.name,
102
102
  cursor: p === null ? void 0 : {
@@ -109,11 +109,11 @@ async function listEntries(e, t, n) {
109
109
  searchMatch: u,
110
110
  sort: o.data,
111
111
  tags: c
112
- }, _ = O(h), v = await all(n, _.sql, ..._.parameters), y = v.slice(0, m), S = y.at(-1), w = {
113
- cursor: v.length > m && S !== void 0 ? N(cursorFromRow(S), d) : null,
114
- entries: y.map((t) => rowToEntry(e, t))
112
+ }, v = j(h), b = await all(n, v.sql, ...v.parameters), x = b.slice(0, m), S = x.at(-1), T = {
113
+ cursor: b.length > m && S !== void 0 ? _(cursorFromRow(S), d) : null,
114
+ entries: x.map((t) => rowToEntry(e, t))
115
115
  };
116
- return b(200, w, x);
116
+ return C(200, T, w);
117
117
  }
118
118
  function collectionMeta(e) {
119
119
  let t = r.safeParse({
@@ -122,17 +122,17 @@ function collectionMeta(e) {
122
122
  tags: [...e.tags].sort()
123
123
  });
124
124
  if (!t.success) throw recordUnavailable();
125
- return b(200, t.data, x);
125
+ return C(200, t.data, w);
126
126
  }
127
127
  async function getEntry(e, t, n) {
128
128
  let r = await entryById(e, t, n);
129
- if (r === null) throw S();
130
- return b(200, r, x);
129
+ if (r === null) throw T();
130
+ return C(200, r, w);
131
131
  }
132
132
  async function healthJson(t, n) {
133
133
  let r = null, a = null;
134
134
  try {
135
- r = (await first(n, T.schemaVersion))?.applied ?? 0;
135
+ r = (await first(n, O.schemaVersion))?.applied ?? 0;
136
136
  } catch {
137
137
  a = "database-unavailable";
138
138
  }
@@ -149,12 +149,12 @@ async function healthJson(t, n) {
149
149
  status: a === null ? "ok" : "degraded"
150
150
  });
151
151
  if (!o.success) throw recordUnavailable();
152
- return b(a === null ? 200 : 503, o.data, x);
152
+ return C(a === null ? 200 : 503, o.data, w);
153
153
  }
154
154
  var identityJson = (e, t) => {
155
155
  let n = a.safeParse(t);
156
156
  if (!n.success) throw recordUnavailable();
157
- return b(e, n.data, x);
157
+ return C(e, n.data, w);
158
158
  };
159
159
  function getMe(e) {
160
160
  return identityJson(200, e?.publisher ? {
@@ -164,18 +164,18 @@ function getMe(e) {
164
164
  } : null);
165
165
  }
166
166
  async function registerMe(e, t, n) {
167
- let r = await w(e), i = L.safeParse(r);
168
- if (!i.success) throw y();
169
- let a = i.data.name ?? "", o = await first(t, T.publisher, n.subject.issuer, n.subject.subject);
167
+ let r = await D(e), i = L.safeParse(r);
168
+ if (!i.success) throw S();
169
+ let a = i.data.name ?? "", o = await first(t, O.publisher, n.subject.issuer, n.subject.subject);
170
170
  if (o !== null) return identityJson(200, {
171
171
  id: o.id,
172
172
  name: o.display_name,
173
173
  operator: n.operator
174
174
  });
175
175
  let s = uuid();
176
- await run(t, T.insertPublisher, s, n.subject.issuer, n.subject.subject, a, now(), n.subject.issuer, n.subject.subject, Date.now());
177
- let c = await first(t, T.publisher, n.subject.issuer, n.subject.subject);
178
- if (c === null) throw await first(t, T.revokedSubject, n.subject.issuer, n.subject.subject, Date.now()) === null ? _() : new h(401, "unauthenticated", "Sign-in is required.");
176
+ await run(t, O.insertPublisher, s, n.subject.issuer, n.subject.subject, a, now(), n.subject.issuer, n.subject.subject, Date.now());
177
+ let c = await first(t, O.publisher, n.subject.issuer, n.subject.subject);
178
+ if (c === null) throw await first(t, O.revokedSubject, n.subject.issuer, n.subject.subject, Date.now()) === null ? b() : new v(401, "unauthenticated", "Sign-in is required.");
179
179
  return identityJson(201, {
180
180
  id: c.id,
181
181
  name: c.display_name,
@@ -183,37 +183,37 @@ async function registerMe(e, t, n) {
183
183
  });
184
184
  }
185
185
  async function renameMe(e, t, n) {
186
- let r = requirePublisherRow(n), i = await w(e), a = R.safeParse(i);
187
- if (!a.success) throw y();
188
- return a.data.name !== r.displayName && await run(t, T.renamePublisher, a.data.name, r.id, a.data.name), identityJson(200, {
186
+ let r = requirePublisherRow(n), i = await D(e), a = R.safeParse(i);
187
+ if (!a.success) throw S();
188
+ return a.data.name !== r.displayName && await run(t, O.renamePublisher, a.data.name, r.id, a.data.name), identityJson(200, {
189
189
  id: r.id,
190
190
  name: a.data.name,
191
191
  operator: n.operator
192
192
  });
193
193
  }
194
194
  async function deleteProfile(e, t) {
195
- return t.publisher !== null && await run(e, T.deletePublisher, t.publisher.id), new Response(null, {
195
+ return t.publisher !== null && await run(e, O.deletePublisher, t.publisher.id), new Response(null, {
196
196
  headers: { "cache-control": "no-store" },
197
197
  status: 204
198
198
  });
199
199
  }
200
200
  async function deleteAccountProfile(e, n) {
201
201
  let { issuer: r, subject: i } = n.subject;
202
- return await databaseCall(() => e.DB.batch([e.DB.prepare(T.revokeSubject).bind(r, i, Date.now() + (t + 10) * 1e3), e.DB.prepare(T.deleteSubjectPublisher).bind(r, i)])), new Response(null, {
203
- headers: x,
202
+ return await databaseCall(() => e.DB.batch([e.DB.prepare(O.revokeSubject).bind(r, i, Date.now() + (t + 10) * 1e3), e.DB.prepare(O.deleteSubjectPublisher).bind(r, i)])), new Response(null, {
203
+ headers: w,
204
204
  status: 204
205
205
  });
206
206
  }
207
207
  async function erasePublisher(e, t, n) {
208
208
  let r = requireOperatorRow(t);
209
- return await databaseCall(() => e.DB.batch([e.DB.prepare(T.auditErasure).bind(uuid(), r.id, now(), n), e.DB.prepare(T.deletePublisher).bind(n)])), new Response(null, {
210
- headers: x,
209
+ return await databaseCall(() => e.DB.batch([e.DB.prepare(O.auditErasure).bind(uuid(), r.id, now(), n), e.DB.prepare(O.deletePublisher).bind(n)])), new Response(null, {
210
+ headers: w,
211
211
  status: 204
212
212
  });
213
213
  }
214
214
  async function listModerationLog(e, t, n) {
215
215
  requireOperatorRow(n);
216
- let r = "moderation-log", { cursor: i, limit: a } = j(e, r), o = k({
216
+ let r = "moderation-log", { cursor: i, limit: a } = h(e, r), o = M({
217
217
  cursor: i,
218
218
  limit: a + 1
219
219
  }), s = await all(t, o.sql, ...o.parameters), c = s.slice(0, a), u = c.at(-1), d = l.safeParse({
@@ -225,55 +225,55 @@ async function listModerationLog(e, t, n) {
225
225
  operatorId: e.operator_id,
226
226
  targetId: e.target_id
227
227
  })),
228
- cursor: s.length > a && u !== void 0 ? N({
228
+ cursor: s.length > a && u !== void 0 ? _({
229
229
  createdAt: u.created_at,
230
230
  id: u.id
231
231
  }, r) : null
232
232
  });
233
233
  if (!d.success) throw recordUnavailable();
234
- return b(200, d.data, x);
234
+ return C(200, d.data, w);
235
235
  }
236
236
  var requirePublisherRow = (e) => {
237
- if (e.publisher === null) throw v("Register before publishing.");
237
+ if (e.publisher === null) throw x("Register before publishing.");
238
238
  return e.publisher;
239
239
  }, requireOperatorRow = (e) => {
240
- if (!e.operator) throw v("Operator access is required.");
241
- if (e.publisher === null) throw v("Register before moderating.");
240
+ if (!e.operator) throw x("Operator access is required.");
241
+ if (e.publisher === null) throw x("Register before moderating.");
242
242
  return e.publisher;
243
243
  }, L = P.object({ name: n.optional() }), R = P.object({ name: n });
244
244
  async function myPublications(t, n, r) {
245
- if (r.publisher === null) return b(200, { entries: [] }, x);
246
- let i = await all(n, T.ownPublications, r.publisher.id, t.name, e.maxPublications);
247
- return b(200, { entries: i.map((e) => rowToOwnedEntry(t, e)) }, x);
245
+ if (r.publisher === null) return C(200, { entries: [] }, w);
246
+ let i = await all(n, O.ownPublications, r.publisher.id, t.name, e.maxPublications);
247
+ return C(200, { entries: i.map((e) => rowToOwnedEntry(t, e)) }, w);
248
248
  }
249
249
  async function myLikes(t, n, r) {
250
250
  let i = new URL(t.url).searchParams.get("ids");
251
251
  if (i === null) {
252
- let e = `likes:${r.publisher?.id ?? "unregistered"}`, { cursor: i, limit: a } = j(t, e);
253
- if (r.publisher === null) return b(200, {
252
+ let e = `likes:${r.publisher?.id ?? "unregistered"}`, { cursor: i, limit: a } = h(t, e);
253
+ if (r.publisher === null) return C(200, {
254
254
  cursor: null,
255
255
  ids: []
256
- }, x);
257
- let o = D(r.publisher.id, {
256
+ }, w);
257
+ let o = A(r.publisher.id, {
258
258
  cursor: i,
259
259
  limit: a + 1
260
260
  }), s = await all(n, o.sql, ...o.parameters), l = s.slice(0, a), u = l.at(-1), d = c.safeParse({
261
- cursor: s.length > a && u !== void 0 ? N({
261
+ cursor: s.length > a && u !== void 0 ? _({
262
262
  createdAt: u.created_at,
263
263
  id: u.publication_id
264
264
  }, e) : null,
265
265
  ids: l.map((e) => e.publication_id)
266
266
  });
267
267
  if (!d.success) throw recordUnavailable();
268
- return b(200, d.data, x);
268
+ return C(200, d.data, w);
269
269
  }
270
- C(new URL(t.url), ["ids"]);
270
+ E(new URL(t.url), ["ids"]);
271
271
  let a = [...new Set(i.split(","))];
272
- if (a.length > e.likeStateIds || a.some((e) => !F.test(e))) throw g();
273
- if (r.publisher === null) return b(200, { ids: [] }, x);
274
- let o = E(r.publisher.id, a), l = await all(n, o.sql, ...o.parameters), u = new Set(l.map((e) => e.publication_id)), d = { ids: a.filter((e) => u.has(e)) }, f = s.safeParse(d);
272
+ if (a.length > e.likeStateIds || a.some((e) => !F.test(e))) throw y();
273
+ if (r.publisher === null) return C(200, { ids: [] }, w);
274
+ let o = k(r.publisher.id, a), l = await all(n, o.sql, ...o.parameters), u = new Set(l.map((e) => e.publication_id)), d = { ids: a.filter((e) => u.has(e)) }, f = s.safeParse(d);
275
275
  if (!f.success) throw recordUnavailable();
276
- return b(200, f.data, x);
276
+ return C(200, f.data, w);
277
277
  }
278
278
  var z = P.object({
279
279
  clientKey: m,
@@ -284,135 +284,135 @@ var z = P.object({
284
284
  });
285
285
  async function resolveRetryRecord(e, t, n, r) {
286
286
  if (n === null || n.expires_at <= Date.now()) return null;
287
- if (n.payload_hash !== r) throw _("That key was used for a different payload.");
288
- if (n.publication_id === null) throw new h(410, "gone", "That publication was removed.");
287
+ if (n.payload_hash !== r) throw b("That key was used for a different payload.");
288
+ if (n.publication_id === null) throw new v(410, "gone", "That publication was removed.");
289
289
  let i = await entryById(e, t, n.publication_id);
290
- return i === null ? "vanished" : b(200, i, x);
290
+ return i === null ? "vanished" : C(200, i, w);
291
291
  }
292
292
  async function publishEntry(t, n, r, i, a) {
293
- if (!i.publishingEnabled) throw new h(503, "publishing-disabled", "Publishing is currently disabled.");
294
- let o = requirePublisherRow(a), s = await w(n), c = z.safeParse(s);
295
- if (!c.success) throw y();
293
+ if (!i.publishingEnabled) throw new v(503, "publishing-disabled", "Publishing is currently disabled.");
294
+ let o = requirePublisherRow(a), s = await D(n), c = z.safeParse(s);
295
+ if (!c.success) throw S();
296
296
  let { clientKey: l } = c.data, u = await t.derive(c.data.payload);
297
- if (u === null) throw y();
298
- let d = u.canonicalJson, f = u.contentHash, p = o.id, m = t.name, g = await resolveRetryRecord(t, r, await first(r, T.request, p, m, l), f);
299
- if (g !== null && g !== "vanished") return g;
300
- let S = await first(r, T.publicationByKey, p, m, l);
301
- if (S !== null) {
302
- if (S.hidden_at !== null) throw v("This entry is under moderation review.");
303
- if (S.content_hash !== f) throw _("That key was used for a different payload.");
304
- return b(200, rowToEntry(t, S), x);
297
+ if (u === null) throw S();
298
+ let d = u.canonicalJson, f = u.contentHash, p = o.id, m = t.name, h = await resolveRetryRecord(t, r, await first(r, O.request, p, m, l), f);
299
+ if (h !== null && h !== "vanished") return h;
300
+ let g = await first(r, O.publicationByKey, p, m, l);
301
+ if (g !== null) {
302
+ if (g.hidden_at !== null) throw x("This entry is under moderation review.");
303
+ if (g.content_hash !== f) throw b("That key was used for a different payload.");
304
+ return C(200, rowToEntry(t, g), w);
305
305
  }
306
- let C = now(), E = Date.now() + e.idempotencyRetentionMs, D = JSON.stringify(u.tags);
306
+ let _ = now(), y = Date.now() + e.idempotencyRetentionMs, T = JSON.stringify(u.tags);
307
307
  if (((await databaseCall(() => r.DB.batch([
308
- r.DB.prepare(T.expireRequests).bind(Date.now()),
309
- r.DB.prepare(T.insertPublication).bind(uuid(), p, m, l, 1, u.contentVersion, d, f, u.filter, u.title, D, C, C, p, m, e.maxPublications, p, m, l, Date.now()),
310
- r.DB.prepare(T.insertRequest).bind(p, m, l, f, C, E, p, m, l, f)
308
+ r.DB.prepare(O.expireRequests).bind(Date.now()),
309
+ r.DB.prepare(O.insertPublication).bind(uuid(), p, m, l, 1, u.contentVersion, d, f, u.filter, u.title, T, _, _, p, m, e.maxPublications, p, m, l, Date.now()),
310
+ r.DB.prepare(O.insertRequest).bind(p, m, l, f, _, y, p, m, l, f)
311
311
  ])))[1]?.meta?.changes ?? 0) === 0) {
312
- let e = await resolveRetryRecord(t, r, await first(r, T.request, p, m, l), f);
313
- if (e === "vanished") throw new h(410, "gone", "That publication was removed.");
312
+ let e = await resolveRetryRecord(t, r, await first(r, O.request, p, m, l), f);
313
+ if (e === "vanished") throw new v(410, "gone", "That publication was removed.");
314
314
  if (e !== null) return e;
315
- let n = await first(r, T.publicationByKey, p, m, l);
316
- if (n !== null && n.hidden_at !== null) throw v("This entry is under moderation review.");
317
- if (n !== null && n.content_hash === f) return b(200, rowToEntry(t, n), x);
318
- throw n === null ? new h(429, "quota", "Publication quota reached.") : _("That key was used for a different payload.");
315
+ let n = await first(r, O.publicationByKey, p, m, l);
316
+ if (n !== null && n.hidden_at !== null) throw x("This entry is under moderation review.");
317
+ if (n !== null && n.content_hash === f) return C(200, rowToEntry(t, n), w);
318
+ throw n === null ? new v(429, "quota", "Publication quota reached.") : b("That key was used for a different payload.");
319
319
  }
320
- let O = await first(r, T.publicationByKey, p, m, l);
321
- if (O === null) throw _();
322
- return b(201, rowToEntry(t, O), x);
320
+ let E = await first(r, O.publicationByKey, p, m, l);
321
+ if (E === null) throw b();
322
+ return C(201, rowToEntry(t, E), w);
323
323
  }
324
324
  async function updateEntry(e, t, n, r, i, a) {
325
- if (!r.publishingEnabled) throw new h(503, "publishing-disabled", "Publishing is currently disabled.");
326
- let o = requirePublisherRow(i), s = await first(n, T.publicationAny, a, e.name);
327
- if (s === null || s.owner_id !== o.id) throw S();
328
- if (s.hidden_at !== null) throw v("This entry is under moderation review.");
329
- let c = await w(t), l = B.safeParse(c);
330
- if (!l.success) throw y();
325
+ if (!r.publishingEnabled) throw new v(503, "publishing-disabled", "Publishing is currently disabled.");
326
+ let o = requirePublisherRow(i), s = await first(n, O.publicationAny, a, e.name);
327
+ if (s === null || s.owner_id !== o.id) throw T();
328
+ if (s.hidden_at !== null) throw x("This entry is under moderation review.");
329
+ let c = await D(t), l = B.safeParse(c);
330
+ if (!l.success) throw S();
331
331
  let { expectedRevision: u } = l.data;
332
- if (u !== s.revision) throw _();
332
+ if (u !== s.revision) throw b();
333
333
  let d = await e.derive(l.data.payload);
334
- if (d === null) throw y();
334
+ if (d === null) throw S();
335
335
  let f = d.canonicalJson, p = d.contentHash;
336
336
  if (p === s.content_hash) {
337
- let t = await first(n, T.publicationAny, a, e.name);
338
- if (t === null || t.owner_id !== o.id) throw S();
339
- if (t.hidden_at !== null) throw v("This entry is under moderation review.");
340
- if (t.revision !== u) throw _();
341
- return b(200, rowToEntry(e, t), x);
337
+ let t = await first(n, O.publicationAny, a, e.name);
338
+ if (t === null || t.owner_id !== o.id) throw T();
339
+ if (t.hidden_at !== null) throw x("This entry is under moderation review.");
340
+ if (t.revision !== u) throw b();
341
+ return C(200, rowToEntry(e, t), w);
342
342
  }
343
343
  let m = JSON.stringify(d.tags);
344
- if (((await run(n, T.updatePublication, 1, d.contentVersion, f, p, d.filter, d.title, m, now(), a, o.id, u, p)).meta?.changes ?? 0) === 0) {
345
- let t = await first(n, T.publicationAny, a, e.name);
346
- throw t === null ? S() : t.hidden_at === null ? _() : v("This entry is under moderation review.");
344
+ if (((await run(n, O.updatePublication, 1, d.contentVersion, f, p, d.filter, d.title, m, now(), a, o.id, u, p)).meta?.changes ?? 0) === 0) {
345
+ let t = await first(n, O.publicationAny, a, e.name);
346
+ throw t === null ? T() : t.hidden_at === null ? b() : x("This entry is under moderation review.");
347
347
  }
348
- let g = await entryById(e, n, a);
349
- if (g === null) throw S();
350
- return b(200, g, x);
348
+ let h = await entryById(e, n, a);
349
+ if (h === null) throw T();
350
+ return C(200, h, w);
351
351
  }
352
352
  async function deleteEntry(e, t, n, r) {
353
353
  if (n.operator) {
354
354
  let i = requireOperatorRow(n);
355
- await databaseCall(() => t.DB.batch([t.DB.prepare(T.auditRemoval).bind(uuid(), i.id, now(), r, e.name), t.DB.prepare(T.removePublicationAny).bind(r, e.name)]));
355
+ await databaseCall(() => t.DB.batch([t.DB.prepare(O.auditRemoval).bind(uuid(), i.id, now(), r, e.name), t.DB.prepare(O.removePublicationAny).bind(r, e.name)]));
356
356
  } else {
357
- let i = requirePublisherRow(n), a = await first(t, T.publicationAny, r, e.name);
357
+ let i = requirePublisherRow(n), a = await first(t, O.publicationAny, r, e.name);
358
358
  if (a === null) return new Response(null, {
359
- headers: x,
359
+ headers: w,
360
360
  status: 204
361
361
  });
362
- if (a.owner_id !== i.id) throw S();
363
- await run(t, T.removePublication, r, i.id);
362
+ if (a.owner_id !== i.id) throw T();
363
+ await run(t, O.removePublication, r, i.id);
364
364
  }
365
365
  return new Response(null, {
366
- headers: x,
366
+ headers: w,
367
367
  status: 204
368
368
  });
369
369
  }
370
370
  async function setLike(e, t, n, r, i, a) {
371
371
  let s = requirePublisherRow(r);
372
- if (a && !n.likesEnabled) throw new h(503, "likes-disabled", "Likes are currently disabled.");
373
- if (await first(t, T.publication, i, e.name) === null) throw S();
374
- a ? await run(t, T.insertLike, s.id, now(), i) : await run(t, T.removeLike, i, s.id);
375
- let c = await first(t, T.likeState, s.id, i);
376
- if (c === null) throw S();
372
+ if (a && !n.likesEnabled) throw new v(503, "likes-disabled", "Likes are currently disabled.");
373
+ if (await first(t, O.publication, i, e.name) === null) throw T();
374
+ a ? await run(t, O.insertLike, s.id, now(), i) : await run(t, O.removeLike, i, s.id);
375
+ let c = await first(t, O.likeState, s.id, i);
376
+ if (c === null) throw T();
377
377
  let l = {
378
378
  id: c.id,
379
379
  likeCount: c.like_count,
380
380
  liked: c.liked === 1
381
381
  }, u = o.safeParse(l);
382
382
  if (!u.success) throw recordUnavailable();
383
- return b(200, u.data, x);
383
+ return C(200, u.data, w);
384
384
  }
385
385
  var V = P.object({ reason: P.string().check(p).trim().min(1).max(e.reportReasonChars) });
386
386
  async function reportEntry(e, t, n, r, i) {
387
- let a = requirePublisherRow(r), o = await w(t), s = V.safeParse(o);
388
- if (!s.success) throw y();
389
- if (await first(n, T.publication, i, e.name) === null) throw S();
390
- return await run(n, T.insertReport, uuid(), a.id, s.data.reason, now(), i, e.name), new Response(null, {
391
- headers: x,
387
+ let a = requirePublisherRow(r), o = await D(t), s = V.safeParse(o);
388
+ if (!s.success) throw S();
389
+ if (await first(n, O.publication, i, e.name) === null) throw T();
390
+ return await run(n, O.insertReport, uuid(), a.id, s.data.reason, now(), i, e.name), new Response(null, {
391
+ headers: w,
392
392
  status: 204
393
393
  });
394
394
  }
395
395
  async function setHidden(e, t, n, r, i) {
396
396
  let a = requireOperatorRow(n);
397
- if (await first(t, T.publicationAny, r, e.name) === null) throw S();
398
- let o = i ? t.DB.prepare(T.hidePublication).bind(now(), r, e.name) : t.DB.prepare(T.unhidePublication).bind(r, e.name);
399
- return await databaseCall(() => t.DB.batch([t.DB.prepare(T.auditHide).bind(uuid(), a.id, i ? "hide" : "unhide", now(), r, e.name, Number(i)), o])), new Response(null, {
400
- headers: x,
397
+ if (await first(t, O.publicationAny, r, e.name) === null) throw T();
398
+ let o = i ? t.DB.prepare(O.hidePublication).bind(now(), r, e.name) : t.DB.prepare(O.unhidePublication).bind(r, e.name);
399
+ return await databaseCall(() => t.DB.batch([t.DB.prepare(O.auditHide).bind(uuid(), a.id, i ? "hide" : "unhide", now(), r, e.name, Number(i)), o])), new Response(null, {
400
+ headers: w,
401
401
  status: 204
402
402
  });
403
403
  }
404
404
  async function listReports(e, t, n, r) {
405
405
  requireOperatorRow(r);
406
406
  let i = new URL(t.url).searchParams.get("status") ?? "unresolved";
407
- if (i !== "unresolved" && i !== "resolved") throw g();
408
- let a = `reports:${e.name}:${i}`, { cursor: o, limit: s } = j(t, a, ["status"]), c = A({
407
+ if (i !== "unresolved" && i !== "resolved") throw y();
408
+ let a = `reports:${e.name}:${i}`, { cursor: o, limit: s } = h(t, a, ["status"]), c = N({
409
409
  collection: e.name,
410
410
  resolved: i === "resolved"
411
411
  }, {
412
412
  cursor: o,
413
413
  limit: s + 1
414
414
  }), l = await all(n, c.sql, ...c.parameters), u = l.slice(0, s), f = u.at(-1), p = d.safeParse({
415
- cursor: l.length > s && f !== void 0 ? N({
415
+ cursor: l.length > s && f !== void 0 ? _({
416
416
  createdAt: f.created_at,
417
417
  id: f.id
418
418
  }, a) : null,
@@ -430,26 +430,26 @@ async function listReports(e, t, n, r) {
430
430
  }))
431
431
  });
432
432
  if (!p.success) throw recordUnavailable();
433
- return b(200, p.data, x);
433
+ return C(200, p.data, w);
434
434
  }
435
435
  async function resolveReport(e, t, n, r) {
436
- if (requireOperatorRow(n), await first(t, T.reportById, r, e.name) === null) throw S();
437
- return await run(t, T.resolveReport, now(), r, e.name), new Response(null, {
438
- headers: x,
436
+ if (requireOperatorRow(n), await first(t, O.reportById, r, e.name) === null) throw T();
437
+ return await run(t, O.resolveReport, now(), r, e.name), new Response(null, {
438
+ headers: w,
439
439
  status: 204
440
440
  });
441
441
  }
442
442
  async function myReports(e, t, n) {
443
- let r = `own-reports:${n.publisher?.id ?? "unregistered"}`, { cursor: i, limit: a } = j(e, r);
444
- if (n.publisher === null) return b(200, {
443
+ let r = `own-reports:${n.publisher?.id ?? "unregistered"}`, { cursor: i, limit: a } = h(e, r);
444
+ if (n.publisher === null) return C(200, {
445
445
  cursor: null,
446
446
  reports: []
447
- }, x);
448
- let o = A({ publisherId: n.publisher.id }, {
447
+ }, w);
448
+ let o = N({ publisherId: n.publisher.id }, {
449
449
  cursor: i,
450
450
  limit: a + 1
451
451
  }), s = await all(t, o.sql, ...o.parameters), c = s.slice(0, a), l = c.at(-1), d = u.safeParse({
452
- cursor: s.length > a && l !== void 0 ? N({
452
+ cursor: s.length > a && l !== void 0 ? _({
453
453
  createdAt: l.created_at,
454
454
  id: l.id
455
455
  }, r) : null,
@@ -463,9 +463,8 @@ async function myReports(e, t, n) {
463
463
  }))
464
464
  });
465
465
  if (!d.success) throw recordUnavailable();
466
- return b(200, d.data, x);
466
+ return C(200, d.data, w);
467
467
  }
468
- //#endregion
469
468
  export { collectionMeta, deleteAccountProfile, deleteEntry, deleteProfile, erasePublisher, getEntry, getMe, healthJson, listEntries, listModerationLog, listReports, myLikes, myPublications, myReports, publishEntry, registerMe, renameMe, reportEntry, requireOperatorRow, requirePublisherRow, resolveReport, setHidden, setLike, updateEntry };
470
469
 
471
470
  //# sourceMappingURL=publications.js.map
@@ -1,4 +1,4 @@
1
- //#region src/worker/sql.ts
1
+ // src/worker/sql.ts
2
2
  var e = {
3
3
  auditErasure: "INSERT INTO moderation_actions(id, operator_id, action, collection, target_id, created_at)\n SELECT ?, ?, 'erase-publisher', NULL, id, ? FROM publishers WHERE id = ?",
4
4
  auditHide: "INSERT INTO moderation_actions(id, operator_id, action, collection, target_id, created_at)\n SELECT ?, ?, ?, collection, id, ? FROM publications\n WHERE id = ? AND collection = ? AND (hidden_at IS NOT NULL) <> ?",
@@ -98,7 +98,6 @@ function likedPublicationsSql(e, t) {
98
98
  AND publication_id IN (${t.map(() => "?").join(", ") || "NULL"})`
99
99
  };
100
100
  }
101
- //#endregion
102
101
  export { e as SQL, likedPublicationsSql, likesPageSql, listPublicationsSql, moderationLogSql, reportsPageSql };
103
102
 
104
103
  //# sourceMappingURL=sql.js.map
@@ -1,2 +1,2 @@
1
- const e=require("../contract.cjs"),t=require("./http.cjs"),n=require("../errors.cjs"),r=require("./sql.cjs"),i=require("./auth.cjs"),a=require("./env.cjs"),o=require("./publications.cjs");var s=[{id:`accountErase`,label:`/me/account`,methods:[`DELETE`],pattern:/^\/api\/v1\/me\/account$/},{id:`health`,label:`/health`,methods:[`GET`],pattern:/^\/api\/v1\/health$/},{id:`signOut`,label:`/sign-out`,methods:[`POST`],pattern:/^\/api\/v1\/sign-out$/},{id:`resolveReport`,label:`/collections/:collection/reports/:id/resolved`,methods:[`PUT`],pattern:/^\/api\/v1\/collections\/([a-z0-9-]+)\/reports\/([0-9a-f-]{36})\/resolved$/i},{id:`meta`,label:`/collections/:collection`,methods:[`GET`],pattern:/^\/api\/v1\/collections\/([a-z0-9-]+)$/},{id:`reports`,label:`/collections/:collection/reports`,methods:[`GET`],pattern:/^\/api\/v1\/collections\/([a-z0-9-]+)\/reports$/},{id:`entries`,label:`/collections/:collection/entries`,methods:[`GET`,`POST`],pattern:/^\/api\/v1\/collections\/([a-z0-9-]+)\/entries$/},{id:`entry`,label:`/collections/:collection/entries/:id`,methods:[`GET`,`PUT`,`DELETE`],pattern:/^\/api\/v1\/collections\/([a-z0-9-]+)\/entries\/([0-9a-f-]{36})$/i},{id:`like`,label:`/collections/:collection/entries/:id/like`,methods:[`PUT`,`DELETE`],pattern:/^\/api\/v1\/collections\/([a-z0-9-]+)\/entries\/([0-9a-f-]{36})\/like$/i},{id:`report`,label:`/collections/:collection/entries/:id/report`,methods:[`POST`],pattern:/^\/api\/v1\/collections\/([a-z0-9-]+)\/entries\/([0-9a-f-]{36})\/report$/i},{id:`hidden`,label:`/collections/:collection/entries/:id/hidden`,methods:[`PUT`,`DELETE`],pattern:/^\/api\/v1\/collections\/([a-z0-9-]+)\/entries\/([0-9a-f-]{36})\/hidden$/i},{id:`modLog`,label:`/moderation-log`,methods:[`GET`],pattern:/^\/api\/v1\/moderation-log$/},{id:`publisherErase`,label:`/publishers/:id`,methods:[`DELETE`],pattern:/^\/api\/v1\/publishers\/([0-9a-f-]{36})$/i},{id:`me`,label:`/me`,methods:[`GET`,`POST`,`PUT`,`DELETE`],pattern:/^\/api\/v1\/me$/},{id:`mePublications`,label:`/me/publications`,methods:[`GET`],pattern:/^\/api\/v1\/me\/publications$/},{id:`meLikes`,label:`/me/likes`,methods:[`GET`],pattern:/^\/api\/v1\/me\/likes$/},{id:`meReports`,label:`/me/reports`,methods:[`GET`],pattern:/^\/api\/v1\/me\/reports$/}],matchRoute=e=>{for(let t of s){let n=e.match(t.pattern);if(n)return{id:t.id,label:t.label,methods:t.methods,segments:n.slice(1)}}return null},routeLabel=e=>matchRoute(e)?.label??`unknown`,c=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,l=new Set([`entry`,`like`,`report`,`hidden`,`resolveReport`]),notAllowed=()=>new t.ApiError(405,`method-not-allowed`,`That method is not supported here.`),u=new WeakMap,rateLimitHash=async(e,t)=>{let n=u.get(e);n===void 0&&(n=crypto.subtle.importKey(`raw`,new TextEncoder().encode(e.rateLimitPepper),{hash:`SHA-256`,name:`HMAC`},!1,[`sign`]),u.set(e,n));let r=await crypto.subtle.sign(`HMAC`,await n,new TextEncoder().encode(t));return[...new Uint8Array(r)].map(e=>e.toString(16).padStart(2,`0`)).join(``)},d=60;async function readLimit(e,n,r){let i=r.headers.get(`cf-connecting-ip`)??(e.appEnv===`local`?`127.0.0.1`:null);if(i===null)throw new t.ApiError(503,`unavailable`,`The service is temporarily unavailable.`);let{success:a}=await n.READ_LIMIT.limit({key:await rateLimitHash(e,i)});if(!a)throw new t.ApiError(429,`rate-limited`,`Too many requests. Try again later.`,{retryAfterSeconds:d})}async function writeLimit(e,n,r,i){let{success:a}=await n.WRITE_LIMIT.limit({key:await rateLimitHash(e,JSON.stringify([r,i]))});if(!a)throw new t.ApiError(429,`rate-limited`,`Too many requests. Try again later.`,{retryAfterSeconds:d})}async function cachedPublicGet(t,n,r,i){if(t.headers.get(`cookie`)!==null||t.headers.get(`authorization`)!==null)return r();let a=new URL(t.url);a.search=new URLSearchParams([...a.searchParams.entries()].sort(([e],[t])=>e<t?-1:+(e>t))).toString();let o=caches.default,s=new Request(a.toString(),{method:`GET`}),c=await o.match(s).catch(()=>{i()});if(c)return c;let l=await r();return l.headers.set(`cache-control`,`public, max-age=${e.ARCHIVE_LIMITS.publicCacheSeconds}, s-maxage=${e.ARCHIVE_LIMITS.publicCacheSeconds}`),n.waitUntil(o.put(s,l.clone()).catch(i)),l}var withCors=(e,n,r)=>{let i=t.corsHeaders(n,r);if(Object.keys(i).length===0)return e;let a=new Response(e.body,e);for(let[e,t]of Object.entries(i))a.headers.set(e,t);return a};function requireExpectedPublisher(n,r,i){if(i||r.publisher===null)return;let a=n.headers.get(e.PUBLISHER_HEADER);if(a===null||!c.test(a))throw new t.ApiError(400,`bad-request`,`The request is not valid.`);if(a!==r.publisher.id)throw new t.ApiError(409,`publisher-changed`,`The signed-in publisher changed since this request was prepared.`)}function createArchiveWorker(s){let u=new Map;for(let e of s){if(u.has(e.name))throw new n.ArchiveDefinitionError(`Duplicate archive collection: ${e.name}`);u.set(e.name,e)}let collectionFor=e=>{let n=u.get(e);if(n===void 0)throw new t.ApiError(404,`not-found`,`No such route.`);return n};async function handle(e,n,r){let s=a.apiConfig(n),d=new URL(e.url),{pathname:f}=d,p=e.method;if(!f.startsWith(`/api/v1/`))throw new t.ApiError(404,`not-found`,`No such route.`);if(p===`OPTIONS`)return t.preflight(e,s);let m=matchRoute(f);if(m===null)throw new t.ApiError(404,`not-found`,`No such route.`);if(!m.methods.includes(p))throw notAllowed();if(l.has(m.id)&&!c.test(m.segments[1]??``))throw t.badRequest();if(m.id===`health`)return withCors(await o.healthJson(s,n),e,s);m.id===`reports`&&t.queryParams(d,[`cursor`,`limit`,`status`]),await readLimit(s,n,e);let h=p!==`GET`&&p!==`HEAD`&&p!==`OPTIONS`;h&&t.requireMutationGuard(e,s);let g=p===`GET`&&(m.id===`meta`||m.id===`entries`||m.id===`entry`),_=m.id===`signOut`&&p===`POST`,v=g||_?null:await i.verifySession(e,s,n,m.id===`accountErase`);if(h){if(_)return withCors(t.json(200,{ok:!0},{"cache-control":`no-store`,"set-cookie":t.expiredSessionCookie(s.appEnv===`production`)}),e,s);let r=i.requireSession(v);requireExpectedPublisher(e,r,m.id===`me`&&p===`POST`),await writeLimit(s,n,r.subject.issuer,r.subject.subject)}let jsonWithCors=t=>withCors(t,e,s);switch(m.id){case`meta`:return t.queryParams(d,[]),jsonWithCors(o.collectionMeta(collectionFor(m.segments[0]??``)));case`entries`:{let t=collectionFor(m.segments[0]??``);if(p===`GET`)return jsonWithCors(await cachedPublicGet(e,r,()=>o.listEntries(t,e,n),()=>emit({diagnostic:{kind:`cache`},type:`cache-error`})));if(p===`POST`)return jsonWithCors(await o.publishEntry(t,e,n,s,i.requireSession(v)));throw notAllowed()}case`entry`:{let r=collectionFor(m.segments[0]??``),a=m.segments[1]??``;if(p===`GET`)return t.queryParams(d,[]),jsonWithCors(await o.getEntry(r,n,a));if(p===`PUT`)return jsonWithCors(await o.updateEntry(r,e,n,s,i.requireSession(v),a));if(p===`DELETE`)return jsonWithCors(await o.deleteEntry(r,n,i.requireSession(v),a));throw notAllowed()}case`like`:{let e=collectionFor(m.segments[0]??``),t=m.segments[1]??``;if(p===`PUT`)return jsonWithCors(await o.setLike(e,n,s,i.requireSession(v),t,!0));if(p===`DELETE`)return jsonWithCors(await o.setLike(e,n,s,i.requireSession(v),t,!1));throw notAllowed()}case`report`:{if(p!==`POST`)throw notAllowed();let t=collectionFor(m.segments[0]??``);return jsonWithCors(await o.reportEntry(t,e,n,i.requireSession(v),m.segments[1]??``))}case`hidden`:{if(p!==`PUT`&&p!==`DELETE`)throw notAllowed();let e=collectionFor(m.segments[0]??``);return jsonWithCors(await o.setHidden(e,n,i.requireSession(v),m.segments[1]??``,p===`PUT`))}case`reports`:{let t=i.requireSession(v);return requireExpectedPublisher(e,t,!1),jsonWithCors(await o.listReports(collectionFor(m.segments[0]??``),e,n,t))}case`resolveReport`:return t.queryParams(d,[]),jsonWithCors(await o.resolveReport(collectionFor(m.segments[0]??``),n,i.requireSession(v),m.segments[1]??``));case`modLog`:{let t=i.requireSession(v);return requireExpectedPublisher(e,t,!1),jsonWithCors(await o.listModerationLog(e,n,t))}case`publisherErase`:if(p!==`DELETE`)throw notAllowed();return jsonWithCors(await o.erasePublisher(n,i.requireSession(v),m.segments[0]??``));case`accountErase`:return t.queryParams(d,[]),jsonWithCors(await o.deleteAccountProfile(n,i.requireSession(v)));case`me`:if(p===`GET`)return jsonWithCors(o.getMe(v));if(p===`POST`)return jsonWithCors(await o.registerMe(e,n,i.requireSession(v)));if(p===`PUT`)return jsonWithCors(await o.renameMe(e,n,i.requireSession(v)));if(p===`DELETE`)return jsonWithCors(await o.deleteProfile(n,i.requireSession(v)));throw notAllowed();case`mePublications`:case`meLikes`:case`meReports`:{let r=i.requireSession(v);if(requireExpectedPublisher(e,r,!1),m.id===`mePublications`){let e=t.queryParams(d,[`collection`]).get(`collection`);if(e===null)throw t.badRequest();let i=u.get(e);if(i===void 0)throw t.badRequest();return jsonWithCors(await o.myPublications(i,n,r))}return jsonWithCors(await(m.id===`meReports`?o.myReports(e,n,r):o.myLikes(e,n,r)))}default:throw notAllowed()}}let d=new Set,emit=e=>{if(d.size!==0)for(let t of d)try{t(e)}catch{}};return{async fetch(r,i,o){let s=crypto.randomUUID(),c=routeLabel(new URL(r.url).pathname),l=Date.now();try{let t=await handle(r,i,o),n;return t.status===503&&c===`/health`&&(n={kind:e.archiveHealthSchema.parse(await t.clone().json()).reason===`database-unavailable`?`database`:`schema`}),emit({code:t.ok?`ok`:`unavailable`,ms:Date.now()-l,requestId:s,route:c,status:t.status,type:`request`,...n===void 0?{}:{diagnostic:n}}),t}catch(e){let o=t.toErrorResponse(e,s);emit({code:e instanceof t.ApiError?e.code:`internal`,diagnostic:e instanceof t.ApiError?e.diagnostic:{kind:e instanceof n.ArchiveDerivationError?`collection`:`internal`},ms:Date.now()-l,requestId:s,route:c,status:o.status,type:`request`});try{return withCors(o,r,a.apiConfig(i))}catch{return o}}},async scheduled(e,t,n){try{let e=Date.now(),n=await t.DB.batch([t.DB.prepare(r.SQL.expireRequests).bind(e),t.DB.prepare(r.SQL.expireRevocations).bind(e)]);emit({removed:n.reduce((e,t)=>e+(t.meta?.changes??0),0),status:`ok`,type:`scheduled`})}catch(e){throw emit({diagnostic:{kind:`database`},status:`failed`,type:`scheduled`}),e}},tap(e,t){let n=t?.signal;if(n?.aborted)return()=>{};let off=()=>{d.delete(e),n?.removeEventListener(`abort`,off)};return d.add(e),n?.addEventListener(`abort`,off,{once:!0}),off}}}exports.createArchiveWorker=createArchiveWorker;
1
+ const e=require("../contract.cjs"),t=require("../errors.cjs"),n=require("./auth.cjs"),r=require("./env.cjs"),i=require("./http.cjs"),a=require("./publications.cjs"),o=require("./sql.cjs");var s=[{id:`accountErase`,label:`/me/account`,methods:[`DELETE`],pattern:/^\/api\/v1\/me\/account$/},{id:`health`,label:`/health`,methods:[`GET`],pattern:/^\/api\/v1\/health$/},{id:`signOut`,label:`/sign-out`,methods:[`POST`],pattern:/^\/api\/v1\/sign-out$/},{id:`resolveReport`,label:`/collections/:collection/reports/:id/resolved`,methods:[`PUT`],pattern:/^\/api\/v1\/collections\/([a-z0-9-]+)\/reports\/([0-9a-f-]{36})\/resolved$/i},{id:`meta`,label:`/collections/:collection`,methods:[`GET`],pattern:/^\/api\/v1\/collections\/([a-z0-9-]+)$/},{id:`reports`,label:`/collections/:collection/reports`,methods:[`GET`],pattern:/^\/api\/v1\/collections\/([a-z0-9-]+)\/reports$/},{id:`entries`,label:`/collections/:collection/entries`,methods:[`GET`,`POST`],pattern:/^\/api\/v1\/collections\/([a-z0-9-]+)\/entries$/},{id:`entry`,label:`/collections/:collection/entries/:id`,methods:[`GET`,`PUT`,`DELETE`],pattern:/^\/api\/v1\/collections\/([a-z0-9-]+)\/entries\/([0-9a-f-]{36})$/i},{id:`like`,label:`/collections/:collection/entries/:id/like`,methods:[`PUT`,`DELETE`],pattern:/^\/api\/v1\/collections\/([a-z0-9-]+)\/entries\/([0-9a-f-]{36})\/like$/i},{id:`report`,label:`/collections/:collection/entries/:id/report`,methods:[`POST`],pattern:/^\/api\/v1\/collections\/([a-z0-9-]+)\/entries\/([0-9a-f-]{36})\/report$/i},{id:`hidden`,label:`/collections/:collection/entries/:id/hidden`,methods:[`PUT`,`DELETE`],pattern:/^\/api\/v1\/collections\/([a-z0-9-]+)\/entries\/([0-9a-f-]{36})\/hidden$/i},{id:`modLog`,label:`/moderation-log`,methods:[`GET`],pattern:/^\/api\/v1\/moderation-log$/},{id:`publisherErase`,label:`/publishers/:id`,methods:[`DELETE`],pattern:/^\/api\/v1\/publishers\/([0-9a-f-]{36})$/i},{id:`me`,label:`/me`,methods:[`GET`,`POST`,`PUT`,`DELETE`],pattern:/^\/api\/v1\/me$/},{id:`mePublications`,label:`/me/publications`,methods:[`GET`],pattern:/^\/api\/v1\/me\/publications$/},{id:`meLikes`,label:`/me/likes`,methods:[`GET`],pattern:/^\/api\/v1\/me\/likes$/},{id:`meReports`,label:`/me/reports`,methods:[`GET`],pattern:/^\/api\/v1\/me\/reports$/}],matchRoute=e=>{for(let t of s){let n=e.match(t.pattern);if(n)return{id:t.id,label:t.label,methods:t.methods,segments:n.slice(1)}}return null},routeLabel=e=>matchRoute(e)?.label??`unknown`,c=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,l=new Set([`entry`,`like`,`report`,`hidden`,`resolveReport`]),notAllowed=()=>new i.ApiError(405,`method-not-allowed`,`That method is not supported here.`),u=new WeakMap,rateLimitHash=async(e,t)=>{let n=u.get(e);n===void 0&&(n=crypto.subtle.importKey(`raw`,new TextEncoder().encode(e.rateLimitPepper),{hash:`SHA-256`,name:`HMAC`},!1,[`sign`]),u.set(e,n));let r=await crypto.subtle.sign(`HMAC`,await n,new TextEncoder().encode(t));return[...new Uint8Array(r)].map(e=>e.toString(16).padStart(2,`0`)).join(``)},d=60;async function readLimit(e,t,n){let r=n.headers.get(`cf-connecting-ip`)??(e.appEnv===`local`?`127.0.0.1`:null);if(r===null)throw new i.ApiError(503,`unavailable`,`The service is temporarily unavailable.`);let{success:a}=await t.READ_LIMIT.limit({key:await rateLimitHash(e,r)});if(!a)throw new i.ApiError(429,`rate-limited`,`Too many requests. Try again later.`,{retryAfterSeconds:d})}async function writeLimit(e,t,n,r){let{success:a}=await t.WRITE_LIMIT.limit({key:await rateLimitHash(e,JSON.stringify([n,r]))});if(!a)throw new i.ApiError(429,`rate-limited`,`Too many requests. Try again later.`,{retryAfterSeconds:d})}async function cachedPublicGet(t,n,r,i){if(t.headers.get(`cookie`)!==null||t.headers.get(`authorization`)!==null)return r();let a=new URL(t.url);a.search=new URLSearchParams([...a.searchParams.entries()].sort(([e],[t])=>e<t?-1:+(e>t))).toString();let o=caches.default,s=new Request(a.toString(),{method:`GET`}),c=await o.match(s).catch(()=>{i()});if(c)return c;let l=await r();return l.headers.set(`cache-control`,`public, max-age=${e.ARCHIVE_LIMITS.publicCacheSeconds}, s-maxage=${e.ARCHIVE_LIMITS.publicCacheSeconds}`),n.waitUntil(o.put(s,l.clone()).catch(i)),l}var withCors=(e,t,n)=>{let r=i.corsHeaders(t,n);if(Object.keys(r).length===0)return e;let a=new Response(e.body,e);for(let[e,t]of Object.entries(r))a.headers.set(e,t);return a};function requireExpectedPublisher(t,n,r){if(r||n.publisher===null)return;let a=t.headers.get(e.PUBLISHER_HEADER);if(a===null||!c.test(a))throw new i.ApiError(400,`bad-request`,`The request is not valid.`);if(a!==n.publisher.id)throw new i.ApiError(409,`publisher-changed`,`The signed-in publisher changed since this request was prepared.`)}function createArchiveWorker(s){let u=new Map;for(let e of s){if(u.has(e.name))throw new t.ArchiveDefinitionError(`Duplicate archive collection: ${e.name}`);u.set(e.name,e)}let collectionFor=e=>{let t=u.get(e);if(t===void 0)throw new i.ApiError(404,`not-found`,`No such route.`);return t};async function handle(e,t,o){let s=r.apiConfig(t),d=new URL(e.url),{pathname:f}=d,p=e.method;if(!f.startsWith(`/api/v1/`))throw new i.ApiError(404,`not-found`,`No such route.`);if(p===`OPTIONS`)return i.preflight(e,s);let m=matchRoute(f);if(m===null)throw new i.ApiError(404,`not-found`,`No such route.`);if(!m.methods.includes(p))throw notAllowed();if(l.has(m.id)&&!c.test(m.segments[1]??``))throw i.badRequest();if(m.id===`health`)return withCors(await a.healthJson(s,t),e,s);m.id===`reports`&&i.queryParams(d,[`cursor`,`limit`,`status`]),await readLimit(s,t,e);let h=p!==`GET`&&p!==`HEAD`&&p!==`OPTIONS`;h&&i.requireMutationGuard(e,s);let g=p===`GET`&&(m.id===`meta`||m.id===`entries`||m.id===`entry`),_=m.id===`signOut`&&p===`POST`,v=g||_?null:await n.verifySession(e,s,t,m.id===`accountErase`);if(h){if(_)return withCors(i.json(200,{ok:!0},{"cache-control":`no-store`,"set-cookie":i.expiredSessionCookie(s.appEnv===`production`)}),e,s);let r=n.requireSession(v);requireExpectedPublisher(e,r,m.id===`me`&&p===`POST`),await writeLimit(s,t,r.subject.issuer,r.subject.subject)}let jsonWithCors=t=>withCors(t,e,s);switch(m.id){case`meta`:return i.queryParams(d,[]),jsonWithCors(a.collectionMeta(collectionFor(m.segments[0]??``)));case`entries`:{let r=collectionFor(m.segments[0]??``);if(p===`GET`)return jsonWithCors(await cachedPublicGet(e,o,()=>a.listEntries(r,e,t),()=>emit({diagnostic:{kind:`cache`},type:`cache-error`})));if(p===`POST`)return jsonWithCors(await a.publishEntry(r,e,t,s,n.requireSession(v)));throw notAllowed()}case`entry`:{let r=collectionFor(m.segments[0]??``),o=m.segments[1]??``;if(p===`GET`)return i.queryParams(d,[]),jsonWithCors(await a.getEntry(r,t,o));if(p===`PUT`)return jsonWithCors(await a.updateEntry(r,e,t,s,n.requireSession(v),o));if(p===`DELETE`)return jsonWithCors(await a.deleteEntry(r,t,n.requireSession(v),o));throw notAllowed()}case`like`:{let e=collectionFor(m.segments[0]??``),r=m.segments[1]??``;if(p===`PUT`)return jsonWithCors(await a.setLike(e,t,s,n.requireSession(v),r,!0));if(p===`DELETE`)return jsonWithCors(await a.setLike(e,t,s,n.requireSession(v),r,!1));throw notAllowed()}case`report`:{if(p!==`POST`)throw notAllowed();let r=collectionFor(m.segments[0]??``);return jsonWithCors(await a.reportEntry(r,e,t,n.requireSession(v),m.segments[1]??``))}case`hidden`:{if(p!==`PUT`&&p!==`DELETE`)throw notAllowed();let e=collectionFor(m.segments[0]??``);return jsonWithCors(await a.setHidden(e,t,n.requireSession(v),m.segments[1]??``,p===`PUT`))}case`reports`:{let r=n.requireSession(v);return requireExpectedPublisher(e,r,!1),jsonWithCors(await a.listReports(collectionFor(m.segments[0]??``),e,t,r))}case`resolveReport`:return i.queryParams(d,[]),jsonWithCors(await a.resolveReport(collectionFor(m.segments[0]??``),t,n.requireSession(v),m.segments[1]??``));case`modLog`:{let r=n.requireSession(v);return requireExpectedPublisher(e,r,!1),jsonWithCors(await a.listModerationLog(e,t,r))}case`publisherErase`:if(p!==`DELETE`)throw notAllowed();return jsonWithCors(await a.erasePublisher(t,n.requireSession(v),m.segments[0]??``));case`accountErase`:return i.queryParams(d,[]),jsonWithCors(await a.deleteAccountProfile(t,n.requireSession(v)));case`me`:if(p===`GET`)return jsonWithCors(a.getMe(v));if(p===`POST`)return jsonWithCors(await a.registerMe(e,t,n.requireSession(v)));if(p===`PUT`)return jsonWithCors(await a.renameMe(e,t,n.requireSession(v)));if(p===`DELETE`)return jsonWithCors(await a.deleteProfile(t,n.requireSession(v)));throw notAllowed();case`mePublications`:case`meLikes`:case`meReports`:{let r=n.requireSession(v);if(requireExpectedPublisher(e,r,!1),m.id===`mePublications`){let e=i.queryParams(d,[`collection`]).get(`collection`);if(e===null)throw i.badRequest();let n=u.get(e);if(n===void 0)throw i.badRequest();return jsonWithCors(await a.myPublications(n,t,r))}return jsonWithCors(await(m.id===`meReports`?a.myReports(e,t,r):a.myLikes(e,t,r)))}default:throw notAllowed()}}let d=new Set,emit=e=>{if(d.size!==0)for(let t of d)try{t(e)}catch{}};return{async fetch(n,a,o){let s=crypto.randomUUID(),c=routeLabel(new URL(n.url).pathname),l=Date.now();try{let t=await handle(n,a,o),r;return t.status===503&&c===`/health`&&(r={kind:e.archiveHealthSchema.parse(await t.clone().json()).reason===`database-unavailable`?`database`:`schema`}),emit({code:t.ok?`ok`:`unavailable`,ms:Date.now()-l,requestId:s,route:c,status:t.status,type:`request`,...r===void 0?{}:{diagnostic:r}}),t}catch(e){let o=i.toErrorResponse(e,s);emit({code:e instanceof i.ApiError?e.code:`internal`,diagnostic:e instanceof i.ApiError?e.diagnostic:{kind:e instanceof t.ArchiveDerivationError?`collection`:`internal`},ms:Date.now()-l,requestId:s,route:c,status:o.status,type:`request`});try{return withCors(o,n,r.apiConfig(a))}catch{return o}}},async scheduled(e,t,n){try{let e=Date.now(),n=await t.DB.batch([t.DB.prepare(o.SQL.expireRequests).bind(e),t.DB.prepare(o.SQL.expireRevocations).bind(e)]);emit({removed:n.reduce((e,t)=>e+(t.meta?.changes??0),0),status:`ok`,type:`scheduled`})}catch(e){throw emit({diagnostic:{kind:`database`},status:`failed`,type:`scheduled`}),e}},tap(e,t){let n=t?.signal;if(n?.aborted)return()=>{};let off=()=>{d.delete(e),n?.removeEventListener(`abort`,off)};return d.add(e),n?.addEventListener(`abort`,off,{once:!0}),off}}}exports.createArchiveWorker=createArchiveWorker;
2
2
  //# sourceMappingURL=worker.cjs.map