@jant/core 0.3.27 → 0.3.28

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 (313) hide show
  1. package/dist/client/client.css +1 -0
  2. package/dist/client/client.js +31561 -0
  3. package/dist/index.js +15209 -15
  4. package/package.json +21 -15
  5. package/src/__tests__/helpers/app.ts +19 -3
  6. package/src/__tests__/helpers/db.ts +44 -0
  7. package/src/__tests__/helpers/lingui-core-macro-mock.ts +33 -0
  8. package/src/app.tsx +111 -174
  9. package/src/client.ts +13 -0
  10. package/src/db/migrations/0007_post_collections_m2m.sql +94 -0
  11. package/src/db/migrations/0008_add_collection_dividers.sql +8 -0
  12. package/src/db/migrations/0009_drop_collection_show_divider.sql +2 -0
  13. package/src/db/migrations/0010_add_performance_indexes.sql +16 -0
  14. package/src/db/schema.ts +24 -4
  15. package/src/i18n/locales/en.po +810 -385
  16. package/src/i18n/locales/en.ts +1 -1
  17. package/src/i18n/locales/zh-Hans.po +733 -522
  18. package/src/i18n/locales/zh-Hans.ts +1 -1
  19. package/src/i18n/locales/zh-Hant.po +733 -522
  20. package/src/i18n/locales/zh-Hant.ts +1 -1
  21. package/src/i18n/middleware.ts +7 -11
  22. package/src/index.ts +1 -1
  23. package/src/lib/__tests__/icons.test.ts +178 -0
  24. package/src/lib/__tests__/resolve-config.test.ts +184 -0
  25. package/src/lib/__tests__/schemas.test.ts +12 -6
  26. package/src/lib/__tests__/theme.test.ts +62 -0
  27. package/src/lib/__tests__/timezones.test.ts +1 -1
  28. package/src/lib/__tests__/url.test.ts +12 -0
  29. package/src/lib/__tests__/view.test.ts +1 -5
  30. package/src/lib/avatar-upload.ts +18 -10
  31. package/src/lib/collection-form-bridge.ts +52 -0
  32. package/src/lib/collections-reorder.ts +28 -0
  33. package/src/lib/compose-bridge.ts +251 -0
  34. package/src/lib/errors.ts +116 -0
  35. package/src/lib/excerpt.ts +1 -1
  36. package/src/lib/favicon.ts +3 -5
  37. package/src/lib/html.ts +22 -0
  38. package/src/lib/icon-catalog.ts +181 -0
  39. package/src/lib/icons.ts +202 -0
  40. package/src/lib/navigation.ts +18 -33
  41. package/src/lib/pagination.ts +3 -2
  42. package/src/lib/post-form-bridge.ts +136 -0
  43. package/src/lib/render.tsx +11 -4
  44. package/src/lib/resolve-config.ts +157 -0
  45. package/src/lib/schemas.ts +76 -12
  46. package/src/lib/settings-bridge.ts +139 -0
  47. package/src/lib/storage.ts +37 -16
  48. package/src/lib/theme.ts +5 -7
  49. package/src/lib/timeline.ts +4 -8
  50. package/src/lib/toast.ts +134 -0
  51. package/src/lib/upload.ts +71 -0
  52. package/src/lib/url.ts +9 -1
  53. package/src/lib/version.ts +16 -0
  54. package/src/lib/view.ts +9 -10
  55. package/src/middleware/__tests__/auth.test.ts +6 -28
  56. package/src/middleware/__tests__/onboarding.test.ts +1 -1
  57. package/src/middleware/auth.ts +6 -12
  58. package/src/middleware/config.ts +51 -0
  59. package/src/middleware/error-handler.ts +56 -0
  60. package/src/middleware/onboarding.ts +1 -1
  61. package/src/preset.css +6 -0
  62. package/src/routes/__tests__/compose.test.ts +104 -17
  63. package/src/routes/api/__tests__/collections.test.ts +93 -2
  64. package/src/routes/api/__tests__/posts.test.ts +2 -1
  65. package/src/routes/api/__tests__/settings.test.ts +1 -1
  66. package/src/routes/api/collections.ts +64 -68
  67. package/src/routes/api/nav-items.ts +21 -59
  68. package/src/routes/api/pages.ts +18 -46
  69. package/src/routes/api/posts.ts +64 -86
  70. package/src/routes/api/search.ts +6 -4
  71. package/src/routes/api/settings.ts +8 -24
  72. package/src/routes/api/upload.ts +55 -53
  73. package/src/routes/auth/__tests__/setup.test.ts +118 -0
  74. package/src/routes/auth/reset.tsx +17 -66
  75. package/src/routes/auth/setup.tsx +67 -11
  76. package/src/routes/auth/signin.tsx +44 -8
  77. package/src/routes/compose.tsx +194 -0
  78. package/src/routes/dash/__tests__/font-theme.test.ts +110 -0
  79. package/src/routes/dash/__tests__/pages.test.ts +2 -2
  80. package/src/routes/dash/__tests__/settings-avatar.test.ts +23 -12
  81. package/src/routes/dash/appearance.tsx +173 -0
  82. package/src/routes/dash/collections.tsx +80 -14
  83. package/src/routes/dash/index.tsx +12 -14
  84. package/src/routes/dash/media.tsx +46 -49
  85. package/src/routes/dash/pages.tsx +85 -37
  86. package/src/routes/dash/posts.tsx +60 -23
  87. package/src/routes/dash/redirects.tsx +43 -33
  88. package/src/routes/dash/settings.tsx +234 -214
  89. package/src/routes/feed/__tests__/rss.test.ts +7 -3
  90. package/src/routes/feed/rss.ts +11 -16
  91. package/src/routes/feed/sitemap.ts +15 -9
  92. package/src/routes/pages/__tests__/collections.test.ts +9 -8
  93. package/src/routes/pages/archive.tsx +2 -2
  94. package/src/routes/pages/collection.tsx +76 -9
  95. package/src/routes/pages/collections.tsx +3 -1
  96. package/src/routes/pages/featured.tsx +2 -2
  97. package/src/routes/pages/home.tsx +3 -3
  98. package/src/routes/pages/latest.tsx +2 -2
  99. package/src/routes/pages/page.tsx +2 -2
  100. package/src/routes/pages/post.tsx +2 -2
  101. package/src/routes/pages/search.tsx +2 -2
  102. package/src/services/__tests__/collection.test.ts +324 -34
  103. package/src/services/__tests__/media.test.ts +1 -1
  104. package/src/services/__tests__/page.test.ts +116 -1
  105. package/src/services/auth.ts +88 -0
  106. package/src/services/collection.ts +169 -30
  107. package/src/services/index.ts +8 -3
  108. package/src/services/media.ts +39 -12
  109. package/src/services/navigation.ts +17 -5
  110. package/src/services/page.ts +24 -4
  111. package/src/services/post.ts +87 -19
  112. package/src/services/search.ts +0 -1
  113. package/src/services/settings.ts +21 -13
  114. package/src/style.css +3 -0
  115. package/src/styles/components.css +42 -1
  116. package/src/styles/tokens.css +4 -0
  117. package/src/styles/ui.css +902 -73
  118. package/src/types/app-context.ts +25 -0
  119. package/src/types/bindings.ts +1 -0
  120. package/src/types/config.ts +60 -23
  121. package/src/types/entities.ts +12 -2
  122. package/src/types/lingui-react-macro.d.ts +3 -3
  123. package/src/types/operations.ts +2 -4
  124. package/src/types/views.ts +1 -3
  125. package/src/ui/__tests__/font-themes.test.ts +27 -8
  126. package/src/ui/color-themes.ts +1 -1
  127. package/src/ui/components/__tests__/jant-collection-form.test.ts +153 -0
  128. package/src/ui/components/__tests__/jant-compose-dialog.test.ts +512 -0
  129. package/src/ui/components/__tests__/jant-compose-editor.test.ts +272 -0
  130. package/src/ui/components/__tests__/jant-post-form.test.ts +172 -0
  131. package/src/ui/components/__tests__/jant-settings-avatar.test.ts +235 -0
  132. package/src/ui/components/__tests__/jant-settings-general.test.ts +319 -0
  133. package/src/ui/components/collection-types.ts +45 -0
  134. package/src/ui/components/compose-types.ts +75 -0
  135. package/src/ui/components/jant-collection-form.ts +512 -0
  136. package/src/ui/components/jant-compose-dialog.ts +494 -0
  137. package/src/ui/components/jant-compose-editor.ts +799 -0
  138. package/src/ui/components/jant-post-form.ts +290 -0
  139. package/src/ui/components/jant-settings-avatar.ts +231 -0
  140. package/src/ui/components/jant-settings-general.ts +436 -0
  141. package/src/ui/components/post-form-template.ts +260 -0
  142. package/src/ui/components/post-form-types.ts +87 -0
  143. package/src/ui/components/settings-types.ts +62 -0
  144. package/src/ui/compose/ComposeDialog.tsx +141 -385
  145. package/src/ui/compose/ComposePrompt.tsx +3 -3
  146. package/src/ui/dash/PostList.tsx +55 -61
  147. package/src/ui/dash/appearance/AdvancedContent.tsx +80 -0
  148. package/src/ui/dash/appearance/AppearanceNav.tsx +56 -0
  149. package/src/ui/dash/appearance/ColorThemeContent.tsx +129 -0
  150. package/src/ui/dash/appearance/FontThemeContent.tsx +98 -0
  151. package/src/ui/dash/collections/CollectionForm.tsx +130 -117
  152. package/src/ui/dash/collections/CollectionsListContent.tsx +102 -41
  153. package/src/ui/dash/collections/IconPickerGrid.tsx +50 -0
  154. package/src/ui/dash/collections/ViewCollectionContent.tsx +14 -3
  155. package/src/ui/dash/index.ts +1 -1
  156. package/src/ui/dash/posts/PostForm.tsx +248 -0
  157. package/src/ui/dash/settings/AccountContent.tsx +69 -80
  158. package/src/ui/dash/settings/GeneralContent.tsx +159 -478
  159. package/src/ui/dash/settings/SettingsNav.tsx +4 -4
  160. package/src/ui/font-themes.ts +115 -32
  161. package/src/ui/layouts/BaseLayout.tsx +49 -19
  162. package/src/ui/layouts/DashLayout.tsx +14 -9
  163. package/src/ui/layouts/SiteLayout.tsx +38 -23
  164. package/src/ui/pages/CollectionPage.tsx +12 -2
  165. package/src/ui/pages/CollectionsPage.tsx +27 -27
  166. package/src/ui/pages/HomePage.tsx +15 -6
  167. package/src/ui/pages/SearchPage.tsx +1 -2
  168. package/src/ui/shared/CollectionsSidebar.tsx +59 -0
  169. package/src/ui/shared/Pagination.tsx +2 -2
  170. package/dist/app.js +0 -267
  171. package/dist/auth.js +0 -39
  172. package/dist/client.js +0 -13
  173. package/dist/db/index.js +0 -10
  174. package/dist/db/schema.js +0 -224
  175. package/dist/i18n/Trans.js +0 -24
  176. package/dist/i18n/context.js +0 -58
  177. package/dist/i18n/detect.js +0 -26
  178. package/dist/i18n/i18n.js +0 -49
  179. package/dist/i18n/index.js +0 -44
  180. package/dist/i18n/locales/en.js +0 -1
  181. package/dist/i18n/locales/zh-Hans.js +0 -1
  182. package/dist/i18n/locales/zh-Hant.js +0 -1
  183. package/dist/i18n/locales.js +0 -13
  184. package/dist/i18n/middleware.js +0 -30
  185. package/dist/lib/avatar-upload.js +0 -134
  186. package/dist/lib/config.js +0 -143
  187. package/dist/lib/constants.js +0 -50
  188. package/dist/lib/excerpt.js +0 -76
  189. package/dist/lib/favicon.js +0 -102
  190. package/dist/lib/feed.js +0 -123
  191. package/dist/lib/image-processor.js +0 -187
  192. package/dist/lib/image.js +0 -97
  193. package/dist/lib/index.js +0 -7
  194. package/dist/lib/markdown.js +0 -83
  195. package/dist/lib/media-helpers.js +0 -49
  196. package/dist/lib/media-upload.js +0 -104
  197. package/dist/lib/nav-reorder.js +0 -27
  198. package/dist/lib/navigation.js +0 -79
  199. package/dist/lib/pagination.js +0 -44
  200. package/dist/lib/render.js +0 -53
  201. package/dist/lib/schemas.js +0 -174
  202. package/dist/lib/sqid.js +0 -72
  203. package/dist/lib/sse.js +0 -218
  204. package/dist/lib/storage.js +0 -164
  205. package/dist/lib/theme.js +0 -65
  206. package/dist/lib/time.js +0 -159
  207. package/dist/lib/timeline.js +0 -95
  208. package/dist/lib/timezones.js +0 -388
  209. package/dist/lib/url.js +0 -89
  210. package/dist/lib/view.js +0 -217
  211. package/dist/middleware/auth.js +0 -52
  212. package/dist/middleware/onboarding.js +0 -41
  213. package/dist/routes/api/collections.js +0 -124
  214. package/dist/routes/api/nav-items.js +0 -104
  215. package/dist/routes/api/pages.js +0 -91
  216. package/dist/routes/api/posts.js +0 -218
  217. package/dist/routes/api/search.js +0 -48
  218. package/dist/routes/api/settings.js +0 -68
  219. package/dist/routes/api/upload.js +0 -246
  220. package/dist/routes/auth/reset.js +0 -221
  221. package/dist/routes/auth/setup.js +0 -194
  222. package/dist/routes/auth/signin.js +0 -176
  223. package/dist/routes/compose.js +0 -48
  224. package/dist/routes/dash/collections.js +0 -115
  225. package/dist/routes/dash/index.js +0 -118
  226. package/dist/routes/dash/media.js +0 -106
  227. package/dist/routes/dash/pages.js +0 -294
  228. package/dist/routes/dash/posts.js +0 -244
  229. package/dist/routes/dash/redirects.js +0 -257
  230. package/dist/routes/dash/settings.js +0 -379
  231. package/dist/routes/feed/rss.js +0 -62
  232. package/dist/routes/feed/sitemap.js +0 -49
  233. package/dist/routes/pages/archive.js +0 -62
  234. package/dist/routes/pages/collection.js +0 -34
  235. package/dist/routes/pages/collections.js +0 -28
  236. package/dist/routes/pages/featured.js +0 -36
  237. package/dist/routes/pages/home.js +0 -64
  238. package/dist/routes/pages/latest.js +0 -45
  239. package/dist/routes/pages/page.js +0 -68
  240. package/dist/routes/pages/post.js +0 -44
  241. package/dist/routes/pages/search.js +0 -54
  242. package/dist/services/collection.js +0 -109
  243. package/dist/services/index.js +0 -24
  244. package/dist/services/media.js +0 -117
  245. package/dist/services/navigation.js +0 -91
  246. package/dist/services/page.js +0 -84
  247. package/dist/services/post.js +0 -229
  248. package/dist/services/redirect.js +0 -48
  249. package/dist/services/search.js +0 -67
  250. package/dist/services/settings.js +0 -68
  251. package/dist/types/bindings.js +0 -3
  252. package/dist/types/config.js +0 -147
  253. package/dist/types/constants.js +0 -27
  254. package/dist/types/entities.js +0 -3
  255. package/dist/types/lingui-react-macro.d.js +0 -9
  256. package/dist/types/operations.js +0 -3
  257. package/dist/types/props.js +0 -3
  258. package/dist/types/sortablejs.d.js +0 -5
  259. package/dist/types/views.js +0 -5
  260. package/dist/types.js +0 -11
  261. package/dist/ui/color-themes.js +0 -268
  262. package/dist/ui/compose/ComposeDialog.js +0 -467
  263. package/dist/ui/compose/ComposePrompt.js +0 -55
  264. package/dist/ui/dash/ActionButtons.js +0 -46
  265. package/dist/ui/dash/CrudPageHeader.js +0 -22
  266. package/dist/ui/dash/DangerZone.js +0 -36
  267. package/dist/ui/dash/FormatBadge.js +0 -27
  268. package/dist/ui/dash/ListItemRow.js +0 -21
  269. package/dist/ui/dash/PageForm.js +0 -195
  270. package/dist/ui/dash/PostForm.js +0 -395
  271. package/dist/ui/dash/PostList.js +0 -83
  272. package/dist/ui/dash/StatusBadge.js +0 -46
  273. package/dist/ui/dash/collections/CollectionForm.js +0 -152
  274. package/dist/ui/dash/collections/CollectionsListContent.js +0 -68
  275. package/dist/ui/dash/collections/ViewCollectionContent.js +0 -96
  276. package/dist/ui/dash/index.js +0 -10
  277. package/dist/ui/dash/media/MediaListContent.js +0 -166
  278. package/dist/ui/dash/media/ViewMediaContent.js +0 -212
  279. package/dist/ui/dash/pages/LinkFormContent.js +0 -130
  280. package/dist/ui/dash/pages/UnifiedPagesContent.js +0 -193
  281. package/dist/ui/dash/settings/AccountContent.js +0 -209
  282. package/dist/ui/dash/settings/AppearanceContent.js +0 -259
  283. package/dist/ui/dash/settings/GeneralContent.js +0 -536
  284. package/dist/ui/dash/settings/SettingsNav.js +0 -41
  285. package/dist/ui/feed/LinkCard.js +0 -72
  286. package/dist/ui/feed/NoteCard.js +0 -58
  287. package/dist/ui/feed/QuoteCard.js +0 -63
  288. package/dist/ui/feed/ThreadPreview.js +0 -48
  289. package/dist/ui/feed/TimelineFeed.js +0 -41
  290. package/dist/ui/feed/TimelineItem.js +0 -27
  291. package/dist/ui/font-themes.js +0 -36
  292. package/dist/ui/layouts/BaseLayout.js +0 -153
  293. package/dist/ui/layouts/DashLayout.js +0 -141
  294. package/dist/ui/layouts/SiteLayout.js +0 -169
  295. package/dist/ui/pages/ArchivePage.js +0 -143
  296. package/dist/ui/pages/CollectionPage.js +0 -70
  297. package/dist/ui/pages/CollectionsPage.js +0 -76
  298. package/dist/ui/pages/FeaturedPage.js +0 -24
  299. package/dist/ui/pages/HomePage.js +0 -24
  300. package/dist/ui/pages/PostPage.js +0 -55
  301. package/dist/ui/pages/SearchPage.js +0 -122
  302. package/dist/ui/pages/SinglePage.js +0 -23
  303. package/dist/ui/shared/EmptyState.js +0 -27
  304. package/dist/ui/shared/MediaGallery.js +0 -35
  305. package/dist/ui/shared/Pagination.js +0 -195
  306. package/dist/ui/shared/ThreadView.js +0 -108
  307. package/dist/ui/shared/index.js +0 -5
  308. package/dist/vendor/datastar.js +0 -1606
  309. package/src/lib/__tests__/config.test.ts +0 -192
  310. package/src/lib/config.ts +0 -167
  311. package/src/routes/compose.ts +0 -63
  312. package/src/ui/dash/PostForm.tsx +0 -360
  313. 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
- });