@jant/core 0.3.26 → 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 (314) 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 +112 -173
  9. package/src/auth.ts +4 -1
  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 -265
  172. package/dist/auth.js +0 -36
  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,95 +0,0 @@
1
- /**
2
- * Timeline Data Assembly
3
- *
4
- * Shared helper for assembling timeline items with media and thread previews.
5
- * Used by page rendering with page-based pagination.
6
- */ import { buildMediaMap } from "./media-helpers.js";
7
- import { createMediaContext, toPostView, toPostViews } from "./view.js";
8
- const DEFAULT_PAGE_SIZE = 20;
9
- /**
10
- * Assembles a page of timeline items with media attachments and thread previews.
11
- *
12
- * Fetches posts using offset-based pagination, batch-loads media, identifies
13
- * threads, and returns render-ready `TimelineItemView[]` with page info.
14
- *
15
- * @param c - Hono context (provides services + env)
16
- * @param options - Optional page number (1-indexed, defaults to 1)
17
- * @returns Assembled timeline items with pagination info
18
- *
19
- * @example
20
- * ```ts
21
- * const { items, currentPage, totalPages } = await assembleTimeline(c);
22
- * const { items, currentPage, totalPages } = await assembleTimeline(c, { page: 2 });
23
- * ```
24
- */ export async function assembleTimeline(c, options) {
25
- const pageSize = parseInt(c.env.PAGE_SIZE ?? String(DEFAULT_PAGE_SIZE), 10) || DEFAULT_PAGE_SIZE;
26
- const page = Math.max(1, options?.page ?? 1);
27
- const offset = (page - 1) * pageSize;
28
- // Get total count for pagination
29
- const totalCount = await c.var.services.posts.count({
30
- status: "published",
31
- excludeReplies: true
32
- });
33
- const totalPages = Math.max(1, Math.ceil(totalCount / pageSize));
34
- // Fetch posts for the current page
35
- const posts = await c.var.services.posts.list({
36
- status: "published",
37
- excludeReplies: true,
38
- limit: pageSize,
39
- offset
40
- });
41
- if (posts.length === 0) {
42
- return {
43
- items: [],
44
- currentPage: page,
45
- totalPages
46
- };
47
- }
48
- // Batch load media attachments
49
- const postIds = posts.map((p)=>p.id);
50
- const rawMediaMap = await c.var.services.media.getByPostIds(postIds);
51
- const mediaCtx = createMediaContext(c);
52
- const mediaMap = buildMediaMap(rawMediaMap, mediaCtx.r2PublicUrl, mediaCtx.imageTransformUrl, mediaCtx.s3PublicUrl);
53
- // Get reply counts to identify thread roots
54
- const replyCounts = await c.var.services.posts.getReplyCounts(postIds);
55
- const threadRootIds = postIds.filter((id)=>(replyCounts.get(id) ?? 0) > 0);
56
- // Batch load thread previews
57
- const threadPreviews = await c.var.services.posts.getThreadPreviews(threadRootIds, 3);
58
- // Batch load media for preview replies
59
- const previewReplyIds = [];
60
- for (const replies of threadPreviews.values()){
61
- for (const reply of replies){
62
- previewReplyIds.push(reply.id);
63
- }
64
- }
65
- const previewMediaMap = previewReplyIds.length > 0 ? buildMediaMap(await c.var.services.media.getByPostIds(previewReplyIds), mediaCtx.r2PublicUrl, mediaCtx.imageTransformUrl, mediaCtx.s3PublicUrl) : new Map();
66
- // Assemble timeline items with View Models
67
- const items = posts.map((post)=>{
68
- const postView = toPostView({
69
- ...post,
70
- mediaAttachments: mediaMap.get(post.id) ?? []
71
- }, mediaCtx);
72
- const replyCount = replyCounts.get(post.id) ?? 0;
73
- const previewReplies = threadPreviews.get(post.id);
74
- if (replyCount > 0 && previewReplies) {
75
- return {
76
- post: postView,
77
- threadPreview: {
78
- replies: toPostViews(previewReplies.map((r)=>({
79
- ...r,
80
- mediaAttachments: previewMediaMap.get(r.id) ?? []
81
- })), mediaCtx),
82
- totalReplyCount: replyCount
83
- }
84
- };
85
- }
86
- return {
87
- post: postView
88
- };
89
- });
90
- return {
91
- items,
92
- currentPage: page,
93
- totalPages
94
- };
95
- }
@@ -1,388 +0,0 @@
1
- /**
2
- * Timezone List
3
- *
4
- * Provides a curated list of timezones for the settings UI
5
- * and a helper to map IANA timezone names to our list entries.
6
- */ export const TIMEZONES = [
7
- {
8
- value: "International Date Line West",
9
- label: "(UTC-12:00) International Date Line West",
10
- offset: "-12:00",
11
- iana: [
12
- "Etc/GMT+12"
13
- ]
14
- },
15
- {
16
- value: "American Samoa",
17
- label: "(UTC-11:00) American Samoa",
18
- offset: "-11:00",
19
- iana: [
20
- "Pacific/Pago_Pago",
21
- "Pacific/Midway",
22
- "Etc/GMT+11"
23
- ]
24
- },
25
- {
26
- value: "Hawaii",
27
- label: "(UTC-10:00) Hawaii",
28
- offset: "-10:00",
29
- iana: [
30
- "Pacific/Honolulu",
31
- "Etc/GMT+10"
32
- ]
33
- },
34
- {
35
- value: "Alaska",
36
- label: "(UTC-09:00) Alaska",
37
- offset: "-09:00",
38
- iana: [
39
- "America/Anchorage",
40
- "America/Juneau",
41
- "America/Nome"
42
- ]
43
- },
44
- {
45
- value: "Pacific Time (US & Canada)",
46
- label: "(UTC-08:00) Pacific Time (US & Canada)",
47
- offset: "-08:00",
48
- iana: [
49
- "America/Los_Angeles",
50
- "America/Vancouver",
51
- "America/Tijuana"
52
- ]
53
- },
54
- {
55
- value: "Mountain Time (US & Canada)",
56
- label: "(UTC-07:00) Mountain Time (US & Canada)",
57
- offset: "-07:00",
58
- iana: [
59
- "America/Denver",
60
- "America/Edmonton",
61
- "America/Phoenix",
62
- "America/Boise"
63
- ]
64
- },
65
- {
66
- value: "Central Time (US & Canada)",
67
- label: "(UTC-06:00) Central Time (US & Canada)",
68
- offset: "-06:00",
69
- iana: [
70
- "America/Chicago",
71
- "America/Winnipeg",
72
- "America/Mexico_City",
73
- "America/Guatemala"
74
- ]
75
- },
76
- {
77
- value: "Eastern Time (US & Canada)",
78
- label: "(UTC-05:00) Eastern Time (US & Canada)",
79
- offset: "-05:00",
80
- iana: [
81
- "America/New_York",
82
- "America/Toronto",
83
- "America/Detroit",
84
- "America/Indiana/Indianapolis",
85
- "America/Bogota",
86
- "America/Lima"
87
- ]
88
- },
89
- {
90
- value: "Atlantic Time (Canada)",
91
- label: "(UTC-04:00) Atlantic Time (Canada)",
92
- offset: "-04:00",
93
- iana: [
94
- "America/Halifax",
95
- "America/Caracas",
96
- "America/Santiago",
97
- "America/La_Paz"
98
- ]
99
- },
100
- {
101
- value: "Newfoundland",
102
- label: "(UTC-03:30) Newfoundland",
103
- offset: "-03:30",
104
- iana: [
105
- "America/St_Johns"
106
- ]
107
- },
108
- {
109
- value: "Buenos Aires",
110
- label: "(UTC-03:00) Buenos Aires",
111
- offset: "-03:00",
112
- iana: [
113
- "America/Argentina/Buenos_Aires",
114
- "America/Sao_Paulo",
115
- "America/Montevideo"
116
- ]
117
- },
118
- {
119
- value: "Mid-Atlantic",
120
- label: "(UTC-02:00) Mid-Atlantic",
121
- offset: "-02:00",
122
- iana: [
123
- "Atlantic/South_Georgia",
124
- "Etc/GMT+2"
125
- ]
126
- },
127
- {
128
- value: "Azores",
129
- label: "(UTC-01:00) Azores",
130
- offset: "-01:00",
131
- iana: [
132
- "Atlantic/Azores",
133
- "Atlantic/Cape_Verde"
134
- ]
135
- },
136
- {
137
- value: "UTC",
138
- label: "(UTC+00:00) UTC",
139
- offset: "+00:00",
140
- iana: [
141
- "Etc/UTC",
142
- "UTC",
143
- "Etc/GMT",
144
- "Africa/Accra"
145
- ]
146
- },
147
- {
148
- value: "London",
149
- label: "(UTC+00:00) London",
150
- offset: "+00:00",
151
- iana: [
152
- "Europe/London",
153
- "Europe/Dublin",
154
- "Europe/Lisbon"
155
- ]
156
- },
157
- {
158
- value: "Central European Time",
159
- label: "(UTC+01:00) Central European Time",
160
- offset: "+01:00",
161
- iana: [
162
- "Europe/Paris",
163
- "Europe/Berlin",
164
- "Europe/Amsterdam",
165
- "Europe/Rome",
166
- "Europe/Madrid",
167
- "Europe/Brussels",
168
- "Europe/Vienna",
169
- "Europe/Warsaw",
170
- "Europe/Prague",
171
- "Europe/Stockholm",
172
- "Europe/Oslo",
173
- "Europe/Copenhagen",
174
- "Europe/Zurich",
175
- "Africa/Lagos"
176
- ]
177
- },
178
- {
179
- value: "Eastern European Time",
180
- label: "(UTC+02:00) Eastern European Time",
181
- offset: "+02:00",
182
- iana: [
183
- "Europe/Helsinki",
184
- "Europe/Athens",
185
- "Europe/Bucharest",
186
- "Europe/Istanbul",
187
- "Africa/Cairo",
188
- "Africa/Johannesburg",
189
- "Asia/Jerusalem",
190
- "Asia/Beirut"
191
- ]
192
- },
193
- {
194
- value: "Moscow",
195
- label: "(UTC+03:00) Moscow",
196
- offset: "+03:00",
197
- iana: [
198
- "Europe/Moscow",
199
- "Europe/Minsk",
200
- "Asia/Baghdad",
201
- "Asia/Riyadh",
202
- "Africa/Nairobi",
203
- "Asia/Kuwait"
204
- ]
205
- },
206
- {
207
- value: "Tehran",
208
- label: "(UTC+03:30) Tehran",
209
- offset: "+03:30",
210
- iana: [
211
- "Asia/Tehran"
212
- ]
213
- },
214
- {
215
- value: "Dubai",
216
- label: "(UTC+04:00) Dubai",
217
- offset: "+04:00",
218
- iana: [
219
- "Asia/Dubai",
220
- "Asia/Muscat",
221
- "Asia/Baku",
222
- "Asia/Tbilisi"
223
- ]
224
- },
225
- {
226
- value: "Kabul",
227
- label: "(UTC+04:30) Kabul",
228
- offset: "+04:30",
229
- iana: [
230
- "Asia/Kabul"
231
- ]
232
- },
233
- {
234
- value: "Karachi",
235
- label: "(UTC+05:00) Karachi",
236
- offset: "+05:00",
237
- iana: [
238
- "Asia/Karachi",
239
- "Asia/Tashkent",
240
- "Asia/Yekaterinburg"
241
- ]
242
- },
243
- {
244
- value: "Mumbai",
245
- label: "(UTC+05:30) Mumbai",
246
- offset: "+05:30",
247
- iana: [
248
- "Asia/Kolkata",
249
- "Asia/Calcutta",
250
- "Asia/Colombo"
251
- ]
252
- },
253
- {
254
- value: "Kathmandu",
255
- label: "(UTC+05:45) Kathmandu",
256
- offset: "+05:45",
257
- iana: [
258
- "Asia/Kathmandu"
259
- ]
260
- },
261
- {
262
- value: "Dhaka",
263
- label: "(UTC+06:00) Dhaka",
264
- offset: "+06:00",
265
- iana: [
266
- "Asia/Dhaka",
267
- "Asia/Almaty",
268
- "Asia/Omsk"
269
- ]
270
- },
271
- {
272
- value: "Yangon",
273
- label: "(UTC+06:30) Yangon",
274
- offset: "+06:30",
275
- iana: [
276
- "Asia/Yangon",
277
- "Asia/Rangoon"
278
- ]
279
- },
280
- {
281
- value: "Bangkok",
282
- label: "(UTC+07:00) Bangkok",
283
- offset: "+07:00",
284
- iana: [
285
- "Asia/Bangkok",
286
- "Asia/Jakarta",
287
- "Asia/Ho_Chi_Minh",
288
- "Asia/Krasnoyarsk"
289
- ]
290
- },
291
- {
292
- value: "Beijing",
293
- label: "(UTC+08:00) Beijing",
294
- offset: "+08:00",
295
- iana: [
296
- "Asia/Shanghai",
297
- "Asia/Hong_Kong",
298
- "Asia/Taipei",
299
- "Asia/Singapore",
300
- "Asia/Kuala_Lumpur",
301
- "Asia/Makassar",
302
- "Asia/Irkutsk",
303
- "Australia/Perth"
304
- ]
305
- },
306
- {
307
- value: "Tokyo",
308
- label: "(UTC+09:00) Tokyo",
309
- offset: "+09:00",
310
- iana: [
311
- "Asia/Tokyo",
312
- "Asia/Seoul",
313
- "Asia/Yakutsk"
314
- ]
315
- },
316
- {
317
- value: "Adelaide",
318
- label: "(UTC+09:30) Adelaide",
319
- offset: "+09:30",
320
- iana: [
321
- "Australia/Adelaide",
322
- "Australia/Darwin"
323
- ]
324
- },
325
- {
326
- value: "Sydney",
327
- label: "(UTC+10:00) Sydney",
328
- offset: "+10:00",
329
- iana: [
330
- "Australia/Sydney",
331
- "Australia/Melbourne",
332
- "Australia/Brisbane",
333
- "Australia/Hobart",
334
- "Pacific/Guam",
335
- "Asia/Vladivostok"
336
- ]
337
- },
338
- {
339
- value: "Noumea",
340
- label: "(UTC+11:00) Noumea",
341
- offset: "+11:00",
342
- iana: [
343
- "Pacific/Noumea",
344
- "Asia/Magadan",
345
- "Pacific/Guadalcanal"
346
- ]
347
- },
348
- {
349
- value: "Auckland",
350
- label: "(UTC+12:00) Auckland",
351
- offset: "+12:00",
352
- iana: [
353
- "Pacific/Auckland",
354
- "Pacific/Fiji",
355
- "Asia/Kamchatka"
356
- ]
357
- },
358
- {
359
- value: "Nuku'alofa",
360
- label: "(UTC+13:00) Nuku'alofa",
361
- offset: "+13:00",
362
- iana: [
363
- "Pacific/Tongatapu",
364
- "Pacific/Apia"
365
- ]
366
- }
367
- ];
368
- /**
369
- * Maps an IANA timezone name (e.g. "Asia/Shanghai") to our timezone list value
370
- * (e.g. "Beijing"). Returns "UTC" if no match found.
371
- *
372
- * @param iana - IANA timezone identifier from Intl.DateTimeFormat
373
- * @returns The matching timezone value from TIMEZONES
374
- *
375
- * @example
376
- * ```ts
377
- * mapIanaToTimezone("Asia/Shanghai"); // "Beijing"
378
- * mapIanaToTimezone("America/New_York"); // "Eastern Time (US & Canada)"
379
- * mapIanaToTimezone("Unknown/Zone"); // "UTC"
380
- * ```
381
- */ export function mapIanaToTimezone(iana) {
382
- for (const tz of TIMEZONES){
383
- if (tz.iana.includes(iana)) {
384
- return tz.value;
385
- }
386
- }
387
- return "UTC";
388
- }
package/dist/lib/url.js DELETED
@@ -1,89 +0,0 @@
1
- /**
2
- * URL Utilities
3
- */ /**
4
- * Extracts the hostname (domain) from a URL string.
5
- *
6
- * Parses a full URL and returns just the hostname portion (e.g., "example.com" from
7
- * "https://example.com/path"). Returns `null` if the URL is malformed or cannot be parsed.
8
- *
9
- * @param url - The full URL string to extract the domain from
10
- * @returns The hostname/domain if valid, or `null` if parsing fails
11
- *
12
- * @example
13
- * ```ts
14
- * const domain = extractDomain("https://www.example.com/path");
15
- * // Returns: "www.example.com"
16
- *
17
- * const invalid = extractDomain("not-a-url");
18
- * // Returns: null
19
- * ```
20
- */ export function extractDomain(url) {
21
- try {
22
- const parsed = new URL(url);
23
- return parsed.hostname;
24
- } catch {
25
- return null;
26
- }
27
- }
28
- /**
29
- * Normalizes a path by removing slashes and converting to lowercase.
30
- *
31
- * Trims whitespace, converts to lowercase, removes leading and trailing slashes,
32
- * and collapses multiple consecutive slashes into single slashes. Used to create
33
- * consistent path representations for routing and storage.
34
- *
35
- * @param path - The path string to normalize
36
- * @returns The normalized path string
37
- *
38
- * @example
39
- * ```ts
40
- * const normalized = normalizePath(" /About/Contact// ");
41
- * // Returns: "about/contact"
42
- * ```
43
- */ export function normalizePath(path) {
44
- return path.trim().toLowerCase().replace(/^\/+|\/+$/g, "").replace(/\/+/g, "/");
45
- }
46
- /**
47
- * Checks if a string is a full URL with HTTP or HTTPS protocol.
48
- *
49
- * Validates whether a string starts with "http://" or "https://", indicating it's
50
- * a full URL rather than a relative path. Useful for distinguishing between internal
51
- * paths and external URLs.
52
- *
53
- * @param str - The string to check
54
- * @returns `true` if the string starts with http:// or https://, `false` otherwise
55
- *
56
- * @example
57
- * ```ts
58
- * isFullUrl("https://example.com"); // Returns: true
59
- * isFullUrl("/about"); // Returns: false
60
- * isFullUrl("example.com"); // Returns: false
61
- * ```
62
- */ export function isFullUrl(str) {
63
- return str.startsWith("http://") || str.startsWith("https://");
64
- }
65
- /**
66
- * Converts text to a URL-friendly slug.
67
- *
68
- * Transforms text into a lowercase, hyphen-separated slug by:
69
- * - Converting to lowercase
70
- * - Removing special characters (keeping only word characters, spaces, and hyphens)
71
- * - Replacing whitespace and underscores with hyphens
72
- * - Removing leading and trailing hyphens
73
- *
74
- * Used for generating clean URLs from titles and names.
75
- *
76
- * @param text - The text to convert to a slug
77
- * @returns The slugified string
78
- *
79
- * @example
80
- * ```ts
81
- * const slug = slugify("Hello World! This is a Test.");
82
- * // Returns: "hello-world-this-is-a-test"
83
- *
84
- * const slug = slugify(" Multiple Spaces ");
85
- * // Returns: "multiple-spaces"
86
- * ```
87
- */ export function slugify(text) {
88
- return text.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
89
- }