@vielzeug/archive 26.10.15 → 26.10.17

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.
Files changed (59) hide show
  1. package/LICENSE +21 -0
  2. package/dist/_crypto.cjs.map +1 -1
  3. package/dist/_crypto.js +5 -1
  4. package/dist/_crypto.js.map +1 -1
  5. package/dist/_json.cjs.map +1 -1
  6. package/dist/_json.js +51 -1
  7. package/dist/_json.js.map +1 -1
  8. package/dist/auth/_confirmation.cjs.map +1 -1
  9. package/dist/auth/_confirmation.js +33 -13
  10. package/dist/auth/_confirmation.js.map +1 -1
  11. package/dist/auth/config.cjs.map +1 -1
  12. package/dist/auth/config.js +156 -1
  13. package/dist/auth/config.js.map +1 -1
  14. package/dist/auth/errors.cjs.map +1 -1
  15. package/dist/auth/errors.js +18 -1
  16. package/dist/auth/errors.js.map +1 -1
  17. package/dist/auth/store.cjs.map +1 -1
  18. package/dist/auth/store.js +102 -18
  19. package/dist/auth/store.js.map +1 -1
  20. package/dist/auth/worker.cjs.map +1 -1
  21. package/dist/auth/worker.js +360 -1
  22. package/dist/auth/worker.js.map +1 -1
  23. package/dist/auth.js +1 -1
  24. package/dist/collection.cjs.map +1 -1
  25. package/dist/collection.js +108 -1
  26. package/dist/collection.js.map +1 -1
  27. package/dist/contract.cjs.map +1 -1
  28. package/dist/contract.js +144 -1
  29. package/dist/contract.js.map +1 -1
  30. package/dist/errors.cjs.map +1 -1
  31. package/dist/errors.js +14 -1
  32. package/dist/errors.js.map +1 -1
  33. package/dist/index.js +4 -1
  34. package/dist/worker/_pagination.cjs.map +1 -1
  35. package/dist/worker/_pagination.js +55 -1
  36. package/dist/worker/_pagination.js.map +1 -1
  37. package/dist/worker/_service.cjs.map +1 -1
  38. package/dist/worker/_service.js +16 -1
  39. package/dist/worker/_service.js.map +1 -1
  40. package/dist/worker/auth.cjs.map +1 -1
  41. package/dist/worker/auth.js +68 -1
  42. package/dist/worker/auth.js.map +1 -1
  43. package/dist/worker/env.cjs.map +1 -1
  44. package/dist/worker/env.js +76 -1
  45. package/dist/worker/env.js.map +1 -1
  46. package/dist/worker/http.cjs.map +1 -1
  47. package/dist/worker/http.js +118 -1
  48. package/dist/worker/http.js.map +1 -1
  49. package/dist/worker/publications.cjs.map +1 -1
  50. package/dist/worker/publications.js +470 -1
  51. package/dist/worker/publications.js.map +1 -1
  52. package/dist/worker/sql.cjs.map +1 -1
  53. package/dist/worker/sql.js +100 -56
  54. package/dist/worker/sql.js.map +1 -1
  55. package/dist/worker/worker.cjs.map +1 -1
  56. package/dist/worker/worker.js +352 -1
  57. package/dist/worker/worker.js.map +1 -1
  58. package/dist/worker.js +1 -1
  59. package/package.json +7 -6
@@ -1,2 +1,471 @@
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";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";import{SQL as T,likedPublicationsSql as E,likesPageSql as D,listPublicationsSql as O,moderationLogSql as k,reportsPageSql as A}from"./sql.js";import{forwardPage as j,forwardPageParams as M,nextCursor as N}from"./_pagination.js";import{s as P}from"@vielzeug/spell";async function databaseCall(e){try{return await e()}catch(e){throw new h(500,`internal`,`The service is not available.`,{cause:e,diagnostic:{kind:`database`}})}}var first=(e,t,...n)=>databaseCall(()=>e.DB.prepare(t).bind(...n).first()),all=(e,t,...n)=>databaseCall(async()=>{let r=await e.DB.prepare(t).bind(...n).all();if(!Array.isArray(r.results))throw Error(`Missing database query results`);return r.results}),run=(e,t,...n)=>databaseCall(()=>e.DB.prepare(t).bind(...n).run()),uuid=()=>crypto.randomUUID(),now=()=>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)=>{let n;try{n=JSON.parse(t.payload_json)}catch{throw recordUnavailable()}let r=e.contract.entry.safeParse({author:{id:t.owner_id,name:t.author_name},contentHash:t.content_hash,contentVersion:t.content_version,id:t.id,likeCount:t.like_count,payload:n,publishedAt:t.published_at,revision:t.revision,schemaVersion:t.schema_version,updatedAt:t.updated_at});if(!r.success)throw recordUnavailable();return r.data},rowToOwnedEntry=(e,t)=>{let n=rowToEntry(e,t);if(!m.safeParse(t.client_key).success)throw recordUnavailable();return{...n,clientKey:t.client_key,hidden:t.hidden_at!==null}},entryById=async(e,t,n)=>{let r=await first(t,T.publication,n,e.name);return r===null?null:rowToEntry(e,r)},searchMatchFor=t=>{if(t===null)return;if(t.length>e.searchChars)throw g();let n=(t.match(/[\p{L}\p{N}]+/gu)??[]).slice(0,e.searchTerms);if(n.length!==0)return n.map(e=>`"${e}"*`).join(` `)},entryCursorContext=e=>JSON.stringify([e.collection,e.sort,e.author??``,e.filters,e.tags,e.searchMatch??``]),cursorFromRow=e=>({createdAt:e.published_at,id:e.id,likeCount:e.like_count}),I=16,vocabValues=(e,t,n)=>{let r=e.getAll(t);if(r.length>I)throw g();let i=[...new Set(r)].sort();if(i.some(e=>!n.includes(e)))throw g();return i};async function listEntries(e,t,n){let r=new URL(t.url),i=C(r,[`author`,`cursor`,`filter`,`limit`,`search`,`sort`,`tag`],[`filter`,`tag`]),a=i.get(`sort`)??`recent`,o=f.safeParse(a);if(!o.success)throw g();let s=vocabValues(i,`filter`,e.filters),c=vocabValues(i,`tag`,e.tags),l=i.get(`author`);if(l!==null&&!F.test(l))throw g();let u=searchMatchFor(i.get(`search`)),d=entryCursorContext({author:l??void 0,collection:e.name,filters:s,searchMatch:u,sort:o.data,tags:c}),{cursor:p,limit:m}=M(i,d),h={author:l??void 0,collection:e.name,cursor:p===null?void 0:{createdAt:p.createdAt,id:p.id,likeCount:p.likeCount??0},filters:s,limit:m+1,searchMatch:u,sort:o.data,tags:c},_=O(h),v=await all(n,_.sql,..._.parameters),y=v.slice(0,m),S=y.at(-1),w={cursor:v.length>m&&S!==void 0?N(cursorFromRow(S),d):null,entries:y.map(t=>rowToEntry(e,t))};return b(200,w,x)}function collectionMeta(e){let t=r.safeParse({filters:[...e.filters].sort(),name:e.name,tags:[...e.tags].sort()});if(!t.success)throw recordUnavailable();return b(200,t.data,x)}async function getEntry(e,t,n){let r=await entryById(e,t,n);if(r===null)throw S();return b(200,r,x)}async function healthJson(t,n){let r=null,a=null;try{r=(await first(n,T.schemaVersion))?.applied??0}catch{a=`database-unavailable`}a===null&&r!==1&&(a=`schema-mismatch`);let o=i.safeParse({dbSchemaVersion:r,features:{likes:t.likesEnabled,publishing:t.publishingEnabled},limits:e,reason:a,schemaVersion:1,status:a===null?`ok`:`degraded`});if(!o.success)throw recordUnavailable();return b(a===null?200:503,o.data,x)}var identityJson=(e,t)=>{let n=a.safeParse(t);if(!n.success)throw recordUnavailable();return b(e,n.data,x)};function getMe(e){return identityJson(200,e?.publisher?{id:e.publisher.id,name:e.publisher.displayName,operator:e.operator}:null)}async function registerMe(e,t,n){let r=await w(e),i=L.safeParse(r);if(!i.success)throw y();let a=i.data.name??``,o=await first(t,T.publisher,n.subject.issuer,n.subject.subject);if(o!==null)return identityJson(200,{id:o.id,name:o.display_name,operator:n.operator});let s=uuid();await run(t,T.insertPublisher,s,n.subject.issuer,n.subject.subject,a,now(),n.subject.issuer,n.subject.subject,Date.now());let c=await first(t,T.publisher,n.subject.issuer,n.subject.subject);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.`);return identityJson(201,{id:c.id,name:c.display_name,operator:n.operator})}async function renameMe(e,t,n){let r=requirePublisherRow(n),i=await w(e),a=R.safeParse(i);if(!a.success)throw y();return a.data.name!==r.displayName&&await run(t,T.renamePublisher,a.data.name,r.id,a.data.name),identityJson(200,{id:r.id,name:a.data.name,operator:n.operator})}async function deleteProfile(e,t){return t.publisher!==null&&await run(e,T.deletePublisher,t.publisher.id),new Response(null,{headers:{"cache-control":`no-store`},status:204})}async function deleteAccountProfile(e,n){let{issuer:r,subject:i}=n.subject;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,{headers:x,status:204})}async function erasePublisher(e,t,n){let r=requireOperatorRow(t);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,{headers:x,status:204})}async function listModerationLog(e,t,n){requireOperatorRow(n);let r=`moderation-log`,{cursor:i,limit:a}=j(e,r),o=k({cursor:i,limit:a+1}),s=await all(t,o.sql,...o.parameters),c=s.slice(0,a),u=c.at(-1),d=l.safeParse({actions:c.map(e=>({action:e.action,collection:e.collection,createdAt:e.created_at,id:e.id,operatorId:e.operator_id,targetId:e.target_id})),cursor:s.length>a&&u!==void 0?N({createdAt:u.created_at,id:u.id},r):null});if(!d.success)throw recordUnavailable();return b(200,d.data,x)}var requirePublisherRow=e=>{if(e.publisher===null)throw v(`Register before publishing.`);return e.publisher},requireOperatorRow=e=>{if(!e.operator)throw v(`Operator access is required.`);if(e.publisher===null)throw v(`Register before moderating.`);return e.publisher},L=P.object({name:n.optional()}),R=P.object({name:n});async function myPublications(t,n,r){if(r.publisher===null)return b(200,{entries:[]},x);let i=await all(n,T.ownPublications,r.publisher.id,t.name,e.maxPublications);return b(200,{entries:i.map(e=>rowToOwnedEntry(t,e))},x)}async function myLikes(t,n,r){let i=new URL(t.url).searchParams.get(`ids`);if(i===null){let e=`likes:${r.publisher?.id??`unregistered`}`,{cursor:i,limit:a}=j(t,e);if(r.publisher===null)return b(200,{cursor:null,ids:[]},x);let o=D(r.publisher.id,{cursor:i,limit:a+1}),s=await all(n,o.sql,...o.parameters),l=s.slice(0,a),u=l.at(-1),d=c.safeParse({cursor:s.length>a&&u!==void 0?N({createdAt:u.created_at,id:u.publication_id},e):null,ids:l.map(e=>e.publication_id)});if(!d.success)throw recordUnavailable();return b(200,d.data,x)}C(new URL(t.url),[`ids`]);let a=[...new Set(i.split(`,`))];if(a.length>e.likeStateIds||a.some(e=>!F.test(e)))throw g();if(r.publisher===null)return b(200,{ids:[]},x);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);if(!f.success)throw recordUnavailable();return b(200,f.data,x)}var z=P.object({clientKey:m,payload:P.unknown()}),B=P.object({expectedRevision:P.number().int().min(1).max(2**53-1),payload:P.unknown()});async function resolveRetryRecord(e,t,n,r){if(n===null||n.expires_at<=Date.now())return null;if(n.payload_hash!==r)throw _(`That key was used for a different payload.`);if(n.publication_id===null)throw new h(410,`gone`,`That publication was removed.`);let i=await entryById(e,t,n.publication_id);return i===null?`vanished`:b(200,i,x)}async function publishEntry(t,n,r,i,a){if(!i.publishingEnabled)throw new h(503,`publishing-disabled`,`Publishing is currently disabled.`);let o=requirePublisherRow(a),s=await w(n),c=z.safeParse(s);if(!c.success)throw y();let{clientKey:l}=c.data,u=await t.derive(c.data.payload);if(u===null)throw y();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);if(g!==null&&g!==`vanished`)return g;let S=await first(r,T.publicationByKey,p,m,l);if(S!==null){if(S.hidden_at!==null)throw v(`This entry is under moderation review.`);if(S.content_hash!==f)throw _(`That key was used for a different payload.`);return b(200,rowToEntry(t,S),x)}let C=now(),E=Date.now()+e.idempotencyRetentionMs,D=JSON.stringify(u.tags);if(((await databaseCall(()=>r.DB.batch([r.DB.prepare(T.expireRequests).bind(Date.now()),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()),r.DB.prepare(T.insertRequest).bind(p,m,l,f,C,E,p,m,l,f)])))[1]?.meta?.changes??0)===0){let e=await resolveRetryRecord(t,r,await first(r,T.request,p,m,l),f);if(e===`vanished`)throw new h(410,`gone`,`That publication was removed.`);if(e!==null)return e;let n=await first(r,T.publicationByKey,p,m,l);if(n!==null&&n.hidden_at!==null)throw v(`This entry is under moderation review.`);if(n!==null&&n.content_hash===f)return b(200,rowToEntry(t,n),x);throw n===null?new h(429,`quota`,`Publication quota reached.`):_(`That key was used for a different payload.`)}let O=await first(r,T.publicationByKey,p,m,l);if(O===null)throw _();return b(201,rowToEntry(t,O),x)}async function updateEntry(e,t,n,r,i,a){if(!r.publishingEnabled)throw new h(503,`publishing-disabled`,`Publishing is currently disabled.`);let o=requirePublisherRow(i),s=await first(n,T.publicationAny,a,e.name);if(s===null||s.owner_id!==o.id)throw S();if(s.hidden_at!==null)throw v(`This entry is under moderation review.`);let c=await w(t),l=B.safeParse(c);if(!l.success)throw y();let{expectedRevision:u}=l.data;if(u!==s.revision)throw _();let d=await e.derive(l.data.payload);if(d===null)throw y();let f=d.canonicalJson,p=d.contentHash;if(p===s.content_hash){let t=await first(n,T.publicationAny,a,e.name);if(t===null||t.owner_id!==o.id)throw S();if(t.hidden_at!==null)throw v(`This entry is under moderation review.`);if(t.revision!==u)throw _();return b(200,rowToEntry(e,t),x)}let m=JSON.stringify(d.tags);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){let t=await first(n,T.publicationAny,a,e.name);throw t===null?S():t.hidden_at===null?_():v(`This entry is under moderation review.`)}let g=await entryById(e,n,a);if(g===null)throw S();return b(200,g,x)}async function deleteEntry(e,t,n,r){if(n.operator){let i=requireOperatorRow(n);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)]))}else{let i=requirePublisherRow(n),a=await first(t,T.publicationAny,r,e.name);if(a===null)return new Response(null,{headers:x,status:204});if(a.owner_id!==i.id)throw S();await run(t,T.removePublication,r,i.id)}return new Response(null,{headers:x,status:204})}async function setLike(e,t,n,r,i,a){let s=requirePublisherRow(r);if(a&&!n.likesEnabled)throw new h(503,`likes-disabled`,`Likes are currently disabled.`);if(await first(t,T.publication,i,e.name)===null)throw S();a?await run(t,T.insertLike,s.id,now(),i):await run(t,T.removeLike,i,s.id);let c=await first(t,T.likeState,s.id,i);if(c===null)throw S();let l={id:c.id,likeCount:c.like_count,liked:c.liked===1},u=o.safeParse(l);if(!u.success)throw recordUnavailable();return b(200,u.data,x)}var V=P.object({reason:P.string().check(p).trim().min(1).max(e.reportReasonChars)});async function reportEntry(e,t,n,r,i){let a=requirePublisherRow(r),o=await w(t),s=V.safeParse(o);if(!s.success)throw y();if(await first(n,T.publication,i,e.name)===null)throw S();return await run(n,T.insertReport,uuid(),a.id,s.data.reason,now(),i,e.name),new Response(null,{headers:x,status:204})}async function setHidden(e,t,n,r,i){let a=requireOperatorRow(n);if(await first(t,T.publicationAny,r,e.name)===null)throw S();let o=i?t.DB.prepare(T.hidePublication).bind(now(),r,e.name):t.DB.prepare(T.unhidePublication).bind(r,e.name);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,{headers:x,status:204})}async function listReports(e,t,n,r){requireOperatorRow(r);let i=new URL(t.url).searchParams.get(`status`)??`unresolved`;if(i!==`unresolved`&&i!==`resolved`)throw g();let a=`reports:${e.name}:${i}`,{cursor:o,limit:s}=j(t,a,[`status`]),c=A({collection:e.name,resolved:i===`resolved`},{cursor:o,limit:s+1}),l=await all(n,c.sql,...c.parameters),u=l.slice(0,s),f=u.at(-1),p=d.safeParse({cursor:l.length>s&&f!==void 0?N({createdAt:f.created_at,id:f.id},a):null,reports:u.map(e=>({createdAt:e.created_at,entryId:e.publication_id,entryTitle:e.entry_title,id:e.id,reason:e.reason,reporter:{id:e.reporter_id,name:e.reporter_name},resolvedAt:e.resolved_at}))});if(!p.success)throw recordUnavailable();return b(200,p.data,x)}async function resolveReport(e,t,n,r){if(requireOperatorRow(n),await first(t,T.reportById,r,e.name)===null)throw S();return await run(t,T.resolveReport,now(),r,e.name),new Response(null,{headers:x,status:204})}async function myReports(e,t,n){let r=`own-reports:${n.publisher?.id??`unregistered`}`,{cursor:i,limit:a}=j(e,r);if(n.publisher===null)return b(200,{cursor:null,reports:[]},x);let o=A({publisherId:n.publisher.id},{cursor:i,limit:a+1}),s=await all(t,o.sql,...o.parameters),c=s.slice(0,a),l=c.at(-1),d=u.safeParse({cursor:s.length>a&&l!==void 0?N({createdAt:l.created_at,id:l.id},r):null,reports:c.map(e=>({collection:e.collection,createdAt:e.created_at,entryId:e.publication_id,id:e.id,reason:e.reason,resolvedAt:e.resolved_at}))});if(!d.success)throw recordUnavailable();return b(200,d.data,x)}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};
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";
5
+ import { s as P } from "@vielzeug/spell";
6
+ //#region src/worker/publications.ts
7
+ async function databaseCall(e) {
8
+ try {
9
+ return await e();
10
+ } catch (e) {
11
+ throw new h(500, "internal", "The service is not available.", {
12
+ cause: e,
13
+ diagnostic: { kind: "database" }
14
+ });
15
+ }
16
+ }
17
+ var first = (e, t, ...n) => databaseCall(() => e.DB.prepare(t).bind(...n).first()), all = (e, t, ...n) => databaseCall(async () => {
18
+ let r = await e.DB.prepare(t).bind(...n).all();
19
+ if (!Array.isArray(r.results)) throw Error("Missing database query results");
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) => {
22
+ let n;
23
+ try {
24
+ n = JSON.parse(t.payload_json);
25
+ } catch {
26
+ throw recordUnavailable();
27
+ }
28
+ let r = e.contract.entry.safeParse({
29
+ author: {
30
+ id: t.owner_id,
31
+ name: t.author_name
32
+ },
33
+ contentHash: t.content_hash,
34
+ contentVersion: t.content_version,
35
+ id: t.id,
36
+ likeCount: t.like_count,
37
+ payload: n,
38
+ publishedAt: t.published_at,
39
+ revision: t.revision,
40
+ schemaVersion: t.schema_version,
41
+ updatedAt: t.updated_at
42
+ });
43
+ if (!r.success) throw recordUnavailable();
44
+ return r.data;
45
+ }, rowToOwnedEntry = (e, t) => {
46
+ let n = rowToEntry(e, t);
47
+ if (!m.safeParse(t.client_key).success) throw recordUnavailable();
48
+ return {
49
+ ...n,
50
+ clientKey: t.client_key,
51
+ hidden: t.hidden_at !== null
52
+ };
53
+ }, entryById = async (e, t, n) => {
54
+ let r = await first(t, T.publication, n, e.name);
55
+ return r === null ? null : rowToEntry(e, r);
56
+ }, searchMatchFor = (t) => {
57
+ if (t === null) return;
58
+ if (t.length > e.searchChars) throw g();
59
+ let n = (t.match(/[\p{L}\p{N}]+/gu) ?? []).slice(0, e.searchTerms);
60
+ if (n.length !== 0) return n.map((e) => `"${e}"*`).join(" ");
61
+ }, entryCursorContext = (e) => JSON.stringify([
62
+ e.collection,
63
+ e.sort,
64
+ e.author ?? "",
65
+ e.filters,
66
+ e.tags,
67
+ e.searchMatch ?? ""
68
+ ]), cursorFromRow = (e) => ({
69
+ createdAt: e.published_at,
70
+ id: e.id,
71
+ likeCount: e.like_count
72
+ }), I = 16, vocabValues = (e, t, n) => {
73
+ let r = e.getAll(t);
74
+ if (r.length > I) throw g();
75
+ let i = [...new Set(r)].sort();
76
+ if (i.some((e) => !n.includes(e))) throw g();
77
+ return i;
78
+ };
79
+ async function listEntries(e, t, n) {
80
+ let r = new URL(t.url), i = C(r, [
81
+ "author",
82
+ "cursor",
83
+ "filter",
84
+ "limit",
85
+ "search",
86
+ "sort",
87
+ "tag"
88
+ ], ["filter", "tag"]), a = i.get("sort") ?? "recent", o = f.safeParse(a);
89
+ if (!o.success) throw g();
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();
92
+ let u = searchMatchFor(i.get("search")), d = entryCursorContext({
93
+ author: l ?? void 0,
94
+ collection: e.name,
95
+ filters: s,
96
+ searchMatch: u,
97
+ sort: o.data,
98
+ tags: c
99
+ }), { cursor: p, limit: m } = M(i, d), h = {
100
+ author: l ?? void 0,
101
+ collection: e.name,
102
+ cursor: p === null ? void 0 : {
103
+ createdAt: p.createdAt,
104
+ id: p.id,
105
+ likeCount: p.likeCount ?? 0
106
+ },
107
+ filters: s,
108
+ limit: m + 1,
109
+ searchMatch: u,
110
+ sort: o.data,
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))
115
+ };
116
+ return b(200, w, x);
117
+ }
118
+ function collectionMeta(e) {
119
+ let t = r.safeParse({
120
+ filters: [...e.filters].sort(),
121
+ name: e.name,
122
+ tags: [...e.tags].sort()
123
+ });
124
+ if (!t.success) throw recordUnavailable();
125
+ return b(200, t.data, x);
126
+ }
127
+ async function getEntry(e, t, n) {
128
+ let r = await entryById(e, t, n);
129
+ if (r === null) throw S();
130
+ return b(200, r, x);
131
+ }
132
+ async function healthJson(t, n) {
133
+ let r = null, a = null;
134
+ try {
135
+ r = (await first(n, T.schemaVersion))?.applied ?? 0;
136
+ } catch {
137
+ a = "database-unavailable";
138
+ }
139
+ a === null && r !== 1 && (a = "schema-mismatch");
140
+ let o = i.safeParse({
141
+ dbSchemaVersion: r,
142
+ features: {
143
+ likes: t.likesEnabled,
144
+ publishing: t.publishingEnabled
145
+ },
146
+ limits: e,
147
+ reason: a,
148
+ schemaVersion: 1,
149
+ status: a === null ? "ok" : "degraded"
150
+ });
151
+ if (!o.success) throw recordUnavailable();
152
+ return b(a === null ? 200 : 503, o.data, x);
153
+ }
154
+ var identityJson = (e, t) => {
155
+ let n = a.safeParse(t);
156
+ if (!n.success) throw recordUnavailable();
157
+ return b(e, n.data, x);
158
+ };
159
+ function getMe(e) {
160
+ return identityJson(200, e?.publisher ? {
161
+ id: e.publisher.id,
162
+ name: e.publisher.displayName,
163
+ operator: e.operator
164
+ } : null);
165
+ }
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);
170
+ if (o !== null) return identityJson(200, {
171
+ id: o.id,
172
+ name: o.display_name,
173
+ operator: n.operator
174
+ });
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.");
179
+ return identityJson(201, {
180
+ id: c.id,
181
+ name: c.display_name,
182
+ operator: n.operator
183
+ });
184
+ }
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, {
189
+ id: r.id,
190
+ name: a.data.name,
191
+ operator: n.operator
192
+ });
193
+ }
194
+ async function deleteProfile(e, t) {
195
+ return t.publisher !== null && await run(e, T.deletePublisher, t.publisher.id), new Response(null, {
196
+ headers: { "cache-control": "no-store" },
197
+ status: 204
198
+ });
199
+ }
200
+ async function deleteAccountProfile(e, n) {
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,
204
+ status: 204
205
+ });
206
+ }
207
+ async function erasePublisher(e, t, n) {
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,
211
+ status: 204
212
+ });
213
+ }
214
+ async function listModerationLog(e, t, n) {
215
+ requireOperatorRow(n);
216
+ let r = "moderation-log", { cursor: i, limit: a } = j(e, r), o = k({
217
+ cursor: i,
218
+ limit: a + 1
219
+ }), s = await all(t, o.sql, ...o.parameters), c = s.slice(0, a), u = c.at(-1), d = l.safeParse({
220
+ actions: c.map((e) => ({
221
+ action: e.action,
222
+ collection: e.collection,
223
+ createdAt: e.created_at,
224
+ id: e.id,
225
+ operatorId: e.operator_id,
226
+ targetId: e.target_id
227
+ })),
228
+ cursor: s.length > a && u !== void 0 ? N({
229
+ createdAt: u.created_at,
230
+ id: u.id
231
+ }, r) : null
232
+ });
233
+ if (!d.success) throw recordUnavailable();
234
+ return b(200, d.data, x);
235
+ }
236
+ var requirePublisherRow = (e) => {
237
+ if (e.publisher === null) throw v("Register before publishing.");
238
+ return e.publisher;
239
+ }, requireOperatorRow = (e) => {
240
+ if (!e.operator) throw v("Operator access is required.");
241
+ if (e.publisher === null) throw v("Register before moderating.");
242
+ return e.publisher;
243
+ }, L = P.object({ name: n.optional() }), R = P.object({ name: n });
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);
248
+ }
249
+ async function myLikes(t, n, r) {
250
+ let i = new URL(t.url).searchParams.get("ids");
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, {
254
+ cursor: null,
255
+ ids: []
256
+ }, x);
257
+ let o = D(r.publisher.id, {
258
+ cursor: i,
259
+ limit: a + 1
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({
262
+ createdAt: u.created_at,
263
+ id: u.publication_id
264
+ }, e) : null,
265
+ ids: l.map((e) => e.publication_id)
266
+ });
267
+ if (!d.success) throw recordUnavailable();
268
+ return b(200, d.data, x);
269
+ }
270
+ C(new URL(t.url), ["ids"]);
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);
275
+ if (!f.success) throw recordUnavailable();
276
+ return b(200, f.data, x);
277
+ }
278
+ var z = P.object({
279
+ clientKey: m,
280
+ payload: P.unknown()
281
+ }), B = P.object({
282
+ expectedRevision: P.number().int().min(1).max(2 ** 53 - 1),
283
+ payload: P.unknown()
284
+ });
285
+ async function resolveRetryRecord(e, t, n, r) {
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.");
289
+ let i = await entryById(e, t, n.publication_id);
290
+ return i === null ? "vanished" : b(200, i, x);
291
+ }
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();
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);
305
+ }
306
+ let C = now(), E = Date.now() + e.idempotencyRetentionMs, D = JSON.stringify(u.tags);
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)
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.");
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.");
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);
323
+ }
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();
331
+ let { expectedRevision: u } = l.data;
332
+ if (u !== s.revision) throw _();
333
+ let d = await e.derive(l.data.payload);
334
+ if (d === null) throw y();
335
+ let f = d.canonicalJson, p = d.contentHash;
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);
342
+ }
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.");
347
+ }
348
+ let g = await entryById(e, n, a);
349
+ if (g === null) throw S();
350
+ return b(200, g, x);
351
+ }
352
+ async function deleteEntry(e, t, n, r) {
353
+ if (n.operator) {
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)]));
356
+ } else {
357
+ let i = requirePublisherRow(n), a = await first(t, T.publicationAny, r, e.name);
358
+ if (a === null) return new Response(null, {
359
+ headers: x,
360
+ status: 204
361
+ });
362
+ if (a.owner_id !== i.id) throw S();
363
+ await run(t, T.removePublication, r, i.id);
364
+ }
365
+ return new Response(null, {
366
+ headers: x,
367
+ status: 204
368
+ });
369
+ }
370
+ async function setLike(e, t, n, r, i, a) {
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();
377
+ let l = {
378
+ id: c.id,
379
+ likeCount: c.like_count,
380
+ liked: c.liked === 1
381
+ }, u = o.safeParse(l);
382
+ if (!u.success) throw recordUnavailable();
383
+ return b(200, u.data, x);
384
+ }
385
+ var V = P.object({ reason: P.string().check(p).trim().min(1).max(e.reportReasonChars) });
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,
392
+ status: 204
393
+ });
394
+ }
395
+ async function setHidden(e, t, n, r, i) {
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,
401
+ status: 204
402
+ });
403
+ }
404
+ async function listReports(e, t, n, r) {
405
+ requireOperatorRow(r);
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({
409
+ collection: e.name,
410
+ resolved: i === "resolved"
411
+ }, {
412
+ cursor: o,
413
+ limit: s + 1
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({
416
+ createdAt: f.created_at,
417
+ id: f.id
418
+ }, a) : null,
419
+ reports: u.map((e) => ({
420
+ createdAt: e.created_at,
421
+ entryId: e.publication_id,
422
+ entryTitle: e.entry_title,
423
+ id: e.id,
424
+ reason: e.reason,
425
+ reporter: {
426
+ id: e.reporter_id,
427
+ name: e.reporter_name
428
+ },
429
+ resolvedAt: e.resolved_at
430
+ }))
431
+ });
432
+ if (!p.success) throw recordUnavailable();
433
+ return b(200, p.data, x);
434
+ }
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,
439
+ status: 204
440
+ });
441
+ }
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, {
445
+ cursor: null,
446
+ reports: []
447
+ }, x);
448
+ let o = A({ publisherId: n.publisher.id }, {
449
+ cursor: i,
450
+ limit: a + 1
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({
453
+ createdAt: l.created_at,
454
+ id: l.id
455
+ }, r) : null,
456
+ reports: c.map((e) => ({
457
+ collection: e.collection,
458
+ createdAt: e.created_at,
459
+ entryId: e.publication_id,
460
+ id: e.id,
461
+ reason: e.reason,
462
+ resolvedAt: e.resolved_at
463
+ }))
464
+ });
465
+ if (!d.success) throw recordUnavailable();
466
+ return b(200, d.data, x);
467
+ }
468
+ //#endregion
469
+ 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
+
2
471
  //# sourceMappingURL=publications.js.map