@geekity/cms 0.13.0 → 0.15.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 (184) hide show
  1. package/README.md +181 -57
  2. package/admin/pages/documents/editor.njk +82 -0
  3. package/admin/pages/media/library.njk +4 -3
  4. package/admin/pages/settings/general.njk +51 -6
  5. package/admin/static/admin.css +33 -0
  6. package/dist/admin/documents.d.ts +5 -0
  7. package/dist/admin/documents.d.ts.map +1 -1
  8. package/dist/admin/documents.js +63 -6
  9. package/dist/admin/documents.js.map +1 -1
  10. package/dist/admin/enclosure-field.d.ts +91 -0
  11. package/dist/admin/enclosure-field.d.ts.map +1 -0
  12. package/dist/admin/enclosure-field.js +282 -0
  13. package/dist/admin/enclosure-field.js.map +1 -0
  14. package/dist/admin/federation.js +2 -2
  15. package/dist/admin/federation.js.map +1 -1
  16. package/dist/admin/headers.d.ts +4 -1
  17. package/dist/admin/headers.d.ts.map +1 -1
  18. package/dist/admin/headers.js +9 -4
  19. package/dist/admin/headers.js.map +1 -1
  20. package/dist/admin/media.d.ts +7 -2
  21. package/dist/admin/media.d.ts.map +1 -1
  22. package/dist/admin/media.js +4 -2
  23. package/dist/admin/media.js.map +1 -1
  24. package/dist/admin/settings-general.d.ts.map +1 -1
  25. package/dist/admin/settings-general.js +63 -4
  26. package/dist/admin/settings-general.js.map +1 -1
  27. package/dist/admin/settings-page.d.ts.map +1 -1
  28. package/dist/admin/settings-page.js +1 -2
  29. package/dist/admin/settings-page.js.map +1 -1
  30. package/dist/admin/settings.d.ts +37 -9
  31. package/dist/admin/settings.d.ts.map +1 -1
  32. package/dist/admin/settings.js +93 -11
  33. package/dist/admin/settings.js.map +1 -1
  34. package/dist/admin/uploads.d.ts +7 -2
  35. package/dist/admin/uploads.d.ts.map +1 -1
  36. package/dist/admin/uploads.js +23 -5
  37. package/dist/admin/uploads.js.map +1 -1
  38. package/dist/config.d.ts +21 -3
  39. package/dist/config.d.ts.map +1 -1
  40. package/dist/config.js +11 -7
  41. package/dist/config.js.map +1 -1
  42. package/dist/content/enclosure.d.ts +69 -0
  43. package/dist/content/enclosure.d.ts.map +1 -0
  44. package/dist/content/enclosure.js +165 -0
  45. package/dist/content/enclosure.js.map +1 -0
  46. package/dist/content/index.d.ts +3 -1
  47. package/dist/content/index.d.ts.map +1 -1
  48. package/dist/content/index.js +1 -0
  49. package/dist/content/index.js.map +1 -1
  50. package/dist/content/media.d.ts +15 -2
  51. package/dist/content/media.d.ts.map +1 -1
  52. package/dist/content/media.js +92 -9
  53. package/dist/content/media.js.map +1 -1
  54. package/dist/content/pinned.d.ts +26 -0
  55. package/dist/content/pinned.d.ts.map +1 -0
  56. package/dist/content/pinned.js +42 -0
  57. package/dist/content/pinned.js.map +1 -0
  58. package/dist/content/store.d.ts +7 -0
  59. package/dist/content/store.d.ts.map +1 -1
  60. package/dist/content/store.js +20 -0
  61. package/dist/content/store.js.map +1 -1
  62. package/dist/env.d.ts +7 -0
  63. package/dist/env.d.ts.map +1 -1
  64. package/dist/federation/actor.d.ts +15 -0
  65. package/dist/federation/actor.d.ts.map +1 -1
  66. package/dist/federation/actor.js +94 -3
  67. package/dist/federation/actor.js.map +1 -1
  68. package/dist/federation/article.d.ts +13 -1
  69. package/dist/federation/article.d.ts.map +1 -1
  70. package/dist/federation/article.js +62 -7
  71. package/dist/federation/article.js.map +1 -1
  72. package/dist/federation/delivery.d.ts.map +1 -1
  73. package/dist/federation/delivery.js +36 -4
  74. package/dist/federation/delivery.js.map +1 -1
  75. package/dist/federation/federation.d.ts.map +1 -1
  76. package/dist/federation/federation.js +24 -2
  77. package/dist/federation/federation.js.map +1 -1
  78. package/dist/federation/mount.js +2 -2
  79. package/dist/federation/mount.js.map +1 -1
  80. package/dist/federation/paths.d.ts +30 -0
  81. package/dist/federation/paths.d.ts.map +1 -1
  82. package/dist/federation/paths.js +36 -0
  83. package/dist/federation/paths.js.map +1 -1
  84. package/dist/images/icons.d.ts +5 -0
  85. package/dist/images/icons.d.ts.map +1 -1
  86. package/dist/images/icons.js +13 -8
  87. package/dist/images/icons.js.map +1 -1
  88. package/dist/images/paths.d.ts +1 -1
  89. package/dist/images/paths.d.ts.map +1 -1
  90. package/dist/images/paths.js.map +1 -1
  91. package/dist/images/variants.d.ts +11 -0
  92. package/dist/images/variants.d.ts.map +1 -1
  93. package/dist/images/variants.js +21 -0
  94. package/dist/images/variants.js.map +1 -1
  95. package/dist/index.d.ts +5 -5
  96. package/dist/index.d.ts.map +1 -1
  97. package/dist/index.js +7 -3
  98. package/dist/index.js.map +1 -1
  99. package/dist/indieauth/identity.d.ts +3 -3
  100. package/dist/indieauth/identity.d.ts.map +1 -1
  101. package/dist/indieauth/identity.js +6 -7
  102. package/dist/indieauth/identity.js.map +1 -1
  103. package/dist/web/admin-bar.d.ts +3 -1
  104. package/dist/web/admin-bar.d.ts.map +1 -1
  105. package/dist/web/admin-bar.js +5 -1
  106. package/dist/web/admin-bar.js.map +1 -1
  107. package/dist/web/assets.d.ts +14 -0
  108. package/dist/web/assets.d.ts.map +1 -1
  109. package/dist/web/assets.js +56 -1
  110. package/dist/web/assets.js.map +1 -1
  111. package/dist/web/authors.d.ts +19 -0
  112. package/dist/web/authors.d.ts.map +1 -1
  113. package/dist/web/authors.js +25 -0
  114. package/dist/web/authors.js.map +1 -1
  115. package/dist/web/context.d.ts +26 -0
  116. package/dist/web/context.d.ts.map +1 -1
  117. package/dist/web/context.js +15 -0
  118. package/dist/web/context.js.map +1 -1
  119. package/dist/web/feed-atom.d.ts.map +1 -1
  120. package/dist/web/feed-atom.js +22 -1
  121. package/dist/web/feed-atom.js.map +1 -1
  122. package/dist/web/feed-item.d.ts +34 -6
  123. package/dist/web/feed-item.d.ts.map +1 -1
  124. package/dist/web/feed-item.js +31 -7
  125. package/dist/web/feed-item.js.map +1 -1
  126. package/dist/web/feed-json.d.ts +15 -2
  127. package/dist/web/feed-json.d.ts.map +1 -1
  128. package/dist/web/feed-json.js +17 -1
  129. package/dist/web/feed-json.js.map +1 -1
  130. package/dist/web/feed-rss.d.ts.map +1 -1
  131. package/dist/web/feed-rss.js +60 -3
  132. package/dist/web/feed-rss.js.map +1 -1
  133. package/dist/web/feed-source.d.ts +3 -0
  134. package/dist/web/feed-source.d.ts.map +1 -1
  135. package/dist/web/feed-source.js.map +1 -1
  136. package/dist/web/feed-xml.d.ts +7 -1
  137. package/dist/web/feed-xml.d.ts.map +1 -1
  138. package/dist/web/feed-xml.js +8 -2
  139. package/dist/web/feed-xml.js.map +1 -1
  140. package/dist/web/feeds.d.ts +1 -1
  141. package/dist/web/feeds.d.ts.map +1 -1
  142. package/dist/web/feeds.js +13 -2
  143. package/dist/web/feeds.js.map +1 -1
  144. package/dist/web/index.d.ts +5 -2
  145. package/dist/web/index.d.ts.map +1 -1
  146. package/dist/web/index.js +3 -1
  147. package/dist/web/index.js.map +1 -1
  148. package/dist/web/license.d.ts +94 -0
  149. package/dist/web/license.d.ts.map +1 -0
  150. package/dist/web/license.js +116 -0
  151. package/dist/web/license.js.map +1 -0
  152. package/dist/web/oembed.d.ts +104 -0
  153. package/dist/web/oembed.d.ts.map +1 -0
  154. package/dist/web/oembed.js +289 -0
  155. package/dist/web/oembed.js.map +1 -0
  156. package/dist/web/opensearch.d.ts +35 -0
  157. package/dist/web/opensearch.d.ts.map +1 -0
  158. package/dist/web/opensearch.js +65 -0
  159. package/dist/web/opensearch.js.map +1 -0
  160. package/dist/web/render.d.ts.map +1 -1
  161. package/dist/web/render.js +16 -8
  162. package/dist/web/render.js.map +1 -1
  163. package/dist/web/routes.d.ts.map +1 -1
  164. package/dist/web/routes.js +82 -6
  165. package/dist/web/routes.js.map +1 -1
  166. package/dist/web/share-image.d.ts +7 -7
  167. package/dist/web/share-image.d.ts.map +1 -1
  168. package/dist/web/share-image.js +8 -7
  169. package/dist/web/share-image.js.map +1 -1
  170. package/dist/web/templates.d.ts.map +1 -1
  171. package/dist/web/templates.js +5 -0
  172. package/dist/web/templates.js.map +1 -1
  173. package/package.json +2 -1
  174. package/templates/site/content/_data/site.json +0 -1
  175. package/themes/default/README.md +142 -48
  176. package/themes/default/layouts/base.njk +25 -6
  177. package/themes/default/layouts/front-page.njk +1 -0
  178. package/themes/default/layouts/home.njk +1 -0
  179. package/themes/default/layouts/post.njk +4 -0
  180. package/themes/default/partials/bio.njk +7 -0
  181. package/themes/default/partials/jsonld.njk +30 -7
  182. package/themes/default/partials/recording.njk +41 -0
  183. package/themes/default/src/style.css +16 -0
  184. package/themes/default/static/style.css +29 -0
@@ -0,0 +1,165 @@
1
+ /**
2
+ * A post's recording: one main audio or video file, the other versions of it,
3
+ * and its transcript (TASK-213).
4
+ *
5
+ * The front matter spells it the way RSS does, so a person reading the file
6
+ * and a person reading the feed see the same names:
7
+ *
8
+ * ```yaml
9
+ * enclosure:
10
+ * url: /uploads/2026/10/episode-12.mp3
11
+ * type: audio/mpeg
12
+ * length: 23456789
13
+ * duration: 1834
14
+ * transcript:
15
+ * url: /uploads/2026/10/episode-12.vtt
16
+ * type: text/vtt
17
+ * alternates:
18
+ * - url: https://cdn.example.com/episode-12.mp4
19
+ * type: video/mp4
20
+ * title: Video
21
+ * ```
22
+ *
23
+ * It is not a key the CMS models in {@link Document}: like `license`, it stays
24
+ * in {@link Document.extra}, so an admin save and an Eleventy build both see
25
+ * exactly what the file says. {@link enclosureOf} is the one reading of it,
26
+ * and the feeds, the theme and the federation read only what it returns.
27
+ */
28
+ import { UPLOAD_ASSET_PREFIX } from '../web/assets.js';
29
+ /** The front matter key the recording is under. */
30
+ export const ENCLOSURE_FRONT_MATTER_KEY = 'enclosure';
31
+ /** The longest title an alternate version may have, as the Podcasting 2.0 namespace says. */
32
+ export const ALTERNATE_TITLE_MAX_LENGTH = 32;
33
+ /** The transcript formats the Podcasting 2.0 namespace names, and so the ones a feed can carry. */
34
+ export const TRANSCRIPT_TYPES = [
35
+ 'text/vtt',
36
+ 'application/x-subrip',
37
+ 'text/html',
38
+ 'text/plain',
39
+ ];
40
+ /**
41
+ * The recording a post's front matter describes, or `undefined` when it has
42
+ * none worth printing.
43
+ *
44
+ * A main file without a URL under `/uploads/`, a media type, or a positive
45
+ * whole number of bytes is no enclosure at all: RSS requires all three, and a
46
+ * guessed one would send a podcast app the wrong file. Anything else that is
47
+ * wrong is dropped on its own — an alternate version with no media type, a
48
+ * transcript of a format nobody plays, a title too long to show — so one bad
49
+ * line in a hand-edited file costs that line and not the episode.
50
+ */
51
+ export function enclosureOf(extra) {
52
+ const raw = record(extra[ENCLOSURE_FRONT_MATTER_KEY]);
53
+ if (raw === undefined)
54
+ return undefined;
55
+ const url = uploadUrl(raw['url']);
56
+ const type = mediaType(raw['type']);
57
+ const length = positiveInteger(raw['length']);
58
+ if (url === undefined || type === undefined || length === undefined)
59
+ return undefined;
60
+ const duration = typeof raw['duration'] === 'number' ? raw['duration'] : undefined;
61
+ return {
62
+ url,
63
+ type,
64
+ length,
65
+ ...(duration !== undefined && Number.isFinite(duration) && duration > 0 ? { duration } : {}),
66
+ ...optional('transcript', transcriptOf(raw['transcript'])),
67
+ alternates: Array.isArray(raw['alternates'])
68
+ ? raw['alternates'].flatMap((entry) => alternateOf(entry) ?? [])
69
+ : [],
70
+ };
71
+ }
72
+ /**
73
+ * Whether a media type is played by a `<video>` and federated as a `Video`,
74
+ * rather than by an `<audio>` as an `Audio`. One answer for both, so the page
75
+ * and the fediverse never disagree about what a file is.
76
+ */
77
+ export function playsAsVideo(type) {
78
+ return type.toLowerCase().startsWith('video/');
79
+ }
80
+ /** Whether a transcript is timed captions, which a player can show over the recording. */
81
+ export function isCaptions(transcript) {
82
+ return transcript.type === 'text/vtt' || transcript.type === 'application/x-subrip';
83
+ }
84
+ /** Whether a value is an absolute `http` or `https` URL. */
85
+ export function isWebUrl(value) {
86
+ try {
87
+ const url = new URL(value);
88
+ return url.protocol === 'http:' || url.protocol === 'https:';
89
+ }
90
+ catch {
91
+ return false;
92
+ }
93
+ }
94
+ /** Whether a value is a site-relative URL under `/uploads/` that cannot climb out of it. */
95
+ export function isUploadUrl(value) {
96
+ return (value.startsWith(UPLOAD_ASSET_PREFIX) &&
97
+ value.length > UPLOAD_ASSET_PREFIX.length &&
98
+ !/[?#\\]/.test(value) &&
99
+ !value.split('/').includes('..'));
100
+ }
101
+ /** Whether a value is a media type, `type/subtype`, with no parameters. */
102
+ export function isMediaType(value) {
103
+ return /^[a-z0-9][a-z0-9!#$&^_.+-]*\/[a-z0-9][a-z0-9!#$&^_.+-]*$/i.test(value);
104
+ }
105
+ function alternateOf(value) {
106
+ const raw = record(value);
107
+ if (raw === undefined)
108
+ return undefined;
109
+ const url = fileUrl(raw['url']);
110
+ const type = mediaType(raw['type']);
111
+ if (url === undefined || type === undefined)
112
+ return undefined;
113
+ const title = trimmed(raw['title']);
114
+ return {
115
+ url,
116
+ type,
117
+ ...optional('length', positiveInteger(raw['length'])),
118
+ ...(title !== undefined && title.length <= ALTERNATE_TITLE_MAX_LENGTH ? { title } : {}),
119
+ ...optional('height', positiveInteger(raw['height'])),
120
+ ...optional('lang', trimmed(raw['lang'])),
121
+ };
122
+ }
123
+ function transcriptOf(value) {
124
+ const raw = record(value);
125
+ if (raw === undefined)
126
+ return undefined;
127
+ const url = fileUrl(raw['url']);
128
+ const type = trimmed(raw['type'])?.toLowerCase();
129
+ if (url === undefined || type === undefined || !isTranscriptType(type))
130
+ return undefined;
131
+ return { url, type };
132
+ }
133
+ function isTranscriptType(value) {
134
+ return TRANSCRIPT_TYPES.includes(value);
135
+ }
136
+ function uploadUrl(value) {
137
+ const url = trimmed(value);
138
+ return url !== undefined && isUploadUrl(url) ? url : undefined;
139
+ }
140
+ function fileUrl(value) {
141
+ const url = trimmed(value);
142
+ return url !== undefined && (isUploadUrl(url) || isWebUrl(url)) ? url : undefined;
143
+ }
144
+ function mediaType(value) {
145
+ const type = trimmed(value)?.toLowerCase();
146
+ return type !== undefined && isMediaType(type) ? type : undefined;
147
+ }
148
+ function positiveInteger(value) {
149
+ return typeof value === 'number' && Number.isSafeInteger(value) && value > 0 ? value : undefined;
150
+ }
151
+ function trimmed(value) {
152
+ if (typeof value !== 'string')
153
+ return undefined;
154
+ const text = value.trim();
155
+ return text === '' ? undefined : text;
156
+ }
157
+ function record(value) {
158
+ return typeof value === 'object' && value !== null && !Array.isArray(value)
159
+ ? value
160
+ : undefined;
161
+ }
162
+ function optional(key, value) {
163
+ return value === undefined ? {} : { [key]: value };
164
+ }
165
+ //# sourceMappingURL=enclosure.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enclosure.js","sourceRoot":"","sources":["../../src/content/enclosure.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,10 @@
1
1
  export { KNOWN_FRONT_MATTER_KEYS } from './document.ts';
2
2
  export type { ActivityPubMetadata, Document, DocumentContent, DocumentType } from './document.ts';
3
+ export { ENCLOSURE_FRONT_MATTER_KEY, enclosureOf, isCaptions, playsAsVideo, TRANSCRIPT_TYPES, } from './enclosure.ts';
4
+ export type { AlternateEnclosure, Enclosure, Transcript, TranscriptType } from './enclosure.ts';
3
5
  export { renderMarkdown } from './markdown.ts';
4
6
  export { KNOWN_UPLOAD_TYPES, matchesSignature, normalizeUploadType, UPLOAD_MEDIA_TYPES, } from './media.ts';
5
- export type { UploadMediaType, UploadSignature } from './media.ts';
7
+ export type { UploadKind, UploadMediaType, UploadSignature } from './media.ts';
6
8
  export { hashDocument, parseDocument, typeForPath } from './parser.ts';
7
9
  export type { ParseDocumentOptions } from './parser.ts';
8
10
  export { discoverPostType, postLabel, postTypeOf } from './post-type.ts';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/content/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AACxD,YAAY,EAAE,mBAAmB,EAAE,QAAQ,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClG,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACvE,YAAY,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACzE,YAAY,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACpE,YAAY,EAAE,oBAAoB,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAC5F,OAAO,EACL,UAAU,EACV,mBAAmB,EACnB,gBAAgB,EAChB,UAAU,EACV,aAAa,EACb,YAAY,GACb,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACtD,YAAY,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AACvD,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,aAAa,EACb,iBAAiB,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,eAAe,EACf,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,YAAY,GACb,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,sBAAsB,EACtB,cAAc,EACd,SAAS,EACT,cAAc,EACd,iBAAiB,GAClB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,aAAa,EACb,WAAW,EACX,uBAAuB,EACvB,aAAa,EACb,gBAAgB,EAChB,WAAW,EACX,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,aAAa,EACb,KAAK,EACL,aAAa,EACb,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,WAAW,EACX,uBAAuB,EACvB,SAAS,EACT,QAAQ,GACT,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAClG,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AACnE,YAAY,EACV,YAAY,EACZ,oBAAoB,EACpB,eAAe,EACf,aAAa,EACb,WAAW,EACX,wBAAwB,EACxB,cAAc,EACd,kBAAkB,EAClB,UAAU,EACV,UAAU,GACX,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/content/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AACxD,YAAY,EAAE,mBAAmB,EAAE,QAAQ,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClG,OAAO,EACL,0BAA0B,EAC1B,WAAW,EACX,UAAU,EACV,YAAY,EACZ,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,kBAAkB,EAAE,SAAS,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChG,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC/E,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACvE,YAAY,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACzE,YAAY,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACpE,YAAY,EAAE,oBAAoB,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAC5F,OAAO,EACL,UAAU,EACV,mBAAmB,EACnB,gBAAgB,EAChB,UAAU,EACV,aAAa,EACb,YAAY,GACb,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACtD,YAAY,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AACvD,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,aAAa,EACb,iBAAiB,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,eAAe,EACf,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,YAAY,GACb,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,sBAAsB,EACtB,cAAc,EACd,SAAS,EACT,cAAc,EACd,iBAAiB,GAClB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,aAAa,EACb,WAAW,EACX,uBAAuB,EACvB,aAAa,EACb,gBAAgB,EAChB,WAAW,EACX,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,aAAa,EACb,KAAK,EACL,aAAa,EACb,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,WAAW,EACX,uBAAuB,EACvB,SAAS,EACT,QAAQ,GACT,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAClG,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AACnE,YAAY,EACV,YAAY,EACZ,oBAAoB,EACpB,eAAe,EACf,aAAa,EACb,WAAW,EACX,wBAAwB,EACxB,cAAc,EACd,kBAAkB,EAClB,UAAU,EACV,UAAU,GACX,MAAM,WAAW,CAAC"}
@@ -1,4 +1,5 @@
1
1
  export { KNOWN_FRONT_MATTER_KEYS } from './document.js';
2
+ export { ENCLOSURE_FRONT_MATTER_KEY, enclosureOf, isCaptions, playsAsVideo, TRANSCRIPT_TYPES, } from './enclosure.js';
2
3
  export { renderMarkdown } from './markdown.js';
3
4
  export { KNOWN_UPLOAD_TYPES, matchesSignature, normalizeUploadType, UPLOAD_MEDIA_TYPES, } from './media.js';
4
5
  export { hashDocument, parseDocument, typeForPath } from './parser.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/content/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAExD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEvE,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEzE,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAEpE,OAAO,EACL,UAAU,EACV,mBAAmB,EACnB,gBAAgB,EAChB,UAAU,EACV,aAAa,EACb,YAAY,GACb,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEtD,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,aAAa,EACb,iBAAiB,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,eAAe,EACf,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,YAAY,GACb,MAAM,eAAe,CAAC;AAQvB,OAAO,EACL,aAAa,EACb,WAAW,EACX,uBAAuB,EACvB,aAAa,EACb,gBAAgB,EAChB,WAAW,EACX,eAAe,GAChB,MAAM,YAAY,CAAC;AAapB,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAClG,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/content/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAExD,OAAO,EACL,0BAA0B,EAC1B,WAAW,EACX,UAAU,EACV,YAAY,EACZ,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEvE,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEzE,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAEpE,OAAO,EACL,UAAU,EACV,mBAAmB,EACnB,gBAAgB,EAChB,UAAU,EACV,aAAa,EACb,YAAY,GACb,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEtD,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,aAAa,EACb,iBAAiB,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,eAAe,EACf,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,YAAY,GACb,MAAM,eAAe,CAAC;AAQvB,OAAO,EACL,aAAa,EACb,WAAW,EACX,uBAAuB,EACvB,aAAa,EACb,gBAAgB,EAChB,WAAW,EACX,eAAe,GAChB,MAAM,YAAY,CAAC;AAapB,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAClG,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC"}
@@ -18,6 +18,13 @@ export interface UploadSignature {
18
18
  /** The bytes themselves. */
19
19
  bytes: readonly number[];
20
20
  }
21
+ /**
22
+ * What an upload is for, which decides what the CMS offers to do with it.
23
+ *
24
+ * An image is embedded and gets variants, audio and video can be a post's
25
+ * enclosure and are held to their own size limit, and a document is linked.
26
+ */
27
+ export type UploadKind = 'image' | 'audio' | 'video' | 'document';
21
28
  /** One kind of file the upload endpoint knows about. */
22
29
  export interface UploadMediaType {
23
30
  /**
@@ -26,8 +33,8 @@ export interface UploadMediaType {
26
33
  * sends no type at all, and there is then nothing to disagree with.
27
34
  */
28
35
  declared: readonly string[];
29
- /** Whether the Markdown for it is an image embed rather than a link. */
30
- image: boolean;
36
+ /** What the file is for. */
37
+ kind: UploadKind;
31
38
  /**
32
39
  * Byte patterns that identify the format, as alternatives: a file matches
33
40
  * when every part of any one of them matches. Absent for the text formats,
@@ -54,6 +61,12 @@ export declare const KNOWN_UPLOAD_TYPES: readonly string[];
54
61
  * file, so they are the same thing here.
55
62
  */
56
63
  export declare function normalizeUploadType(value: string): string;
64
+ /**
65
+ * The media type a feed or an attachment announces for this kind of file: the
66
+ * first one {@link UploadMediaType.declared} lists, which is the registered
67
+ * name where a browser may also send an older one.
68
+ */
69
+ export declare function canonicalType(media: UploadMediaType): string | undefined;
57
70
  /**
58
71
  * Whether the first bytes of a file are the ones its extension implies.
59
72
  *
@@ -1 +1 @@
1
- {"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../../src/content/media.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,6DAA6D;AAC7D,MAAM,WAAW,eAAe;IAC9B,uCAAuC;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,4BAA4B;IAC5B,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1B;AAED,wDAAwD;AACxD,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,wEAAwE;IACxE,KAAK,EAAE,OAAO,CAAC;IACf;;;;OAIG;IACH,UAAU,CAAC,EAAE,SAAS,CAAC,SAAS,eAAe,EAAE,CAAC,EAAE,GAAG,SAAS,CAAC;CAClE;AAOD;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,EAAE,WAAW,CAAC,MAAM,EAAE,eAAe,CA2FlE,CAAC;AAEH,8DAA8D;AAC9D,eAAO,MAAM,kBAAkB,EAAE,SAAS,MAAM,EAA0C,CAAC;AAE3F;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIzD;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CASnF"}
1
+ {"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../../src/content/media.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,6DAA6D;AAC7D,MAAM,WAAW,eAAe;IAC9B,uCAAuC;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,4BAA4B;IAC5B,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1B;AAED;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,UAAU,CAAC;AAElE,wDAAwD;AACxD,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,4BAA4B;IAC5B,IAAI,EAAE,UAAU,CAAC;IACjB;;;;OAIG;IACH,UAAU,CAAC,EAAE,SAAS,CAAC,SAAS,eAAe,EAAE,CAAC,EAAE,GAAG,SAAS,CAAC;CAClE;AAgBD;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,EAAE,WAAW,CAAC,MAAM,EAAE,eAAe,CA+JlE,CAAC;AAEH,8DAA8D;AAC9D,eAAO,MAAM,kBAAkB,EAAE,SAAS,MAAM,EAA0C,CAAC;AAE3F;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIzD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,GAAG,SAAS,CAExE;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CASnF"}
@@ -15,6 +15,13 @@
15
15
  function ascii(value) {
16
16
  return [...value].map((character) => character.charCodeAt(0));
17
17
  }
18
+ /**
19
+ * An ISO base media file — MP4, M4A, M4V — whatever its brand: a box length,
20
+ * then `ftyp`.
21
+ */
22
+ const ISO_BASE_MEDIA = [{ offset: 4, bytes: ascii('ftyp') }];
23
+ /** An Ogg page, whatever codec is inside it. */
24
+ const OGG = [{ offset: 0, bytes: ascii('OggS') }];
18
25
  /**
19
26
  * Every extension the upload endpoint can accept, and what it takes to be one.
20
27
  *
@@ -29,7 +36,7 @@ export const UPLOAD_MEDIA_TYPES = new Map([
29
36
  '.png',
30
37
  {
31
38
  declared: ['image/png'],
32
- image: true,
39
+ kind: 'image',
33
40
  signatures: [[{ offset: 0, bytes: [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a] }]],
34
41
  },
35
42
  ],
@@ -37,7 +44,7 @@ export const UPLOAD_MEDIA_TYPES = new Map([
37
44
  '.jpg',
38
45
  {
39
46
  declared: ['image/jpeg', 'image/jpg'],
40
- image: true,
47
+ kind: 'image',
41
48
  signatures: [[{ offset: 0, bytes: [0xff, 0xd8, 0xff] }]],
42
49
  },
43
50
  ],
@@ -45,7 +52,7 @@ export const UPLOAD_MEDIA_TYPES = new Map([
45
52
  '.jpeg',
46
53
  {
47
54
  declared: ['image/jpeg', 'image/jpg'],
48
- image: true,
55
+ kind: 'image',
49
56
  signatures: [[{ offset: 0, bytes: [0xff, 0xd8, 0xff] }]],
50
57
  },
51
58
  ],
@@ -53,7 +60,7 @@ export const UPLOAD_MEDIA_TYPES = new Map([
53
60
  '.gif',
54
61
  {
55
62
  declared: ['image/gif'],
56
- image: true,
63
+ kind: 'image',
57
64
  signatures: [
58
65
  [{ offset: 0, bytes: ascii('GIF87a') }],
59
66
  [{ offset: 0, bytes: ascii('GIF89a') }],
@@ -64,7 +71,7 @@ export const UPLOAD_MEDIA_TYPES = new Map([
64
71
  '.webp',
65
72
  {
66
73
  declared: ['image/webp'],
67
- image: true,
74
+ kind: 'image',
68
75
  // A WebP is a RIFF container whose form type is WEBP, so both halves of
69
76
  // the header have to be there.
70
77
  signatures: [
@@ -79,7 +86,7 @@ export const UPLOAD_MEDIA_TYPES = new Map([
79
86
  '.avif',
80
87
  {
81
88
  declared: ['image/avif'],
82
- image: true,
89
+ kind: 'image',
83
90
  // An ISO base media file: a length, then `ftyp`, then the brand.
84
91
  signatures: [
85
92
  [
@@ -97,19 +104,87 @@ export const UPLOAD_MEDIA_TYPES = new Map([
97
104
  '.pdf',
98
105
  {
99
106
  declared: ['application/pdf'],
100
- image: false,
107
+ kind: 'document',
101
108
  signatures: [[{ offset: 0, bytes: ascii('%PDF-') }]],
102
109
  },
103
110
  ],
104
111
  // The two text formats have no header. Nothing is sniffed, and nothing needs
105
112
  // to be: they are served as text, and `application/octet-stream` is allowed
106
113
  // because that is what a browser sends for a file type it does not know.
107
- ['.txt', { declared: ['text/plain', 'application/octet-stream'], image: false }],
114
+ ['.txt', { declared: ['text/plain', 'application/octet-stream'], kind: 'document' }],
108
115
  [
109
116
  '.md',
110
117
  {
111
118
  declared: ['text/markdown', 'text/x-markdown', 'text/plain', 'application/octet-stream'],
112
- image: false,
119
+ kind: 'document',
120
+ },
121
+ ],
122
+ // WebVTT opens with its name, after a byte order mark when the editor that
123
+ // wrote it added one. SubRip has no header, so it is taken as text like the
124
+ // two formats above.
125
+ [
126
+ '.vtt',
127
+ {
128
+ declared: ['text/vtt', 'text/plain', 'application/octet-stream'],
129
+ kind: 'document',
130
+ signatures: [
131
+ [{ offset: 0, bytes: ascii('WEBVTT') }],
132
+ [{ offset: 0, bytes: [0xef, 0xbb, 0xbf, ...ascii('WEBVTT')] }],
133
+ ],
134
+ },
135
+ ],
136
+ [
137
+ '.srt',
138
+ {
139
+ declared: ['application/x-subrip', 'text/srt', 'text/plain', 'application/octet-stream'],
140
+ kind: 'document',
141
+ },
142
+ ],
143
+ [
144
+ '.mp3',
145
+ {
146
+ declared: ['audio/mpeg', 'audio/mp3'],
147
+ kind: 'audio',
148
+ // An ID3 tag, or straight into an MPEG audio frame: eleven sync bits,
149
+ // then MPEG-1 or MPEG-2 layer III, with or without a CRC.
150
+ signatures: [
151
+ [{ offset: 0, bytes: ascii('ID3') }],
152
+ [{ offset: 0, bytes: [0xff, 0xfb] }],
153
+ [{ offset: 0, bytes: [0xff, 0xfa] }],
154
+ [{ offset: 0, bytes: [0xff, 0xf3] }],
155
+ [{ offset: 0, bytes: [0xff, 0xf2] }],
156
+ ],
157
+ },
158
+ ],
159
+ [
160
+ '.m4a',
161
+ {
162
+ declared: ['audio/mp4', 'audio/x-m4a', 'audio/m4a'],
163
+ kind: 'audio',
164
+ signatures: [ISO_BASE_MEDIA],
165
+ },
166
+ ],
167
+ [
168
+ '.aac',
169
+ {
170
+ declared: ['audio/aac', 'audio/x-aac', 'audio/aacp'],
171
+ kind: 'audio',
172
+ // A raw ADTS stream: twelve sync bits, MPEG-4 or MPEG-2, no CRC.
173
+ signatures: [[{ offset: 0, bytes: [0xff, 0xf1] }], [{ offset: 0, bytes: [0xff, 0xf9] }]],
174
+ },
175
+ ],
176
+ ['.ogg', { declared: ['audio/ogg', 'application/ogg'], kind: 'audio', signatures: [OGG] }],
177
+ ['.oga', { declared: ['audio/ogg', 'application/ogg'], kind: 'audio', signatures: [OGG] }],
178
+ ['.opus', { declared: ['audio/ogg', 'audio/opus'], kind: 'audio', signatures: [OGG] }],
179
+ ['.mp4', { declared: ['video/mp4'], kind: 'video', signatures: [ISO_BASE_MEDIA] }],
180
+ ['.m4v', { declared: ['video/mp4', 'video/x-m4v'], kind: 'video', signatures: [ISO_BASE_MEDIA] }],
181
+ [
182
+ '.webm',
183
+ {
184
+ declared: ['video/webm', 'audio/webm'],
185
+ kind: 'video',
186
+ // The EBML header every Matroska file, WebM included, opens with.
187
+ signatures: [[{ offset: 0, bytes: [0x1a, 0x45, 0xdf, 0xa3] }]],
113
188
  },
114
189
  ],
115
190
  ]);
@@ -127,6 +202,14 @@ export function normalizeUploadType(value) {
127
202
  return '';
128
203
  return trimmed.startsWith('.') ? trimmed : `.${trimmed}`;
129
204
  }
205
+ /**
206
+ * The media type a feed or an attachment announces for this kind of file: the
207
+ * first one {@link UploadMediaType.declared} lists, which is the registered
208
+ * name where a browser may also send an older one.
209
+ */
210
+ export function canonicalType(media) {
211
+ return media.declared[0];
212
+ }
130
213
  /**
131
214
  * Whether the first bytes of a file are the ones its extension implies.
132
215
  *
@@ -1 +1 @@
1
- {"version":3,"file":"media.js","sourceRoot":"","sources":["../../src/content/media.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"media.js","sourceRoot":"","sources":["../../src/content/media.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { Document } from './document.ts';
2
+ /**
3
+ * The front matter key that pins a post to the top of its author's profile
4
+ * (TASK-207): `pinned: <ISO instant>`, written when the author pinned it.
5
+ *
6
+ * An instant rather than `true`, so the order the fediverse shows pins in
7
+ * (most recently pinned first, as Mastodon does) lives in the file with the
8
+ * pin itself (decision-9) instead of in an index that a rebuild would lose.
9
+ */
10
+ export declare const PINNED_FRONT_MATTER_KEY = "pinned";
11
+ /** How many posts one author can pin, which is Mastodon's own limit. */
12
+ export declare const PINNED_POST_LIMIT = 5;
13
+ /**
14
+ * When a post was pinned, as a UTC instant, or `undefined` for one that is not.
15
+ *
16
+ * YAML hands back an unquoted timestamp as a `Date` and a quoted one as a
17
+ * string; both are read. A hand-written `pinned: true` has no moment of its
18
+ * own, so it reads as the post's date and sorts among the others by that.
19
+ */
20
+ export declare function pinnedAt(document: Document): string | undefined;
21
+ /**
22
+ * The pinned ones among some posts, most recently pinned first and no more
23
+ * than {@link PINNED_POST_LIMIT} of them: an author's featured collection.
24
+ */
25
+ export declare function featuredPosts(documents: readonly Document[]): Document[];
26
+ //# sourceMappingURL=pinned.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pinned.d.ts","sourceRoot":"","sources":["../../src/content/pinned.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAG9C;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,WAAW,CAAC;AAEhD,wEAAwE;AACxE,eAAO,MAAM,iBAAiB,IAAI,CAAC;AAEnC;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS,CAK/D;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,SAAS,QAAQ,EAAE,GAAG,QAAQ,EAAE,CASxE"}
@@ -0,0 +1,42 @@
1
+ import { toUtcInstant } from './time.js';
2
+ /**
3
+ * The front matter key that pins a post to the top of its author's profile
4
+ * (TASK-207): `pinned: <ISO instant>`, written when the author pinned it.
5
+ *
6
+ * An instant rather than `true`, so the order the fediverse shows pins in
7
+ * (most recently pinned first, as Mastodon does) lives in the file with the
8
+ * pin itself (decision-9) instead of in an index that a rebuild would lose.
9
+ */
10
+ export const PINNED_FRONT_MATTER_KEY = 'pinned';
11
+ /** How many posts one author can pin, which is Mastodon's own limit. */
12
+ export const PINNED_POST_LIMIT = 5;
13
+ /**
14
+ * When a post was pinned, as a UTC instant, or `undefined` for one that is not.
15
+ *
16
+ * YAML hands back an unquoted timestamp as a `Date` and a quoted one as a
17
+ * string; both are read. A hand-written `pinned: true` has no moment of its
18
+ * own, so it reads as the post's date and sorts among the others by that.
19
+ */
20
+ export function pinnedAt(document) {
21
+ const value = document.extra[PINNED_FRONT_MATTER_KEY];
22
+ if (value === true)
23
+ return document.date ?? '';
24
+ if (typeof value !== 'string' && !(value instanceof Date))
25
+ return undefined;
26
+ return toUtcInstant(value, 'UTC');
27
+ }
28
+ /**
29
+ * The pinned ones among some posts, most recently pinned first and no more
30
+ * than {@link PINNED_POST_LIMIT} of them: an author's featured collection.
31
+ */
32
+ export function featuredPosts(documents) {
33
+ return documents
34
+ .flatMap((document) => {
35
+ const at = pinnedAt(document);
36
+ return at === undefined ? [] : [{ document, at }];
37
+ })
38
+ .sort((a, b) => (a.at < b.at ? 1 : a.at > b.at ? -1 : 0))
39
+ .slice(0, PINNED_POST_LIMIT)
40
+ .map(({ document }) => document);
41
+ }
42
+ //# sourceMappingURL=pinned.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pinned.js","sourceRoot":"","sources":["../../src/content/pinned.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAEzC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,QAAQ,CAAC;AAEhD,wEAAwE;AACxE,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC;AAEnC;;;;;;GAMG;AACH,MAAM,UAAU,QAAQ,CAAC,QAAkB;IACzC,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC;IACtD,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,QAAQ,CAAC,IAAI,IAAI,EAAE,CAAC;IAC/C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,CAAC,KAAK,YAAY,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IAC5E,OAAO,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AACpC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,SAA8B;IAC1D,OAAO,SAAS;SACb,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE;QACpB,MAAM,EAAE,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC9B,OAAO,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;IACpD,CAAC,CAAC;SACD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;SACxD,KAAK,CAAC,CAAC,EAAE,iBAAiB,CAAC;SAC3B,GAAG,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC;AACrC,CAAC"}
@@ -166,6 +166,13 @@ export interface ContentStore {
166
166
  * of the furniture of the site rather than of anybody's body of work.
167
167
  */
168
168
  listByAuthor(names: readonly string[], options?: ListOptions): Document[];
169
+ /**
170
+ * The posts of one user's archive that carry a `pinned` key, most recently
171
+ * pinned first and capped at `PINNED_POST_LIMIT`: their featured collection
172
+ * (TASK-207). The same archive {@link ContentStore.listByAuthor} reads, so a
173
+ * draft, a trashed post or a scheduled one is never featured.
174
+ */
175
+ listPinnedByAuthor(names: readonly string[]): Document[];
169
176
  /** Everything the admin may see, trash and drafts included unless filtered. */
170
177
  listAll(options?: ListAllOptions): Document[];
171
178
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/content/store.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAuB,QAAQ,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAGjF,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,+EAA+E;AAC/E,eAAO,MAAM,eAAe,WAAW,CAAC;AAExC;;;;;GAKG;AACH,MAAM,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC;AAE/B,wDAAwD;AACxD,eAAO,MAAM,WAAW,EAAE,KAAwB,CAAC;AAEnD,wDAAwD;AACxD,MAAM,WAAW,uBAAuB;IACtC,iEAAiE;IACjE,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,GAAG,CAAC,EAAE,KAAK,GAAG,SAAS,CAAC;CACzB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC3B,wCAAwC;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;;;;;OAQG;IACH,GAAG,IAAI,IAAI,CAAC;IACZ;;;;;;OAMG;IACH,MAAM,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;IACjC,0EAA0E;IAC1E,SAAS,CAAC,SAAS,EAAE,QAAQ,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC;IAC/C,+EAA+E;IAC/E,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC9B;;;;;;;;;;;;;;;;OAgBG;IACH,KAAK,IAAI,IAAI,CAAC;IACd,+DAA+D;IAC/D,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;IAC9C,6EAA6E;IAC7E,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;IACxD;;;;;;;;;OASG;IACH,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;IAC5D;;;;;;;;OAQG;IACH,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;IAC9D;;;OAGG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;IAC9C,6DAA6D;IAC7D,SAAS,CAAC,OAAO,CAAC,EAAE,WAAW,GAAG,QAAQ,EAAE,CAAC;IAC7C;;;;;;;;;OASG;IACH,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,EAAE,CAAC;IAC5C;;;;;;;;;;;;;;OAcG;IACH,UAAU,CAAC,QAAQ,EAAE,QAAQ,GAAG,kBAAkB,CAAC;IACnD;;;;;;OAMG;IACH,OAAO,IAAI,MAAM,GAAG,SAAS,CAAC;IAC9B;;;;;;OAMG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,EAAE,CAAC;IACxC,mEAAmE;IACnE,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,QAAQ,EAAE,CAAC;IAC/D,2EAA2E;IAC3E,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,QAAQ,EAAE,CAAC;IACzE;;;;;;;;;;;;;;OAcG;IACH,YAAY,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,QAAQ,EAAE,CAAC;IAC1E,+EAA+E;IAC/E,OAAO,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,QAAQ,EAAE,CAAC;IAC9C;;;;;;;;;;OAUG;IACH,aAAa,CAAC,OAAO,CAAC,EAAE,WAAW,GAAG,QAAQ,EAAE,CAAC;IACjD,iFAAiF;IACjF,SAAS,IAAI,MAAM,EAAE,CAAC;IACtB,iDAAiD;IACjD,MAAM,IAAI,aAAa,CAAC;IACxB,2DAA2D;IAC3D,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,MAAM,CAAC;IAC5D,0EAA0E;IAC1E,QAAQ,IAAI,QAAQ,EAAE,CAAC;IACvB,0EAA0E;IAC1E,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,MAAM,CAAC;IACtE,gGAAgG;IAChG,aAAa,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAAC;IAChD,+EAA+E;IAC/E,cAAc,IAAI,aAAa,EAAE,CAAC;IAClC;;;;;;;;;;;OAWG;IACH,aAAa,CAAC,QAAQ,EAAE,YAAY,GAAG,SAAS,EAAE,CAAC;IACnD;;;;;;;;;;OAUG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,SAAS,EAAE,CAAC;IAC1D,gFAAgF;IAChF,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACnC,8CAA8C;IAC9C,KAAK,IAAI,IAAI,CAAC;CACf;AAED;;;;;;GAMG;AACH,MAAM,WAAW,kBAAkB;IACjC,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAChC,kEAAkE;IAClE,IAAI,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;CAC7B;AAED,oEAAoE;AACpE,MAAM,WAAW,SAAS;IACxB,oBAAoB;IACpB,QAAQ,EAAE,QAAQ,CAAC;IACnB;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,sCAAsC;AACtC,MAAM,WAAW,WAAW;IAC1B,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,6CAA6C;IAC7C,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B;AAED,8DAA8D;AAC9D,MAAM,WAAW,gBAAiB,SAAQ,WAAW;IACnD,oDAAoD;IACpD,IAAI,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;CACjC;AAED,0EAA0E;AAC1E,MAAM,WAAW,cAAe,SAAQ,WAAW;IACjD,oDAAoD;IACpD,IAAI,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;IAChC,4EAA4E;IAC5E,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC5B,6EAA6E;IAC7E,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAChC,+CAA+C;IAC/C,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,uDAAuD;IACvD,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/B;AAED,gDAAgD;AAChD,MAAM,WAAW,aAAa;IAC5B,8CAA8C;IAC9C,KAAK,EAAE,MAAM,CAAC;IACd,+EAA+E;IAC/E,KAAK,EAAE,MAAM,CAAC;IACd,+CAA+C;IAC/C,KAAK,EAAE,MAAM,CAAC;IACd,uCAAuC;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;IAClB,iCAAiC;IACjC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,yDAAyD;AACzD,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wEAAwE;AACxE,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,UAAU,CAAC;AAE9C,qDAAqD;AACrD,MAAM,WAAW,SAAS;IACxB,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,2EAA2E;IAC3E,SAAS,EAAE,MAAM,CAAC;IAClB,8EAA8E;IAC9E,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,SAAkB,IAAI,6BAA6B;IACnD,0CAA0C;IAC1C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,uCAAuC;IACvC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4EAA4E;IAC5E,QAAQ,CAAC,eAAe,EAAE,MAAM,GAAG,SAAS,CAAC;IAE7C,YAAY,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,SAAS,EAS/E;CACF;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAInE;AAuDD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,YAAY,CA+gB/E"}
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/content/store.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { databaseFile, openDatabase } from '../cache.js';
2
+ import { featuredPosts, PINNED_FRONT_MATTER_KEY } from './pinned.js';
2
3
  import { searchExpression, searchText, SNIPPET_CLOSE, SNIPPET_OPEN } from './search.js';
3
4
  export { DATABASE_FILE } from '../cache.js';
4
5
  /** Directory name that marks a document as thrown away but not yet deleted. */
@@ -60,6 +61,13 @@ export function dateSortKey(date) {
60
61
  const DUE_CLAUSE = '(date_sort IS NULL OR date_sort <= ?)';
61
62
  /** The reverse: a document whose date is still ahead of the clock. */
62
63
  const SCHEDULED_CLAUSE = '(date_sort IS NOT NULL AND date_sort > ?)';
64
+ /**
65
+ * A document whose front matter carries a `pinned` key that is not `false`.
66
+ * Which moment it was pinned at, and whether it is readable at all, is
67
+ * `content/pinned.ts`'s to decide; this only keeps the rest of an archive from
68
+ * being hydrated to find out.
69
+ */
70
+ const PINNED_CLAUSE = `COALESCE(json_extract(extra, '$.${PINNED_FRONT_MATTER_KEY}'), 0) NOT IN (0, '')`;
63
71
  /**
64
72
  * The clause that picks out a document some follower holds a copy of: one
65
73
  * whose `activitypub` block records when it was announced.
@@ -434,6 +442,18 @@ export function openContentStore(options) {
434
442
  return [];
435
443
  return select(["type = 'post'", 'draft = 0', 'trashed = 0', DUE_CLAUSE, authorClause(names)], [nowKey(), ...names], options);
436
444
  },
445
+ listPinnedByAuthor(names) {
446
+ if (names.length === 0)
447
+ return [];
448
+ return featuredPosts(select([
449
+ "type = 'post'",
450
+ 'draft = 0',
451
+ 'trashed = 0',
452
+ DUE_CLAUSE,
453
+ authorClause(names),
454
+ PINNED_CLAUSE,
455
+ ], [nowKey(), ...names], {}));
456
+ },
437
457
  listAll(options = {}) {
438
458
  const where = [];
439
459
  const params = [];