@geekity/cms 0.3.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 (238) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +870 -0
  3. package/admin/layouts/_flash.njk +5 -0
  4. package/admin/layouts/base.njk +27 -0
  5. package/admin/layouts/dashboard.njk +78 -0
  6. package/admin/layouts/document-conflict.njk +49 -0
  7. package/admin/layouts/document-editor.njk +167 -0
  8. package/admin/layouts/document-list.njk +92 -0
  9. package/admin/layouts/federation.njk +145 -0
  10. package/admin/layouts/login.njk +23 -0
  11. package/admin/layouts/placeholder.njk +10 -0
  12. package/admin/layouts/settings.njk +116 -0
  13. package/admin/layouts/setup.njk +27 -0
  14. package/admin/layouts/shell.njk +37 -0
  15. package/admin/layouts/users.njk +91 -0
  16. package/admin/static/admin.css +726 -0
  17. package/admin/static/editor.js +30 -0
  18. package/dist/admin/assets.d.ts +26 -0
  19. package/dist/admin/assets.d.ts.map +1 -0
  20. package/dist/admin/assets.js +38 -0
  21. package/dist/admin/assets.js.map +1 -0
  22. package/dist/admin/credentials.d.ts +28 -0
  23. package/dist/admin/credentials.d.ts.map +1 -0
  24. package/dist/admin/credentials.js +39 -0
  25. package/dist/admin/credentials.js.map +1 -0
  26. package/dist/admin/documents.d.ts +134 -0
  27. package/dist/admin/documents.d.ts.map +1 -0
  28. package/dist/admin/documents.js +613 -0
  29. package/dist/admin/documents.js.map +1 -0
  30. package/dist/admin/federation.d.ts +194 -0
  31. package/dist/admin/federation.d.ts.map +1 -0
  32. package/dist/admin/federation.js +299 -0
  33. package/dist/admin/federation.js.map +1 -0
  34. package/dist/admin/flash.d.ts +24 -0
  35. package/dist/admin/flash.d.ts.map +1 -0
  36. package/dist/admin/flash.js +31 -0
  37. package/dist/admin/flash.js.map +1 -0
  38. package/dist/admin/index.d.ts +23 -0
  39. package/dist/admin/index.d.ts.map +1 -0
  40. package/dist/admin/index.js +15 -0
  41. package/dist/admin/index.js.map +1 -0
  42. package/dist/admin/passwords.d.ts +49 -0
  43. package/dist/admin/passwords.d.ts.map +1 -0
  44. package/dist/admin/passwords.js +122 -0
  45. package/dist/admin/passwords.js.map +1 -0
  46. package/dist/admin/preview.d.ts +18 -0
  47. package/dist/admin/preview.d.ts.map +1 -0
  48. package/dist/admin/preview.js +83 -0
  49. package/dist/admin/preview.js.map +1 -0
  50. package/dist/admin/routes.d.ts +54 -0
  51. package/dist/admin/routes.d.ts.map +1 -0
  52. package/dist/admin/routes.js +383 -0
  53. package/dist/admin/routes.js.map +1 -0
  54. package/dist/admin/session.d.ts +44 -0
  55. package/dist/admin/session.d.ts.map +1 -0
  56. package/dist/admin/session.js +69 -0
  57. package/dist/admin/session.js.map +1 -0
  58. package/dist/admin/settings.d.ts +194 -0
  59. package/dist/admin/settings.d.ts.map +1 -0
  60. package/dist/admin/settings.js +470 -0
  61. package/dist/admin/settings.js.map +1 -0
  62. package/dist/admin/store.d.ts +407 -0
  63. package/dist/admin/store.d.ts.map +1 -0
  64. package/dist/admin/store.js +704 -0
  65. package/dist/admin/store.js.map +1 -0
  66. package/dist/admin/templates.d.ts +36 -0
  67. package/dist/admin/templates.d.ts.map +1 -0
  68. package/dist/admin/templates.js +43 -0
  69. package/dist/admin/templates.js.map +1 -0
  70. package/dist/admin/uploads.d.ts +96 -0
  71. package/dist/admin/uploads.d.ts.map +1 -0
  72. package/dist/admin/uploads.js +179 -0
  73. package/dist/admin/uploads.js.map +1 -0
  74. package/dist/admin/users.d.ts +95 -0
  75. package/dist/admin/users.d.ts.map +1 -0
  76. package/dist/admin/users.js +233 -0
  77. package/dist/admin/users.js.map +1 -0
  78. package/dist/cli.d.ts +85 -0
  79. package/dist/cli.d.ts.map +1 -0
  80. package/dist/cli.js +424 -0
  81. package/dist/cli.js.map +1 -0
  82. package/dist/config.d.ts +172 -0
  83. package/dist/config.d.ts.map +1 -0
  84. package/dist/config.js +184 -0
  85. package/dist/config.js.map +1 -0
  86. package/dist/content/document.d.ts +66 -0
  87. package/dist/content/document.d.ts.map +1 -0
  88. package/dist/content/document.js +13 -0
  89. package/dist/content/document.js.map +1 -0
  90. package/dist/content/index.d.ts +17 -0
  91. package/dist/content/index.d.ts.map +1 -0
  92. package/dist/content/index.js +10 -0
  93. package/dist/content/index.js.map +1 -0
  94. package/dist/content/markdown.d.ts +3 -0
  95. package/dist/content/markdown.d.ts.map +1 -0
  96. package/dist/content/markdown.js +39 -0
  97. package/dist/content/markdown.js.map +1 -0
  98. package/dist/content/media.d.ts +65 -0
  99. package/dist/content/media.d.ts.map +1 -0
  100. package/dist/content/media.js +143 -0
  101. package/dist/content/media.js.map +1 -0
  102. package/dist/content/parser.d.ts +24 -0
  103. package/dist/content/parser.d.ts.map +1 -0
  104. package/dist/content/parser.js +165 -0
  105. package/dist/content/parser.js.map +1 -0
  106. package/dist/content/save.d.ts +60 -0
  107. package/dist/content/save.d.ts.map +1 -0
  108. package/dist/content/save.js +103 -0
  109. package/dist/content/save.js.map +1 -0
  110. package/dist/content/slug.d.ts +25 -0
  111. package/dist/content/slug.d.ts.map +1 -0
  112. package/dist/content/slug.js +75 -0
  113. package/dist/content/slug.js.map +1 -0
  114. package/dist/content/store.d.ts +133 -0
  115. package/dist/content/store.d.ts.map +1 -0
  116. package/dist/content/store.js +411 -0
  117. package/dist/content/store.js.map +1 -0
  118. package/dist/content/sync.d.ts +146 -0
  119. package/dist/content/sync.d.ts.map +1 -0
  120. package/dist/content/sync.js +421 -0
  121. package/dist/content/sync.js.map +1 -0
  122. package/dist/content/writer.d.ts +37 -0
  123. package/dist/content/writer.d.ts.map +1 -0
  124. package/dist/content/writer.js +97 -0
  125. package/dist/content/writer.js.map +1 -0
  126. package/dist/env.d.ts +48 -0
  127. package/dist/env.d.ts.map +1 -0
  128. package/dist/env.js +2 -0
  129. package/dist/env.js.map +1 -0
  130. package/dist/federation/actor.d.ts +55 -0
  131. package/dist/federation/actor.d.ts.map +1 -0
  132. package/dist/federation/actor.js +101 -0
  133. package/dist/federation/actor.js.map +1 -0
  134. package/dist/federation/article.d.ts +83 -0
  135. package/dist/federation/article.d.ts.map +1 -0
  136. package/dist/federation/article.js +169 -0
  137. package/dist/federation/article.js.map +1 -0
  138. package/dist/federation/delivery.d.ts +90 -0
  139. package/dist/federation/delivery.d.ts.map +1 -0
  140. package/dist/federation/delivery.js +289 -0
  141. package/dist/federation/delivery.js.map +1 -0
  142. package/dist/federation/federation.d.ts +75 -0
  143. package/dist/federation/federation.d.ts.map +1 -0
  144. package/dist/federation/federation.js +190 -0
  145. package/dist/federation/federation.js.map +1 -0
  146. package/dist/federation/followers.d.ts +43 -0
  147. package/dist/federation/followers.d.ts.map +1 -0
  148. package/dist/federation/followers.js +69 -0
  149. package/dist/federation/followers.js.map +1 -0
  150. package/dist/federation/inbox.d.ts +68 -0
  151. package/dist/federation/inbox.d.ts.map +1 -0
  152. package/dist/federation/inbox.js +171 -0
  153. package/dist/federation/inbox.js.map +1 -0
  154. package/dist/federation/index.d.ts +14 -0
  155. package/dist/federation/index.d.ts.map +1 -0
  156. package/dist/federation/index.js +10 -0
  157. package/dist/federation/index.js.map +1 -0
  158. package/dist/federation/keys.d.ts +27 -0
  159. package/dist/federation/keys.d.ts.map +1 -0
  160. package/dist/federation/keys.js +60 -0
  161. package/dist/federation/keys.js.map +1 -0
  162. package/dist/federation/mount.d.ts +23 -0
  163. package/dist/federation/mount.d.ts.map +1 -0
  164. package/dist/federation/mount.js +71 -0
  165. package/dist/federation/mount.js.map +1 -0
  166. package/dist/federation/paths.d.ts +85 -0
  167. package/dist/federation/paths.d.ts.map +1 -0
  168. package/dist/federation/paths.js +97 -0
  169. package/dist/federation/paths.js.map +1 -0
  170. package/dist/index.d.ts +106 -0
  171. package/dist/index.d.ts.map +1 -0
  172. package/dist/index.js +148 -0
  173. package/dist/index.js.map +1 -0
  174. package/dist/init.d.ts +71 -0
  175. package/dist/init.d.ts.map +1 -0
  176. package/dist/init.js +125 -0
  177. package/dist/init.js.map +1 -0
  178. package/dist/web/assets.d.ts +65 -0
  179. package/dist/web/assets.d.ts.map +1 -0
  180. package/dist/web/assets.js +152 -0
  181. package/dist/web/assets.js.map +1 -0
  182. package/dist/web/context.d.ts +121 -0
  183. package/dist/web/context.d.ts.map +1 -0
  184. package/dist/web/context.js +112 -0
  185. package/dist/web/context.js.map +1 -0
  186. package/dist/web/documents.d.ts +28 -0
  187. package/dist/web/documents.d.ts.map +1 -0
  188. package/dist/web/documents.js +39 -0
  189. package/dist/web/documents.js.map +1 -0
  190. package/dist/web/feeds.d.ts +122 -0
  191. package/dist/web/feeds.d.ts.map +1 -0
  192. package/dist/web/feeds.js +229 -0
  193. package/dist/web/feeds.js.map +1 -0
  194. package/dist/web/index.d.ts +17 -0
  195. package/dist/web/index.d.ts.map +1 -0
  196. package/dist/web/index.js +10 -0
  197. package/dist/web/index.js.map +1 -0
  198. package/dist/web/negotiate.d.ts +219 -0
  199. package/dist/web/negotiate.d.ts.map +1 -0
  200. package/dist/web/negotiate.js +409 -0
  201. package/dist/web/negotiate.js.map +1 -0
  202. package/dist/web/pagination.d.ts +48 -0
  203. package/dist/web/pagination.d.ts.map +1 -0
  204. package/dist/web/pagination.js +30 -0
  205. package/dist/web/pagination.js.map +1 -0
  206. package/dist/web/render.d.ts +72 -0
  207. package/dist/web/render.d.ts.map +1 -0
  208. package/dist/web/render.js +73 -0
  209. package/dist/web/render.js.map +1 -0
  210. package/dist/web/routes.d.ts +31 -0
  211. package/dist/web/routes.d.ts.map +1 -0
  212. package/dist/web/routes.js +366 -0
  213. package/dist/web/routes.js.map +1 -0
  214. package/dist/web/templates.d.ts +34 -0
  215. package/dist/web/templates.d.ts.map +1 -0
  216. package/dist/web/templates.js +106 -0
  217. package/dist/web/templates.js.map +1 -0
  218. package/package.json +68 -0
  219. package/templates/site/content/_data/site.json +8 -0
  220. package/templates/site/content/pages/about.md +11 -0
  221. package/templates/site/content/pages/pages.json +3 -0
  222. package/templates/site/content/posts/2026-01-01-hello-world.md +34 -0
  223. package/templates/site/content/posts/posts.json +4 -0
  224. package/templates/site/geekity.config.ts +11 -0
  225. package/templates/site/gitignore +3 -0
  226. package/templates/site/server.ts +20 -0
  227. package/templates/site/tsconfig.json +18 -0
  228. package/themes/default/README.md +148 -0
  229. package/themes/default/layouts/404.njk +9 -0
  230. package/themes/default/layouts/base.njk +59 -0
  231. package/themes/default/layouts/home.njk +9 -0
  232. package/themes/default/layouts/page.njk +13 -0
  233. package/themes/default/layouts/post.njk +30 -0
  234. package/themes/default/layouts/tag.njk +19 -0
  235. package/themes/default/partials/pagination.njk +14 -0
  236. package/themes/default/partials/post-list.njk +24 -0
  237. package/themes/default/partials/tags.njk +12 -0
  238. package/themes/default/static/style.css +327 -0
package/dist/config.js ADDED
@@ -0,0 +1,184 @@
1
+ import path from 'node:path';
2
+ import { KNOWN_UPLOAD_TYPES, normalizeUploadType } from './content/media.js';
3
+ export const DEFAULT_PORT = 3000;
4
+ export const DEFAULT_CONTENT_DIR = 'content';
5
+ export const DEFAULT_DATA_DIR = 'data';
6
+ export const DEFAULT_THEME_DIR = 'theme';
7
+ /** How long an admin login lasts by default: fourteen days, in seconds. */
8
+ export const DEFAULT_SESSION_LIFETIME = 14 * 24 * 60 * 60;
9
+ /** Largest upload a site accepts by default: ten mebibytes. */
10
+ export const DEFAULT_UPLOAD_MAX_BYTES = 10 * 1024 * 1024;
11
+ /**
12
+ * What the editor may upload by default: the raster image formats a browser
13
+ * displays, PDFs, and the two text formats. Everything the CMS knows about
14
+ * except SVG, which is script-bearing markup served from the site's own origin
15
+ * and so is opt-in.
16
+ */
17
+ export const DEFAULT_UPLOAD_TYPES = KNOWN_UPLOAD_TYPES;
18
+ /**
19
+ * Identity helper that gives a `geekity.config.ts` file type checking and
20
+ * completion without the site importing the type by hand.
21
+ */
22
+ export function defineConfig(config) {
23
+ return config;
24
+ }
25
+ /**
26
+ * Merge a site's config with environment overrides and defaults.
27
+ *
28
+ * Precedence is environment variable, then config file, then default. Relative
29
+ * directories resolve against `cwd`; absolute ones are left alone.
30
+ */
31
+ export function resolveConfig(config = {}, context = {}) {
32
+ const cwd = context.cwd ?? process.cwd();
33
+ const env = context.env ?? process.env;
34
+ const port = resolvePort(config.port, env);
35
+ return {
36
+ port,
37
+ contentDir: resolveDir(cwd, env['GEEKITY_CONTENT_DIR'], config.contentDir, DEFAULT_CONTENT_DIR),
38
+ dataDir: resolveDir(cwd, env['GEEKITY_DATA_DIR'], config.dataDir, DEFAULT_DATA_DIR),
39
+ themeDir: resolveDir(cwd, env['GEEKITY_THEME_DIR'], config.themeDir, DEFAULT_THEME_DIR),
40
+ baseUrl: resolveBaseUrl(env['GEEKITY_BASE_URL'], config.baseUrl, port),
41
+ baseUrlSource: baseUrlSource(env['GEEKITY_BASE_URL'], config.baseUrl),
42
+ watch: resolveWatch(env['GEEKITY_WATCH'], config.watch),
43
+ sessionLifetime: resolveSessionLifetime(env['GEEKITY_SESSION_LIFETIME'], config.sessionLifetime),
44
+ uploadMaxBytes: resolveUploadMaxBytes(env['GEEKITY_UPLOAD_MAX_BYTES'], config.uploadMaxBytes),
45
+ uploadTypes: resolveUploadTypes(env['GEEKITY_UPLOAD_TYPES'], config.uploadTypes),
46
+ onDocumentChange: config.onDocumentChange,
47
+ onPublish: config.onPublish,
48
+ federation: config.federation ?? {},
49
+ };
50
+ }
51
+ /** Bytes, a positive whole number of them. */
52
+ function resolveUploadMaxBytes(fromEnv, configured) {
53
+ if (fromEnv !== undefined && fromEnv !== '') {
54
+ const parsed = Number(fromEnv);
55
+ if (!isValidByteCount(parsed)) {
56
+ throw new TypeError(`GEEKITY_UPLOAD_MAX_BYTES must be a positive whole number of bytes, received ${JSON.stringify(fromEnv)}`);
57
+ }
58
+ return parsed;
59
+ }
60
+ if (configured === undefined)
61
+ return DEFAULT_UPLOAD_MAX_BYTES;
62
+ if (!isValidByteCount(configured)) {
63
+ throw new TypeError(`config.uploadMaxBytes must be a positive whole number of bytes, received ${JSON.stringify(configured)}`);
64
+ }
65
+ return configured;
66
+ }
67
+ function isValidByteCount(value) {
68
+ return Number.isInteger(value) && value > 0;
69
+ }
70
+ /**
71
+ * An upload allowlist, normalised and checked.
72
+ *
73
+ * A name the CMS has no media type for is refused rather than ignored: a
74
+ * config that says `.svg` and silently gets nothing would be a config that
75
+ * lies, and a typo in an allowlist is worth hearing about at boot.
76
+ */
77
+ function resolveUploadTypes(fromEnv, configured) {
78
+ if (fromEnv !== undefined && fromEnv !== '') {
79
+ return checkedUploadTypes(fromEnv.split(','), 'GEEKITY_UPLOAD_TYPES');
80
+ }
81
+ if (configured === undefined)
82
+ return [...DEFAULT_UPLOAD_TYPES];
83
+ return checkedUploadTypes(configured, 'config.uploadTypes');
84
+ }
85
+ function checkedUploadTypes(values, source) {
86
+ const types = [];
87
+ for (const value of values) {
88
+ const normalized = normalizeUploadType(value);
89
+ if (normalized === '')
90
+ continue;
91
+ if (!KNOWN_UPLOAD_TYPES.includes(normalized)) {
92
+ throw new TypeError(`${source} names ${JSON.stringify(normalized)}, which the CMS has no media type for. It knows ${KNOWN_UPLOAD_TYPES.join(', ')}.`);
93
+ }
94
+ if (!types.includes(normalized))
95
+ types.push(normalized);
96
+ }
97
+ return types;
98
+ }
99
+ /** `true`, `1`, `yes` and `on` mean yes; `false`, `0`, `no` and `off` mean no. */
100
+ function resolveWatch(fromEnv, configured) {
101
+ if (fromEnv !== undefined && fromEnv !== '') {
102
+ const normalized = fromEnv.trim().toLowerCase();
103
+ if (['1', 'true', 'yes', 'on'].includes(normalized))
104
+ return true;
105
+ if (['0', 'false', 'no', 'off'].includes(normalized))
106
+ return false;
107
+ throw new TypeError(`GEEKITY_WATCH must be a boolean such as true or false, received ${JSON.stringify(fromEnv)}`);
108
+ }
109
+ return configured ?? true;
110
+ }
111
+ /** Seconds, positive and finite. Fractions are allowed; zero and below are not. */
112
+ function resolveSessionLifetime(fromEnv, configured) {
113
+ if (fromEnv !== undefined && fromEnv !== '') {
114
+ const parsed = Number(fromEnv);
115
+ if (!isValidLifetime(parsed)) {
116
+ throw new TypeError(`GEEKITY_SESSION_LIFETIME must be a positive number of seconds, received ${JSON.stringify(fromEnv)}`);
117
+ }
118
+ return parsed;
119
+ }
120
+ if (configured === undefined)
121
+ return DEFAULT_SESSION_LIFETIME;
122
+ if (!isValidLifetime(configured)) {
123
+ throw new TypeError(`config.sessionLifetime must be a positive number of seconds, received ${JSON.stringify(configured)}`);
124
+ }
125
+ return configured;
126
+ }
127
+ function isValidLifetime(value) {
128
+ return Number.isFinite(value) && value > 0;
129
+ }
130
+ function resolvePort(configured, env) {
131
+ const fromEnv = env['GEEKITY_PORT'] ?? env['PORT'];
132
+ if (fromEnv !== undefined && fromEnv !== '') {
133
+ const parsed = Number(fromEnv);
134
+ if (!isValidPort(parsed)) {
135
+ throw new TypeError(`GEEKITY_PORT/PORT must be an integer between 0 and 65535, received ${JSON.stringify(fromEnv)}`);
136
+ }
137
+ return parsed;
138
+ }
139
+ if (configured === undefined)
140
+ return DEFAULT_PORT;
141
+ if (!isValidPort(configured)) {
142
+ throw new TypeError(`config.port must be an integer between 0 and 65535, received ${JSON.stringify(configured)}`);
143
+ }
144
+ return configured;
145
+ }
146
+ function isValidPort(value) {
147
+ return Number.isInteger(value) && value >= 0 && value <= 65535;
148
+ }
149
+ function resolveDir(cwd, fromEnv, configured, fallback) {
150
+ const chosen = firstNonEmpty(fromEnv, configured) ?? fallback;
151
+ return path.resolve(cwd, chosen);
152
+ }
153
+ /** Which of the two doors named the base URL, if either did. */
154
+ function baseUrlSource(fromEnv, configured) {
155
+ if (fromEnv !== undefined && fromEnv !== '')
156
+ return 'environment';
157
+ if (configured !== undefined && configured !== '')
158
+ return 'config';
159
+ return 'default';
160
+ }
161
+ function resolveBaseUrl(fromEnv, configured, port) {
162
+ const chosen = firstNonEmpty(fromEnv, configured);
163
+ if (chosen === undefined)
164
+ return `http://localhost:${port}`;
165
+ let parsed;
166
+ try {
167
+ parsed = new URL(chosen);
168
+ }
169
+ catch {
170
+ throw new TypeError(`config.baseUrl must be an absolute URL, received ${JSON.stringify(chosen)}`);
171
+ }
172
+ return parsed.origin + stripTrailingSlash(parsed.pathname);
173
+ }
174
+ function stripTrailingSlash(value) {
175
+ return value.endsWith('/') ? value.slice(0, -1) : value;
176
+ }
177
+ function firstNonEmpty(...values) {
178
+ for (const value of values) {
179
+ if (value !== undefined && value !== '')
180
+ return value;
181
+ }
182
+ return undefined;
183
+ }
184
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ /** Posts are dated and syndicated; pages are standing content. */
2
+ export type DocumentType = 'post' | 'page';
3
+ /**
4
+ * The ActivityStreams identity of a document, written back once the post has
5
+ * been federated so later Update and Delete activities name the same object.
6
+ * Eleventy ignores the whole block.
7
+ */
8
+ export interface ActivityPubMetadata {
9
+ /** The ActivityStreams object id. */
10
+ id?: string | undefined;
11
+ /** When the object was first delivered, ISO 8601. */
12
+ published?: string | undefined;
13
+ }
14
+ /**
15
+ * One Markdown file, parsed.
16
+ *
17
+ * The file on disk is the source of truth; a Document is what the rest of the
18
+ * CMS reads. Every front-matter key the CMS does not model survives in
19
+ * {@link Document.extra}, so hand-added data is not lost by an admin save.
20
+ *
21
+ * Optional fields are absent rather than `undefined`, so two Documents parsed
22
+ * from equivalent files compare equal.
23
+ */
24
+ export interface Document {
25
+ /** Whether the file lives under `posts/` or `pages/`. */
26
+ type: DocumentType;
27
+ /** Path relative to the content directory, with `/` separators. */
28
+ path: string;
29
+ /** Last segment of the permalink; the handle the admin UI edits. */
30
+ slug: string;
31
+ /** Output URL, always explicit so Eleventy and the CMS agree. Ends in `/`. */
32
+ permalink: string;
33
+ /** Display title. */
34
+ title: string;
35
+ /** Publish date, ISO 8601 with offset. Required for posts. */
36
+ date?: string | undefined;
37
+ /** Last modified date, ISO 8601. Written on every admin save. */
38
+ updated?: string | undefined;
39
+ /** Taxonomy. The `post` tag comes from `posts.json`, not from the file. */
40
+ tags: string[];
41
+ /** `true` hides the document from the public site and the feeds. */
42
+ draft: boolean;
43
+ /** Meta description and excerpt fallback. */
44
+ description?: string | undefined;
45
+ /** User login, resolved to a display name at render time. */
46
+ author?: string | undefined;
47
+ /** Federation identity, present once the document has been delivered. */
48
+ activitypub?: ActivityPubMetadata | undefined;
49
+ /** Front-matter keys the CMS does not model, preserved verbatim. */
50
+ extra: Record<string, unknown>;
51
+ /** Markdown body, without the front matter, trimmed. */
52
+ body: string;
53
+ /** {@link Document.body} rendered to HTML. */
54
+ html: string;
55
+ /**
56
+ * SHA-256 of the document's canonical file text (what
57
+ * `serializeDocument` would write). Two files that differ only in
58
+ * formatting hash the same, so a rewrite that changes nothing is a no-op.
59
+ */
60
+ hash: string;
61
+ }
62
+ /** The part of a {@link Document} that a file is written from. */
63
+ export type DocumentContent = Pick<Document, 'title' | 'date' | 'updated' | 'permalink' | 'tags' | 'draft' | 'description' | 'author' | 'activitypub' | 'extra' | 'body'>;
64
+ /** Front-matter keys the CMS models. Everything else lands in `extra`. */
65
+ export declare const KNOWN_FRONT_MATTER_KEYS: readonly ['title', 'date', 'updated', 'permalink', 'tags', 'draft', 'description', 'author', 'activitypub'];
66
+ //# sourceMappingURL=document.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document.d.ts","sourceRoot":"","sources":["../../src/content/document.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,CAAC;AAE3C;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,qCAAqC;IACrC,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxB,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,QAAQ;IACvB,yDAAyD;IACzD,IAAI,EAAE,YAAY,CAAC;IACnB,mEAAmE;IACnE,IAAI,EAAE,MAAM,CAAC;IACb,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,SAAS,EAAE,MAAM,CAAC;IAClB,qBAAqB;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,8DAA8D;IAC9D,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,iEAAiE;IACjE,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,2EAA2E;IAC3E,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,oEAAoE;IACpE,KAAK,EAAE,OAAO,CAAC;IACf,6CAA6C;IAC7C,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,6DAA6D;IAC7D,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,yEAAyE;IACzE,WAAW,CAAC,EAAE,mBAAmB,GAAG,SAAS,CAAC;IAC9C,oEAAoE;IACpE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;IACb,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;CACd;AAED,kEAAkE;AAClE,MAAM,MAAM,eAAe,GAAG,IAAI,CAChC,QAAQ,EACN,OAAO,GACP,MAAM,GACN,SAAS,GACT,WAAW,GACX,MAAM,GACN,OAAO,GACP,aAAa,GACb,QAAQ,GACR,aAAa,GACb,OAAO,GACP,MAAM,CACT,CAAC;AAEF,0EAA0E;AAC1E,eAAO,MAAM,uBAAuB,YAClC,OAAO,EACP,MAAM,EACN,SAAS,EACT,WAAW,EACX,MAAM,EACN,OAAO,EACP,aAAa,EACb,QAAQ,EACR,aAAa,CACL,CAAC"}
@@ -0,0 +1,13 @@
1
+ /** Front-matter keys the CMS models. Everything else lands in `extra`. */
2
+ export const KNOWN_FRONT_MATTER_KEYS = [
3
+ 'title',
4
+ 'date',
5
+ 'updated',
6
+ 'permalink',
7
+ 'tags',
8
+ 'draft',
9
+ 'description',
10
+ 'author',
11
+ 'activitypub',
12
+ ];
13
+ //# sourceMappingURL=document.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document.js","sourceRoot":"","sources":["../../src/content/document.ts"],"names":[],"mappings":"AAgFA,0EAA0E;AAC1E,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,OAAO;IACP,MAAM;IACN,SAAS;IACT,WAAW;IACX,MAAM;IACN,OAAO;IACP,aAAa;IACb,QAAQ;IACR,aAAa;CACL,CAAC"}
@@ -0,0 +1,17 @@
1
+ export { KNOWN_FRONT_MATTER_KEYS } from './document.ts';
2
+ export type { ActivityPubMetadata, Document, DocumentContent, DocumentType } from './document.ts';
3
+ export { renderMarkdown } from './markdown.ts';
4
+ export { KNOWN_UPLOAD_TYPES, matchesSignature, normalizeUploadType, UPLOAD_MEDIA_TYPES, } from './media.ts';
5
+ export type { UploadMediaType, UploadSignature } from './media.ts';
6
+ export { hashDocument, parseDocument, typeForPath } from './parser.ts';
7
+ export type { ParseDocumentOptions } from './parser.ts';
8
+ export { contentFilePath, freeSlug, saveDocument } from './save.ts';
9
+ export type { ContentFilePathInput, FreeSlugOptions, SaveDocumentOptions } from './save.ts';
10
+ export { defaultPermalink, slugify } from './slug.ts';
11
+ export type { DefaultPermalinkInput } from './slug.ts';
12
+ export { documentContent, documentFrontMatter, normalizeBody, serializeDocument, } from './writer.ts';
13
+ export { DATABASE_FILE, dateSortKey, DuplicatePermalinkError, isTrashedPath, openContentStore, TRASH_DIRECTORY, } from './store.ts';
14
+ export type { ContentCounts, ContentStore, ListAllOptions, ListByTagOptions, ListOptions, OpenContentStoreOptions, TagCount, } from './store.ts';
15
+ export { createContentSync, DEFAULT_DEBOUNCE_MS } from './sync.ts';
16
+ export type { ChangeOrigin, ContentEventListener, ContentEventMap, ContentEvents, ContentSync, CreateContentSyncOptions, DocumentChange, DocumentChangeType, SyncLogger, SyncResult, } from './sync.ts';
17
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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,eAAe,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACpE,YAAY,EAAE,oBAAoB,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAC5F,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,aAAa,EACb,WAAW,EACX,uBAAuB,EACvB,aAAa,EACb,gBAAgB,EAChB,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,aAAa,EACb,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,WAAW,EACX,uBAAuB,EACvB,QAAQ,GACT,MAAM,YAAY,CAAC;AACpB,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"}
@@ -0,0 +1,10 @@
1
+ export { KNOWN_FRONT_MATTER_KEYS } from './document.js';
2
+ export { renderMarkdown } from './markdown.js';
3
+ export { KNOWN_UPLOAD_TYPES, matchesSignature, normalizeUploadType, UPLOAD_MEDIA_TYPES, } from './media.js';
4
+ export { hashDocument, parseDocument, typeForPath } from './parser.js';
5
+ export { contentFilePath, freeSlug, saveDocument } from './save.js';
6
+ export { defaultPermalink, slugify } from './slug.js';
7
+ export { documentContent, documentFrontMatter, normalizeBody, serializeDocument, } from './writer.js';
8
+ export { DATABASE_FILE, dateSortKey, DuplicatePermalinkError, isTrashedPath, openContentStore, TRASH_DIRECTORY, } from './store.js';
9
+ export { createContentSync, DEFAULT_DEBOUNCE_MS } from './sync.js';
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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,eAAe,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAEpE,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,aAAa,EACb,WAAW,EACX,uBAAuB,EACvB,aAAa,EACb,gBAAgB,EAChB,eAAe,GAChB,MAAM,YAAY,CAAC;AAUpB,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC"}
@@ -0,0 +1,3 @@
1
+ /** Render a Markdown body to the HTML the site and the feeds serve. */
2
+ export declare function renderMarkdown(body: string): string;
3
+ //# sourceMappingURL=markdown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/content/markdown.ts"],"names":[],"mappings":"AAeA,uEAAuE;AACvE,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD"}
@@ -0,0 +1,39 @@
1
+ import MarkdownIt from 'markdown-it';
2
+ import footnote from 'markdown-it-footnote';
3
+ import { slugify } from './slug.js';
4
+ /**
5
+ * The renderer: markdown-it with Eleventy's default option (`html: true`),
6
+ * plus footnotes and heading anchors. Code blocks get a language class and no
7
+ * highlighting, so a theme can pick its own highlighter on the client.
8
+ */
9
+ const markdown = new MarkdownIt({ html: true })
10
+ .use(footnote)
11
+ .use(headingAnchors);
12
+ /** Render a Markdown body to the HTML the site and the feeds serve. */
13
+ export function renderMarkdown(body) {
14
+ return markdown.render(body);
15
+ }
16
+ /**
17
+ * Give every heading an `id` so a URL can point at it. Ids come from the same
18
+ * slugifier as permalinks, and repeat headings get a numeric suffix so the ids
19
+ * stay unique within one document.
20
+ */
21
+ function headingAnchors(md) {
22
+ md.core.ruler.push('geekity_heading_anchors', (state) => {
23
+ const used = new Map();
24
+ let index = 0;
25
+ for (const [position, token] of state.tokens.entries()) {
26
+ if (token.type !== 'heading_open')
27
+ continue;
28
+ index += 1;
29
+ const inline = state.tokens[position + 1];
30
+ const text = inline === undefined ? '' : inline.content;
31
+ const base = slugify(text) === '' ? `section-${String(index)}` : slugify(text);
32
+ const seen = used.get(base) ?? 0;
33
+ used.set(base, seen + 1);
34
+ token.attrSet('id', seen === 0 ? base : `${base}-${String(seen + 1)}`);
35
+ }
36
+ return true;
37
+ });
38
+ }
39
+ //# sourceMappingURL=markdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../src/content/markdown.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,aAAa,CAAC;AAErC,OAAO,QAAQ,MAAM,sBAAsB,CAAC;AAE5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC;;;;GAIG;AACH,MAAM,QAAQ,GAAuB,IAAI,UAAU,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;KAChE,GAAG,CAAC,QAAQ,CAAC;KACb,GAAG,CAAC,cAAc,CAAC,CAAC;AAEvB,uEAAuE;AACvE,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AAC/B,CAAC;AAED;;;;GAIG;AACH,SAAS,cAAc,CAAC,EAAsB;IAC5C,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,yBAAyB,EAAE,CAAC,KAAK,EAAE,EAAE;QACtD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAC;QACvC,IAAI,KAAK,GAAG,CAAC,CAAC;QAEd,KAAK,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC;YACvD,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc;gBAAE,SAAS;YAC5C,KAAK,IAAI,CAAC,CAAC;YAEX,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;YAC1C,MAAM,IAAI,GAAG,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;YACxD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,WAAW,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAE/E,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC;YACzB,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;QACzE,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * What may be uploaded into `content/uploads/`, described once.
3
+ *
4
+ * The upload endpoint is the only door into the content directory that does
5
+ * not write Markdown, so what it accepts is a list rather than a judgement:
6
+ * an extension the CMS has never heard of is refused, and one it has is
7
+ * checked against the media type the browser declared and, where the format
8
+ * has one, against the bytes the file actually starts with.
9
+ *
10
+ * The table lives here rather than beside the endpoint because
11
+ * {@link ResolvedConfig} validates a site's allowlist against it, and config
12
+ * may not depend on the admin.
13
+ */
14
+ /** Bytes a file of some format carries at a fixed offset. */
15
+ export interface UploadSignature {
16
+ /** Where in the file the bytes are. */
17
+ offset: number;
18
+ /** The bytes themselves. */
19
+ bytes: readonly number[];
20
+ }
21
+ /** One kind of file the upload endpoint knows about. */
22
+ export interface UploadMediaType {
23
+ /**
24
+ * Media types a browser may declare for this extension. An empty string is
25
+ * always accepted alongside these: a browser that does not recognise a file
26
+ * sends no type at all, and there is then nothing to disagree with.
27
+ */
28
+ declared: readonly string[];
29
+ /** Whether the Markdown for it is an image embed rather than a link. */
30
+ image: boolean;
31
+ /**
32
+ * Byte patterns that identify the format, as alternatives: a file matches
33
+ * when every part of any one of them matches. Absent for the text formats,
34
+ * which have no header to check.
35
+ */
36
+ signatures?: readonly (readonly UploadSignature[])[] | undefined;
37
+ }
38
+ /**
39
+ * Every extension the upload endpoint can accept, and what it takes to be one.
40
+ *
41
+ * SVG is deliberately absent. An SVG is a document that may carry script, and
42
+ * an upload is served from the site's own origin, so accepting one would hand
43
+ * anybody who can reach the editor a stored cross-site scripting hole. A site
44
+ * that wants them anyway can put `.svg` in `uploadTypes` — and then it has
45
+ * chosen that, which is the point.
46
+ */
47
+ export declare const UPLOAD_MEDIA_TYPES: ReadonlyMap<string, UploadMediaType>;
48
+ /** The extensions a site may put in its allowlist, sorted. */
49
+ export declare const KNOWN_UPLOAD_TYPES: readonly string[];
50
+ /**
51
+ * An extension as the allowlist spells it: lower case, with the dot.
52
+ *
53
+ * `PNG`, `.Png` and `png` are all the same thing to somebody writing a config
54
+ * file, so they are the same thing here.
55
+ */
56
+ export declare function normalizeUploadType(value: string): string;
57
+ /**
58
+ * Whether the first bytes of a file are the ones its extension implies.
59
+ *
60
+ * A format with no signature — the text ones — matches anything, because there
61
+ * is nothing to compare. Returns `false` for a file too short to hold the
62
+ * header it claims.
63
+ */
64
+ export declare function matchesSignature(media: UploadMediaType, bytes: Uint8Array): boolean;
65
+ //# sourceMappingURL=media.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,143 @@
1
+ /**
2
+ * What may be uploaded into `content/uploads/`, described once.
3
+ *
4
+ * The upload endpoint is the only door into the content directory that does
5
+ * not write Markdown, so what it accepts is a list rather than a judgement:
6
+ * an extension the CMS has never heard of is refused, and one it has is
7
+ * checked against the media type the browser declared and, where the format
8
+ * has one, against the bytes the file actually starts with.
9
+ *
10
+ * The table lives here rather than beside the endpoint because
11
+ * {@link ResolvedConfig} validates a site's allowlist against it, and config
12
+ * may not depend on the admin.
13
+ */
14
+ /** The bytes of an ASCII string, for spelling a signature readably. */
15
+ function ascii(value) {
16
+ return [...value].map((character) => character.charCodeAt(0));
17
+ }
18
+ /**
19
+ * Every extension the upload endpoint can accept, and what it takes to be one.
20
+ *
21
+ * SVG is deliberately absent. An SVG is a document that may carry script, and
22
+ * an upload is served from the site's own origin, so accepting one would hand
23
+ * anybody who can reach the editor a stored cross-site scripting hole. A site
24
+ * that wants them anyway can put `.svg` in `uploadTypes` — and then it has
25
+ * chosen that, which is the point.
26
+ */
27
+ export const UPLOAD_MEDIA_TYPES = new Map([
28
+ [
29
+ '.png',
30
+ {
31
+ declared: ['image/png'],
32
+ image: true,
33
+ signatures: [[{ offset: 0, bytes: [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a] }]],
34
+ },
35
+ ],
36
+ [
37
+ '.jpg',
38
+ {
39
+ declared: ['image/jpeg', 'image/jpg'],
40
+ image: true,
41
+ signatures: [[{ offset: 0, bytes: [0xff, 0xd8, 0xff] }]],
42
+ },
43
+ ],
44
+ [
45
+ '.jpeg',
46
+ {
47
+ declared: ['image/jpeg', 'image/jpg'],
48
+ image: true,
49
+ signatures: [[{ offset: 0, bytes: [0xff, 0xd8, 0xff] }]],
50
+ },
51
+ ],
52
+ [
53
+ '.gif',
54
+ {
55
+ declared: ['image/gif'],
56
+ image: true,
57
+ signatures: [
58
+ [{ offset: 0, bytes: ascii('GIF87a') }],
59
+ [{ offset: 0, bytes: ascii('GIF89a') }],
60
+ ],
61
+ },
62
+ ],
63
+ [
64
+ '.webp',
65
+ {
66
+ declared: ['image/webp'],
67
+ image: true,
68
+ // A WebP is a RIFF container whose form type is WEBP, so both halves of
69
+ // the header have to be there.
70
+ signatures: [
71
+ [
72
+ { offset: 0, bytes: ascii('RIFF') },
73
+ { offset: 8, bytes: ascii('WEBP') },
74
+ ],
75
+ ],
76
+ },
77
+ ],
78
+ [
79
+ '.avif',
80
+ {
81
+ declared: ['image/avif'],
82
+ image: true,
83
+ // An ISO base media file: a length, then `ftyp`, then the brand.
84
+ signatures: [
85
+ [
86
+ { offset: 4, bytes: ascii('ftyp') },
87
+ { offset: 8, bytes: ascii('avif') },
88
+ ],
89
+ [
90
+ { offset: 4, bytes: ascii('ftyp') },
91
+ { offset: 8, bytes: ascii('avis') },
92
+ ],
93
+ ],
94
+ },
95
+ ],
96
+ [
97
+ '.pdf',
98
+ {
99
+ declared: ['application/pdf'],
100
+ image: false,
101
+ signatures: [[{ offset: 0, bytes: ascii('%PDF-') }]],
102
+ },
103
+ ],
104
+ // The two text formats have no header. Nothing is sniffed, and nothing needs
105
+ // to be: they are served as text, and `application/octet-stream` is allowed
106
+ // 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 }],
108
+ [
109
+ '.md',
110
+ {
111
+ declared: ['text/markdown', 'text/x-markdown', 'text/plain', 'application/octet-stream'],
112
+ image: false,
113
+ },
114
+ ],
115
+ ]);
116
+ /** The extensions a site may put in its allowlist, sorted. */
117
+ export const KNOWN_UPLOAD_TYPES = [...UPLOAD_MEDIA_TYPES.keys()].sort();
118
+ /**
119
+ * An extension as the allowlist spells it: lower case, with the dot.
120
+ *
121
+ * `PNG`, `.Png` and `png` are all the same thing to somebody writing a config
122
+ * file, so they are the same thing here.
123
+ */
124
+ export function normalizeUploadType(value) {
125
+ const trimmed = value.trim().toLowerCase();
126
+ if (trimmed === '')
127
+ return '';
128
+ return trimmed.startsWith('.') ? trimmed : `.${trimmed}`;
129
+ }
130
+ /**
131
+ * Whether the first bytes of a file are the ones its extension implies.
132
+ *
133
+ * A format with no signature — the text ones — matches anything, because there
134
+ * is nothing to compare. Returns `false` for a file too short to hold the
135
+ * header it claims.
136
+ */
137
+ export function matchesSignature(media, bytes) {
138
+ const alternatives = media.signatures;
139
+ if (alternatives === undefined)
140
+ return true;
141
+ return alternatives.some((signature) => signature.every((part) => part.bytes.every((byte, index) => bytes[part.offset + index] === byte)));
142
+ }
143
+ //# sourceMappingURL=media.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media.js","sourceRoot":"","sources":["../../src/content/media.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import type { Document, DocumentContent, DocumentType } from './document.ts';
2
+ /** Where the file came from, which is what tells a post from a page. */
3
+ export interface ParseDocumentOptions {
4
+ /** Path relative to the content directory, e.g. `posts/2026-09-02-hello.md`. */
5
+ path: string;
6
+ /** Overrides the type the path implies. Needed for files under `_trash/`. */
7
+ type?: DocumentType | undefined;
8
+ }
9
+ /**
10
+ * Parse the text of a Markdown file into a {@link Document}.
11
+ *
12
+ * Front matter is read with gray-matter and the body rendered with
13
+ * markdown-it. Dates become ISO 8601 strings whether the file quoted them or
14
+ * left YAML to parse them, and every key the CMS does not model is kept.
15
+ */
16
+ export declare function parseDocument(source: string, options: ParseDocumentOptions): Document;
17
+ /** The SHA-256 a {@link Document} carries: a hash of its canonical file text. */
18
+ export declare function hashDocument(content: DocumentContent): string;
19
+ /**
20
+ * Decide whether a path holds a post or a page. Only the directory matters, so
21
+ * a file that moves between `posts/` and `pages/` changes type with it.
22
+ */
23
+ export declare function typeForPath(path: string): DocumentType;
24
+ //# sourceMappingURL=parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/content/parser.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAuB,QAAQ,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAOlG,wEAAwE;AACxE,MAAM,WAAW,oBAAoB;IACnC,gFAAgF;IAChF,IAAI,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,IAAI,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;CACjC;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,oBAAoB,GAAG,QAAQ,CAgDrF;AAED,iFAAiF;AACjF,wBAAgB,YAAY,CAAC,OAAO,EAAE,eAAe,GAAG,MAAM,CAE7D;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAQtD"}