@openrails/contentkit-ui 0.69.0 → 0.71.0

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 (44) hide show
  1. package/README.md +17 -13
  2. package/dist/client/content/comments.d.ts +2 -2
  3. package/dist/client/content/comments.js +3 -3
  4. package/dist/client/content/comments.js.map +1 -1
  5. package/dist/client/content/polls.d.ts +2 -2
  6. package/dist/client/content/polls.js.map +1 -1
  7. package/dist/client/content/posts.d.ts +20 -12
  8. package/dist/client/content/posts.js +5 -13
  9. package/dist/client/content/posts.js.map +1 -1
  10. package/dist/client/content/reactions.js +4 -4
  11. package/dist/client/content/reactions.js.map +1 -1
  12. package/dist/client/content/types.d.ts +2 -2
  13. package/dist/client/content/types.js +1 -1
  14. package/dist/client/content/types.js.map +1 -1
  15. package/dist/client/generated/error-codes.js +2 -2
  16. package/dist/client/generated/error-codes.js.map +1 -1
  17. package/dist/client/generated/routes.d.ts +120 -172
  18. package/dist/client/generated/routes.js +207 -191
  19. package/dist/client/generated/routes.js.map +1 -1
  20. package/dist/client/generated/wire.d.ts +6 -1
  21. package/dist/client/generated/wire.js.map +1 -1
  22. package/dist/client/index.d.ts +1 -1
  23. package/dist/client/media/client.d.ts +7 -5
  24. package/dist/client/media/client.js +1 -1
  25. package/dist/client/media/client.js.map +1 -1
  26. package/dist/client/media/rules.d.ts +2 -2
  27. package/dist/client/media/rules.js +1 -5
  28. package/dist/client/media/rules.js.map +1 -1
  29. package/dist/client/taxonomy.d.ts +1 -1
  30. package/dist/client/taxonomy.js.map +1 -1
  31. package/dist/client.js +5 -5
  32. package/dist/components/social/poll-editor.d.ts +1 -1
  33. package/dist/components/social/poll-editor.js.map +1 -1
  34. package/dist/react/comments.d.ts +1 -1
  35. package/dist/react/comments.js.map +1 -1
  36. package/dist/react/polls.d.ts +2 -2
  37. package/dist/react/polls.js.map +1 -1
  38. package/dist/react/posts.d.ts +12 -10
  39. package/dist/react/posts.js +132 -99
  40. package/dist/react/posts.js.map +1 -1
  41. package/dist/react/public.d.ts +2 -2
  42. package/dist/react/public.js +7 -4
  43. package/dist/react/public.js.map +1 -1
  44. package/package.json +1 -1
package/README.md CHANGED
@@ -88,11 +88,14 @@ operation. This does not change the operation's result or its authentication.
88
88
  `comments.adminList`.
89
89
  - **Images of posts and polls** go to the item's server-named upload path:
90
90
  `media.uploadNamed(ref, file)` resolves the name content routes take;
91
- `media.uploadInline(postId, file)` resolves a body image's public URL;
92
- `posts.uploadCover`, `polls.uploadImage` and `polls.uploadOptionImage` do
93
- both steps. `folders` renames the post and poll media kinds. A post's
94
- public image files exist only while it is published; `usePost` shows an
95
- unpublished post's images to its editors (below).
91
+ `media.uploadInline(postId, file)` resolves a body image's reference
92
+ (`ref`, `contentkit:i-{uuid}`, what the body stores: `imageRef(name)`) and
93
+ a URL that shows it now; `posts.uploadCover`, `polls.uploadImage` and
94
+ `polls.uploadOptionImage` do both steps. Reads resolve references: a
95
+ post's `body` shows the images' current public files and `images` maps
96
+ each name to its URL. `folders` renames the post and poll media kinds. A
97
+ post's public image files exist only while it is published; `usePost`
98
+ shows an unpublished post's images to its editors (below).
96
99
  - **`client.config()`** says what the content module allows: which
97
100
  interactions signed-out visitors may make (`content.Options.Anonymous`,
98
101
  none by default) and the longest comment (`CommentMaxLength`, 2,200 by
@@ -142,15 +145,15 @@ const editor = usePollEditor(pollId); // staff
142
145
 
143
146
  A post editor puts `editorBody` in its rich-text field and inserts what
144
147
  `uploadImage(file)` resolves: URLs that show the images now (the file for a
145
- fresh upload, signed editor views of an unpublished post's images, read and
146
- refreshed as needed). `update({ body })` stores the images' public URLs
147
- (`storedBody(html)` does the same for a host's own save); `imageSrc(url)` shows
148
- the cover.
148
+ fresh upload, public files, signed editor views of an unpublished post's
149
+ images, read and refreshed as needed). `update({ body })` stores each of
150
+ those URLs as the image's reference (`storedBody(html)` does the same for a
151
+ host's own save); `coverSrc` shows the cover, `imageSrc(name)` any image.
149
152
 
150
153
  ```tsx
151
154
  const editor = usePost(postId);
152
155
  <RichText value={editor.editorBody} onImage={editor.uploadImage} onSave={(html) => editor.update({ body: html })} />
153
- <img src={editor.imageSrc(editor.post?.cover_url)} alt="" />
156
+ <img src={editor.coverSrc} alt="" />
154
157
  ```
155
158
 
156
159
  Also `useContentConfig` (`client.config()`, read once), `useCommentReplies`,
@@ -226,9 +229,10 @@ const { image, rule, isDefault } = usePublicImage("user", userId, "avatar");
226
229
 
227
230
  An item's image for a public preset: the exact renditions from a read's
228
231
  `public`, or from a host listing passed as `image` (no read), else the
229
- kind's default image. Published files carry a generation, so a URL built
230
- from a preset template only ever names the default; `rule` (from
231
- `GET /media/presets`) gives the shape, widths and narrowest edit.
232
+ kind's default image when the preset has one (`rule.default`), else none.
233
+ Published files carry a generation, so a URL built from a preset template
234
+ only ever names the default; `rule` (from `GET /media/presets`) gives the
235
+ shape, widths and narrowest edit.
232
236
 
233
237
  ### useCanonicalContent
234
238
 
@@ -29,8 +29,8 @@ export declare class CommentsClient {
29
29
  standing(ref: RefBody, signal?: AbortSignal): Promise<CommentStanding>;
30
30
  /** The newest published comments across the site, with their items; a page may under-fill. */
31
31
  latest(q?: PageQuery, signal?: AbortSignal): Promise<FeedItem[]>;
32
- /** Every comment, newest first, deleted, held and rejected ones with their bodies (CommentModerate). */
32
+ /** Every comment, newest first, deleted, held and rejected ones with their bodies (root:comments:moderate). */
33
33
  adminList(q?: AdminCommentQuery, signal?: AbortSignal): Promise<AdminComment[]>;
34
- /** Restores a deleted comment (CommentModerate). */
34
+ /** Restores a deleted comment (root:comments:moderate). */
35
35
  restore(id: string): Promise<void>;
36
36
  }
@@ -52,12 +52,12 @@ var n = (e) => ({
52
52
  });
53
53
  }
54
54
  async react(n, r) {
55
- let i = this.http.captureChanges(), a = await e(this.http, "POST", `/comments/{cid}/${t(r)}`, { params: { cid: n } });
55
+ let i = this.http.captureChanges(), a = { cid: n }, o = r === 0 ? await e(this.http, "DELETE", "/comments/{cid}/reaction", { params: a }) : await e(this.http, "POST", `/comments/{cid}/${t(r)}`, { params: a });
56
56
  return i({
57
57
  type: "comment.reacted",
58
58
  id: n,
59
- counts: a
60
- }), a;
59
+ counts: o
60
+ }), o;
61
61
  }
62
62
  standing(t, r) {
63
63
  return e(this.http, "GET", "/{kind}/{id}/can-comment", {
@@ -1 +1 @@
1
- {"version":3,"file":"comments.js","names":[],"sources":["../../../src/client/content/comments.ts"],"sourcesContent":["import type { AdminComment, Comment, CommentInput, CommentStanding, FeedItem, ReactionCounts, RefBody } from \"../generated/wire.js\";\nimport type { Http } from \"../http.js\";\nimport { call, type PageQuery } from \"../route.js\";\nimport { reactionVerb, type Reaction, type Sort } from \"./types.js\";\n\nexport interface CommentQuery extends PageQuery {\n sort?: Sort;\n}\n\nexport interface AdminCommentQuery extends PageQuery {\n /** Only comments on this content kind. */\n contentKind?: string;\n}\n\n// Content ids are the host's opaque text (not only media's UUIDv7s).\nconst target = (ref: RefBody) => ({ kind: ref.kind, id: ref.id });\n\n/** Comments on an item: threads, replies, edits, tombstones, reactions, standing, the latest feed and moderation. */\nexport class CommentsClient {\n constructor(private readonly http: Http) {}\n\n /** The item's top-level comments with reply counts; the caller also gets its own held and rejected ones. */\n list(ref: RefBody, q: CommentQuery = {}, signal?: AbortSignal): Promise<Comment[]> {\n return call(this.http, \"GET\", \"/{kind}/{id}/comments\", { params: target(ref), query: q, signal });\n }\n /** A top-level comment's replies, oldest first. */\n replies(id: string, q: PageQuery = {}, signal?: AbortSignal): Promise<Comment[]> {\n return call(this.http, \"GET\", \"/comments/{cid}/replies\", { params: { cid: id }, query: q, signal });\n }\n /** Comments on the item, or replies to a top-level comment (reply_to_id); anonymous callers give anon_name. */\n async create(ref: RefBody, input: CommentInput): Promise<Comment> {\n const emit = this.http.captureChanges();\n const comment: Comment = await call(this.http, \"POST\", \"/{kind}/{id}/comments\", { params: target(ref), body: input });\n emit({ type: \"comment.created\", ref: target(ref), comment });\n return comment;\n }\n /** Replaces the body: its author, or a moderator. */\n async edit(id: string, body: string): Promise<Comment> {\n const emit = this.http.captureChanges();\n const comment: Comment = await call(this.http, \"PATCH\", \"/comments/{cid}\", { params: { cid: id }, body: { body } });\n emit({ type: \"comment.updated\", comment });\n return comment;\n }\n /** Leaves a tombstone: its author, or a moderator. */\n async delete(id: string): Promise<void> {\n const emit = this.http.captureChanges();\n await call(this.http, \"DELETE\", \"/comments/{cid}\", { params: { cid: id } });\n emit({ type: \"comment.deleted\", id });\n }\n /** Sets the caller's reaction to a comment. */\n async react(id: string, value: Reaction): Promise<ReactionCounts> {\n const emit = this.http.captureChanges();\n const counts: ReactionCounts = await call(this.http, \"POST\", `/comments/{cid}/${reactionVerb(value)}` as const, { params: { cid: id } });\n emit({ type: \"comment.reacted\", id, counts });\n return counts;\n }\n /** Whether the caller may comment on the item, the ban that stops it, and what it may do to others' comments. */\n standing(ref: RefBody, signal?: AbortSignal): Promise<CommentStanding> {\n return call(this.http, \"GET\", \"/{kind}/{id}/can-comment\", { params: target(ref), signal });\n }\n /** The newest published comments across the site, with their items; a page may under-fill. */\n latest(q: PageQuery = {}, signal?: AbortSignal): Promise<FeedItem[]> {\n return call(this.http, \"GET\", \"/comments/latest\", { query: q, signal });\n }\n /** Every comment, newest first, deleted, held and rejected ones with their bodies (CommentModerate). */\n adminList(q: AdminCommentQuery = {}, signal?: AbortSignal): Promise<AdminComment[]> {\n const { contentKind, ...page } = q;\n return call(this.http, \"GET\", \"/comments/admin\", { query: { ...page, content_kind: contentKind }, signal });\n }\n /** Restores a deleted comment (CommentModerate). */\n async restore(id: string): Promise<void> {\n const emit = this.http.captureChanges();\n await call(this.http, \"POST\", \"/comments/{cid}/restore\", { params: { cid: id } });\n emit({ type: \"comment.restored\", id });\n }\n}\n"],"mappings":";;;AAeA,IAAM,KAAU,OAAkB;CAAE,MAAM,EAAI;CAAM,IAAI,EAAI;AAAG,IAGlD,IAAb,MAA4B;CACG;CAA7B,YAAY,GAA6B;EAAZ,KAAA,OAAA;CAAa;CAG1C,KAAK,GAAc,IAAkB,CAAC,GAAG,GAA0C;EACjF,OAAO,EAAK,KAAK,MAAM,OAAO,yBAAyB;GAAE,QAAQ,EAAO,CAAG;GAAG,OAAO;GAAG;EAAO,CAAC;CAClG;CAEA,QAAQ,GAAY,IAAe,CAAC,GAAG,GAA0C;EAC/E,OAAO,EAAK,KAAK,MAAM,OAAO,2BAA2B;GAAE,QAAQ,EAAE,KAAK,EAAG;GAAG,OAAO;GAAG;EAAO,CAAC;CACpG;CAEA,MAAM,OAAO,GAAc,GAAuC;EAChE,IAAM,IAAO,KAAK,KAAK,eAAe,GAChC,IAAmB,MAAM,EAAK,KAAK,MAAM,QAAQ,yBAAyB;GAAE,QAAQ,EAAO,CAAG;GAAG,MAAM;EAAM,CAAC;EAEpH,OADA,EAAK;GAAE,MAAM;GAAmB,KAAK,EAAO,CAAG;GAAG;EAAQ,CAAC,GACpD;CACT;CAEA,MAAM,KAAK,GAAY,GAAgC;EACrD,IAAM,IAAO,KAAK,KAAK,eAAe,GAChC,IAAmB,MAAM,EAAK,KAAK,MAAM,SAAS,mBAAmB;GAAE,QAAQ,EAAE,KAAK,EAAG;GAAG,MAAM,EAAE,QAAK;EAAE,CAAC;EAElH,OADA,EAAK;GAAE,MAAM;GAAmB;EAAQ,CAAC,GAClC;CACT;CAEA,MAAM,OAAO,GAA2B;EACtC,IAAM,IAAO,KAAK,KAAK,eAAe;EAEtC,AADA,MAAM,EAAK,KAAK,MAAM,UAAU,mBAAmB,EAAE,QAAQ,EAAE,KAAK,EAAG,EAAE,CAAC,GAC1E,EAAK;GAAE,MAAM;GAAmB;EAAG,CAAC;CACtC;CAEA,MAAM,MAAM,GAAY,GAA0C;EAChE,IAAM,IAAO,KAAK,KAAK,eAAe,GAChC,IAAyB,MAAM,EAAK,KAAK,MAAM,QAAQ,mBAAmB,EAAa,CAAK,KAAc,EAAE,QAAQ,EAAE,KAAK,EAAG,EAAE,CAAC;EAEvI,OADA,EAAK;GAAE,MAAM;GAAmB;GAAI;EAAO,CAAC,GACrC;CACT;CAEA,SAAS,GAAc,GAAgD;EACrE,OAAO,EAAK,KAAK,MAAM,OAAO,4BAA4B;GAAE,QAAQ,EAAO,CAAG;GAAG;EAAO,CAAC;CAC3F;CAEA,OAAO,IAAe,CAAC,GAAG,GAA2C;EACnE,OAAO,EAAK,KAAK,MAAM,OAAO,oBAAoB;GAAE,OAAO;GAAG;EAAO,CAAC;CACxE;CAEA,UAAU,IAAuB,CAAC,GAAG,GAA+C;EAClF,IAAM,EAAE,gBAAa,GAAG,MAAS;EACjC,OAAO,EAAK,KAAK,MAAM,OAAO,mBAAmB;GAAE,OAAO;IAAE,GAAG;IAAM,cAAc;GAAY;GAAG;EAAO,CAAC;CAC5G;CAEA,MAAM,QAAQ,GAA2B;EACvC,IAAM,IAAO,KAAK,KAAK,eAAe;EAEtC,AADA,MAAM,EAAK,KAAK,MAAM,QAAQ,2BAA2B,EAAE,QAAQ,EAAE,KAAK,EAAG,EAAE,CAAC,GAChF,EAAK;GAAE,MAAM;GAAoB;EAAG,CAAC;CACvC;AACF"}
1
+ {"version":3,"file":"comments.js","names":[],"sources":["../../../src/client/content/comments.ts"],"sourcesContent":["import type { AdminComment, Comment, CommentInput, CommentStanding, FeedItem, ReactionCounts, RefBody } from \"../generated/wire.js\";\nimport type { Http } from \"../http.js\";\nimport { call, type PageQuery } from \"../route.js\";\nimport { reactionVerb, type Reaction, type Sort } from \"./types.js\";\n\nexport interface CommentQuery extends PageQuery {\n sort?: Sort;\n}\n\nexport interface AdminCommentQuery extends PageQuery {\n /** Only comments on this content kind. */\n contentKind?: string;\n}\n\n// Content ids are the host's opaque text (not only media's UUIDv7s).\nconst target = (ref: RefBody) => ({ kind: ref.kind, id: ref.id });\n\n/** Comments on an item: threads, replies, edits, tombstones, reactions, standing, the latest feed and moderation. */\nexport class CommentsClient {\n constructor(private readonly http: Http) {}\n\n /** The item's top-level comments with reply counts; the caller also gets its own held and rejected ones. */\n list(ref: RefBody, q: CommentQuery = {}, signal?: AbortSignal): Promise<Comment[]> {\n return call(this.http, \"GET\", \"/{kind}/{id}/comments\", { params: target(ref), query: q, signal });\n }\n /** A top-level comment's replies, oldest first. */\n replies(id: string, q: PageQuery = {}, signal?: AbortSignal): Promise<Comment[]> {\n return call(this.http, \"GET\", \"/comments/{cid}/replies\", { params: { cid: id }, query: q, signal });\n }\n /** Comments on the item, or replies to a top-level comment (reply_to_id); anonymous callers give anon_name. */\n async create(ref: RefBody, input: CommentInput): Promise<Comment> {\n const emit = this.http.captureChanges();\n const comment: Comment = await call(this.http, \"POST\", \"/{kind}/{id}/comments\", { params: target(ref), body: input });\n emit({ type: \"comment.created\", ref: target(ref), comment });\n return comment;\n }\n /** Replaces the body: its author, or a moderator. */\n async edit(id: string, body: string): Promise<Comment> {\n const emit = this.http.captureChanges();\n const comment: Comment = await call(this.http, \"PATCH\", \"/comments/{cid}\", { params: { cid: id }, body: { body } });\n emit({ type: \"comment.updated\", comment });\n return comment;\n }\n /** Leaves a tombstone: its author, or a moderator. */\n async delete(id: string): Promise<void> {\n const emit = this.http.captureChanges();\n await call(this.http, \"DELETE\", \"/comments/{cid}\", { params: { cid: id } });\n emit({ type: \"comment.deleted\", id });\n }\n /** Sets the caller's reaction to a comment. */\n async react(id: string, value: Reaction): Promise<ReactionCounts> {\n const emit = this.http.captureChanges();\n const params = { cid: id };\n const counts: ReactionCounts =\n value === 0 ? await call(this.http, \"DELETE\", \"/comments/{cid}/reaction\", { params }) : await call(this.http, \"POST\", `/comments/{cid}/${reactionVerb(value)}` as const, { params });\n emit({ type: \"comment.reacted\", id, counts });\n return counts;\n }\n /** Whether the caller may comment on the item, the ban that stops it, and what it may do to others' comments. */\n standing(ref: RefBody, signal?: AbortSignal): Promise<CommentStanding> {\n return call(this.http, \"GET\", \"/{kind}/{id}/can-comment\", { params: target(ref), signal });\n }\n /** The newest published comments across the site, with their items; a page may under-fill. */\n latest(q: PageQuery = {}, signal?: AbortSignal): Promise<FeedItem[]> {\n return call(this.http, \"GET\", \"/comments/latest\", { query: q, signal });\n }\n /** Every comment, newest first, deleted, held and rejected ones with their bodies (root:comments:moderate). */\n adminList(q: AdminCommentQuery = {}, signal?: AbortSignal): Promise<AdminComment[]> {\n const { contentKind, ...page } = q;\n return call(this.http, \"GET\", \"/comments/admin\", { query: { ...page, content_kind: contentKind }, signal });\n }\n /** Restores a deleted comment (root:comments:moderate). */\n async restore(id: string): Promise<void> {\n const emit = this.http.captureChanges();\n await call(this.http, \"POST\", \"/comments/{cid}/restore\", { params: { cid: id } });\n emit({ type: \"comment.restored\", id });\n }\n}\n"],"mappings":";;;AAeA,IAAM,KAAU,OAAkB;CAAE,MAAM,EAAI;CAAM,IAAI,EAAI;AAAG,IAGlD,IAAb,MAA4B;CACG;CAA7B,YAAY,GAA6B;EAAZ,KAAA,OAAA;CAAa;CAG1C,KAAK,GAAc,IAAkB,CAAC,GAAG,GAA0C;EACjF,OAAO,EAAK,KAAK,MAAM,OAAO,yBAAyB;GAAE,QAAQ,EAAO,CAAG;GAAG,OAAO;GAAG;EAAO,CAAC;CAClG;CAEA,QAAQ,GAAY,IAAe,CAAC,GAAG,GAA0C;EAC/E,OAAO,EAAK,KAAK,MAAM,OAAO,2BAA2B;GAAE,QAAQ,EAAE,KAAK,EAAG;GAAG,OAAO;GAAG;EAAO,CAAC;CACpG;CAEA,MAAM,OAAO,GAAc,GAAuC;EAChE,IAAM,IAAO,KAAK,KAAK,eAAe,GAChC,IAAmB,MAAM,EAAK,KAAK,MAAM,QAAQ,yBAAyB;GAAE,QAAQ,EAAO,CAAG;GAAG,MAAM;EAAM,CAAC;EAEpH,OADA,EAAK;GAAE,MAAM;GAAmB,KAAK,EAAO,CAAG;GAAG;EAAQ,CAAC,GACpD;CACT;CAEA,MAAM,KAAK,GAAY,GAAgC;EACrD,IAAM,IAAO,KAAK,KAAK,eAAe,GAChC,IAAmB,MAAM,EAAK,KAAK,MAAM,SAAS,mBAAmB;GAAE,QAAQ,EAAE,KAAK,EAAG;GAAG,MAAM,EAAE,QAAK;EAAE,CAAC;EAElH,OADA,EAAK;GAAE,MAAM;GAAmB;EAAQ,CAAC,GAClC;CACT;CAEA,MAAM,OAAO,GAA2B;EACtC,IAAM,IAAO,KAAK,KAAK,eAAe;EAEtC,AADA,MAAM,EAAK,KAAK,MAAM,UAAU,mBAAmB,EAAE,QAAQ,EAAE,KAAK,EAAG,EAAE,CAAC,GAC1E,EAAK;GAAE,MAAM;GAAmB;EAAG,CAAC;CACtC;CAEA,MAAM,MAAM,GAAY,GAA0C;EAChE,IAAM,IAAO,KAAK,KAAK,eAAe,GAChC,IAAS,EAAE,KAAK,EAAG,GACnB,IACJ,MAAU,IAAI,MAAM,EAAK,KAAK,MAAM,UAAU,4BAA4B,EAAE,UAAO,CAAC,IAAI,MAAM,EAAK,KAAK,MAAM,QAAQ,mBAAmB,EAAa,CAAK,KAAc,EAAE,UAAO,CAAC;EAErL,OADA,EAAK;GAAE,MAAM;GAAmB;GAAI;EAAO,CAAC,GACrC;CACT;CAEA,SAAS,GAAc,GAAgD;EACrE,OAAO,EAAK,KAAK,MAAM,OAAO,4BAA4B;GAAE,QAAQ,EAAO,CAAG;GAAG;EAAO,CAAC;CAC3F;CAEA,OAAO,IAAe,CAAC,GAAG,GAA2C;EACnE,OAAO,EAAK,KAAK,MAAM,OAAO,oBAAoB;GAAE,OAAO;GAAG;EAAO,CAAC;CACxE;CAEA,UAAU,IAAuB,CAAC,GAAG,GAA+C;EAClF,IAAM,EAAE,gBAAa,GAAG,MAAS;EACjC,OAAO,EAAK,KAAK,MAAM,OAAO,mBAAmB;GAAE,OAAO;IAAE,GAAG;IAAM,cAAc;GAAY;GAAG;EAAO,CAAC;CAC5G;CAEA,MAAM,QAAQ,GAA2B;EACvC,IAAM,IAAO,KAAK,KAAK,eAAe;EAEtC,AADA,MAAM,EAAK,KAAK,MAAM,QAAQ,2BAA2B,EAAE,QAAQ,EAAE,KAAK,EAAG,EAAE,CAAC,GAChF,EAAK;GAAE,MAAM;GAAoB;EAAG,CAAC;CACvC;AACF"}
@@ -9,14 +9,14 @@ export interface PollQuery extends PageQuery {
9
9
  /** Polls live on this day, YYYY-MM-DD. */
10
10
  date?: string;
11
11
  }
12
- /** Site-wide polls: multiple choice or free text; staff write them (PollWrite). */
12
+ /** Site-wide polls: multiple choice or free text; staff write them (root:polls:write). */
13
13
  export declare class PollsClient {
14
14
  private readonly http;
15
15
  private readonly media;
16
16
  constructor(http: Http, media: MediaClient);
17
17
  /** Live polls, newest first. */
18
18
  list(q?: PollQuery, signal?: AbortSignal): Promise<Poll[]>;
19
- /** Every poll, scheduled and inactive ones included (PollWrite). */
19
+ /** Every poll, scheduled and inactive ones included (root:polls:write). */
20
20
  adminList(q?: PollQuery, signal?: AbortSignal): Promise<Poll[]>;
21
21
  /** A poll with the caller's vote or answer. */
22
22
  get(id: string, signal?: AbortSignal): Promise<Poll>;
@@ -1 +1 @@
1
- {"version":3,"file":"polls.js","names":[],"sources":["../../../src/client/content/polls.ts"],"sourcesContent":["import type { Poll, PollImage, PollInput, PollOption, PollOptionPatch, PollUpdate } from \"../generated/wire.js\";\nimport type { Http } from \"../http.js\";\nimport type { MediaClient, NamedOptions } from \"../media/client.js\";\nimport { call, type PageQuery } from \"../route.js\";\n\nexport interface PollQuery extends PageQuery {\n language?: string;\n /** Polls live in this month, YYYY-MM. */\n month?: string;\n /** Polls live on this day, YYYY-MM-DD. */\n date?: string;\n}\n\n/** Site-wide polls: multiple choice or free text; staff write them (PollWrite). */\nexport class PollsClient {\n constructor(\n private readonly http: Http,\n private readonly media: MediaClient,\n ) {}\n\n /** Live polls, newest first. */\n list(q: PollQuery = {}, signal?: AbortSignal): Promise<Poll[]> {\n return call(this.http, \"GET\", \"/polls\", { query: q, signal });\n }\n /** Every poll, scheduled and inactive ones included (PollWrite). */\n adminList(q: PollQuery = {}, signal?: AbortSignal): Promise<Poll[]> {\n return call(this.http, \"GET\", \"/polls/admin\", { query: q, signal });\n }\n /** A poll with the caller's vote or answer. */\n get(id: string, signal?: AbortSignal): Promise<Poll> {\n return call(this.http, \"GET\", \"/polls/{id}\", { params: { id }, signal });\n }\n /** Creates a poll with its options (images are set once it exists). */\n async create(input: PollInput): Promise<Poll> {\n const emit = this.http.captureChanges();\n const poll: Poll = await call(this.http, \"POST\", \"/polls\", { body: input });\n emit({ type: \"poll.created\", poll });\n return poll;\n }\n /** Updates the given fields. */\n async update(id: string, patch: PollUpdate): Promise<Poll> {\n const emit = this.http.captureChanges();\n const poll: Poll = await call(this.http, \"PATCH\", \"/polls/{id}\", { params: { id }, body: patch });\n emit({ type: \"poll.updated\", poll });\n return poll;\n }\n /** Deletes the poll and its images. */\n async delete(id: string): Promise<void> {\n const emit = this.http.captureChanges();\n await call(this.http, \"DELETE\", \"/polls/{id}\", { params: { id } });\n emit({ type: \"poll.deleted\", id });\n }\n /** Votes for an option of an open multiple-choice poll; a vote is final, and voting again changes nothing. */\n async vote(id: string, option: string): Promise<Poll> {\n const emit = this.http.captureChanges();\n const poll: Poll = await call(this.http, \"POST\", \"/polls/{id}/vote\", { params: { id }, body: { option_id: option } });\n emit({ type: \"poll.updated\", poll });\n return poll;\n }\n /** Stores or replaces the signed-in caller's answer to an open free-text poll. */\n async answer(id: string, text: string): Promise<Poll> {\n const emit = this.http.captureChanges();\n const poll: Poll = await call(this.http, \"POST\", \"/polls/{id}/answer\", { params: { id }, body: { text } });\n emit({ type: \"poll.updated\", poll });\n return poll;\n }\n /** Sets the question image to an inline image name of the poll's folder, or clears it; resolves with its URL. */\n async setImage(id: string, image: string | null): Promise<string | null> {\n const emit = this.http.captureChanges();\n const r: PollImage = await call(this.http, \"PUT\", \"/polls/{id}/image\", { params: { id }, body: { image: image ?? \"\" } });\n emit({ type: \"poll.changed\", id });\n return r.image_url;\n }\n /** Uploads file to the poll's folder as the question image (null clears it). */\n async uploadImage(id: string, file: Blob | null, o?: NamedOptions): Promise<string | null> {\n return this.setImage(id, file ? (await this.media.uploadNamed(this.media.pollRef(id), file, o)).name : null);\n }\n /** Adds an option, at the end unless position is given. */\n async addOption(id: string, option: PollOptionPatch): Promise<PollOption> {\n const emit = this.http.captureChanges();\n const o: PollOption = await call(this.http, \"POST\", \"/polls/{id}/options\", { params: { id }, body: option });\n emit({ type: \"poll.changed\", id });\n return o;\n }\n async updateOption(id: string, option: string, patch: PollOptionPatch): Promise<PollOption> {\n const emit = this.http.captureChanges();\n const o: PollOption = await call(this.http, \"PATCH\", \"/polls/{id}/options/{oid}\", { params: { id, oid: option }, body: patch });\n emit({ type: \"poll.changed\", id });\n return o;\n }\n /** Removes an option and its votes; a poll keeps at least two. */\n async deleteOption(id: string, option: string): Promise<void> {\n const emit = this.http.captureChanges();\n await call(this.http, \"DELETE\", \"/polls/{id}/options/{oid}\", { params: { id, oid: option } });\n emit({ type: \"poll.changed\", id });\n }\n /** Gives the options the positions of their order, patching those that moved. */\n async reorderOptions(id: string, options: readonly Pick<PollOption, \"id\" | \"position\">[]): Promise<void> {\n const emit = this.http.captureChanges();\n for (const [position, o] of options.entries()) {\n if (o.position !== position) await call(this.http, \"PATCH\", \"/polls/{id}/options/{oid}\", { params: { id, oid: o.id }, body: { position } });\n }\n emit({ type: \"poll.changed\", id });\n }\n async setOptionImage(id: string, option: string, image: string | null): Promise<string | null> {\n const emit = this.http.captureChanges();\n const r: PollImage = await call(this.http, \"PUT\", \"/polls/{id}/options/{oid}/image\", { params: { id, oid: option }, body: { image: image ?? \"\" } });\n emit({ type: \"poll.changed\", id });\n return r.image_url;\n }\n /** Uploads file to the poll's folder as the option's image (null clears it). */\n async uploadOptionImage(id: string, option: string, file: Blob | null, o?: NamedOptions): Promise<string | null> {\n return this.setOptionImage(id, option, file ? (await this.media.uploadNamed(this.media.pollRef(id), file, o)).name : null);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"polls.js","names":[],"sources":["../../../src/client/content/polls.ts"],"sourcesContent":["import type { Poll, PollImage, PollInput, PollOption, PollOptionPatch, PollUpdate } from \"../generated/wire.js\";\nimport type { Http } from \"../http.js\";\nimport type { MediaClient, NamedOptions } from \"../media/client.js\";\nimport { call, type PageQuery } from \"../route.js\";\n\nexport interface PollQuery extends PageQuery {\n language?: string;\n /** Polls live in this month, YYYY-MM. */\n month?: string;\n /** Polls live on this day, YYYY-MM-DD. */\n date?: string;\n}\n\n/** Site-wide polls: multiple choice or free text; staff write them (root:polls:write). */\nexport class PollsClient {\n constructor(\n private readonly http: Http,\n private readonly media: MediaClient,\n ) {}\n\n /** Live polls, newest first. */\n list(q: PollQuery = {}, signal?: AbortSignal): Promise<Poll[]> {\n return call(this.http, \"GET\", \"/polls\", { query: q, signal });\n }\n /** Every poll, scheduled and inactive ones included (root:polls:write). */\n adminList(q: PollQuery = {}, signal?: AbortSignal): Promise<Poll[]> {\n return call(this.http, \"GET\", \"/polls/admin\", { query: q, signal });\n }\n /** A poll with the caller's vote or answer. */\n get(id: string, signal?: AbortSignal): Promise<Poll> {\n return call(this.http, \"GET\", \"/polls/{id}\", { params: { id }, signal });\n }\n /** Creates a poll with its options (images are set once it exists). */\n async create(input: PollInput): Promise<Poll> {\n const emit = this.http.captureChanges();\n const poll: Poll = await call(this.http, \"POST\", \"/polls\", { body: input });\n emit({ type: \"poll.created\", poll });\n return poll;\n }\n /** Updates the given fields. */\n async update(id: string, patch: PollUpdate): Promise<Poll> {\n const emit = this.http.captureChanges();\n const poll: Poll = await call(this.http, \"PATCH\", \"/polls/{id}\", { params: { id }, body: patch });\n emit({ type: \"poll.updated\", poll });\n return poll;\n }\n /** Deletes the poll and its images. */\n async delete(id: string): Promise<void> {\n const emit = this.http.captureChanges();\n await call(this.http, \"DELETE\", \"/polls/{id}\", { params: { id } });\n emit({ type: \"poll.deleted\", id });\n }\n /** Votes for an option of an open multiple-choice poll; a vote is final, and voting again changes nothing. */\n async vote(id: string, option: string): Promise<Poll> {\n const emit = this.http.captureChanges();\n const poll: Poll = await call(this.http, \"POST\", \"/polls/{id}/vote\", { params: { id }, body: { option_id: option } });\n emit({ type: \"poll.updated\", poll });\n return poll;\n }\n /** Stores or replaces the signed-in caller's answer to an open free-text poll. */\n async answer(id: string, text: string): Promise<Poll> {\n const emit = this.http.captureChanges();\n const poll: Poll = await call(this.http, \"POST\", \"/polls/{id}/answer\", { params: { id }, body: { text } });\n emit({ type: \"poll.updated\", poll });\n return poll;\n }\n /** Sets the question image to an inline image name of the poll's folder, or clears it; resolves with its URL. */\n async setImage(id: string, image: string | null): Promise<string | null> {\n const emit = this.http.captureChanges();\n const r: PollImage = await call(this.http, \"PUT\", \"/polls/{id}/image\", { params: { id }, body: { image: image ?? \"\" } });\n emit({ type: \"poll.changed\", id });\n return r.image_url;\n }\n /** Uploads file to the poll's folder as the question image (null clears it). */\n async uploadImage(id: string, file: Blob | null, o?: NamedOptions): Promise<string | null> {\n return this.setImage(id, file ? (await this.media.uploadNamed(this.media.pollRef(id), file, o)).name : null);\n }\n /** Adds an option, at the end unless position is given. */\n async addOption(id: string, option: PollOptionPatch): Promise<PollOption> {\n const emit = this.http.captureChanges();\n const o: PollOption = await call(this.http, \"POST\", \"/polls/{id}/options\", { params: { id }, body: option });\n emit({ type: \"poll.changed\", id });\n return o;\n }\n async updateOption(id: string, option: string, patch: PollOptionPatch): Promise<PollOption> {\n const emit = this.http.captureChanges();\n const o: PollOption = await call(this.http, \"PATCH\", \"/polls/{id}/options/{oid}\", { params: { id, oid: option }, body: patch });\n emit({ type: \"poll.changed\", id });\n return o;\n }\n /** Removes an option and its votes; a poll keeps at least two. */\n async deleteOption(id: string, option: string): Promise<void> {\n const emit = this.http.captureChanges();\n await call(this.http, \"DELETE\", \"/polls/{id}/options/{oid}\", { params: { id, oid: option } });\n emit({ type: \"poll.changed\", id });\n }\n /** Gives the options the positions of their order, patching those that moved. */\n async reorderOptions(id: string, options: readonly Pick<PollOption, \"id\" | \"position\">[]): Promise<void> {\n const emit = this.http.captureChanges();\n for (const [position, o] of options.entries()) {\n if (o.position !== position) await call(this.http, \"PATCH\", \"/polls/{id}/options/{oid}\", { params: { id, oid: o.id }, body: { position } });\n }\n emit({ type: \"poll.changed\", id });\n }\n async setOptionImage(id: string, option: string, image: string | null): Promise<string | null> {\n const emit = this.http.captureChanges();\n const r: PollImage = await call(this.http, \"PUT\", \"/polls/{id}/options/{oid}/image\", { params: { id, oid: option }, body: { image: image ?? \"\" } });\n emit({ type: \"poll.changed\", id });\n return r.image_url;\n }\n /** Uploads file to the poll's folder as the option's image (null clears it). */\n async uploadOptionImage(id: string, option: string, file: Blob | null, o?: NamedOptions): Promise<string | null> {\n return this.setOptionImage(id, option, file ? (await this.media.uploadNamed(this.media.pollRef(id), file, o)).name : null);\n }\n}\n"],"mappings":"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"}
@@ -1,8 +1,18 @@
1
- import { Post, PostInput } from '../generated/wire.js';
1
+ import { InlineImage, Post, PostInput } from '../generated/wire.js';
2
2
  import { Http } from '../http.js';
3
3
  import { MediaClient, NamedOptions } from '../media/client.js';
4
4
  import { PageQuery } from '../route.js';
5
- import { Reaction, Sort } from './types.js';
5
+ import { Sort } from './types.js';
6
+ /** The URL scheme of an image reference in a post body. */
7
+ export declare const IMAGE_SCHEME = "contentkit:";
8
+ /**
9
+ * The reference a post body stores for one of its images, by name
10
+ * ("contentkit:i-{uuid}"), where the image's URL would go: reads resolve it to
11
+ * the image's current public file (`Post.images`).
12
+ */
13
+ export declare const imageRef: (name: string) => string;
14
+ /** Matches every image reference; the name is group 1. */
15
+ export declare const imageRefs: () => RegExp;
6
16
  export interface PostQuery extends PageQuery {
7
17
  language?: string;
8
18
  sort?: Sort;
@@ -23,22 +33,20 @@ export declare class PostsClient {
23
33
  constructor(http: Http, media: MediaClient);
24
34
  /** Published posts. */
25
35
  list(q?: PostQuery, signal?: AbortSignal): Promise<Post[]>;
26
- /** Every post for staff: drafts, scheduled, held and rejected ones included; deleted ones on their own; q searches (PostWrite). */
36
+ /** Every post for staff: drafts, scheduled, held and rejected ones included; deleted ones on their own; q searches (root:posts:write). */
27
37
  adminList(q?: PostAdminQuery, signal?: AbortSignal): Promise<Post[]>;
28
38
  get(id: string, signal?: AbortSignal): Promise<Post>;
29
- /** Creates a post (PostWrite); moderation "held" when the moderator holds it. */
39
+ /** Creates a post (root:posts:write); moderation "held" when the moderator holds it. */
30
40
  create(input: PostInput): Promise<Post>;
31
- /** Updates the given fields (PostWrite). */
41
+ /** Updates the given fields (root:posts:write). */
32
42
  update(id: string, input: PostInput): Promise<Post>;
33
43
  delete(id: string): Promise<void>;
34
- /** Restores a deleted post as it was (PostWrite); conflict when a live post took its slug. */
44
+ /** Restores a deleted post as it was (root:posts:write); conflict when a live post took its slug. */
35
45
  restore(id: string): Promise<Post>;
36
- /** Sets the caller's reaction to a published post; resolves with the post's new totals. */
37
- react(id: string, value: Reaction): Promise<Post>;
38
- /** Sets the cover to an inline image name ("i-{uuid}") of the post's folder, or clears it; resolves with its URL. */
46
+ /** Sets the cover to an inline image name ("i-{uuid}") of the post's folder, or clears it; resolves with its public URL (null until it has one). */
39
47
  setCover(id: string, image: string | null): Promise<string | null>;
40
- /** Uploads file to the post's folder and makes it the cover (null clears it); resolves with its URL. */
48
+ /** Uploads file to the post's folder and makes it the cover (null clears it); resolves with its public URL (null until it has one). */
41
49
  uploadCover(id: string, file: Blob | null, o?: NamedOptions): Promise<string | null>;
42
- /** The public URL of an inline image of the post's folder, to place in its body. */
43
- imageURL(id: string, image: string, signal?: AbortSignal): Promise<string>;
50
+ /** An inline image of the post's folder: the reference to store in its body and a URL that shows it to the editor now (null while none exists). */
51
+ inlineImage(id: string, image: string, signal?: AbortSignal): Promise<InlineImage>;
44
52
  }
@@ -1,7 +1,6 @@
1
1
  import { call as e } from "../route.js";
2
- import { reactionVerb as t } from "./types.js";
3
2
  //#region src/client/content/posts.ts
4
- var n = class {
3
+ var t = "contentkit:", n = (e) => t + e, r = () => /contentkit:(i-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/g, i = class {
5
4
  http;
6
5
  media;
7
6
  constructor(e, t) {
@@ -56,13 +55,6 @@ var n = class {
56
55
  post: r
57
56
  }), r;
58
57
  }
59
- async react(n, r) {
60
- let i = this.http.captureChanges(), a = await e(this.http, "POST", `/posts/{id}/${t(r)}`, { params: { id: n } });
61
- return i({
62
- type: "post.updated",
63
- post: a
64
- }), a;
65
- }
66
58
  async setCover(t, n) {
67
59
  let r = this.http.captureChanges(), i = await e(this.http, "PUT", "/posts/{id}/cover", {
68
60
  params: { id: t },
@@ -78,15 +70,15 @@ var n = class {
78
70
  let r = await this.media.uploadNamed(this.media.postRef(e), t, n);
79
71
  return this.setCover(e, r.name);
80
72
  }
81
- async imageURL(t, n, r) {
82
- return (await e(this.http, "POST", "/posts/{id}/images", {
73
+ inlineImage(t, n, r) {
74
+ return e(this.http, "POST", "/posts/{id}/images", {
83
75
  params: { id: t },
84
76
  body: { image: n },
85
77
  signal: r
86
- })).url;
78
+ });
87
79
  }
88
80
  };
89
81
  //#endregion
90
- export { n as PostsClient };
82
+ export { t as IMAGE_SCHEME, i as PostsClient, n as imageRef, r as imageRefs };
91
83
 
92
84
  //# sourceMappingURL=posts.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"posts.js","names":[],"sources":["../../../src/client/content/posts.ts"],"sourcesContent":["import type { InlineImage, Post, PostCover, PostInput } from \"../generated/wire.js\";\nimport type { Http } from \"../http.js\";\nimport type { MediaClient, NamedOptions } from \"../media/client.js\";\nimport { call, type PageQuery } from \"../route.js\";\nimport { reactionVerb, type Reaction, type Sort } from \"./types.js\";\n\nexport interface PostQuery extends PageQuery {\n language?: string;\n sort?: Sort;\n}\n\nexport interface PostAdminQuery extends PageQuery {\n language?: string;\n /** true: only drafts; false: only posts that are not drafts. */\n draft?: boolean;\n /** true: only deleted posts (with deleted_at); otherwise only live ones. */\n deleted?: boolean;\n /** Only posts whose title, excerpt or body contains this text, ignoring case. */\n q?: string;\n}\n\n/** Posts: the published list, staff CRUD, search and restore, reactions, the cover and body images. */\nexport class PostsClient {\n constructor(\n private readonly http: Http,\n private readonly media: MediaClient,\n ) {}\n\n /** Published posts. */\n list(q: PostQuery = {}, signal?: AbortSignal): Promise<Post[]> {\n return call(this.http, \"GET\", \"/posts\", { query: q, signal });\n }\n /** Every post for staff: drafts, scheduled, held and rejected ones included; deleted ones on their own; q searches (PostWrite). */\n adminList(q: PostAdminQuery = {}, signal?: AbortSignal): Promise<Post[]> {\n return call(this.http, \"GET\", \"/posts/admin\", { query: q, signal });\n }\n get(id: string, signal?: AbortSignal): Promise<Post> {\n return call(this.http, \"GET\", \"/posts/{id}\", { params: { id }, signal });\n }\n /** Creates a post (PostWrite); moderation \"held\" when the moderator holds it. */\n async create(input: PostInput): Promise<Post> {\n const emit = this.http.captureChanges();\n const post: Post = await call(this.http, \"POST\", \"/posts\", { body: input });\n emit({ type: \"post.created\", post });\n return post;\n }\n /** Updates the given fields (PostWrite). */\n async update(id: string, input: PostInput): Promise<Post> {\n const emit = this.http.captureChanges();\n const post: Post = await call(this.http, \"PATCH\", \"/posts/{id}\", { params: { id }, body: input });\n emit({ type: \"post.updated\", post });\n return post;\n }\n async delete(id: string): Promise<void> {\n const emit = this.http.captureChanges();\n await call(this.http, \"DELETE\", \"/posts/{id}\", { params: { id } });\n emit({ type: \"post.deleted\", id });\n }\n /** Restores a deleted post as it was (PostWrite); conflict when a live post took its slug. */\n async restore(id: string): Promise<Post> {\n const emit = this.http.captureChanges();\n const post: Post = await call(this.http, \"POST\", \"/posts/{id}/restore\", { params: { id } });\n emit({ type: \"post.restored\", post });\n return post;\n }\n /** Sets the caller's reaction to a published post; resolves with the post's new totals. */\n async react(id: string, value: Reaction): Promise<Post> {\n const emit = this.http.captureChanges();\n const post: Post = await call(this.http, \"POST\", `/posts/{id}/${reactionVerb(value)}` as const, { params: { id } });\n emit({ type: \"post.updated\", post });\n return post;\n }\n /** Sets the cover to an inline image name (\"i-{uuid}\") of the post's folder, or clears it; resolves with its URL. */\n async setCover(id: string, image: string | null): Promise<string | null> {\n const emit = this.http.captureChanges();\n const r: PostCover = await call(this.http, \"PUT\", \"/posts/{id}/cover\", { params: { id }, body: { image: image ?? \"\" } });\n emit({ type: \"post.changed\", id });\n return r.cover_url;\n }\n /** Uploads file to the post's folder and makes it the cover (null clears it); resolves with its URL. */\n async uploadCover(id: string, file: Blob | null, o?: NamedOptions): Promise<string | null> {\n if (!file) return this.setCover(id, null);\n const up = await this.media.uploadNamed(this.media.postRef(id), file, o);\n return this.setCover(id, up.name);\n }\n /** The public URL of an inline image of the post's folder, to place in its body. */\n async imageURL(id: string, image: string, signal?: AbortSignal): Promise<string> {\n const r: InlineImage = await call(this.http, \"POST\", \"/posts/{id}/images\", { params: { id }, body: { image }, signal });\n return r.url;\n }\n}\n"],"mappings":";;;AAsBA,IAAa,IAAb,MAAyB;CAEJ;CACA;CAFnB,YACE,GACA,GACA;EADiB,AADA,KAAA,OAAA,GACA,KAAA,QAAA;CAChB;CAGH,KAAK,IAAe,CAAC,GAAG,GAAuC;EAC7D,OAAO,EAAK,KAAK,MAAM,OAAO,UAAU;GAAE,OAAO;GAAG;EAAO,CAAC;CAC9D;CAEA,UAAU,IAAoB,CAAC,GAAG,GAAuC;EACvE,OAAO,EAAK,KAAK,MAAM,OAAO,gBAAgB;GAAE,OAAO;GAAG;EAAO,CAAC;CACpE;CACA,IAAI,GAAY,GAAqC;EACnD,OAAO,EAAK,KAAK,MAAM,OAAO,eAAe;GAAE,QAAQ,EAAE,MAAG;GAAG;EAAO,CAAC;CACzE;CAEA,MAAM,OAAO,GAAiC;EAC5C,IAAM,IAAO,KAAK,KAAK,eAAe,GAChC,IAAa,MAAM,EAAK,KAAK,MAAM,QAAQ,UAAU,EAAE,MAAM,EAAM,CAAC;EAE1E,OADA,EAAK;GAAE,MAAM;GAAgB;EAAK,CAAC,GAC5B;CACT;CAEA,MAAM,OAAO,GAAY,GAAiC;EACxD,IAAM,IAAO,KAAK,KAAK,eAAe,GAChC,IAAa,MAAM,EAAK,KAAK,MAAM,SAAS,eAAe;GAAE,QAAQ,EAAE,MAAG;GAAG,MAAM;EAAM,CAAC;EAEhG,OADA,EAAK;GAAE,MAAM;GAAgB;EAAK,CAAC,GAC5B;CACT;CACA,MAAM,OAAO,GAA2B;EACtC,IAAM,IAAO,KAAK,KAAK,eAAe;EAEtC,AADA,MAAM,EAAK,KAAK,MAAM,UAAU,eAAe,EAAE,QAAQ,EAAE,MAAG,EAAE,CAAC,GACjE,EAAK;GAAE,MAAM;GAAgB;EAAG,CAAC;CACnC;CAEA,MAAM,QAAQ,GAA2B;EACvC,IAAM,IAAO,KAAK,KAAK,eAAe,GAChC,IAAa,MAAM,EAAK,KAAK,MAAM,QAAQ,uBAAuB,EAAE,QAAQ,EAAE,MAAG,EAAE,CAAC;EAE1F,OADA,EAAK;GAAE,MAAM;GAAiB;EAAK,CAAC,GAC7B;CACT;CAEA,MAAM,MAAM,GAAY,GAAgC;EACtD,IAAM,IAAO,KAAK,KAAK,eAAe,GAChC,IAAa,MAAM,EAAK,KAAK,MAAM,QAAQ,eAAe,EAAa,CAAK,KAAc,EAAE,QAAQ,EAAE,MAAG,EAAE,CAAC;EAElH,OADA,EAAK;GAAE,MAAM;GAAgB;EAAK,CAAC,GAC5B;CACT;CAEA,MAAM,SAAS,GAAY,GAA8C;EACvE,IAAM,IAAO,KAAK,KAAK,eAAe,GAChC,IAAe,MAAM,EAAK,KAAK,MAAM,OAAO,qBAAqB;GAAE,QAAQ,EAAE,MAAG;GAAG,MAAM,EAAE,OAAO,KAAS,GAAG;EAAE,CAAC;EAEvH,OADA,EAAK;GAAE,MAAM;GAAgB;EAAG,CAAC,GAC1B,EAAE;CACX;CAEA,MAAM,YAAY,GAAY,GAAmB,GAA0C;EACzF,IAAI,CAAC,GAAM,OAAO,KAAK,SAAS,GAAI,IAAI;EACxC,IAAM,IAAK,MAAM,KAAK,MAAM,YAAY,KAAK,MAAM,QAAQ,CAAE,GAAG,GAAM,CAAC;EACvE,OAAO,KAAK,SAAS,GAAI,EAAG,IAAI;CAClC;CAEA,MAAM,SAAS,GAAY,GAAe,GAAuC;EAE/E,QAAO,MADsB,EAAK,KAAK,MAAM,QAAQ,sBAAsB;GAAE,QAAQ,EAAE,MAAG;GAAG,MAAM,EAAE,SAAM;GAAG;EAAO,CAAC,EAAA,CAC7G;CACX;AACF"}
1
+ {"version":3,"file":"posts.js","names":[],"sources":["../../../src/client/content/posts.ts"],"sourcesContent":["import type { InlineImage, Post, PostCover, PostInput } from \"../generated/wire.js\";\nimport type { Http } from \"../http.js\";\nimport type { MediaClient, NamedOptions } from \"../media/client.js\";\nimport { call, type PageQuery } from \"../route.js\";\nimport type { Sort } from \"./types.js\";\n\n/** The URL scheme of an image reference in a post body. */\nexport const IMAGE_SCHEME = \"contentkit:\";\n\n/**\n * The reference a post body stores for one of its images, by name\n * (\"contentkit:i-{uuid}\"), where the image's URL would go: reads resolve it to\n * the image's current public file (`Post.images`).\n */\nexport const imageRef = (name: string) => IMAGE_SCHEME + name;\n\n/** Matches every image reference; the name is group 1. */\nexport const imageRefs = () => /contentkit:(i-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/g;\n\nexport interface PostQuery extends PageQuery {\n language?: string;\n sort?: Sort;\n}\n\nexport interface PostAdminQuery extends PageQuery {\n language?: string;\n /** true: only drafts; false: only posts that are not drafts. */\n draft?: boolean;\n /** true: only deleted posts (with deleted_at); otherwise only live ones. */\n deleted?: boolean;\n /** Only posts whose title, excerpt or body contains this text, ignoring case. */\n q?: string;\n}\n\n/** Posts: the published list, staff CRUD, search and restore, reactions, the cover and body images. */\nexport class PostsClient {\n constructor(\n private readonly http: Http,\n private readonly media: MediaClient,\n ) {}\n\n /** Published posts. */\n list(q: PostQuery = {}, signal?: AbortSignal): Promise<Post[]> {\n return call(this.http, \"GET\", \"/posts\", { query: q, signal });\n }\n /** Every post for staff: drafts, scheduled, held and rejected ones included; deleted ones on their own; q searches (root:posts:write). */\n adminList(q: PostAdminQuery = {}, signal?: AbortSignal): Promise<Post[]> {\n return call(this.http, \"GET\", \"/posts/admin\", { query: q, signal });\n }\n get(id: string, signal?: AbortSignal): Promise<Post> {\n return call(this.http, \"GET\", \"/posts/{id}\", { params: { id }, signal });\n }\n /** Creates a post (root:posts:write); moderation \"held\" when the moderator holds it. */\n async create(input: PostInput): Promise<Post> {\n const emit = this.http.captureChanges();\n const post: Post = await call(this.http, \"POST\", \"/posts\", { body: input });\n emit({ type: \"post.created\", post });\n return post;\n }\n /** Updates the given fields (root:posts:write). */\n async update(id: string, input: PostInput): Promise<Post> {\n const emit = this.http.captureChanges();\n const post: Post = await call(this.http, \"PATCH\", \"/posts/{id}\", { params: { id }, body: input });\n emit({ type: \"post.updated\", post });\n return post;\n }\n async delete(id: string): Promise<void> {\n const emit = this.http.captureChanges();\n await call(this.http, \"DELETE\", \"/posts/{id}\", { params: { id } });\n emit({ type: \"post.deleted\", id });\n }\n /** Restores a deleted post as it was (root:posts:write); conflict when a live post took its slug. */\n async restore(id: string): Promise<Post> {\n const emit = this.http.captureChanges();\n const post: Post = await call(this.http, \"POST\", \"/posts/{id}/restore\", { params: { id } });\n emit({ type: \"post.restored\", post });\n return post;\n }\n /** Sets the cover to an inline image name (\"i-{uuid}\") of the post's folder, or clears it; resolves with its public URL (null until it has one). */\n async setCover(id: string, image: string | null): Promise<string | null> {\n const emit = this.http.captureChanges();\n const r: PostCover = await call(this.http, \"PUT\", \"/posts/{id}/cover\", { params: { id }, body: { image: image ?? \"\" } });\n emit({ type: \"post.changed\", id });\n return r.cover_url;\n }\n /** Uploads file to the post's folder and makes it the cover (null clears it); resolves with its public URL (null until it has one). */\n async uploadCover(id: string, file: Blob | null, o?: NamedOptions): Promise<string | null> {\n if (!file) return this.setCover(id, null);\n const up = await this.media.uploadNamed(this.media.postRef(id), file, o);\n return this.setCover(id, up.name);\n }\n /** An inline image of the post's folder: the reference to store in its body and a URL that shows it to the editor now (null while none exists). */\n inlineImage(id: string, image: string, signal?: AbortSignal): Promise<InlineImage> {\n return call(this.http, \"POST\", \"/posts/{id}/images\", { params: { id }, body: { image }, signal });\n }\n}\n"],"mappings":";;AAOA,IAAa,IAAe,eAOf,KAAY,MAAiB,IAAe,GAG5C,UAAkB,gFAkBlB,IAAb,MAAyB;CAEJ;CACA;CAFnB,YACE,GACA,GACA;EADiB,AADA,KAAA,OAAA,GACA,KAAA,QAAA;CAChB;CAGH,KAAK,IAAe,CAAC,GAAG,GAAuC;EAC7D,OAAO,EAAK,KAAK,MAAM,OAAO,UAAU;GAAE,OAAO;GAAG;EAAO,CAAC;CAC9D;CAEA,UAAU,IAAoB,CAAC,GAAG,GAAuC;EACvE,OAAO,EAAK,KAAK,MAAM,OAAO,gBAAgB;GAAE,OAAO;GAAG;EAAO,CAAC;CACpE;CACA,IAAI,GAAY,GAAqC;EACnD,OAAO,EAAK,KAAK,MAAM,OAAO,eAAe;GAAE,QAAQ,EAAE,MAAG;GAAG;EAAO,CAAC;CACzE;CAEA,MAAM,OAAO,GAAiC;EAC5C,IAAM,IAAO,KAAK,KAAK,eAAe,GAChC,IAAa,MAAM,EAAK,KAAK,MAAM,QAAQ,UAAU,EAAE,MAAM,EAAM,CAAC;EAE1E,OADA,EAAK;GAAE,MAAM;GAAgB;EAAK,CAAC,GAC5B;CACT;CAEA,MAAM,OAAO,GAAY,GAAiC;EACxD,IAAM,IAAO,KAAK,KAAK,eAAe,GAChC,IAAa,MAAM,EAAK,KAAK,MAAM,SAAS,eAAe;GAAE,QAAQ,EAAE,MAAG;GAAG,MAAM;EAAM,CAAC;EAEhG,OADA,EAAK;GAAE,MAAM;GAAgB;EAAK,CAAC,GAC5B;CACT;CACA,MAAM,OAAO,GAA2B;EACtC,IAAM,IAAO,KAAK,KAAK,eAAe;EAEtC,AADA,MAAM,EAAK,KAAK,MAAM,UAAU,eAAe,EAAE,QAAQ,EAAE,MAAG,EAAE,CAAC,GACjE,EAAK;GAAE,MAAM;GAAgB;EAAG,CAAC;CACnC;CAEA,MAAM,QAAQ,GAA2B;EACvC,IAAM,IAAO,KAAK,KAAK,eAAe,GAChC,IAAa,MAAM,EAAK,KAAK,MAAM,QAAQ,uBAAuB,EAAE,QAAQ,EAAE,MAAG,EAAE,CAAC;EAE1F,OADA,EAAK;GAAE,MAAM;GAAiB;EAAK,CAAC,GAC7B;CACT;CAEA,MAAM,SAAS,GAAY,GAA8C;EACvE,IAAM,IAAO,KAAK,KAAK,eAAe,GAChC,IAAe,MAAM,EAAK,KAAK,MAAM,OAAO,qBAAqB;GAAE,QAAQ,EAAE,MAAG;GAAG,MAAM,EAAE,OAAO,KAAS,GAAG;EAAE,CAAC;EAEvH,OADA,EAAK;GAAE,MAAM;GAAgB;EAAG,CAAC,GAC1B,EAAE;CACX;CAEA,MAAM,YAAY,GAAY,GAAmB,GAA0C;EACzF,IAAI,CAAC,GAAM,OAAO,KAAK,SAAS,GAAI,IAAI;EACxC,IAAM,IAAK,MAAM,KAAK,MAAM,YAAY,KAAK,MAAM,QAAQ,CAAE,GAAG,GAAM,CAAC;EACvE,OAAO,KAAK,SAAS,GAAI,EAAG,IAAI;CAClC;CAEA,YAAY,GAAY,GAAe,GAA4C;EACjF,OAAO,EAAK,KAAK,MAAM,QAAQ,sBAAsB;GAAE,QAAQ,EAAE,MAAG;GAAG,MAAM,EAAE,SAAM;GAAG;EAAO,CAAC;CAClG;AACF"}
@@ -16,18 +16,18 @@ var n = class {
16
16
  });
17
17
  }
18
18
  async set(n, r) {
19
- let i = this.http.captureChanges(), a = await e(this.http, "POST", `/{kind}/{id}/${t(r)}`, { params: {
19
+ let i = this.http.captureChanges(), a = {
20
20
  kind: n.kind,
21
21
  id: n.id
22
- } });
22
+ }, o = r === 0 ? await e(this.http, "DELETE", "/{kind}/{id}/reaction", { params: a }) : await e(this.http, "POST", `/{kind}/{id}/${t(r)}`, { params: a });
23
23
  return i({
24
24
  type: "reaction.changed",
25
25
  ref: {
26
26
  kind: n.kind,
27
27
  id: n.id
28
28
  },
29
- counts: a
30
- }), a;
29
+ counts: o
30
+ }), o;
31
31
  }
32
32
  };
33
33
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"reactions.js","names":[],"sources":["../../../src/client/content/reactions.ts"],"sourcesContent":["import type { ReactionCounts, RefBody } from \"../generated/wire.js\";\nimport type { Http } from \"../http.js\";\nimport { call } from \"../route.js\";\nimport { reactionVerb, type Reaction } from \"./types.js\";\n\n/** Likes and dislikes of an item (a host kind, or \"post\"). */\nexport class ReactionsClient {\n constructor(private readonly http: Http) {}\n\n /** The item's counts and the caller's own reaction. */\n get(ref: RefBody, signal?: AbortSignal): Promise<ReactionCounts> {\n return call(this.http, \"GET\", \"/{kind}/{id}/reaction\", { params: { kind: ref.kind, id: ref.id }, signal });\n }\n /** Sets the caller's reaction (0 clears it); resolves with the new counts. */\n async set(ref: RefBody, value: Reaction): Promise<ReactionCounts> {\n const emit = this.http.captureChanges();\n const counts: ReactionCounts = await call(this.http, \"POST\", `/{kind}/{id}/${reactionVerb(value)}` as const, { params: { kind: ref.kind, id: ref.id } });\n emit({ type: \"reaction.changed\", ref: { kind: ref.kind, id: ref.id }, counts });\n return counts;\n }\n}\n"],"mappings":";;;AAMA,IAAa,IAAb,MAA6B;CACE;CAA7B,YAAY,GAA6B;EAAZ,KAAA,OAAA;CAAa;CAG1C,IAAI,GAAc,GAA+C;EAC/D,OAAO,EAAK,KAAK,MAAM,OAAO,yBAAyB;GAAE,QAAQ;IAAE,MAAM,EAAI;IAAM,IAAI,EAAI;GAAG;GAAG;EAAO,CAAC;CAC3G;CAEA,MAAM,IAAI,GAAc,GAA0C;EAChE,IAAM,IAAO,KAAK,KAAK,eAAe,GAChC,IAAyB,MAAM,EAAK,KAAK,MAAM,QAAQ,gBAAgB,EAAa,CAAK,KAAc,EAAE,QAAQ;GAAE,MAAM,EAAI;GAAM,IAAI,EAAI;EAAG,EAAE,CAAC;EAEvJ,OADA,EAAK;GAAE,MAAM;GAAoB,KAAK;IAAE,MAAM,EAAI;IAAM,IAAI,EAAI;GAAG;GAAG;EAAO,CAAC,GACvE;CACT;AACF"}
1
+ {"version":3,"file":"reactions.js","names":[],"sources":["../../../src/client/content/reactions.ts"],"sourcesContent":["import type { ReactionCounts, RefBody } from \"../generated/wire.js\";\nimport type { Http } from \"../http.js\";\nimport { call } from \"../route.js\";\nimport { reactionVerb, type Reaction } from \"./types.js\";\n\n/** Likes and dislikes of an item (a host kind, or \"post\"). */\nexport class ReactionsClient {\n constructor(private readonly http: Http) {}\n\n /** The item's counts and the caller's own reaction. */\n get(ref: RefBody, signal?: AbortSignal): Promise<ReactionCounts> {\n return call(this.http, \"GET\", \"/{kind}/{id}/reaction\", { params: { kind: ref.kind, id: ref.id }, signal });\n }\n /** Sets the caller's reaction (0 clears it); resolves with the new counts. */\n async set(ref: RefBody, value: Reaction): Promise<ReactionCounts> {\n const emit = this.http.captureChanges();\n const params = { kind: ref.kind, id: ref.id };\n const counts: ReactionCounts =\n value === 0 ? await call(this.http, \"DELETE\", \"/{kind}/{id}/reaction\", { params }) : await call(this.http, \"POST\", `/{kind}/{id}/${reactionVerb(value)}` as const, { params });\n emit({ type: \"reaction.changed\", ref: { kind: ref.kind, id: ref.id }, counts });\n return counts;\n }\n}\n"],"mappings":";;;AAMA,IAAa,IAAb,MAA6B;CACE;CAA7B,YAAY,GAA6B;EAAZ,KAAA,OAAA;CAAa;CAG1C,IAAI,GAAc,GAA+C;EAC/D,OAAO,EAAK,KAAK,MAAM,OAAO,yBAAyB;GAAE,QAAQ;IAAE,MAAM,EAAI;IAAM,IAAI,EAAI;GAAG;GAAG;EAAO,CAAC;CAC3G;CAEA,MAAM,IAAI,GAAc,GAA0C;EAChE,IAAM,IAAO,KAAK,KAAK,eAAe,GAChC,IAAS;GAAE,MAAM,EAAI;GAAM,IAAI,EAAI;EAAG,GACtC,IACJ,MAAU,IAAI,MAAM,EAAK,KAAK,MAAM,UAAU,yBAAyB,EAAE,UAAO,CAAC,IAAI,MAAM,EAAK,KAAK,MAAM,QAAQ,gBAAgB,EAAa,CAAK,KAAc,EAAE,UAAO,CAAC;EAE/K,OADA,EAAK;GAAE,MAAM;GAAoB,KAAK;IAAE,MAAM,EAAI;IAAM,IAAI,EAAI;GAAG;GAAG;EAAO,CAAC,GACvE;CACT;AACF"}
@@ -1,8 +1,8 @@
1
1
  import { CommentBan, CommentStanding, Comment, Poll, Post, ReactionCounts, RefBody, ResolveInput } from '../generated/wire.js';
2
2
  /** A reaction: like 1, dislike -1, none 0. */
3
3
  export type Reaction = -1 | 0 | 1;
4
- /** The route verb that sets a reaction. */
5
- export declare function reactionVerb(value: Reaction): "like" | "dislike" | "neutral";
4
+ /** The route verb that sets a like or dislike; clearing is DELETE …/reaction. */
5
+ export declare function reactionVerb(value: -1 | 1): "like" | "dislike";
6
6
  /** Comment and post order: newest (the default), likes (most liked) or best (Wilson lower bound). */
7
7
  export type Sort = "newest" | "likes" | "best";
8
8
  /** A ban route family: "owner" bans from the caller's own content, "global" from the whole site. */
@@ -1,6 +1,6 @@
1
1
  //#region src/client/content/types.ts
2
2
  function e(e) {
3
- return e === 1 ? "like" : e === -1 ? "dislike" : "neutral";
3
+ return e === 1 ? "like" : "dislike";
4
4
  }
5
5
  //#endregion
6
6
  export { e as reactionVerb };
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","names":[],"sources":["../../../src/client/content/types.ts"],"sourcesContent":["import type { CommentBan, CommentStanding, Comment, Poll, Post, ReactionCounts, RefBody, ResolveInput } from \"../generated/wire.js\";\n\n/** A reaction: like 1, dislike -1, none 0. */\nexport type Reaction = -1 | 0 | 1;\n\n/** The route verb that sets a reaction. */\nexport function reactionVerb(value: Reaction): \"like\" | \"dislike\" | \"neutral\" {\n return value === 1 ? \"like\" : value === -1 ? \"dislike\" : \"neutral\";\n}\n\n/** Comment and post order: newest (the default), likes (most liked) or best (Wilson lower bound). */\nexport type Sort = \"newest\" | \"likes\" | \"best\";\n\n/** A ban route family: \"owner\" bans from the caller's own content, \"global\" from the whole site. */\nexport type BanScope = CommentStanding[\"ban_scopes\"][number];\n\n/** What the review queue holds. */\nexport type HeldKind = \"comment\" | \"post\";\n\n/** A reviewer's decision on a held item. */\nexport type Decision = Exclude<ResolveInput[\"decision\"], \"review\">;\n\n/** A content-module mutation made through the client. */\nexport type ContentChange =\n | { type: \"post.created\"; post: Post }\n | { type: \"post.updated\"; post: Post }\n /** The cover changed: refetch the post. */\n | { type: \"post.changed\"; id: string }\n | { type: \"post.deleted\"; id: string }\n | { type: \"post.restored\"; post: Post }\n /** ref is the commented item. */\n | { type: \"comment.created\"; ref: RefBody; comment: Comment }\n | { type: \"comment.updated\"; comment: Comment }\n | { type: \"comment.deleted\"; id: string }\n | { type: \"comment.restored\"; id: string }\n | { type: \"comment.reacted\"; id: string; counts: ReactionCounts }\n | { type: \"reaction.changed\"; ref: RefBody; counts: ReactionCounts }\n | { type: \"favorite.changed\"; ref: RefBody; favorited: boolean; count: number }\n /** Created, updated, voted or answered: the poll as the server now answers it. */\n | { type: \"poll.created\"; poll: Poll }\n | { type: \"poll.updated\"; poll: Poll }\n /** An option or image changed: refetch the poll. */\n | { type: \"poll.changed\"; id: string }\n | { type: \"poll.deleted\"; id: string }\n | { type: \"ban.saved\"; scope: BanScope; ban: CommentBan }\n | { type: \"ban.lifted\"; scope: BanScope; user: string }\n | { type: \"moderation.resolved\"; kind: HeldKind; id: string; decision: Decision }\n | { type: \"taxonomy.changed\" };\n"],"mappings":";AAMA,SAAgB,EAAa,GAAiD;CAC5E,OAAO,MAAU,IAAI,SAAS,MAAU,KAAK,YAAY;AAC3D"}
1
+ {"version":3,"file":"types.js","names":[],"sources":["../../../src/client/content/types.ts"],"sourcesContent":["import type { CommentBan, CommentStanding, Comment, Poll, Post, ReactionCounts, RefBody, ResolveInput } from \"../generated/wire.js\";\n\n/** A reaction: like 1, dislike -1, none 0. */\nexport type Reaction = -1 | 0 | 1;\n\n/** The route verb that sets a like or dislike; clearing is DELETE …/reaction. */\nexport function reactionVerb(value: -1 | 1): \"like\" | \"dislike\" {\n return value === 1 ? \"like\" : \"dislike\";\n}\n\n/** Comment and post order: newest (the default), likes (most liked) or best (Wilson lower bound). */\nexport type Sort = \"newest\" | \"likes\" | \"best\";\n\n/** A ban route family: \"owner\" bans from the caller's own content, \"global\" from the whole site. */\nexport type BanScope = CommentStanding[\"ban_scopes\"][number];\n\n/** What the review queue holds. */\nexport type HeldKind = \"comment\" | \"post\";\n\n/** A reviewer's decision on a held item. */\nexport type Decision = Exclude<ResolveInput[\"decision\"], \"review\">;\n\n/** A content-module mutation made through the client. */\nexport type ContentChange =\n | { type: \"post.created\"; post: Post }\n | { type: \"post.updated\"; post: Post }\n /** The cover changed: refetch the post. */\n | { type: \"post.changed\"; id: string }\n | { type: \"post.deleted\"; id: string }\n | { type: \"post.restored\"; post: Post }\n /** ref is the commented item. */\n | { type: \"comment.created\"; ref: RefBody; comment: Comment }\n | { type: \"comment.updated\"; comment: Comment }\n | { type: \"comment.deleted\"; id: string }\n | { type: \"comment.restored\"; id: string }\n | { type: \"comment.reacted\"; id: string; counts: ReactionCounts }\n | { type: \"reaction.changed\"; ref: RefBody; counts: ReactionCounts }\n | { type: \"favorite.changed\"; ref: RefBody; favorited: boolean; count: number }\n /** Created, updated, voted or answered: the poll as the server now answers it. */\n | { type: \"poll.created\"; poll: Poll }\n | { type: \"poll.updated\"; poll: Poll }\n /** An option or image changed: refetch the poll. */\n | { type: \"poll.changed\"; id: string }\n | { type: \"poll.deleted\"; id: string }\n | { type: \"ban.saved\"; scope: BanScope; ban: CommentBan }\n | { type: \"ban.lifted\"; scope: BanScope; user: string }\n | { type: \"moderation.resolved\"; kind: HeldKind; id: string; decision: Decision }\n | { type: \"taxonomy.changed\" };\n"],"mappings":";AAMA,SAAgB,EAAa,GAAmC;CAC9D,OAAO,MAAU,IAAI,SAAS;AAChC"}
@@ -102,11 +102,11 @@ var e = {
102
102
  },
103
103
  unauthorized: {
104
104
  status: 401,
105
- meaning: "The route needs a signed-in actor."
105
+ meaning: "The request needs a valid credential: it has none where the route needs one, or one that is expired, revoked or invalid. WWW-Authenticate says which."
106
106
  },
107
107
  unavailable: {
108
108
  status: 503,
109
- meaning: "Media storage cannot be reached, or a commit's recovery is pending; retry."
109
+ meaning: "The host's auth or media storage cannot be reached, or a commit's recovery is pending; retry."
110
110
  },
111
111
  upgrade_required: {
112
112
  status: 503,
@@ -1 +1 @@
1
- {"version":3,"file":"error-codes.js","names":[],"sources":["../../../src/client/generated/error-codes.ts"],"sourcesContent":["// Generated by `go generate ./internal/contract` from ContentKit's error-code registry. Do not edit.\n\nexport type ErrorCode =\n | \"animation_not_allowed\"\n | \"animation_too_long\"\n | \"animation_unsupported\"\n | \"checksum_mismatch\"\n | \"comment_banned\"\n | \"comment_too_long\"\n | \"conflict\"\n | \"forbidden\"\n | \"gone\"\n | \"image_too_large\"\n | \"image_too_small\"\n | \"image_unreadable\"\n | \"incomplete\"\n | \"internal_error\"\n | \"invalid_request\"\n | \"moderation_rejected\"\n | \"not_configured\"\n | \"not_found\"\n | \"not_uploaded\"\n | \"quota_exceeded\"\n | \"rate_limited\"\n | \"tenant_mismatch\"\n | \"too_large\"\n | \"too_many_files\"\n | \"type_not_allowed\"\n | \"unauthorized\"\n | \"unavailable\"\n | \"upgrade_required\"\n | \"video_aspect_unsupported\"\n | \"video_over_budget\"\n | \"video_too_large\"\n | \"video_too_long\"\n;\n\n// Each code's HTTP status and meaning. A message on the wire is for people;\n// classify by code.\nexport const CONTENTKIT_ERROR_CODES: Readonly<Record<ErrorCode, { status: number; meaning: string }>> = {\n animation_not_allowed: { status: 422, meaning: \"An animated image where the upload path refuses animation.\" },\n animation_too_long: { status: 422, meaning: \"The animation has more frames or seconds than the processor allows.\" },\n animation_unsupported: { status: 415, meaning: \"An AVIF or HEIF image sequence, which is decoded as one frame.\" },\n checksum_mismatch: { status: 422, meaning: \"The stored bytes differ from the declared SHA-256.\" },\n comment_banned: { status: 403, meaning: \"The actor is banned from commenting on this target; `ban` says the scope, the reason and when it ends.\" },\n comment_too_long: { status: 422, meaning: \"The comment is longer than the site allows; `details.max` is the limit in characters.\" },\n conflict: { status: 409, meaning: \"A concurrent change, a path or slug already taken, or an operation id already used for another commit.\" },\n forbidden: { status: 403, meaning: \"The actor may not do this.\" },\n gone: { status: 410, meaning: \"The content was removed.\" },\n image_too_large: { status: 413, meaning: \"The image has more pixels or frames than the processor decodes.\" },\n image_too_small: { status: 422, meaning: \"The edited image is narrower than its public preset's minimum width.\" },\n image_unreadable: { status: 422, meaning: \"The file is not a decodable image of its declared type.\" },\n incomplete: { status: 409, meaning: \"A multipart upload is missing parts.\" },\n internal_error: { status: 500, meaning: \"A server fault; the cause is in the server log.\" },\n invalid_request: { status: 400, meaning: \"The request is malformed: its body, a query parameter or a path value.\" },\n moderation_rejected: { status: 422, meaning: \"The moderator refused the text; `error` says why.\" },\n not_configured: { status: 501, meaning: \"The capability needs a port the host did not configure; retrying does not help.\" },\n not_found: { status: 404, meaning: \"Nothing by that name, or nothing the actor may see.\" },\n not_uploaded: { status: 409, meaning: \"A commit names an upload that has not landed; `blobs` lists the ones to upload again.\" },\n quota_exceeded: { status: 413, meaning: \"The uploader's storage quota is used up.\" },\n rate_limited: { status: 429, meaning: \"Too many requests; retry after `retry_after` seconds (also the Retry-After header).\" },\n tenant_mismatch: { status: 500, meaning: \"A host port answered with another tenant's data: a configuration fault.\" },\n too_large: { status: 413, meaning: \"The file is larger than its upload path allows; `details` has `size` and `max_bytes`.\" },\n too_many_files: { status: 409, meaning: \"The commit would put more files in an upload path, or the item, than allowed; `details.max` is the limit.\" },\n type_not_allowed: { status: 415, meaning: \"The file type is not allowed in the upload path; `details.allowed` lists the types.\" },\n unauthorized: { status: 401, meaning: \"The route needs a signed-in actor.\" },\n unavailable: { status: 503, meaning: \"Media storage cannot be reached, or a commit's recovery is pending; retry.\" },\n upgrade_required: { status: 503, meaning: \"The media item was stored before v0.68 and is served once the host's media upgrade (contentkit_media_upgrade) has converted it.\" },\n video_aspect_unsupported: { status: 422, meaning: \"The video's display aspect is outside what its upload path accepts; `details` has `min_aspect` and `max_aspect` (width/height).\" },\n video_over_budget: { status: 422, meaning: \"The planned encode costs more than its upload path allows, or the source averages under a frame a second.\" },\n video_too_large: { status: 422, meaning: \"The video's frames are larger than its upload path allows.\" },\n video_too_long: { status: 422, meaning: \"The video or audio runs longer than its upload path allows.\" },\n};\n"],"mappings":";AAuCA,IAAa,IAA2F;CACtG,uBAAuB;EAAE,QAAQ;EAAK,SAAS;CAA6D;CAC5G,oBAAoB;EAAE,QAAQ;EAAK,SAAS;CAAsE;CAClH,uBAAuB;EAAE,QAAQ;EAAK,SAAS;CAAiE;CAChH,mBAAmB;EAAE,QAAQ;EAAK,SAAS;CAAqD;CAChG,gBAAgB;EAAE,QAAQ;EAAK,SAAS;CAAyG;CACjJ,kBAAkB;EAAE,QAAQ;EAAK,SAAS;CAAwF;CAClI,UAAU;EAAE,QAAQ;EAAK,SAAS;CAAyG;CAC3I,WAAW;EAAE,QAAQ;EAAK,SAAS;CAA6B;CAChE,MAAM;EAAE,QAAQ;EAAK,SAAS;CAA2B;CACzD,iBAAiB;EAAE,QAAQ;EAAK,SAAS;CAAkE;CAC3G,iBAAiB;EAAE,QAAQ;EAAK,SAAS;CAAuE;CAChH,kBAAkB;EAAE,QAAQ;EAAK,SAAS;CAA0D;CACpG,YAAY;EAAE,QAAQ;EAAK,SAAS;CAAuC;CAC3E,gBAAgB;EAAE,QAAQ;EAAK,SAAS;CAAkD;CAC1F,iBAAiB;EAAE,QAAQ;EAAK,SAAS;CAAyE;CAClH,qBAAqB;EAAE,QAAQ;EAAK,SAAS;CAAoD;CACjG,gBAAgB;EAAE,QAAQ;EAAK,SAAS;CAAkF;CAC1H,WAAW;EAAE,QAAQ;EAAK,SAAS;CAAsD;CACzF,cAAc;EAAE,QAAQ;EAAK,SAAS;CAAwF;CAC9H,gBAAgB;EAAE,QAAQ;EAAK,SAAS;CAA2C;CACnF,cAAc;EAAE,QAAQ;EAAK,SAAS;CAAsF;CAC5H,iBAAiB;EAAE,QAAQ;EAAK,SAAS;CAA0E;CACnH,WAAW;EAAE,QAAQ;EAAK,SAAS;CAAwF;CAC3H,gBAAgB;EAAE,QAAQ;EAAK,SAAS;CAA4G;CACpJ,kBAAkB;EAAE,QAAQ;EAAK,SAAS;CAAsF;CAChI,cAAc;EAAE,QAAQ;EAAK,SAAS;CAAqC;CAC3E,aAAa;EAAE,QAAQ;EAAK,SAAS;CAA6E;CAClH,kBAAkB;EAAE,QAAQ;EAAK,SAAS;CAAkI;CAC5K,0BAA0B;EAAE,QAAQ;EAAK,SAAS;CAAkI;CACpL,mBAAmB;EAAE,QAAQ;EAAK,SAAS;CAA4G;CACvJ,iBAAiB;EAAE,QAAQ;EAAK,SAAS;CAA6D;CACtG,gBAAgB;EAAE,QAAQ;EAAK,SAAS;CAA8D;AACxG"}
1
+ {"version":3,"file":"error-codes.js","names":[],"sources":["../../../src/client/generated/error-codes.ts"],"sourcesContent":["// Generated by `go generate ./internal/contract` from ContentKit's error-code registry. Do not edit.\n\nexport type ErrorCode =\n | \"animation_not_allowed\"\n | \"animation_too_long\"\n | \"animation_unsupported\"\n | \"checksum_mismatch\"\n | \"comment_banned\"\n | \"comment_too_long\"\n | \"conflict\"\n | \"forbidden\"\n | \"gone\"\n | \"image_too_large\"\n | \"image_too_small\"\n | \"image_unreadable\"\n | \"incomplete\"\n | \"internal_error\"\n | \"invalid_request\"\n | \"moderation_rejected\"\n | \"not_configured\"\n | \"not_found\"\n | \"not_uploaded\"\n | \"quota_exceeded\"\n | \"rate_limited\"\n | \"tenant_mismatch\"\n | \"too_large\"\n | \"too_many_files\"\n | \"type_not_allowed\"\n | \"unauthorized\"\n | \"unavailable\"\n | \"upgrade_required\"\n | \"video_aspect_unsupported\"\n | \"video_over_budget\"\n | \"video_too_large\"\n | \"video_too_long\"\n;\n\n// Each code's HTTP status and meaning. A message on the wire is for people;\n// classify by code.\nexport const CONTENTKIT_ERROR_CODES: Readonly<Record<ErrorCode, { status: number; meaning: string }>> = {\n animation_not_allowed: { status: 422, meaning: \"An animated image where the upload path refuses animation.\" },\n animation_too_long: { status: 422, meaning: \"The animation has more frames or seconds than the processor allows.\" },\n animation_unsupported: { status: 415, meaning: \"An AVIF or HEIF image sequence, which is decoded as one frame.\" },\n checksum_mismatch: { status: 422, meaning: \"The stored bytes differ from the declared SHA-256.\" },\n comment_banned: { status: 403, meaning: \"The actor is banned from commenting on this target; `ban` says the scope, the reason and when it ends.\" },\n comment_too_long: { status: 422, meaning: \"The comment is longer than the site allows; `details.max` is the limit in characters.\" },\n conflict: { status: 409, meaning: \"A concurrent change, a path or slug already taken, or an operation id already used for another commit.\" },\n forbidden: { status: 403, meaning: \"The actor may not do this.\" },\n gone: { status: 410, meaning: \"The content was removed.\" },\n image_too_large: { status: 413, meaning: \"The image has more pixels or frames than the processor decodes.\" },\n image_too_small: { status: 422, meaning: \"The edited image is narrower than its public preset's minimum width.\" },\n image_unreadable: { status: 422, meaning: \"The file is not a decodable image of its declared type.\" },\n incomplete: { status: 409, meaning: \"A multipart upload is missing parts.\" },\n internal_error: { status: 500, meaning: \"A server fault; the cause is in the server log.\" },\n invalid_request: { status: 400, meaning: \"The request is malformed: its body, a query parameter or a path value.\" },\n moderation_rejected: { status: 422, meaning: \"The moderator refused the text; `error` says why.\" },\n not_configured: { status: 501, meaning: \"The capability needs a port the host did not configure; retrying does not help.\" },\n not_found: { status: 404, meaning: \"Nothing by that name, or nothing the actor may see.\" },\n not_uploaded: { status: 409, meaning: \"A commit names an upload that has not landed; `blobs` lists the ones to upload again.\" },\n quota_exceeded: { status: 413, meaning: \"The uploader's storage quota is used up.\" },\n rate_limited: { status: 429, meaning: \"Too many requests; retry after `retry_after` seconds (also the Retry-After header).\" },\n tenant_mismatch: { status: 500, meaning: \"A host port answered with another tenant's data: a configuration fault.\" },\n too_large: { status: 413, meaning: \"The file is larger than its upload path allows; `details` has `size` and `max_bytes`.\" },\n too_many_files: { status: 409, meaning: \"The commit would put more files in an upload path, or the item, than allowed; `details.max` is the limit.\" },\n type_not_allowed: { status: 415, meaning: \"The file type is not allowed in the upload path; `details.allowed` lists the types.\" },\n unauthorized: { status: 401, meaning: \"The request needs a valid credential: it has none where the route needs one, or one that is expired, revoked or invalid. WWW-Authenticate says which.\" },\n unavailable: { status: 503, meaning: \"The host's auth or media storage cannot be reached, or a commit's recovery is pending; retry.\" },\n upgrade_required: { status: 503, meaning: \"The media item was stored before v0.68 and is served once the host's media upgrade (contentkit_media_upgrade) has converted it.\" },\n video_aspect_unsupported: { status: 422, meaning: \"The video's display aspect is outside what its upload path accepts; `details` has `min_aspect` and `max_aspect` (width/height).\" },\n video_over_budget: { status: 422, meaning: \"The planned encode costs more than its upload path allows, or the source averages under a frame a second.\" },\n video_too_large: { status: 422, meaning: \"The video's frames are larger than its upload path allows.\" },\n video_too_long: { status: 422, meaning: \"The video or audio runs longer than its upload path allows.\" },\n};\n"],"mappings":";AAuCA,IAAa,IAA2F;CACtG,uBAAuB;EAAE,QAAQ;EAAK,SAAS;CAA6D;CAC5G,oBAAoB;EAAE,QAAQ;EAAK,SAAS;CAAsE;CAClH,uBAAuB;EAAE,QAAQ;EAAK,SAAS;CAAiE;CAChH,mBAAmB;EAAE,QAAQ;EAAK,SAAS;CAAqD;CAChG,gBAAgB;EAAE,QAAQ;EAAK,SAAS;CAAyG;CACjJ,kBAAkB;EAAE,QAAQ;EAAK,SAAS;CAAwF;CAClI,UAAU;EAAE,QAAQ;EAAK,SAAS;CAAyG;CAC3I,WAAW;EAAE,QAAQ;EAAK,SAAS;CAA6B;CAChE,MAAM;EAAE,QAAQ;EAAK,SAAS;CAA2B;CACzD,iBAAiB;EAAE,QAAQ;EAAK,SAAS;CAAkE;CAC3G,iBAAiB;EAAE,QAAQ;EAAK,SAAS;CAAuE;CAChH,kBAAkB;EAAE,QAAQ;EAAK,SAAS;CAA0D;CACpG,YAAY;EAAE,QAAQ;EAAK,SAAS;CAAuC;CAC3E,gBAAgB;EAAE,QAAQ;EAAK,SAAS;CAAkD;CAC1F,iBAAiB;EAAE,QAAQ;EAAK,SAAS;CAAyE;CAClH,qBAAqB;EAAE,QAAQ;EAAK,SAAS;CAAoD;CACjG,gBAAgB;EAAE,QAAQ;EAAK,SAAS;CAAkF;CAC1H,WAAW;EAAE,QAAQ;EAAK,SAAS;CAAsD;CACzF,cAAc;EAAE,QAAQ;EAAK,SAAS;CAAwF;CAC9H,gBAAgB;EAAE,QAAQ;EAAK,SAAS;CAA2C;CACnF,cAAc;EAAE,QAAQ;EAAK,SAAS;CAAsF;CAC5H,iBAAiB;EAAE,QAAQ;EAAK,SAAS;CAA0E;CACnH,WAAW;EAAE,QAAQ;EAAK,SAAS;CAAwF;CAC3H,gBAAgB;EAAE,QAAQ;EAAK,SAAS;CAA4G;CACpJ,kBAAkB;EAAE,QAAQ;EAAK,SAAS;CAAsF;CAChI,cAAc;EAAE,QAAQ;EAAK,SAAS;CAAwJ;CAC9L,aAAa;EAAE,QAAQ;EAAK,SAAS;CAAgG;CACrI,kBAAkB;EAAE,QAAQ;EAAK,SAAS;CAAkI;CAC5K,0BAA0B;EAAE,QAAQ;EAAK,SAAS;CAAkI;CACpL,mBAAmB;EAAE,QAAQ;EAAK,SAAS;CAA4G;CACvJ,iBAAiB;EAAE,QAAQ;EAAK,SAAS;CAA6D;CACtG,gBAAgB;EAAE,QAAQ;EAAK,SAAS;CAA8D;AACxG"}