@jant/core 0.3.27 → 0.3.29

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 (314) hide show
  1. package/bin/reset-password.js +22 -0
  2. package/dist/client/client.css +1 -0
  3. package/dist/client/client.js +31561 -0
  4. package/dist/index.js +15209 -15
  5. package/package.json +25 -15
  6. package/src/__tests__/helpers/app.ts +19 -3
  7. package/src/__tests__/helpers/db.ts +44 -0
  8. package/src/__tests__/helpers/lingui-core-macro-mock.ts +33 -0
  9. package/src/app.tsx +111 -174
  10. package/src/client.ts +13 -0
  11. package/src/db/migrations/0007_post_collections_m2m.sql +94 -0
  12. package/src/db/migrations/0008_add_collection_dividers.sql +8 -0
  13. package/src/db/migrations/0009_drop_collection_show_divider.sql +2 -0
  14. package/src/db/migrations/0010_add_performance_indexes.sql +16 -0
  15. package/src/db/schema.ts +24 -4
  16. package/src/i18n/locales/en.po +810 -385
  17. package/src/i18n/locales/en.ts +1 -1
  18. package/src/i18n/locales/zh-Hans.po +733 -522
  19. package/src/i18n/locales/zh-Hans.ts +1 -1
  20. package/src/i18n/locales/zh-Hant.po +733 -522
  21. package/src/i18n/locales/zh-Hant.ts +1 -1
  22. package/src/i18n/middleware.ts +7 -11
  23. package/src/index.ts +1 -1
  24. package/src/lib/__tests__/icons.test.ts +178 -0
  25. package/src/lib/__tests__/resolve-config.test.ts +184 -0
  26. package/src/lib/__tests__/schemas.test.ts +12 -6
  27. package/src/lib/__tests__/theme.test.ts +62 -0
  28. package/src/lib/__tests__/timezones.test.ts +1 -1
  29. package/src/lib/__tests__/url.test.ts +12 -0
  30. package/src/lib/__tests__/view.test.ts +1 -5
  31. package/src/lib/avatar-upload.ts +18 -10
  32. package/src/lib/collection-form-bridge.ts +52 -0
  33. package/src/lib/collections-reorder.ts +28 -0
  34. package/src/lib/compose-bridge.ts +251 -0
  35. package/src/lib/errors.ts +116 -0
  36. package/src/lib/excerpt.ts +1 -1
  37. package/src/lib/favicon.ts +3 -5
  38. package/src/lib/html.ts +22 -0
  39. package/src/lib/icon-catalog.ts +181 -0
  40. package/src/lib/icons.ts +202 -0
  41. package/src/lib/navigation.ts +18 -33
  42. package/src/lib/pagination.ts +3 -2
  43. package/src/lib/post-form-bridge.ts +136 -0
  44. package/src/lib/render.tsx +11 -4
  45. package/src/lib/resolve-config.ts +157 -0
  46. package/src/lib/schemas.ts +76 -12
  47. package/src/lib/settings-bridge.ts +139 -0
  48. package/src/lib/storage.ts +37 -16
  49. package/src/lib/theme.ts +5 -7
  50. package/src/lib/timeline.ts +4 -8
  51. package/src/lib/toast.ts +134 -0
  52. package/src/lib/upload.ts +71 -0
  53. package/src/lib/url.ts +9 -1
  54. package/src/lib/version.ts +16 -0
  55. package/src/lib/view.ts +9 -10
  56. package/src/middleware/__tests__/auth.test.ts +6 -28
  57. package/src/middleware/__tests__/onboarding.test.ts +1 -1
  58. package/src/middleware/auth.ts +6 -12
  59. package/src/middleware/config.ts +51 -0
  60. package/src/middleware/error-handler.ts +56 -0
  61. package/src/middleware/onboarding.ts +1 -1
  62. package/src/preset.css +6 -0
  63. package/src/routes/__tests__/compose.test.ts +104 -17
  64. package/src/routes/api/__tests__/collections.test.ts +93 -2
  65. package/src/routes/api/__tests__/posts.test.ts +2 -1
  66. package/src/routes/api/__tests__/settings.test.ts +1 -1
  67. package/src/routes/api/collections.ts +64 -68
  68. package/src/routes/api/nav-items.ts +21 -59
  69. package/src/routes/api/pages.ts +18 -46
  70. package/src/routes/api/posts.ts +64 -86
  71. package/src/routes/api/search.ts +6 -4
  72. package/src/routes/api/settings.ts +8 -24
  73. package/src/routes/api/upload.ts +55 -53
  74. package/src/routes/auth/__tests__/setup.test.ts +118 -0
  75. package/src/routes/auth/reset.tsx +17 -66
  76. package/src/routes/auth/setup.tsx +67 -11
  77. package/src/routes/auth/signin.tsx +44 -8
  78. package/src/routes/compose.tsx +194 -0
  79. package/src/routes/dash/__tests__/font-theme.test.ts +110 -0
  80. package/src/routes/dash/__tests__/pages.test.ts +2 -2
  81. package/src/routes/dash/__tests__/settings-avatar.test.ts +23 -12
  82. package/src/routes/dash/appearance.tsx +173 -0
  83. package/src/routes/dash/collections.tsx +80 -14
  84. package/src/routes/dash/index.tsx +12 -14
  85. package/src/routes/dash/media.tsx +46 -49
  86. package/src/routes/dash/pages.tsx +85 -37
  87. package/src/routes/dash/posts.tsx +60 -23
  88. package/src/routes/dash/redirects.tsx +43 -33
  89. package/src/routes/dash/settings.tsx +234 -214
  90. package/src/routes/feed/__tests__/rss.test.ts +7 -3
  91. package/src/routes/feed/rss.ts +11 -16
  92. package/src/routes/feed/sitemap.ts +15 -9
  93. package/src/routes/pages/__tests__/collections.test.ts +9 -8
  94. package/src/routes/pages/archive.tsx +2 -2
  95. package/src/routes/pages/collection.tsx +76 -9
  96. package/src/routes/pages/collections.tsx +3 -1
  97. package/src/routes/pages/featured.tsx +2 -2
  98. package/src/routes/pages/home.tsx +3 -3
  99. package/src/routes/pages/latest.tsx +2 -2
  100. package/src/routes/pages/page.tsx +2 -2
  101. package/src/routes/pages/post.tsx +2 -2
  102. package/src/routes/pages/search.tsx +2 -2
  103. package/src/services/__tests__/collection.test.ts +324 -34
  104. package/src/services/__tests__/media.test.ts +1 -1
  105. package/src/services/__tests__/page.test.ts +116 -1
  106. package/src/services/auth.ts +88 -0
  107. package/src/services/collection.ts +169 -30
  108. package/src/services/index.ts +8 -3
  109. package/src/services/media.ts +39 -12
  110. package/src/services/navigation.ts +17 -5
  111. package/src/services/page.ts +24 -4
  112. package/src/services/post.ts +87 -19
  113. package/src/services/search.ts +0 -1
  114. package/src/services/settings.ts +21 -13
  115. package/src/style.css +3 -0
  116. package/src/styles/components.css +42 -1
  117. package/src/styles/tokens.css +4 -0
  118. package/src/styles/ui.css +902 -73
  119. package/src/types/app-context.ts +25 -0
  120. package/src/types/bindings.ts +1 -0
  121. package/src/types/config.ts +60 -23
  122. package/src/types/entities.ts +12 -2
  123. package/src/types/lingui-react-macro.d.ts +3 -3
  124. package/src/types/operations.ts +2 -4
  125. package/src/types/views.ts +1 -3
  126. package/src/ui/__tests__/font-themes.test.ts +27 -8
  127. package/src/ui/color-themes.ts +1 -1
  128. package/src/ui/components/__tests__/jant-collection-form.test.ts +153 -0
  129. package/src/ui/components/__tests__/jant-compose-dialog.test.ts +512 -0
  130. package/src/ui/components/__tests__/jant-compose-editor.test.ts +272 -0
  131. package/src/ui/components/__tests__/jant-post-form.test.ts +172 -0
  132. package/src/ui/components/__tests__/jant-settings-avatar.test.ts +235 -0
  133. package/src/ui/components/__tests__/jant-settings-general.test.ts +319 -0
  134. package/src/ui/components/collection-types.ts +45 -0
  135. package/src/ui/components/compose-types.ts +75 -0
  136. package/src/ui/components/jant-collection-form.ts +512 -0
  137. package/src/ui/components/jant-compose-dialog.ts +494 -0
  138. package/src/ui/components/jant-compose-editor.ts +799 -0
  139. package/src/ui/components/jant-post-form.ts +290 -0
  140. package/src/ui/components/jant-settings-avatar.ts +231 -0
  141. package/src/ui/components/jant-settings-general.ts +436 -0
  142. package/src/ui/components/post-form-template.ts +260 -0
  143. package/src/ui/components/post-form-types.ts +87 -0
  144. package/src/ui/components/settings-types.ts +62 -0
  145. package/src/ui/compose/ComposeDialog.tsx +141 -385
  146. package/src/ui/compose/ComposePrompt.tsx +3 -3
  147. package/src/ui/dash/PostList.tsx +55 -61
  148. package/src/ui/dash/appearance/AdvancedContent.tsx +80 -0
  149. package/src/ui/dash/appearance/AppearanceNav.tsx +56 -0
  150. package/src/ui/dash/appearance/ColorThemeContent.tsx +129 -0
  151. package/src/ui/dash/appearance/FontThemeContent.tsx +98 -0
  152. package/src/ui/dash/collections/CollectionForm.tsx +130 -117
  153. package/src/ui/dash/collections/CollectionsListContent.tsx +102 -41
  154. package/src/ui/dash/collections/IconPickerGrid.tsx +50 -0
  155. package/src/ui/dash/collections/ViewCollectionContent.tsx +14 -3
  156. package/src/ui/dash/index.ts +1 -1
  157. package/src/ui/dash/posts/PostForm.tsx +248 -0
  158. package/src/ui/dash/settings/AccountContent.tsx +69 -80
  159. package/src/ui/dash/settings/GeneralContent.tsx +159 -478
  160. package/src/ui/dash/settings/SettingsNav.tsx +4 -4
  161. package/src/ui/font-themes.ts +115 -32
  162. package/src/ui/layouts/BaseLayout.tsx +49 -19
  163. package/src/ui/layouts/DashLayout.tsx +14 -9
  164. package/src/ui/layouts/SiteLayout.tsx +38 -23
  165. package/src/ui/pages/CollectionPage.tsx +12 -2
  166. package/src/ui/pages/CollectionsPage.tsx +27 -27
  167. package/src/ui/pages/HomePage.tsx +15 -6
  168. package/src/ui/pages/SearchPage.tsx +1 -2
  169. package/src/ui/shared/CollectionsSidebar.tsx +59 -0
  170. package/src/ui/shared/Pagination.tsx +2 -2
  171. package/dist/app.js +0 -267
  172. package/dist/auth.js +0 -39
  173. package/dist/client.js +0 -13
  174. package/dist/db/index.js +0 -10
  175. package/dist/db/schema.js +0 -224
  176. package/dist/i18n/Trans.js +0 -24
  177. package/dist/i18n/context.js +0 -58
  178. package/dist/i18n/detect.js +0 -26
  179. package/dist/i18n/i18n.js +0 -49
  180. package/dist/i18n/index.js +0 -44
  181. package/dist/i18n/locales/en.js +0 -1
  182. package/dist/i18n/locales/zh-Hans.js +0 -1
  183. package/dist/i18n/locales/zh-Hant.js +0 -1
  184. package/dist/i18n/locales.js +0 -13
  185. package/dist/i18n/middleware.js +0 -30
  186. package/dist/lib/avatar-upload.js +0 -134
  187. package/dist/lib/config.js +0 -143
  188. package/dist/lib/constants.js +0 -50
  189. package/dist/lib/excerpt.js +0 -76
  190. package/dist/lib/favicon.js +0 -102
  191. package/dist/lib/feed.js +0 -123
  192. package/dist/lib/image-processor.js +0 -187
  193. package/dist/lib/image.js +0 -97
  194. package/dist/lib/index.js +0 -7
  195. package/dist/lib/markdown.js +0 -83
  196. package/dist/lib/media-helpers.js +0 -49
  197. package/dist/lib/media-upload.js +0 -104
  198. package/dist/lib/nav-reorder.js +0 -27
  199. package/dist/lib/navigation.js +0 -79
  200. package/dist/lib/pagination.js +0 -44
  201. package/dist/lib/render.js +0 -53
  202. package/dist/lib/schemas.js +0 -174
  203. package/dist/lib/sqid.js +0 -72
  204. package/dist/lib/sse.js +0 -218
  205. package/dist/lib/storage.js +0 -164
  206. package/dist/lib/theme.js +0 -65
  207. package/dist/lib/time.js +0 -159
  208. package/dist/lib/timeline.js +0 -95
  209. package/dist/lib/timezones.js +0 -388
  210. package/dist/lib/url.js +0 -89
  211. package/dist/lib/view.js +0 -217
  212. package/dist/middleware/auth.js +0 -52
  213. package/dist/middleware/onboarding.js +0 -41
  214. package/dist/routes/api/collections.js +0 -124
  215. package/dist/routes/api/nav-items.js +0 -104
  216. package/dist/routes/api/pages.js +0 -91
  217. package/dist/routes/api/posts.js +0 -218
  218. package/dist/routes/api/search.js +0 -48
  219. package/dist/routes/api/settings.js +0 -68
  220. package/dist/routes/api/upload.js +0 -246
  221. package/dist/routes/auth/reset.js +0 -221
  222. package/dist/routes/auth/setup.js +0 -194
  223. package/dist/routes/auth/signin.js +0 -176
  224. package/dist/routes/compose.js +0 -48
  225. package/dist/routes/dash/collections.js +0 -115
  226. package/dist/routes/dash/index.js +0 -118
  227. package/dist/routes/dash/media.js +0 -106
  228. package/dist/routes/dash/pages.js +0 -294
  229. package/dist/routes/dash/posts.js +0 -244
  230. package/dist/routes/dash/redirects.js +0 -257
  231. package/dist/routes/dash/settings.js +0 -379
  232. package/dist/routes/feed/rss.js +0 -62
  233. package/dist/routes/feed/sitemap.js +0 -49
  234. package/dist/routes/pages/archive.js +0 -62
  235. package/dist/routes/pages/collection.js +0 -34
  236. package/dist/routes/pages/collections.js +0 -28
  237. package/dist/routes/pages/featured.js +0 -36
  238. package/dist/routes/pages/home.js +0 -64
  239. package/dist/routes/pages/latest.js +0 -45
  240. package/dist/routes/pages/page.js +0 -68
  241. package/dist/routes/pages/post.js +0 -44
  242. package/dist/routes/pages/search.js +0 -54
  243. package/dist/services/collection.js +0 -109
  244. package/dist/services/index.js +0 -24
  245. package/dist/services/media.js +0 -117
  246. package/dist/services/navigation.js +0 -91
  247. package/dist/services/page.js +0 -84
  248. package/dist/services/post.js +0 -229
  249. package/dist/services/redirect.js +0 -48
  250. package/dist/services/search.js +0 -67
  251. package/dist/services/settings.js +0 -68
  252. package/dist/types/bindings.js +0 -3
  253. package/dist/types/config.js +0 -147
  254. package/dist/types/constants.js +0 -27
  255. package/dist/types/entities.js +0 -3
  256. package/dist/types/lingui-react-macro.d.js +0 -9
  257. package/dist/types/operations.js +0 -3
  258. package/dist/types/props.js +0 -3
  259. package/dist/types/sortablejs.d.js +0 -5
  260. package/dist/types/views.js +0 -5
  261. package/dist/types.js +0 -11
  262. package/dist/ui/color-themes.js +0 -268
  263. package/dist/ui/compose/ComposeDialog.js +0 -467
  264. package/dist/ui/compose/ComposePrompt.js +0 -55
  265. package/dist/ui/dash/ActionButtons.js +0 -46
  266. package/dist/ui/dash/CrudPageHeader.js +0 -22
  267. package/dist/ui/dash/DangerZone.js +0 -36
  268. package/dist/ui/dash/FormatBadge.js +0 -27
  269. package/dist/ui/dash/ListItemRow.js +0 -21
  270. package/dist/ui/dash/PageForm.js +0 -195
  271. package/dist/ui/dash/PostForm.js +0 -395
  272. package/dist/ui/dash/PostList.js +0 -83
  273. package/dist/ui/dash/StatusBadge.js +0 -46
  274. package/dist/ui/dash/collections/CollectionForm.js +0 -152
  275. package/dist/ui/dash/collections/CollectionsListContent.js +0 -68
  276. package/dist/ui/dash/collections/ViewCollectionContent.js +0 -96
  277. package/dist/ui/dash/index.js +0 -10
  278. package/dist/ui/dash/media/MediaListContent.js +0 -166
  279. package/dist/ui/dash/media/ViewMediaContent.js +0 -212
  280. package/dist/ui/dash/pages/LinkFormContent.js +0 -130
  281. package/dist/ui/dash/pages/UnifiedPagesContent.js +0 -193
  282. package/dist/ui/dash/settings/AccountContent.js +0 -209
  283. package/dist/ui/dash/settings/AppearanceContent.js +0 -259
  284. package/dist/ui/dash/settings/GeneralContent.js +0 -536
  285. package/dist/ui/dash/settings/SettingsNav.js +0 -41
  286. package/dist/ui/feed/LinkCard.js +0 -72
  287. package/dist/ui/feed/NoteCard.js +0 -58
  288. package/dist/ui/feed/QuoteCard.js +0 -63
  289. package/dist/ui/feed/ThreadPreview.js +0 -48
  290. package/dist/ui/feed/TimelineFeed.js +0 -41
  291. package/dist/ui/feed/TimelineItem.js +0 -27
  292. package/dist/ui/font-themes.js +0 -36
  293. package/dist/ui/layouts/BaseLayout.js +0 -153
  294. package/dist/ui/layouts/DashLayout.js +0 -141
  295. package/dist/ui/layouts/SiteLayout.js +0 -169
  296. package/dist/ui/pages/ArchivePage.js +0 -143
  297. package/dist/ui/pages/CollectionPage.js +0 -70
  298. package/dist/ui/pages/CollectionsPage.js +0 -76
  299. package/dist/ui/pages/FeaturedPage.js +0 -24
  300. package/dist/ui/pages/HomePage.js +0 -24
  301. package/dist/ui/pages/PostPage.js +0 -55
  302. package/dist/ui/pages/SearchPage.js +0 -122
  303. package/dist/ui/pages/SinglePage.js +0 -23
  304. package/dist/ui/shared/EmptyState.js +0 -27
  305. package/dist/ui/shared/MediaGallery.js +0 -35
  306. package/dist/ui/shared/Pagination.js +0 -195
  307. package/dist/ui/shared/ThreadView.js +0 -108
  308. package/dist/ui/shared/index.js +0 -5
  309. package/dist/vendor/datastar.js +0 -1606
  310. package/src/lib/__tests__/config.test.ts +0 -192
  311. package/src/lib/config.ts +0 -167
  312. package/src/routes/compose.ts +0 -63
  313. package/src/ui/dash/PostForm.tsx +0 -360
  314. package/src/ui/dash/settings/AppearanceContent.tsx +0 -254
@@ -1,192 +0,0 @@
1
- import { describe, it, expect, beforeEach } from "vitest";
2
- import { createTestDatabase } from "../../__tests__/helpers/db.js";
3
- import { createSettingsService } from "../../services/settings.js";
4
- import type { Database } from "../../db/index.js";
5
- import {
6
- getConfig,
7
- getHomeDefaultView,
8
- getConfigFallback,
9
- getTimeZone,
10
- getSiteFooter,
11
- isNoIndex,
12
- } from "../config.js";
13
- import type { Context } from "hono";
14
-
15
- function createMockContext(
16
- services: { settings: ReturnType<typeof createSettingsService> },
17
- env: Record<string, string> = {},
18
- ): Context {
19
- return {
20
- env,
21
- var: { services },
22
- } as unknown as Context;
23
- }
24
-
25
- describe("getConfig", () => {
26
- let db: Database;
27
- let settingsService: ReturnType<typeof createSettingsService>;
28
-
29
- beforeEach(() => {
30
- const testDb = createTestDatabase();
31
- db = testDb.db as unknown as Database;
32
- settingsService = createSettingsService(db);
33
- });
34
-
35
- it("returns default value when no DB or ENV value exists", async () => {
36
- const c = createMockContext({ settings: settingsService });
37
- const result = await getConfig(c, "HOME_DEFAULT_VIEW");
38
- expect(result).toBe("latest");
39
- });
40
-
41
- it("returns DB value when set", async () => {
42
- await settingsService.set("HOME_DEFAULT_VIEW", "featured");
43
- const c = createMockContext({ settings: settingsService });
44
- const result = await getConfig(c, "HOME_DEFAULT_VIEW");
45
- expect(result).toBe("featured");
46
- });
47
-
48
- it("returns env value when DB is empty", async () => {
49
- const c = createMockContext(
50
- { settings: settingsService },
51
- {
52
- HOME_DEFAULT_VIEW: "featured",
53
- },
54
- );
55
- const result = await getConfig(c, "HOME_DEFAULT_VIEW");
56
- expect(result).toBe("featured");
57
- });
58
-
59
- it("DB value takes precedence over env value", async () => {
60
- await settingsService.set("HOME_DEFAULT_VIEW", "featured");
61
- const c = createMockContext(
62
- { settings: settingsService },
63
- {
64
- HOME_DEFAULT_VIEW: "latest",
65
- },
66
- );
67
- const result = await getConfig(c, "HOME_DEFAULT_VIEW");
68
- expect(result).toBe("featured");
69
- });
70
- });
71
-
72
- describe("getHomeDefaultView", () => {
73
- let db: Database;
74
- let settingsService: ReturnType<typeof createSettingsService>;
75
-
76
- beforeEach(() => {
77
- const testDb = createTestDatabase();
78
- db = testDb.db as unknown as Database;
79
- settingsService = createSettingsService(db);
80
- });
81
-
82
- it("returns 'latest' by default", async () => {
83
- const c = createMockContext({ settings: settingsService });
84
- const result = await getHomeDefaultView(c);
85
- expect(result).toBe("latest");
86
- });
87
-
88
- it("returns 'featured' when set in DB", async () => {
89
- await settingsService.set("HOME_DEFAULT_VIEW", "featured");
90
- const c = createMockContext({ settings: settingsService });
91
- const result = await getHomeDefaultView(c);
92
- expect(result).toBe("featured");
93
- });
94
- });
95
-
96
- describe("getTimeZone", () => {
97
- let db: Database;
98
- let settingsService: ReturnType<typeof createSettingsService>;
99
-
100
- beforeEach(() => {
101
- const testDb = createTestDatabase();
102
- db = testDb.db as unknown as Database;
103
- settingsService = createSettingsService(db);
104
- });
105
-
106
- it("returns 'UTC' by default", async () => {
107
- const c = createMockContext({ settings: settingsService });
108
- const result = await getTimeZone(c);
109
- expect(result).toBe("UTC");
110
- });
111
-
112
- it("returns DB value when set", async () => {
113
- await settingsService.set("TIME_ZONE", "Beijing");
114
- const c = createMockContext({ settings: settingsService });
115
- const result = await getTimeZone(c);
116
- expect(result).toBe("Beijing");
117
- });
118
- });
119
-
120
- describe("getSiteFooter", () => {
121
- let db: Database;
122
- let settingsService: ReturnType<typeof createSettingsService>;
123
-
124
- beforeEach(() => {
125
- const testDb = createTestDatabase();
126
- db = testDb.db as unknown as Database;
127
- settingsService = createSettingsService(db);
128
- });
129
-
130
- it("returns empty string by default", async () => {
131
- const c = createMockContext({ settings: settingsService });
132
- const result = await getSiteFooter(c);
133
- expect(result).toBe("");
134
- });
135
-
136
- it("returns DB value when set", async () => {
137
- await settingsService.set("SITE_FOOTER", "**Footer text**");
138
- const c = createMockContext({ settings: settingsService });
139
- const result = await getSiteFooter(c);
140
- expect(result).toBe("**Footer text**");
141
- });
142
- });
143
-
144
- describe("isNoIndex", () => {
145
- let db: Database;
146
- let settingsService: ReturnType<typeof createSettingsService>;
147
-
148
- beforeEach(() => {
149
- const testDb = createTestDatabase();
150
- db = testDb.db as unknown as Database;
151
- settingsService = createSettingsService(db);
152
- });
153
-
154
- it("returns false by default", async () => {
155
- const c = createMockContext({ settings: settingsService });
156
- const result = await isNoIndex(c);
157
- expect(result).toBe(false);
158
- });
159
-
160
- it("returns true when NOINDEX is set to 'true'", async () => {
161
- await settingsService.set("NOINDEX", "true");
162
- const c = createMockContext({ settings: settingsService });
163
- const result = await isNoIndex(c);
164
- expect(result).toBe(true);
165
- });
166
-
167
- it("returns false when NOINDEX is set to other value", async () => {
168
- await settingsService.set("NOINDEX", "false");
169
- const c = createMockContext({ settings: settingsService });
170
- const result = await isNoIndex(c);
171
- expect(result).toBe(false);
172
- });
173
- });
174
-
175
- describe("getConfigFallback", () => {
176
- it("returns default when no env value", () => {
177
- const c = createMockContext({
178
- settings: {} as ReturnType<typeof createSettingsService>,
179
- });
180
- const result = getConfigFallback(c, "HOME_DEFAULT_VIEW");
181
- expect(result).toBe("latest");
182
- });
183
-
184
- it("returns env value when set", () => {
185
- const c = createMockContext(
186
- { settings: {} as ReturnType<typeof createSettingsService> },
187
- { HOME_DEFAULT_VIEW: "featured" },
188
- );
189
- const result = getConfigFallback(c, "HOME_DEFAULT_VIEW");
190
- expect(result).toBe("featured");
191
- });
192
- });
package/src/lib/config.ts DELETED
@@ -1,167 +0,0 @@
1
- /**
2
- * Unified Configuration System
3
- *
4
- * Provides a flexible configuration system with two priority modes:
5
- * - User-configurable (envOnly: false): Database > Environment > Default
6
- * - Environment-only (envOnly: true): Environment > Default
7
- *
8
- * All configuration fields are defined in CONFIG_FIELDS (types.ts).
9
- */
10
-
11
- import type { Context } from "hono";
12
- import { CONFIG_FIELDS, type ConfigKey } from "../types.js";
13
-
14
- /**
15
- * Get the fallback value for a config key (ENV > Default), skipping the database.
16
- * Used for placeholder values in forms where the DB value is shown separately.
17
- *
18
- * @param c - Hono context
19
- * @param key - Configuration key from CONFIG_FIELDS
20
- * @returns Fallback value from environment or default
21
- *
22
- * @example
23
- * ```typescript
24
- * const placeholder = getConfigFallback(c, "SITE_NAME");
25
- * // Returns: c.env.SITE_NAME ?? "Jant"
26
- * ```
27
- */
28
- export function getConfigFallback(c: Context, key: ConfigKey): string {
29
- const field = CONFIG_FIELDS[key];
30
- const envValue = c.env[key as keyof typeof c.env];
31
- if (envValue && typeof envValue === "string") return envValue;
32
- return field.defaultValue;
33
- }
34
-
35
- /**
36
- * Generic configuration getter that respects priority settings
37
- *
38
- * @param c - Hono context
39
- * @param key - Configuration key from CONFIG_FIELDS
40
- * @returns Configuration value following the defined priority
41
- *
42
- * @example
43
- * ```typescript
44
- * // For user-configurable configs (SITE_NAME):
45
- * // Returns: (DB: SITE_NAME) ?? c.env.SITE_NAME ?? "Jant"
46
- *
47
- * // For environment-only configs (SITE_URL):
48
- * // Returns: c.env.SITE_URL ?? ""
49
- * ```
50
- */
51
- export async function getConfig(c: Context, key: ConfigKey): Promise<string> {
52
- const field = CONFIG_FIELDS[key];
53
-
54
- if (!field.envOnly) {
55
- // User-configurable: DB > ENV > Default
56
- // 1. Check database setting first
57
- const dbValue = await c.var.services.settings.get(key);
58
- if (dbValue) {
59
- return dbValue;
60
- }
61
- }
62
-
63
- // ENV > Default
64
- // 2. Check environment variable
65
- const envValue = c.env[key as keyof typeof c.env];
66
- if (envValue && typeof envValue === "string") {
67
- return envValue;
68
- }
69
-
70
- // 3. Default value
71
- return field.defaultValue;
72
- }
73
-
74
- /**
75
- * Get site name with fallback chain: DB > ENV > Default
76
- *
77
- * @param c - Hono context
78
- * @returns Site name
79
- *
80
- * @example
81
- * ```typescript
82
- * const siteName = await getSiteName(c);
83
- * // Returns: (DB: SITE_NAME) ?? c.env.SITE_NAME ?? "Jant"
84
- * ```
85
- */
86
- export async function getSiteName(c: Context): Promise<string> {
87
- return getConfig(c, "SITE_NAME");
88
- }
89
-
90
- /**
91
- * Get site description with fallback chain: DB > ENV > Default
92
- *
93
- * @param c - Hono context
94
- * @returns Site description
95
- *
96
- * @example
97
- * ```typescript
98
- * const description = await getSiteDescription(c);
99
- * // Returns: (DB: SITE_DESCRIPTION) ?? c.env.SITE_DESCRIPTION ?? "A microblog powered by Jant"
100
- * ```
101
- */
102
- export async function getSiteDescription(c: Context): Promise<string> {
103
- return getConfig(c, "SITE_DESCRIPTION");
104
- }
105
-
106
- /**
107
- * Get site language with fallback chain: DB > ENV > Default
108
- *
109
- * @param c - Hono context
110
- * @returns Site language code
111
- *
112
- * @example
113
- * ```typescript
114
- * const lang = await getSiteLanguage(c);
115
- * // Returns: (DB: SITE_LANGUAGE) ?? c.env.SITE_LANGUAGE ?? "en"
116
- * ```
117
- */
118
- export async function getSiteLanguage(c: Context): Promise<string> {
119
- return getConfig(c, "SITE_LANGUAGE");
120
- }
121
-
122
- /**
123
- * Get home default view with fallback chain: DB > ENV > Default
124
- *
125
- * @param c - Hono context
126
- * @returns Home default view ("latest" or "featured")
127
- *
128
- * @example
129
- * ```typescript
130
- * const view = await getHomeDefaultView(c);
131
- * // Returns: (DB: HOME_DEFAULT_VIEW) ?? "latest"
132
- * ```
133
- */
134
- export async function getHomeDefaultView(c: Context): Promise<string> {
135
- return getConfig(c, "HOME_DEFAULT_VIEW");
136
- }
137
-
138
- /**
139
- * Get timezone with fallback chain: DB > ENV > Default
140
- *
141
- * @param c - Hono context
142
- * @returns Timezone string (e.g. "Beijing", "UTC")
143
- */
144
- export async function getTimeZone(c: Context): Promise<string> {
145
- return getConfig(c, "TIME_ZONE");
146
- }
147
-
148
- /**
149
- * Get site footer markdown with fallback chain: DB > ENV > Default
150
- *
151
- * @param c - Hono context
152
- * @returns Footer markdown string (empty string if not set)
153
- */
154
- export async function getSiteFooter(c: Context): Promise<string> {
155
- return getConfig(c, "SITE_FOOTER");
156
- }
157
-
158
- /**
159
- * Check if search engine indexing is disabled
160
- *
161
- * @param c - Hono context
162
- * @returns true if NOINDEX is set to "true"
163
- */
164
- export async function isNoIndex(c: Context): Promise<boolean> {
165
- const value = await getConfig(c, "NOINDEX");
166
- return value === "true";
167
- }
@@ -1,63 +0,0 @@
1
- /**
2
- * Compose Route
3
- *
4
- * Handles post creation from the public-site compose dialog.
5
- * Returns dsRedirect to the new post's permalink (Datastar form pattern).
6
- */
7
-
8
- import { Hono } from "hono";
9
- import type { Bindings } from "../types.js";
10
- import type { AppVariables } from "../app.js";
11
- import { requireAuth } from "../middleware/auth.js";
12
- import { CreatePostSchema, validateMediaCount } from "../lib/schemas.js";
13
- import * as sqid from "../lib/sqid.js";
14
- import { dsRedirect, dsToast } from "../lib/sse.js";
15
-
16
- type Env = { Bindings: Bindings; Variables: AppVariables };
17
-
18
- export const composeRoutes = new Hono<Env>();
19
-
20
- // All compose routes require authentication
21
- composeRoutes.use("*", requireAuth());
22
-
23
- composeRoutes.post("/", async (c) => {
24
- const raw = await c.req.json();
25
-
26
- const result = CreatePostSchema.safeParse(raw);
27
- if (!result.success) {
28
- const firstError = result.error.issues[0]?.message ?? "Invalid input";
29
- return dsToast(firstError, "error");
30
- }
31
-
32
- const data = result.data;
33
-
34
- // Validate media count
35
- if (data.mediaIds) {
36
- const mediaError = validateMediaCount(data.mediaIds);
37
- if (mediaError) {
38
- return dsToast(mediaError, "error");
39
- }
40
- }
41
-
42
- const post = await c.var.services.posts.create({
43
- format: data.format,
44
- title: data.title || undefined,
45
- body: data.body || undefined,
46
- status: data.status ?? "published",
47
- featured: data.featured,
48
- pinned: data.pinned,
49
- url: data.url || undefined,
50
- quoteText: data.quoteText || undefined,
51
- rating: data.rating || undefined,
52
- collectionId: data.collectionId || undefined,
53
- });
54
-
55
- // Attach media if provided
56
- if (data.mediaIds && data.mediaIds.length > 0) {
57
- await c.var.services.media.attachToPost(post.id, data.mediaIds);
58
- }
59
-
60
- // Redirect to the new post's permalink
61
- const permalink = post.path ? `/${post.path}` : `/p/${sqid.encode(post.id)}`;
62
- return dsRedirect(permalink);
63
- });