rune-lab 0.3.1 → 0.4.1

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 (397) hide show
  1. package/.gitignore +12 -0
  2. package/README.md +4 -4
  3. package/deno.json +50 -0
  4. package/justfile +5 -0
  5. package/package.json +3 -80
  6. package/scripts/build_pkg.ts +51 -0
  7. package/scripts/ci.just +26 -0
  8. package/scripts/deploy.just +21 -0
  9. package/scripts/dev.just +4 -0
  10. package/scripts/inject.just +72 -0
  11. package/src/RuneProvider.svelte +105 -0
  12. package/src/i18n/message-resolver.test.ts +103 -0
  13. package/src/i18n/message-resolver.ts +89 -0
  14. package/src/i18n/project.inlang/settings.json +26 -0
  15. package/src/i18n/translations/ar.json +89 -0
  16. package/src/i18n/translations/de.json +89 -0
  17. package/src/i18n/translations/en.json +89 -0
  18. package/src/i18n/translations/es.json +89 -0
  19. package/src/i18n/translations/fr.json +89 -0
  20. package/src/i18n/translations/hi.json +89 -0
  21. package/src/i18n/translations/it.json +89 -0
  22. package/src/i18n/translations/ja.json +89 -0
  23. package/src/i18n/translations/ko.json +89 -0
  24. package/src/i18n/translations/pt.json +89 -0
  25. package/src/i18n/translations/ru.json +89 -0
  26. package/src/i18n/translations/vi.json +89 -0
  27. package/src/i18n/translations/zh.json +89 -0
  28. package/src/kernel/deno.json +5 -0
  29. package/src/kernel/src/actions/portal.ts +41 -0
  30. package/src/kernel/src/actions/shortcut-listener.ts +50 -0
  31. package/src/kernel/src/context/app.svelte.ts +98 -0
  32. package/src/kernel/src/context/context.ts +31 -0
  33. package/src/kernel/src/context/stores.svelte.ts +96 -0
  34. package/src/kernel/src/context/types.ts +110 -0
  35. package/src/kernel/src/context/useRuneLab.ts +38 -0
  36. package/src/kernel/src/mod.ts +13 -0
  37. package/src/kernel/src/persistence/createConfigStore.svelte.ts +120 -0
  38. package/src/kernel/src/persistence/drivers.test.ts +95 -0
  39. package/src/kernel/src/persistence/drivers.ts +77 -0
  40. package/src/kernel/src/persistence/provider.test.ts +58 -0
  41. package/src/kernel/src/persistence/provider.ts +58 -0
  42. package/src/kernel/src/persistence/types.ts +19 -0
  43. package/src/kernel/src/persistence/usePersistence.ts +9 -0
  44. package/src/kernel/src/registry/mod.ts +221 -0
  45. package/src/kernel/src/registry/registry.test.ts +162 -0
  46. package/src/kernel/src/tokens/props.ts +94 -0
  47. package/src/mod.ts +10 -0
  48. package/src/runes/layout/deno.json +5 -0
  49. package/src/runes/layout/src/APP_CONFIGURATIONS.ts +56 -0
  50. package/{dist/ui/features/config → src/runes/layout/src}/AppSettingSelector.svelte +1 -1
  51. package/{dist/ui/layout → src/runes/layout/src}/ConnectedNavigationPanel.svelte +7 -8
  52. package/{dist/ui/layout → src/runes/layout/src}/ConnectedWorkspaceStrip.svelte +5 -3
  53. package/src/runes/layout/src/Icon.svelte +7 -0
  54. package/src/runes/layout/src/LanguageSelector.svelte +45 -0
  55. package/{dist/ui/layout → src/runes/layout/src}/NavigationPanel.svelte +1 -4
  56. package/src/runes/layout/src/ResourceSelector.svelte +90 -0
  57. package/src/runes/layout/src/ThemeSelector.svelte +50 -0
  58. package/{dist/ui/layout → src/runes/layout/src}/WorkspaceLayout.svelte +13 -3
  59. package/{dist/ui/layout → src/runes/layout/src}/WorkspaceStrip.svelte +1 -1
  60. package/src/runes/layout/src/connection-factory.ts +95 -0
  61. package/src/runes/layout/src/language.svelte.ts +44 -0
  62. package/src/runes/layout/src/mod.ts +71 -0
  63. package/src/runes/layout/src/store.svelte.ts +142 -0
  64. package/src/runes/layout/src/theme.svelte.ts +73 -0
  65. package/src/runes/layout/src/types.ts +11 -0
  66. package/src/runes/palettes/deno.json +8 -0
  67. package/{dist/ui/features/command-palette → src/runes/palettes/src/commands}/CommandPalette.svelte +4 -4
  68. package/src/runes/palettes/src/commands/mod.ts +2 -0
  69. package/src/runes/palettes/src/commands/store.svelte.ts +80 -0
  70. package/src/runes/palettes/src/mod.ts +51 -0
  71. package/{dist/ui/components → src/runes/palettes/src/notifications}/Toaster.svelte +1 -2
  72. package/src/runes/palettes/src/notifications/bridge.ts +56 -0
  73. package/src/runes/palettes/src/notifications/mod.ts +4 -0
  74. package/src/runes/palettes/src/notifications/store.svelte.ts +58 -0
  75. package/{dist/ui/features → src/runes/palettes/src}/shortcuts/ShortcutPalette.svelte +4 -4
  76. package/src/runes/palettes/src/shortcuts/mod.ts +4 -0
  77. package/src/runes/palettes/src/shortcuts/store.svelte.ts +85 -0
  78. package/src/runes/palettes/src/shortcuts/types.ts +4 -0
  79. package/src/runes/palettes/src/shortcuts/useShortcuts.ts +85 -0
  80. package/src/runes/plugins/money/deno.json +8 -0
  81. package/src/runes/plugins/money/src/CurrencySelector.svelte +41 -0
  82. package/src/runes/plugins/money/src/MoneyDisplay.svelte +142 -0
  83. package/src/runes/plugins/money/src/MoneyInput.svelte +208 -0
  84. package/src/runes/plugins/money/src/currency.svelte.ts +95 -0
  85. package/src/runes/plugins/money/src/currency.test.ts +40 -0
  86. package/src/runes/plugins/money/src/exchange-rate.svelte.ts +176 -0
  87. package/src/runes/plugins/money/src/exchange-rate.test.ts +95 -0
  88. package/src/runes/plugins/money/src/mod.ts +79 -0
  89. package/src/runes/plugins/money/src/money-primitive.test.ts +217 -0
  90. package/src/runes/plugins/money/src/money-primitive.ts +220 -0
  91. package/src/runes/plugins/money/src/money.test.ts +183 -0
  92. package/src/runes/plugins/money/src/money.ts +382 -0
  93. package/src/runes/plugins/money/src/strategies.test.ts +143 -0
  94. package/src/runes/plugins/money/src/strategies.ts +95 -0
  95. package/src/runes/plugins/money/src/types.ts +27 -0
  96. package/src/runes/plugins/money/src/useMoney.ts +261 -0
  97. package/src/runes/plugins/money/src/useMoneyFilter.ts +132 -0
  98. package/tsconfig.json +41 -0
  99. package/vite.config.ts +44 -0
  100. package/dist/core/index.d.ts +0 -4
  101. package/dist/core/index.d.ts.map +0 -1
  102. package/dist/core/index.js +0 -3
  103. package/dist/core/internal/message-resolver.d.ts +0 -42
  104. package/dist/core/internal/message-resolver.d.ts.map +0 -1
  105. package/dist/core/internal/message-resolver.js +0 -47
  106. package/dist/core/money/index.d.ts +0 -2
  107. package/dist/core/money/index.d.ts.map +0 -1
  108. package/dist/core/money/index.js +0 -2
  109. package/dist/core/money/money.d.ts +0 -57
  110. package/dist/core/money/money.d.ts.map +0 -1
  111. package/dist/core/money/money.js +0 -132
  112. package/dist/core/persistence/types.d.ts +0 -18
  113. package/dist/core/persistence/types.d.ts.map +0 -1
  114. package/dist/core/persistence/types.js +0 -2
  115. package/dist/index.d.ts +0 -5
  116. package/dist/index.js +0 -5
  117. package/dist/state/api.svelte.d.ts +0 -26
  118. package/dist/state/api.svelte.js +0 -65
  119. package/dist/state/app.svelte.d.ts +0 -48
  120. package/dist/state/app.svelte.js +0 -82
  121. package/dist/state/auth/index.d.ts +0 -2
  122. package/dist/state/auth/index.js +0 -2
  123. package/dist/state/auth/session.svelte.d.ts +0 -40
  124. package/dist/state/auth/session.svelte.js +0 -57
  125. package/dist/state/auth/types.d.ts +0 -30
  126. package/dist/state/auth/types.js +0 -3
  127. package/dist/state/cart.svelte.d.ts +0 -64
  128. package/dist/state/cart.svelte.js +0 -122
  129. package/dist/state/commands.svelte.d.ts +0 -46
  130. package/dist/state/commands.svelte.js +0 -62
  131. package/dist/state/composables/useMoney.d.ts +0 -15
  132. package/dist/state/composables/useMoney.js +0 -62
  133. package/dist/state/composables/usePersistence.d.ts +0 -2
  134. package/dist/state/composables/usePersistence.js +0 -5
  135. package/dist/state/composables/useRuneLab.d.ts +0 -19
  136. package/dist/state/composables/useRuneLab.js +0 -30
  137. package/dist/state/context.d.ts +0 -14
  138. package/dist/state/context.js +0 -15
  139. package/dist/state/createConfigStore.svelte.d.ts +0 -55
  140. package/dist/state/createConfigStore.svelte.js +0 -61
  141. package/dist/state/currency.svelte.d.ts +0 -28
  142. package/dist/state/currency.svelte.js +0 -80
  143. package/dist/state/daisyui.d.ts +0 -4
  144. package/dist/state/index.d.ts +0 -19
  145. package/dist/state/index.js +0 -25
  146. package/dist/state/language.svelte.d.ts +0 -64
  147. package/dist/state/language.svelte.js +0 -44
  148. package/dist/state/layout.svelte.d.ts +0 -49
  149. package/dist/state/layout.svelte.js +0 -108
  150. package/dist/state/persistence/drivers.d.ts +0 -12
  151. package/dist/state/persistence/drivers.js +0 -73
  152. package/dist/state/shortcuts.svelte.d.ts +0 -121
  153. package/dist/state/shortcuts.svelte.js +0 -222
  154. package/dist/state/theme.svelte.d.ts +0 -22
  155. package/dist/state/theme.svelte.js +0 -92
  156. package/dist/state/toast-bridge.d.ts +0 -29
  157. package/dist/state/toast-bridge.js +0 -43
  158. package/dist/state/toast.svelte.d.ts +0 -27
  159. package/dist/state/toast.svelte.js +0 -43
  160. package/dist/ui/actions/portal.d.ts +0 -7
  161. package/dist/ui/actions/portal.js +0 -32
  162. package/dist/ui/components/ApiMonitor.svelte +0 -162
  163. package/dist/ui/components/ApiMonitor.svelte.d.ts +0 -3
  164. package/dist/ui/components/Icon.svelte +0 -108
  165. package/dist/ui/components/Icon.svelte.d.ts +0 -14
  166. package/dist/ui/components/Kyntharil.svelte +0 -48
  167. package/dist/ui/components/Kyntharil.svelte.d.ts +0 -6
  168. package/dist/ui/components/RuneProvider.svelte +0 -198
  169. package/dist/ui/components/RuneProvider.svelte.d.ts +0 -42
  170. package/dist/ui/components/Toaster.svelte.d.ts +0 -18
  171. package/dist/ui/components/money/MoneyDisplay.svelte +0 -69
  172. package/dist/ui/components/money/MoneyDisplay.svelte.d.ts +0 -21
  173. package/dist/ui/components/money/MoneyDisplay.svelte.test.d.ts +0 -1
  174. package/dist/ui/components/money/MoneyDisplay.svelte.test.js +0 -74
  175. package/dist/ui/components/money/MoneyInput.svelte +0 -131
  176. package/dist/ui/components/money/MoneyInput.svelte.d.ts +0 -25
  177. package/dist/ui/components/money/index.d.ts +0 -2
  178. package/dist/ui/components/money/index.js +0 -3
  179. package/dist/ui/components/user/UserAvatar.svelte +0 -66
  180. package/dist/ui/components/user/UserAvatar.svelte.d.ts +0 -13
  181. package/dist/ui/components/user/UserProfile.svelte +0 -79
  182. package/dist/ui/components/user/UserProfile.svelte.d.ts +0 -17
  183. package/dist/ui/components/user/index.d.ts +0 -2
  184. package/dist/ui/components/user/index.js +0 -3
  185. package/dist/ui/features/command-palette/CommandPalette.svelte.d.ts +0 -6
  186. package/dist/ui/features/config/AppSettingSelector.svelte.d.ts +0 -13
  187. package/dist/ui/features/config/CurrencySelector.svelte +0 -67
  188. package/dist/ui/features/config/CurrencySelector.svelte.d.ts +0 -8
  189. package/dist/ui/features/config/LanguageSelector.svelte +0 -66
  190. package/dist/ui/features/config/LanguageSelector.svelte.d.ts +0 -8
  191. package/dist/ui/features/config/ThemeSelector.svelte +0 -73
  192. package/dist/ui/features/config/ThemeSelector.svelte.d.ts +0 -8
  193. package/dist/ui/features/notifications/NotificationBell.svelte.d.ts +0 -11
  194. package/dist/ui/features/notifications/index.d.ts +0 -1
  195. package/dist/ui/features/notifications/index.js +0 -2
  196. package/dist/ui/features/shortcuts/ShortcutPalette.svelte.d.ts +0 -6
  197. package/dist/ui/index.d.ts +0 -26
  198. package/dist/ui/index.js +0 -41
  199. package/dist/ui/layout/ConnectedNavigationPanel.svelte.d.ts +0 -10
  200. package/dist/ui/layout/ConnectedWorkspaceStrip.svelte.d.ts +0 -9
  201. package/dist/ui/layout/ContentArea.svelte.d.ts +0 -8
  202. package/dist/ui/layout/DetailPanel.svelte.d.ts +0 -7
  203. package/dist/ui/layout/NavigationPanel.svelte.d.ts +0 -14
  204. package/dist/ui/layout/WorkspaceLayout.svelte.d.ts +0 -25
  205. package/dist/ui/layout/WorkspaceStrip.svelte.d.ts +0 -11
  206. package/dist/ui/layout/index.d.ts +0 -7
  207. package/dist/ui/layout/index.js +0 -7
  208. package/dist/ui/paraglide/README.md +0 -94
  209. package/dist/ui/paraglide/messages/_index.d.ts +0 -87
  210. package/dist/ui/paraglide/messages/_index.js +0 -88
  211. package/dist/ui/paraglide/messages/abyss.d.ts +0 -16
  212. package/dist/ui/paraglide/messages/abyss.js +0 -84
  213. package/dist/ui/paraglide/messages/acid.d.ts +0 -16
  214. package/dist/ui/paraglide/messages/acid.js +0 -84
  215. package/dist/ui/paraglide/messages/active_toasts.d.ts +0 -16
  216. package/dist/ui/paraglide/messages/active_toasts.js +0 -84
  217. package/dist/ui/paraglide/messages/aed3.d.ts +0 -17
  218. package/dist/ui/paraglide/messages/aed3.js +0 -85
  219. package/dist/ui/paraglide/messages/all_currencies.d.ts +0 -16
  220. package/dist/ui/paraglide/messages/all_currencies.js +0 -84
  221. package/dist/ui/paraglide/messages/all_languages.d.ts +0 -16
  222. package/dist/ui/paraglide/messages/all_languages.js +0 -84
  223. package/dist/ui/paraglide/messages/all_themes.d.ts +0 -16
  224. package/dist/ui/paraglide/messages/all_themes.js +0 -84
  225. package/dist/ui/paraglide/messages/api_status.d.ts +0 -16
  226. package/dist/ui/paraglide/messages/api_status.js +0 -84
  227. package/dist/ui/paraglide/messages/app_info.d.ts +0 -16
  228. package/dist/ui/paraglide/messages/app_info.js +0 -84
  229. package/dist/ui/paraglide/messages/appearance.d.ts +0 -16
  230. package/dist/ui/paraglide/messages/appearance.js +0 -84
  231. package/dist/ui/paraglide/messages/aqua.d.ts +0 -16
  232. package/dist/ui/paraglide/messages/aqua.js +0 -84
  233. package/dist/ui/paraglide/messages/ar.d.ts +0 -16
  234. package/dist/ui/paraglide/messages/ar.js +0 -84
  235. package/dist/ui/paraglide/messages/autumn.d.ts +0 -16
  236. package/dist/ui/paraglide/messages/autumn.js +0 -84
  237. package/dist/ui/paraglide/messages/black.d.ts +0 -16
  238. package/dist/ui/paraglide/messages/black.js +0 -84
  239. package/dist/ui/paraglide/messages/brl3.d.ts +0 -17
  240. package/dist/ui/paraglide/messages/brl3.js +0 -85
  241. package/dist/ui/paraglide/messages/bumblebee.d.ts +0 -16
  242. package/dist/ui/paraglide/messages/bumblebee.js +0 -84
  243. package/dist/ui/paraglide/messages/business.d.ts +0 -16
  244. package/dist/ui/paraglide/messages/business.js +0 -84
  245. package/dist/ui/paraglide/messages/cad3.d.ts +0 -17
  246. package/dist/ui/paraglide/messages/cad3.js +0 -85
  247. package/dist/ui/paraglide/messages/caramellatte.d.ts +0 -16
  248. package/dist/ui/paraglide/messages/caramellatte.js +0 -84
  249. package/dist/ui/paraglide/messages/cmyk.d.ts +0 -16
  250. package/dist/ui/paraglide/messages/cmyk.js +0 -84
  251. package/dist/ui/paraglide/messages/cny3.d.ts +0 -17
  252. package/dist/ui/paraglide/messages/cny3.js +0 -85
  253. package/dist/ui/paraglide/messages/coffee.d.ts +0 -16
  254. package/dist/ui/paraglide/messages/coffee.js +0 -84
  255. package/dist/ui/paraglide/messages/commands_label.d.ts +0 -16
  256. package/dist/ui/paraglide/messages/commands_label.js +0 -84
  257. package/dist/ui/paraglide/messages/corporate.d.ts +0 -16
  258. package/dist/ui/paraglide/messages/corporate.js +0 -84
  259. package/dist/ui/paraglide/messages/cupcake.d.ts +0 -16
  260. package/dist/ui/paraglide/messages/cupcake.js +0 -84
  261. package/dist/ui/paraglide/messages/currency.d.ts +0 -16
  262. package/dist/ui/paraglide/messages/currency.js +0 -84
  263. package/dist/ui/paraglide/messages/current_currency.d.ts +0 -16
  264. package/dist/ui/paraglide/messages/current_currency.js +0 -84
  265. package/dist/ui/paraglide/messages/current_language.d.ts +0 -16
  266. package/dist/ui/paraglide/messages/current_language.js +0 -84
  267. package/dist/ui/paraglide/messages/current_theme.d.ts +0 -16
  268. package/dist/ui/paraglide/messages/current_theme.js +0 -84
  269. package/dist/ui/paraglide/messages/currently_in_queue.d.ts +0 -16
  270. package/dist/ui/paraglide/messages/currently_in_queue.js +0 -84
  271. package/dist/ui/paraglide/messages/cyberpunk.d.ts +0 -16
  272. package/dist/ui/paraglide/messages/cyberpunk.js +0 -84
  273. package/dist/ui/paraglide/messages/dark.d.ts +0 -16
  274. package/dist/ui/paraglide/messages/dark.js +0 -84
  275. package/dist/ui/paraglide/messages/de.d.ts +0 -16
  276. package/dist/ui/paraglide/messages/de.js +0 -84
  277. package/dist/ui/paraglide/messages/dim.d.ts +0 -16
  278. package/dist/ui/paraglide/messages/dim.js +0 -84
  279. package/dist/ui/paraglide/messages/dracula.d.ts +0 -16
  280. package/dist/ui/paraglide/messages/dracula.js +0 -84
  281. package/dist/ui/paraglide/messages/emerald.d.ts +0 -16
  282. package/dist/ui/paraglide/messages/emerald.js +0 -84
  283. package/dist/ui/paraglide/messages/en.d.ts +0 -16
  284. package/dist/ui/paraglide/messages/en.js +0 -84
  285. package/dist/ui/paraglide/messages/es.d.ts +0 -16
  286. package/dist/ui/paraglide/messages/es.js +0 -84
  287. package/dist/ui/paraglide/messages/eur3.d.ts +0 -17
  288. package/dist/ui/paraglide/messages/eur3.js +0 -85
  289. package/dist/ui/paraglide/messages/extended_controls.d.ts +0 -16
  290. package/dist/ui/paraglide/messages/extended_controls.js +0 -84
  291. package/dist/ui/paraglide/messages/fantasy.d.ts +0 -16
  292. package/dist/ui/paraglide/messages/fantasy.js +0 -84
  293. package/dist/ui/paraglide/messages/forest.d.ts +0 -16
  294. package/dist/ui/paraglide/messages/forest.js +0 -84
  295. package/dist/ui/paraglide/messages/fr.d.ts +0 -16
  296. package/dist/ui/paraglide/messages/fr.js +0 -84
  297. package/dist/ui/paraglide/messages/garden.d.ts +0 -16
  298. package/dist/ui/paraglide/messages/garden.js +0 -84
  299. package/dist/ui/paraglide/messages/gbp3.d.ts +0 -17
  300. package/dist/ui/paraglide/messages/gbp3.js +0 -85
  301. package/dist/ui/paraglide/messages/halloween.d.ts +0 -16
  302. package/dist/ui/paraglide/messages/halloween.js +0 -84
  303. package/dist/ui/paraglide/messages/hello_world.d.ts +0 -18
  304. package/dist/ui/paraglide/messages/hello_world.js +0 -84
  305. package/dist/ui/paraglide/messages/hi.d.ts +0 -16
  306. package/dist/ui/paraglide/messages/hi.js +0 -84
  307. package/dist/ui/paraglide/messages/inr3.d.ts +0 -17
  308. package/dist/ui/paraglide/messages/inr3.js +0 -85
  309. package/dist/ui/paraglide/messages/it.d.ts +0 -16
  310. package/dist/ui/paraglide/messages/it.js +0 -84
  311. package/dist/ui/paraglide/messages/ja.d.ts +0 -16
  312. package/dist/ui/paraglide/messages/ja.js +0 -84
  313. package/dist/ui/paraglide/messages/jpy3.d.ts +0 -17
  314. package/dist/ui/paraglide/messages/jpy3.js +0 -85
  315. package/dist/ui/paraglide/messages/ko.d.ts +0 -16
  316. package/dist/ui/paraglide/messages/ko.js +0 -84
  317. package/dist/ui/paraglide/messages/krw3.d.ts +0 -17
  318. package/dist/ui/paraglide/messages/krw3.js +0 -85
  319. package/dist/ui/paraglide/messages/languages.d.ts +0 -16
  320. package/dist/ui/paraglide/messages/languages.js +0 -84
  321. package/dist/ui/paraglide/messages/lemonade.d.ts +0 -16
  322. package/dist/ui/paraglide/messages/lemonade.js +0 -84
  323. package/dist/ui/paraglide/messages/light.d.ts +0 -16
  324. package/dist/ui/paraglide/messages/light.js +0 -84
  325. package/dist/ui/paraglide/messages/live_store_dashboard.d.ts +0 -16
  326. package/dist/ui/paraglide/messages/live_store_dashboard.js +0 -84
  327. package/dist/ui/paraglide/messages/localization.d.ts +0 -16
  328. package/dist/ui/paraglide/messages/localization.js +0 -84
  329. package/dist/ui/paraglide/messages/lofi.d.ts +0 -16
  330. package/dist/ui/paraglide/messages/lofi.js +0 -84
  331. package/dist/ui/paraglide/messages/luxury.d.ts +0 -16
  332. package/dist/ui/paraglide/messages/luxury.js +0 -84
  333. package/dist/ui/paraglide/messages/mxn3.d.ts +0 -17
  334. package/dist/ui/paraglide/messages/mxn3.js +0 -85
  335. package/dist/ui/paraglide/messages/name_label.d.ts +0 -16
  336. package/dist/ui/paraglide/messages/name_label.js +0 -84
  337. package/dist/ui/paraglide/messages/night.d.ts +0 -16
  338. package/dist/ui/paraglide/messages/night.js +0 -84
  339. package/dist/ui/paraglide/messages/nord.d.ts +0 -16
  340. package/dist/ui/paraglide/messages/nord.js +0 -84
  341. package/dist/ui/paraglide/messages/pastel.d.ts +0 -16
  342. package/dist/ui/paraglide/messages/pastel.js +0 -84
  343. package/dist/ui/paraglide/messages/pt.d.ts +0 -16
  344. package/dist/ui/paraglide/messages/pt.js +0 -84
  345. package/dist/ui/paraglide/messages/real_time_monitor_desc.d.ts +0 -16
  346. package/dist/ui/paraglide/messages/real_time_monitor_desc.js +0 -84
  347. package/dist/ui/paraglide/messages/registered_in_registry.d.ts +0 -16
  348. package/dist/ui/paraglide/messages/registered_in_registry.js +0 -84
  349. package/dist/ui/paraglide/messages/retro.d.ts +0 -16
  350. package/dist/ui/paraglide/messages/retro.js +0 -84
  351. package/dist/ui/paraglide/messages/ru.d.ts +0 -16
  352. package/dist/ui/paraglide/messages/ru.js +0 -84
  353. package/dist/ui/paraglide/messages/silk.d.ts +0 -16
  354. package/dist/ui/paraglide/messages/silk.js +0 -84
  355. package/dist/ui/paraglide/messages/state_label.d.ts +0 -16
  356. package/dist/ui/paraglide/messages/state_label.js +0 -84
  357. package/dist/ui/paraglide/messages/sunset.d.ts +0 -16
  358. package/dist/ui/paraglide/messages/sunset.js +0 -84
  359. package/dist/ui/paraglide/messages/synthwave.d.ts +0 -16
  360. package/dist/ui/paraglide/messages/synthwave.js +0 -84
  361. package/dist/ui/paraglide/messages/system.d.ts +0 -16
  362. package/dist/ui/paraglide/messages/system.js +0 -84
  363. package/dist/ui/paraglide/messages/theme.d.ts +0 -16
  364. package/dist/ui/paraglide/messages/theme.js +0 -84
  365. package/dist/ui/paraglide/messages/themes.d.ts +0 -16
  366. package/dist/ui/paraglide/messages/themes.js +0 -84
  367. package/dist/ui/paraglide/messages/url_label.d.ts +0 -16
  368. package/dist/ui/paraglide/messages/url_label.js +0 -84
  369. package/dist/ui/paraglide/messages/usd3.d.ts +0 -17
  370. package/dist/ui/paraglide/messages/usd3.js +0 -85
  371. package/dist/ui/paraglide/messages/valentine.d.ts +0 -16
  372. package/dist/ui/paraglide/messages/valentine.js +0 -84
  373. package/dist/ui/paraglide/messages/version_label.d.ts +0 -16
  374. package/dist/ui/paraglide/messages/version_label.js +0 -84
  375. package/dist/ui/paraglide/messages/vi.d.ts +0 -16
  376. package/dist/ui/paraglide/messages/vi.js +0 -84
  377. package/dist/ui/paraglide/messages/winter.d.ts +0 -16
  378. package/dist/ui/paraglide/messages/winter.js +0 -84
  379. package/dist/ui/paraglide/messages/wireframe.d.ts +0 -16
  380. package/dist/ui/paraglide/messages/wireframe.js +0 -84
  381. package/dist/ui/paraglide/messages/zh.d.ts +0 -16
  382. package/dist/ui/paraglide/messages/zh.js +0 -84
  383. package/dist/ui/paraglide/messages.d.ts +0 -2
  384. package/dist/ui/paraglide/messages.js +0 -4
  385. package/dist/ui/paraglide/registry.d.ts +0 -21
  386. package/dist/ui/paraglide/registry.js +0 -31
  387. package/dist/ui/paraglide/runtime.d.ts +0 -749
  388. package/dist/ui/paraglide/runtime.js +0 -1777
  389. package/dist/ui/paraglide/server.d.ts +0 -103
  390. package/dist/ui/paraglide/server.js +0 -248
  391. package/dist/ui/primitives/DatePicker.svelte +0 -257
  392. package/dist/ui/primitives/DatePicker.svelte.d.ts +0 -17
  393. package/dist/ui/primitives/index.d.ts +0 -1
  394. package/dist/ui/primitives/index.js +0 -3
  395. /package/{dist/ui/layout → src/runes/layout/src}/ContentArea.svelte +0 -0
  396. /package/{dist/ui/layout → src/runes/layout/src}/DetailPanel.svelte +0 -0
  397. /package/{dist/ui/features → src/runes/palettes/src}/notifications/NotificationBell.svelte +0 -0
@@ -1,1777 +0,0 @@
1
- /* eslint-disable */
2
-
3
- /** @type {any} */
4
- const URLPattern = {}
5
-
6
- /**
7
- * The project's base locale.
8
- *
9
- * @example
10
- * if (locale === baseLocale) {
11
- * // do something
12
- * }
13
- */
14
- export const baseLocale = "en";
15
- /**
16
- * The project's locales that have been specified in the settings.
17
- *
18
- * @example
19
- * if (locales.includes(userSelectedLocale) === false) {
20
- * throw new Error('Locale is not available');
21
- * }
22
- */
23
- export const locales = /** @type {const} */ (["es", "fr", "it", "pt", "en", "de", "ru", "hi", "ar", "zh", "ja", "ko", "vi"]);
24
- /** @type {string} */
25
- export const cookieName = "PARAGLIDE_LOCALE";
26
- /** @type {number} */
27
- export const cookieMaxAge = 34560000;
28
- /** @type {string} */
29
- export const cookieDomain = "";
30
- /** @type {string} */
31
- export const localStorageKey = "PARAGLIDE_LOCALE";
32
- /**
33
- * @type {Array<"cookie" | "baseLocale" | "globalVariable" | "url" | "preferredLanguage" | "localStorage" | `custom-${string}`>}
34
- */
35
- export const strategy = [
36
- "cookie",
37
- "globalVariable",
38
- "baseLocale"
39
- ];
40
- /**
41
- * Route-level strategy overrides.
42
- *
43
- * `match` uses URLPattern syntax.
44
- *
45
- * @type {Array<{
46
- * match: string;
47
- * strategy?: Array<"cookie" | "baseLocale" | "globalVariable" | "url" | "preferredLanguage" | "localStorage" | `custom-${string}`>;
48
- * exclude?: boolean;
49
- * }>}
50
- */
51
- export const routeStrategies = [];
52
- /**
53
- * The used URL patterns.
54
- *
55
- * @type {Array<{ pattern: string, localized: Array<[Locale, string]> }> }
56
- */
57
- export const urlPatterns = [
58
- {
59
- "pattern": ":protocol://:domain(.*)::port?/:path(.*)?",
60
- "localized": [
61
- [
62
- "es",
63
- ":protocol://:domain(.*)::port?/es/:path(.*)?"
64
- ],
65
- [
66
- "fr",
67
- ":protocol://:domain(.*)::port?/fr/:path(.*)?"
68
- ],
69
- [
70
- "it",
71
- ":protocol://:domain(.*)::port?/it/:path(.*)?"
72
- ],
73
- [
74
- "pt",
75
- ":protocol://:domain(.*)::port?/pt/:path(.*)?"
76
- ],
77
- [
78
- "de",
79
- ":protocol://:domain(.*)::port?/de/:path(.*)?"
80
- ],
81
- [
82
- "ru",
83
- ":protocol://:domain(.*)::port?/ru/:path(.*)?"
84
- ],
85
- [
86
- "hi",
87
- ":protocol://:domain(.*)::port?/hi/:path(.*)?"
88
- ],
89
- [
90
- "ar",
91
- ":protocol://:domain(.*)::port?/ar/:path(.*)?"
92
- ],
93
- [
94
- "zh",
95
- ":protocol://:domain(.*)::port?/zh/:path(.*)?"
96
- ],
97
- [
98
- "ja",
99
- ":protocol://:domain(.*)::port?/ja/:path(.*)?"
100
- ],
101
- [
102
- "ko",
103
- ":protocol://:domain(.*)::port?/ko/:path(.*)?"
104
- ],
105
- [
106
- "vi",
107
- ":protocol://:domain(.*)::port?/vi/:path(.*)?"
108
- ],
109
- [
110
- "en",
111
- ":protocol://:domain(.*)::port?/:path(.*)?"
112
- ]
113
- ]
114
- }
115
- ];
116
- /** @type {string | undefined} */
117
- let cachedRouteStrategyUrl;
118
- /** @type {{ match: string; strategy?: Array<string>; exclude?: boolean } | undefined} */
119
- let cachedRouteStrategy;
120
- /**
121
- * @param {string | URL} url
122
- * @returns {{ match: string; strategy?: Array<string>; exclude?: boolean } | undefined}
123
- */
124
- function findMatchingRouteStrategy(url) {
125
- if (routeStrategies.length === 0) {
126
- return undefined;
127
- }
128
- const urlString = typeof url === "string" ? url : url.href;
129
- if (cachedRouteStrategyUrl === urlString) {
130
- return cachedRouteStrategy;
131
- }
132
- const urlObject = new URL(urlString, "http://dummy.com");
133
- let match;
134
- for (const routeStrategy of routeStrategies) {
135
- const pattern = new URLPattern(routeStrategy.match, urlObject.href);
136
- if (pattern.exec(urlObject.href)) {
137
- match = routeStrategy;
138
- break;
139
- }
140
- }
141
- cachedRouteStrategyUrl = urlString;
142
- cachedRouteStrategy = match;
143
- return match;
144
- }
145
- /**
146
- * Returns the strategy to use for a specific URL.
147
- *
148
- * If no route strategy matches (or the matching rule is `exclude: true`),
149
- * the global strategy is returned.
150
- *
151
- * @param {string | URL} url
152
- * @returns {typeof strategy}
153
- */
154
- export function getStrategyForUrl(url) {
155
- const routeStrategy = findMatchingRouteStrategy(url);
156
- if (routeStrategy &&
157
- routeStrategy.exclude !== true &&
158
- Array.isArray(routeStrategy.strategy)) {
159
- // @ts-ignore - runtime value is injected and validated by compiler types.
160
- return routeStrategy.strategy;
161
- }
162
- return strategy;
163
- }
164
- /**
165
- * Returns whether the given URL is excluded from middleware i18n processing.
166
- *
167
- * @param {string | URL} url
168
- * @returns {boolean}
169
- */
170
- export function isExcludedByRouteStrategy(url) {
171
- return findMatchingRouteStrategy(url)?.exclude === true;
172
- }
173
- /**
174
- * @typedef {{
175
- * getStore(): {
176
- * locale?: Locale,
177
- * origin?: string,
178
- * messageCalls?: Set<string>
179
- * } | undefined,
180
- * run: (store: { locale?: Locale, origin?: string, messageCalls?: Set<string>},
181
- * cb: any) => any
182
- * }} ParaglideAsyncLocalStorage
183
- */
184
- /**
185
- * Server side async local storage that is set by `serverMiddleware()`.
186
- *
187
- * The variable is used to retrieve the locale and origin in a server-side
188
- * rendering context without effecting other requests.
189
- *
190
- * @type {ParaglideAsyncLocalStorage | undefined}
191
- */
192
- export let serverAsyncLocalStorage = undefined;
193
- export const disableAsyncLocalStorage = false;
194
- export const experimentalMiddlewareLocaleSplitting = false;
195
- export const isServer = typeof window === 'undefined';
196
- /** @type {Locale | undefined} */
197
- // @ts-ignore - injected by bundlers at compile time
198
- export const experimentalStaticLocale = undefined;
199
- /**
200
- * Sets the server side async local storage.
201
- *
202
- * The function is needed because the `runtime.js` file
203
- * must define the `serverAsyncLocalStorage` variable to
204
- * avoid a circular import between `runtime.js` and
205
- * `server.js` files.
206
- *
207
- * @param {ParaglideAsyncLocalStorage | undefined} value
208
- */
209
- export function overwriteServerAsyncLocalStorage(value) {
210
- serverAsyncLocalStorage = value;
211
- }
212
- const TREE_SHAKE_COOKIE_STRATEGY_USED = true;
213
- const TREE_SHAKE_URL_STRATEGY_USED = false;
214
- const TREE_SHAKE_GLOBAL_VARIABLE_STRATEGY_USED = true;
215
- const TREE_SHAKE_PREFERRED_LANGUAGE_STRATEGY_USED = false;
216
- const TREE_SHAKE_DEFAULT_URL_PATTERN_USED = true;
217
- const TREE_SHAKE_LOCAL_STORAGE_STRATEGY_USED = false;
218
-
219
- globalThis.__paraglide = {}
220
-
221
- /**
222
- * This is a fallback to get started with a custom
223
- * strategy and avoid type errors.
224
- *
225
- * The implementation is overwritten
226
- * by \`overwriteGetLocale()\` and \`defineSetLocale()\`.
227
- *
228
- * @type {Locale|undefined}
229
- */
230
- let _locale;
231
- let localeInitiallySet = false;
232
- /**
233
- * Get the current locale.
234
- *
235
- * The locale is resolved using your configured strategies (URL, cookie, localStorage, etc.)
236
- * in the order they are defined. In SSR contexts, the locale is retrieved from AsyncLocalStorage
237
- * which is set by the `paraglideMiddleware()`.
238
- *
239
- * @see https://inlang.com/m/gerre34r/library-inlang-paraglideJs/strategy - Configure locale detection strategies
240
- *
241
- * @example
242
- * if (getLocale() === 'de') {
243
- * console.log('Germany 🇩🇪');
244
- * } else if (getLocale() === 'nl') {
245
- * console.log('Netherlands 🇳🇱');
246
- * }
247
- *
248
- * @type {() => Locale}
249
- */
250
- export let getLocale = () => {
251
- if (experimentalStaticLocale !== undefined) {
252
- return assertIsLocale(experimentalStaticLocale);
253
- }
254
- // if running in a server-side rendering context
255
- // retrieve the locale from the async local storage
256
- if (serverAsyncLocalStorage) {
257
- const locale = serverAsyncLocalStorage?.getStore()?.locale;
258
- if (locale) {
259
- return locale;
260
- }
261
- }
262
- let strategyToUse = strategy;
263
- if (!isServer && typeof window !== "undefined" && window.location?.href) {
264
- strategyToUse = getStrategyForUrl(window.location.href);
265
- }
266
- const resolved = resolveLocaleWithStrategies(strategyToUse, typeof window !== "undefined" ? window.location?.href : undefined);
267
- if (resolved) {
268
- if (!localeInitiallySet) {
269
- _locale = resolved;
270
- // https://github.com/opral/inlang-paraglide-js/issues/455
271
- localeInitiallySet = true;
272
- setLocale(resolved, { reload: false });
273
- }
274
- return resolved;
275
- }
276
- throw new Error("No locale found. Read the docs https://inlang.com/m/gerre34r/library-inlang-paraglideJs/errors#no-locale-found");
277
- };
278
- /**
279
- * Resolve locale for a given URL using route-aware strategies.
280
- *
281
- * @param {string | URL} url
282
- * @returns {Locale}
283
- */
284
- export function getLocaleForUrl(url) {
285
- if (experimentalStaticLocale !== undefined) {
286
- return assertIsLocale(experimentalStaticLocale);
287
- }
288
- const strategyToUse = getStrategyForUrl(url);
289
- const resolved = resolveLocaleWithStrategies(strategyToUse, typeof url === "string" ? url : url.href);
290
- if (resolved) {
291
- return resolved;
292
- }
293
- throw new Error("No locale found. Read the docs https://inlang.com/m/gerre34r/library-inlang-paraglideJs/errors#no-locale-found");
294
- }
295
- /**
296
- * @param {typeof strategy} strategyToUse
297
- * @param {string | undefined} urlForUrlStrategy
298
- * @returns {Locale | undefined}
299
- */
300
- function resolveLocaleWithStrategies(strategyToUse, urlForUrlStrategy) {
301
- /** @type {string | undefined} */
302
- let locale;
303
- for (const strat of strategyToUse) {
304
- if (TREE_SHAKE_COOKIE_STRATEGY_USED && strat === "cookie") {
305
- locale = extractLocaleFromCookie();
306
- }
307
- else if (strat === "baseLocale") {
308
- locale = baseLocale;
309
- }
310
- else if (TREE_SHAKE_URL_STRATEGY_USED &&
311
- strat === "url" &&
312
- !isServer &&
313
- typeof urlForUrlStrategy === "string") {
314
- locale = extractLocaleFromUrl(urlForUrlStrategy);
315
- }
316
- else if (TREE_SHAKE_GLOBAL_VARIABLE_STRATEGY_USED &&
317
- strat === "globalVariable" &&
318
- _locale !== undefined) {
319
- locale = _locale;
320
- }
321
- else if (TREE_SHAKE_PREFERRED_LANGUAGE_STRATEGY_USED &&
322
- strat === "preferredLanguage" &&
323
- !isServer) {
324
- locale = extractLocaleFromNavigator();
325
- }
326
- else if (TREE_SHAKE_LOCAL_STORAGE_STRATEGY_USED &&
327
- strat === "localStorage" &&
328
- !isServer) {
329
- locale = localStorage.getItem(localStorageKey) ?? undefined;
330
- }
331
- else if (isCustomStrategy(strat) && customClientStrategies.has(strat)) {
332
- const handler = customClientStrategies.get(strat);
333
- if (handler) {
334
- const result = handler.getLocale();
335
- // Handle both sync and async results - skip async in sync getLocale
336
- if (result instanceof Promise) {
337
- // Can't await in sync function, skip async strategies
338
- continue;
339
- }
340
- locale = result;
341
- }
342
- }
343
- if (locale !== undefined) {
344
- return assertIsLocale(locale);
345
- }
346
- }
347
- return undefined;
348
- }
349
- /**
350
- * Overwrite the `getLocale()` function.
351
- *
352
- * Use this function to overwrite how the locale is resolved. This is useful
353
- * for custom locale resolution or advanced use cases like SSG with concurrent rendering.
354
- *
355
- * @see https://inlang.com/m/gerre34r/library-inlang-paraglideJs/strategy
356
- *
357
- * @example
358
- * overwriteGetLocale(() => {
359
- * return Cookies.get('locale') ?? baseLocale
360
- * });
361
- *
362
- * @type {(fn: () => Locale) => void}
363
- */
364
- export const overwriteGetLocale = (fn) => {
365
- getLocale = fn;
366
- };
367
-
368
- const rtlLanguages = new Set([
369
- "ar",
370
- "dv",
371
- "fa",
372
- "he",
373
- "ks",
374
- "ku",
375
- "ps",
376
- "sd",
377
- "ug",
378
- "ur",
379
- "yi",
380
- ]);
381
- /**
382
- * Get writing direction for a locale.
383
- *
384
- * Uses `Intl.Locale` text info when available and falls back to a
385
- * language-based RTL check for runtimes without `getTextInfo()`.
386
- *
387
- * @example
388
- * getTextDirection(); // "ltr" or "rtl" for current locale
389
- * getTextDirection("ar"); // "rtl"
390
- * getTextDirection("en"); // "ltr"
391
- *
392
- * @param {string} [locale] - Target locale. If not provided, uses `getLocale()`
393
- * @returns {"ltr" | "rtl"}
394
- */
395
- export function getTextDirection(locale = getLocale()) {
396
- try {
397
- const intlLocale = /** @type {Intl.Locale & {
398
- getTextInfo?: () => { direction?: string };
399
- textInfo?: { direction?: string };
400
- }} */ (new Intl.Locale(locale));
401
- const direction = intlLocale.getTextInfo?.().direction ?? intlLocale.textInfo?.direction;
402
- if (direction === "ltr" || direction === "rtl") {
403
- return direction;
404
- }
405
- }
406
- catch {
407
- // Ignore Intl.Locale parsing/runtime errors and use fallback below.
408
- }
409
- const language = locale.split("-")[0]?.toLowerCase();
410
- return rtlLanguages.has(language ?? "") ? "rtl" : "ltr";
411
- }
412
-
413
- /**
414
- * Navigates to the localized URL, or reloads the current page
415
- *
416
- * @param {string} [newLocation] The new location
417
- * @return {undefined}
418
- */
419
- const navigateOrReload = (newLocation) => {
420
- if (newLocation) {
421
- // reload the page by navigating to the new url
422
- window.location.href = newLocation;
423
- }
424
- else {
425
- // reload the page to reflect the new locale
426
- window.location.reload();
427
- }
428
- };
429
- /**
430
- * @typedef {(newLocale: Locale, options?: { reload?: boolean }) => void | Promise<void>} SetLocaleFn
431
- */
432
- /**
433
- * Set the locale.
434
- *
435
- * Updates the locale using your configured strategies (cookie, localStorage, URL, etc.).
436
- * By default, this reloads the page on the client to reflect the new locale. Reloading
437
- * can be disabled by passing `reload: false` as an option, but you'll need to ensure
438
- * the UI updates to reflect the new locale.
439
- *
440
- * If any custom strategy's `setLocale` function is async, then this function
441
- * will become async as well.
442
- *
443
- * @see https://inlang.com/m/gerre34r/library-inlang-paraglideJs/strategy
444
- *
445
- * @example
446
- * setLocale('en');
447
- *
448
- * @example
449
- * setLocale('en', { reload: false });
450
- *
451
- * @type {SetLocaleFn}
452
- */
453
- export let setLocale = (newLocale, options) => {
454
- const optionsWithDefaults = {
455
- reload: true,
456
- ...options,
457
- };
458
- // locale is already set
459
- // https://github.com/opral/inlang-paraglide-js/issues/430
460
- /** @type {Locale | undefined} */
461
- let currentLocale;
462
- try {
463
- currentLocale = getLocale();
464
- }
465
- catch {
466
- // do nothing, no locale has been set yet.
467
- }
468
- /** @type {Array<Promise<any>>} */
469
- const customSetLocalePromises = [];
470
- /** @type {string | undefined} */
471
- let newLocation = undefined;
472
- let strategyToUse = strategy;
473
- if (!isServer && typeof window !== "undefined" && window.location?.href) {
474
- strategyToUse = getStrategyForUrl(window.location.href);
475
- }
476
- for (const strat of strategyToUse) {
477
- if (TREE_SHAKE_GLOBAL_VARIABLE_STRATEGY_USED &&
478
- strat === "globalVariable") {
479
- // a default for a custom strategy to get started quickly
480
- // is likely overwritten by `defineSetLocale()`
481
- _locale = newLocale;
482
- }
483
- else if (TREE_SHAKE_COOKIE_STRATEGY_USED && strat === "cookie") {
484
- if (isServer ||
485
- typeof document === "undefined" ||
486
- typeof window === "undefined") {
487
- continue;
488
- }
489
- // set the cookie
490
- const cookieString = `${cookieName}=${newLocale}; path=/; max-age=${cookieMaxAge}`;
491
- document.cookie = cookieDomain
492
- ? `${cookieString}; domain=${cookieDomain}`
493
- : cookieString;
494
- }
495
- else if (strat === "baseLocale") {
496
- // nothing to be set here. baseLocale is only a fallback
497
- continue;
498
- }
499
- else if (TREE_SHAKE_URL_STRATEGY_USED &&
500
- strat === "url" &&
501
- typeof window !== "undefined") {
502
- // route to the new url
503
- //
504
- // this triggers a page reload but a user rarely
505
- // switches locales, so this should be fine.
506
- //
507
- // if the behavior is not desired, the implementation
508
- // can be overwritten by `defineSetLocale()` to avoid
509
- // a full page reload.
510
- newLocation = localizeUrl(window.location.href, {
511
- locale: newLocale,
512
- }).href;
513
- }
514
- else if (TREE_SHAKE_LOCAL_STORAGE_STRATEGY_USED &&
515
- strat === "localStorage" &&
516
- typeof window !== "undefined") {
517
- // set the localStorage
518
- localStorage.setItem(localStorageKey, newLocale);
519
- }
520
- else if (isCustomStrategy(strat) && customClientStrategies.has(strat)) {
521
- const handler = customClientStrategies.get(strat);
522
- if (handler) {
523
- let result = handler.setLocale(newLocale);
524
- // Handle async setLocale
525
- if (result instanceof Promise) {
526
- result = result.catch((error) => {
527
- throw new Error(`Custom strategy "${strat}" setLocale failed.`, {
528
- cause: error,
529
- });
530
- });
531
- customSetLocalePromises.push(result);
532
- }
533
- }
534
- }
535
- }
536
- const runReload = () => {
537
- if (!isServer &&
538
- optionsWithDefaults.reload &&
539
- window.location &&
540
- newLocale !== currentLocale) {
541
- navigateOrReload(newLocation);
542
- }
543
- };
544
- if (customSetLocalePromises.length) {
545
- return Promise.all(customSetLocalePromises).then(() => {
546
- runReload();
547
- });
548
- }
549
- runReload();
550
- return;
551
- };
552
- /**
553
- * Overwrite the \`setLocale()\` function.
554
- *
555
- * Use this function to overwrite how the locale is set. For example,
556
- * modify a cookie, env variable, or a user's preference.
557
- *
558
- * @example
559
- * overwriteSetLocale((newLocale) => {
560
- * // set the locale in a cookie
561
- * return Cookies.set('locale', newLocale)
562
- * });
563
- *
564
- * @param {SetLocaleFn} fn
565
- */
566
- export const overwriteSetLocale = (fn) => {
567
- setLocale = /** @type {SetLocaleFn} */ (fn);
568
- };
569
-
570
- /**
571
- * The origin of the current URL.
572
- *
573
- * Defaults to "http://y.com" in non-browser environments. If this
574
- * behavior is not desired, the implementation can be overwritten
575
- * by `overwriteGetUrlOrigin()`.
576
- *
577
- * @type {() => string}
578
- */
579
- export let getUrlOrigin = () => {
580
- if (serverAsyncLocalStorage) {
581
- return serverAsyncLocalStorage.getStore()?.origin ?? "http://fallback.com";
582
- }
583
- else if (typeof window !== "undefined") {
584
- return window.location.origin;
585
- }
586
- return "http://fallback.com";
587
- };
588
- /**
589
- * Overwrite the getUrlOrigin function.
590
- *
591
- * Use this function in server environments to
592
- * define how the URL origin is resolved.
593
- *
594
- * @type {(fn: () => string) => void}
595
- */
596
- export let overwriteGetUrlOrigin = (fn) => {
597
- getUrlOrigin = fn;
598
- };
599
-
600
- /**
601
- * Check if something is an available locale.
602
- *
603
- * @example
604
- * if (isLocale(params.locale)) {
605
- * setLocale(params.locale);
606
- * } else {
607
- * setLocale('en');
608
- * }
609
- *
610
- * @param {any} locale
611
- * @returns {locale is Locale}
612
- */
613
- export function isLocale(locale) {
614
- if (typeof locale !== "string")
615
- return false;
616
- return !locale
617
- ? false
618
- : locales.some((item) => item.toLowerCase() === locale.toLowerCase());
619
- }
620
-
621
- /**
622
- * Asserts that the input is a locale.
623
- *
624
- * @param {any} input - The input to check.
625
- * @returns {Locale} The input if it is a locale.
626
- * @throws {Error} If the input is not a locale.
627
- */
628
- export function assertIsLocale(input) {
629
- if (typeof input !== "string") {
630
- throw new Error(`Invalid locale: ${input}. Expected a string.`);
631
- }
632
- const lowerInput = input.toLowerCase();
633
- const matchedLocale = locales.find((item) => item.toLowerCase() === lowerInput);
634
- if (!matchedLocale) {
635
- throw new Error(`Invalid locale: ${input}. Expected one of: ${locales.join(", ")}`);
636
- }
637
- return matchedLocale;
638
- }
639
-
640
- /**
641
- * Extracts a locale from a request.
642
- *
643
- * Use the function on the server to extract the locale
644
- * from a request.
645
- *
646
- * The function goes through the strategies in the order
647
- * they are defined. If a strategy returns an invalid locale,
648
- * it will fall back to the next strategy.
649
- *
650
- * Note: Custom server strategies are not supported in this synchronous version.
651
- * Use `extractLocaleFromRequestAsync` if you need custom server strategies with async getLocale methods.
652
- *
653
- * @example
654
- * const locale = extractLocaleFromRequest(request);
655
- *
656
- * @type {(request: Request) => Locale}
657
- */
658
- export const extractLocaleFromRequest = (request) => {
659
- return extractLocaleFromRequestWithStrategies(request, getStrategyForUrl(request.url));
660
- };
661
- /**
662
- * Extracts a locale from a request using the provided strategy order.
663
- *
664
- * @param {Request} request
665
- * @param {typeof strategy} strategies
666
- * @returns {Locale}
667
- */
668
- export const extractLocaleFromRequestWithStrategies = (request, strategies) => {
669
- /** @type {string|undefined} */
670
- let locale;
671
- for (const strat of strategies) {
672
- if (TREE_SHAKE_COOKIE_STRATEGY_USED && strat === "cookie") {
673
- locale = request.headers
674
- .get("cookie")
675
- ?.split("; ")
676
- .find((c) => c.startsWith(cookieName + "="))
677
- ?.split("=")[1];
678
- }
679
- else if (TREE_SHAKE_URL_STRATEGY_USED && strat === "url") {
680
- locale = extractLocaleFromUrl(request.url);
681
- }
682
- else if (TREE_SHAKE_PREFERRED_LANGUAGE_STRATEGY_USED &&
683
- strat === "preferredLanguage") {
684
- locale = extractLocaleFromHeader(request);
685
- }
686
- else if (strat === "globalVariable") {
687
- locale = _locale;
688
- }
689
- else if (strat === "baseLocale") {
690
- return baseLocale;
691
- }
692
- else if (strat === "localStorage") {
693
- continue;
694
- }
695
- else if (isCustomStrategy(strat)) {
696
- // Custom strategies are not supported in sync version
697
- // Use extractLocaleFromRequestAsync for custom server strategies
698
- continue;
699
- }
700
- if (locale !== undefined) {
701
- if (!isLocale(locale)) {
702
- locale = undefined;
703
- }
704
- else {
705
- return assertIsLocale(locale);
706
- }
707
- }
708
- }
709
- throw new Error("No locale found. There is an error in your strategy. Try adding 'baseLocale' as the very last strategy. Read more here https://inlang.com/m/gerre34r/library-inlang-paraglideJs/errors#no-locale-found");
710
- };
711
-
712
- /**
713
- * Asynchronously extracts a locale from a request.
714
- *
715
- * This function supports async custom server strategies, unlike the synchronous
716
- * `extractLocaleFromRequest`. Use this function when you have custom server strategies
717
- * that need to perform asynchronous operations (like database calls) in their getLocale method.
718
- *
719
- * The function first processes any custom server strategies asynchronously, then falls back
720
- * to the synchronous `extractLocaleFromRequest` for all other strategies.
721
- *
722
- * @see {@link https://github.com/opral/inlang-paraglide-js/issues/527#issuecomment-2978151022}
723
- *
724
- * @example
725
- * // Basic usage
726
- * const locale = await extractLocaleFromRequestAsync(request);
727
- *
728
- * @example
729
- * // With custom async server strategy
730
- * defineCustomServerStrategy("custom-database", {
731
- * getLocale: async (request) => {
732
- * const userId = extractUserIdFromRequest(request);
733
- * return await getUserLocaleFromDatabase(userId);
734
- * }
735
- * });
736
- *
737
- * const locale = await extractLocaleFromRequestAsync(request);
738
- *
739
- * @type {(request: Request) => Promise<Locale>}
740
- */
741
- export const extractLocaleFromRequestAsync = async (request) => {
742
- /** @type {string|undefined} */
743
- let locale;
744
- const strategy = getStrategyForUrl(request.url);
745
- // Process custom strategies first, in order
746
- for (const strat of strategy) {
747
- if (isCustomStrategy(strat) && customServerStrategies.has(strat)) {
748
- const handler = customServerStrategies.get(strat);
749
- if (handler) {
750
- /** @type {string|undefined} */
751
- locale = await handler.getLocale(request);
752
- }
753
- // If we got a valid locale from this custom strategy, use it
754
- if (locale !== undefined && isLocale(locale)) {
755
- return assertIsLocale(locale);
756
- }
757
- }
758
- }
759
- // If no custom strategy provided a valid locale, fall back to sync version
760
- locale = extractLocaleFromRequestWithStrategies(request, strategy);
761
- return assertIsLocale(locale);
762
- };
763
-
764
- /**
765
- * Extracts a cookie from the document.
766
- *
767
- * Will return undefined if the document is not available or if the cookie is not set.
768
- * The `document` object is not available in server-side rendering, so this function should not be called in that context.
769
- *
770
- * @returns {string | undefined}
771
- */
772
- export function extractLocaleFromCookie() {
773
- if (typeof document === "undefined" || !document.cookie) {
774
- return;
775
- }
776
- const match = document.cookie.match(new RegExp(`(^| )${cookieName}=([^;]+)`));
777
- const locale = match?.[2];
778
- if (isLocale(locale)) {
779
- return locale;
780
- }
781
- return undefined;
782
- }
783
-
784
- /**
785
- * Extracts a locale from the accept-language header.
786
- *
787
- * Use the function on the server to extract the locale
788
- * from the accept-language header that is sent by the client.
789
- *
790
- * @example
791
- * const locale = extractLocaleFromHeader(request);
792
- *
793
- * @type {(request: Request) => Locale}
794
- * @param {Request} request - The request object to extract the locale from.
795
- * @returns {string|undefined} The negotiated preferred language.
796
- */
797
- export function extractLocaleFromHeader(request) {
798
- const acceptLanguageHeader = request.headers.get("accept-language");
799
- if (acceptLanguageHeader) {
800
- // Parse language preferences with their q-values and base language codes
801
- const languages = acceptLanguageHeader
802
- .split(",")
803
- .map((lang) => {
804
- const [tag, q = "1"] = lang.trim().split(";q=");
805
- // Get both the full tag and base language code
806
- const baseTag = tag?.split("-")[0]?.toLowerCase();
807
- return {
808
- fullTag: tag?.toLowerCase(),
809
- baseTag,
810
- q: Number(q),
811
- };
812
- })
813
- .sort((a, b) => b.q - a.q);
814
- for (const lang of languages) {
815
- if (isLocale(lang.fullTag)) {
816
- return lang.fullTag;
817
- }
818
- else if (isLocale(lang.baseTag)) {
819
- return lang.baseTag;
820
- }
821
- }
822
- return undefined;
823
- }
824
- return undefined;
825
- }
826
-
827
- /**
828
- * Negotiates a preferred language from navigator.languages.
829
- *
830
- * Use the function on the client to extract the locale
831
- * from the navigator.languages array.
832
- *
833
- * @example
834
- * const locale = extractLocaleFromNavigator();
835
- *
836
- * @type {() => Locale | undefined}
837
- * @returns {string | undefined}
838
- */
839
- export function extractLocaleFromNavigator() {
840
- if (!navigator?.languages?.length) {
841
- return undefined;
842
- }
843
- const languages = navigator.languages.map((lang) => ({
844
- fullTag: lang.toLowerCase(),
845
- baseTag: lang.split("-")[0]?.toLowerCase(),
846
- }));
847
- for (const lang of languages) {
848
- if (isLocale(lang.fullTag)) {
849
- return lang.fullTag;
850
- }
851
- else if (isLocale(lang.baseTag)) {
852
- return lang.baseTag;
853
- }
854
- }
855
- return undefined;
856
- }
857
-
858
- /**
859
- * If extractLocaleFromUrl is called many times on the same page and the URL
860
- * hasn't changed, we don't need to recompute it every time which can get expensive.
861
- * We might use a LRU cache if needed, but for now storing only the last result is enough.
862
- * https://github.com/opral/monorepo/pull/3575#discussion_r2066731243
863
- */
864
- /** @type {string|undefined} */
865
- let cachedUrl;
866
- /** @type {Locale|undefined} */
867
- let cachedLocale;
868
- /**
869
- * Extracts the locale from a given URL using native URLPattern.
870
- *
871
- * @param {URL|string} url - The full URL from which to extract the locale.
872
- * @returns {Locale|undefined} The extracted locale, or undefined if no locale is found.
873
- */
874
- export function extractLocaleFromUrl(url) {
875
- const urlString = typeof url === "string" ? url : url.href;
876
- if (cachedUrl === urlString) {
877
- return cachedLocale;
878
- }
879
- let result;
880
- if (TREE_SHAKE_DEFAULT_URL_PATTERN_USED) {
881
- result = defaultUrlPatternExtractLocale(url);
882
- }
883
- else {
884
- const urlObj = typeof url === "string" ? new URL(url) : url;
885
- // Iterate over URL patterns
886
- for (const element of urlPatterns) {
887
- for (const [locale, localizedPattern] of element.localized) {
888
- const match = new URLPattern(localizedPattern, urlObj.href).exec(urlObj.href);
889
- if (!match) {
890
- continue;
891
- }
892
- // Check if the locale is valid
893
- if (assertIsLocale(locale)) {
894
- result = locale;
895
- break;
896
- }
897
- }
898
- if (result)
899
- break;
900
- }
901
- }
902
- cachedUrl = urlString;
903
- cachedLocale = result;
904
- return result;
905
- }
906
- /**
907
- * https://github.com/opral/inlang-paraglide-js/issues/381
908
- *
909
- * @param {URL|string} url - The full URL from which to extract the locale.
910
- * @returns {Locale|undefined} The extracted locale, or undefined if no locale is found.
911
- */
912
- function defaultUrlPatternExtractLocale(url) {
913
- const urlObj = new URL(url, "http://dummy.com");
914
- const pathSegments = urlObj.pathname.split("/").filter(Boolean);
915
- if (pathSegments.length > 0) {
916
- const potentialLocale = pathSegments[0];
917
- if (isLocale(potentialLocale)) {
918
- return potentialLocale;
919
- }
920
- }
921
- // everything else has to be the base locale
922
- return baseLocale;
923
- }
924
-
925
- /**
926
- * Lower-level URL localization function, primarily used in server contexts.
927
- *
928
- * This function is designed for server-side usage where you need precise control
929
- * over URL localization, such as in middleware or request handlers. It works with
930
- * URL objects and always returns absolute URLs.
931
- *
932
- * For client-side UI components, use `localizeHref()` instead, which provides
933
- * a more convenient API with relative paths and automatic locale detection.
934
- *
935
- * @see https://inlang.com/m/gerre34r/library-inlang-paraglideJs/i18n-routing
936
- *
937
- * @example
938
- * ```typescript
939
- * // Server middleware example
940
- * app.use((req, res, next) => {
941
- * const url = new URL(req.url, `${req.protocol}://${req.headers.host}`);
942
- * const localized = localizeUrl(url, { locale: "de" });
943
- *
944
- * if (localized.href !== url.href) {
945
- * return res.redirect(localized.href);
946
- * }
947
- * next();
948
- * });
949
- * ```
950
- *
951
- * @example
952
- * ```typescript
953
- * // Using with URL patterns
954
- * const url = new URL("https://example.com/about");
955
- * localizeUrl(url, { locale: "de" });
956
- * // => URL("https://example.com/de/about")
957
- *
958
- * // Using with domain-based localization
959
- * const url = new URL("https://example.com/store");
960
- * localizeUrl(url, { locale: "de" });
961
- * // => URL("https://de.example.com/store")
962
- * ```
963
- *
964
- * @param {string | URL} url - The URL to localize. If string, must be absolute.
965
- * @param {Object} [options] - Options for localization
966
- * @param {string} [options.locale] - Target locale. If not provided, uses getLocale()
967
- * @returns {URL} The localized URL, always absolute
968
- */
969
- export function localizeUrl(url, options) {
970
- if (TREE_SHAKE_DEFAULT_URL_PATTERN_USED) {
971
- return localizeUrlDefaultPattern(url, options);
972
- }
973
- const targetLocale = options?.locale ?? getLocale();
974
- const urlObj = typeof url === "string" ? new URL(url) : url;
975
- // Iterate over URL patterns
976
- for (const element of urlPatterns) {
977
- // match localized patterns
978
- for (const [, localizedPattern] of element.localized) {
979
- const match = new URLPattern(localizedPattern, urlObj.href).exec(urlObj.href);
980
- if (!match) {
981
- continue;
982
- }
983
- const targetPattern = element.localized.find(([locale]) => locale === targetLocale)?.[1];
984
- if (!targetPattern) {
985
- continue;
986
- }
987
- const localizedUrl = fillPattern(targetPattern, aggregateGroups(match), urlObj.origin);
988
- return fillMissingUrlParts(localizedUrl, match);
989
- }
990
- const unlocalizedMatch = new URLPattern(element.pattern, urlObj.href).exec(urlObj.href);
991
- if (unlocalizedMatch) {
992
- const targetPattern = element.localized.find(([locale]) => locale === targetLocale)?.[1];
993
- if (targetPattern) {
994
- const localizedUrl = fillPattern(targetPattern, aggregateGroups(unlocalizedMatch), urlObj.origin);
995
- return fillMissingUrlParts(localizedUrl, unlocalizedMatch);
996
- }
997
- }
998
- }
999
- // If no match found, return the original URL
1000
- return urlObj;
1001
- }
1002
- /**
1003
- * https://github.com/opral/inlang-paraglide-js/issues/381
1004
- *
1005
- * @param {string | URL} url
1006
- * @param {Object} [options]
1007
- * @param {string} [options.locale]
1008
- * @returns {URL}
1009
- */
1010
- function localizeUrlDefaultPattern(url, options) {
1011
- const urlObj = typeof url === "string" ? new URL(url, getUrlOrigin()) : new URL(url);
1012
- const locale = options?.locale ?? getLocale();
1013
- const currentLocale = extractLocaleFromUrl(urlObj);
1014
- // If current locale matches target locale, no change needed
1015
- if (currentLocale === locale) {
1016
- return urlObj;
1017
- }
1018
- const pathSegments = urlObj.pathname.split("/").filter(Boolean);
1019
- // If current path starts with a locale, remove it
1020
- if (pathSegments.length > 0 && isLocale(pathSegments[0])) {
1021
- pathSegments.shift();
1022
- }
1023
- // For base locale, don't add prefix
1024
- if (locale === baseLocale) {
1025
- urlObj.pathname = "/" + pathSegments.join("/");
1026
- }
1027
- else {
1028
- // For other locales, add prefix
1029
- urlObj.pathname = "/" + locale + "/" + pathSegments.join("/");
1030
- }
1031
- return urlObj;
1032
- }
1033
- /**
1034
- * Low-level URL de-localization function, primarily used in server contexts.
1035
- *
1036
- * This function is designed for server-side usage where you need precise control
1037
- * over URL de-localization, such as in middleware or request handlers. It works with
1038
- * URL objects and always returns absolute URLs.
1039
- *
1040
- * For client-side UI components, use `deLocalizeHref()` instead, which provides
1041
- * a more convenient API with relative paths.
1042
- *
1043
- * @see https://inlang.com/m/gerre34r/library-inlang-paraglideJs/i18n-routing
1044
- *
1045
- * @example
1046
- * ```typescript
1047
- * // Server middleware example
1048
- * app.use((req, res, next) => {
1049
- * const url = new URL(req.url, `${req.protocol}://${req.headers.host}`);
1050
- * const baseUrl = deLocalizeUrl(url);
1051
- *
1052
- * // Store the base URL for later use
1053
- * req.baseUrl = baseUrl;
1054
- * next();
1055
- * });
1056
- * ```
1057
- *
1058
- * @example
1059
- * ```typescript
1060
- * // Using with URL patterns
1061
- * const url = new URL("https://example.com/de/about");
1062
- * deLocalizeUrl(url); // => URL("https://example.com/about")
1063
- *
1064
- * // Using with domain-based localization
1065
- * const url = new URL("https://de.example.com/store");
1066
- * deLocalizeUrl(url); // => URL("https://example.com/store")
1067
- * ```
1068
- *
1069
- * @param {string | URL} url - The URL to de-localize. If string, must be absolute.
1070
- * @returns {URL} The de-localized URL, always absolute
1071
- */
1072
- export function deLocalizeUrl(url) {
1073
- if (TREE_SHAKE_DEFAULT_URL_PATTERN_USED) {
1074
- return deLocalizeUrlDefaultPattern(url);
1075
- }
1076
- const urlObj = typeof url === "string" ? new URL(url) : url;
1077
- // Iterate over URL patterns
1078
- for (const element of urlPatterns) {
1079
- // Iterate over localized versions
1080
- for (const [, localizedPattern] of element.localized) {
1081
- const match = new URLPattern(localizedPattern, urlObj.href).exec(urlObj.href);
1082
- if (match) {
1083
- // Convert localized URL back to the base pattern
1084
- const groups = aggregateGroups(match);
1085
- const baseUrl = fillPattern(element.pattern, groups, urlObj.origin);
1086
- return fillMissingUrlParts(baseUrl, match);
1087
- }
1088
- }
1089
- // match unlocalized pattern
1090
- const unlocalizedMatch = new URLPattern(element.pattern, urlObj.href).exec(urlObj.href);
1091
- if (unlocalizedMatch) {
1092
- const baseUrl = fillPattern(element.pattern, aggregateGroups(unlocalizedMatch), urlObj.origin);
1093
- return fillMissingUrlParts(baseUrl, unlocalizedMatch);
1094
- }
1095
- }
1096
- // no match found return the original url
1097
- return urlObj;
1098
- }
1099
- /**
1100
- * De-localizes a URL using the default pattern (/:locale/*)
1101
- * @param {string|URL} url
1102
- * @returns {URL}
1103
- */
1104
- function deLocalizeUrlDefaultPattern(url) {
1105
- const urlObj = typeof url === "string" ? new URL(url, getUrlOrigin()) : new URL(url);
1106
- const pathSegments = urlObj.pathname.split("/").filter(Boolean);
1107
- // If first segment is a locale, remove it
1108
- if (pathSegments.length > 0 && isLocale(pathSegments[0])) {
1109
- urlObj.pathname = "/" + pathSegments.slice(1).join("/");
1110
- }
1111
- return urlObj;
1112
- }
1113
- /**
1114
- * Takes matches of implicit wildcards in the UrlPattern (when a part is missing
1115
- * it is equal to '*') and adds them back to the result of fillPattern.
1116
- *
1117
- * At least protocol and hostname are required to create a valid URL inside fillPattern.
1118
- *
1119
- * @param {URL} url
1120
- * @param {any} match
1121
- * @returns {URL}
1122
- */
1123
- function fillMissingUrlParts(url, match) {
1124
- if (match.protocol.groups["0"]) {
1125
- url.protocol = match.protocol.groups["0"] ?? "";
1126
- }
1127
- if (match.hostname.groups["0"]) {
1128
- url.hostname = match.hostname.groups["0"] ?? "";
1129
- }
1130
- if (match.username.groups["0"]) {
1131
- url.username = match.username.groups["0"] ?? "";
1132
- }
1133
- if (match.password.groups["0"]) {
1134
- url.password = match.password.groups["0"] ?? "";
1135
- }
1136
- if (match.port.groups["0"]) {
1137
- url.port = match.port.groups["0"] ?? "";
1138
- }
1139
- if (match.pathname.groups["0"]) {
1140
- url.pathname = match.pathname.groups["0"] ?? "";
1141
- }
1142
- if (match.search.groups["0"]) {
1143
- url.search = match.search.groups["0"] ?? "";
1144
- }
1145
- if (match.hash.groups["0"]) {
1146
- url.hash = match.hash.groups["0"] ?? "";
1147
- }
1148
- return url;
1149
- }
1150
- /**
1151
- * Fills a URL pattern with values for named groups, supporting all URLPattern-style modifiers.
1152
- *
1153
- * This function will eventually be replaced by https://github.com/whatwg/urlpattern/issues/73
1154
- *
1155
- * Matches:
1156
- * - :name -> Simple
1157
- * - :name? -> Optional
1158
- * - :name+ -> One or more
1159
- * - :name* -> Zero or more
1160
- * - :name(...) -> Regex group
1161
- * - {text} -> Group delimiter
1162
- * - {text}? -> Optional group delimiter
1163
- *
1164
- * If the value is `null`, the segment is removed.
1165
- *
1166
- * @param {string} pattern - The URL pattern containing named groups.
1167
- * @param {Record<string, string | null | undefined>} values - Object of values for named groups.
1168
- * @param {string} origin - Base URL to use for URL construction.
1169
- * @returns {URL} - The constructed URL with named groups filled.
1170
- */
1171
- function fillPattern(pattern, values, origin) {
1172
- // Pre-process the pattern to handle explicit port numbers
1173
- // This detects patterns like "http://localhost:5173" and protects the port number
1174
- // from being interpreted as a parameter
1175
- let processedPattern = pattern.replace(/(https?:\/\/[^:/]+):(\d+)(\/|$)/g, (_, protocol, port, slash) => {
1176
- // Replace ":5173" with "#PORT-5173#" to protect it from parameter replacement
1177
- return `${protocol}#PORT-${port}#${slash}`;
1178
- });
1179
- // First, handle group delimiters with curly braces
1180
- let processedGroupDelimiters = processedPattern.replace(/\{([^{}]*)\}([?+*]?)/g, (_, content, modifier) => {
1181
- // For optional group delimiters
1182
- if (modifier === "?") {
1183
- // For optional groups, we'll include the content
1184
- return content;
1185
- }
1186
- // For non-optional group delimiters, always include the content
1187
- return content;
1188
- });
1189
- // Then handle named groups
1190
- let filled = processedGroupDelimiters.replace(/(\/?):([a-zA-Z0-9_]+)(\([^)]*\))?([?+*]?)/g, (_, slash, name, __, modifier) => {
1191
- const value = values[name];
1192
- if (value === null) {
1193
- // If value is null, remove the entire segment including the preceding slash
1194
- return "";
1195
- }
1196
- if (modifier === "?") {
1197
- // Optional segment
1198
- return value !== undefined ? `${slash}${value}` : "";
1199
- }
1200
- if (modifier === "+" || modifier === "*") {
1201
- // Repeatable segments
1202
- if (value === undefined && modifier === "+") {
1203
- throw new Error(`Missing value for "${name}" (one or more required)`);
1204
- }
1205
- return value ? `${slash}${value}` : "";
1206
- }
1207
- // Simple named group (no modifier)
1208
- if (value === undefined) {
1209
- throw new Error(`Missing value for "${name}"`);
1210
- }
1211
- return `${slash}${value}`;
1212
- });
1213
- // Restore port numbers
1214
- filled = filled.replace(/#PORT-(\d+)#/g, ":$1");
1215
- return new URL(filled, origin);
1216
- }
1217
- /**
1218
- * Aggregates named groups from various parts of the URLPattern match result.
1219
- *
1220
- *
1221
- * @type {(match: any) => Record<string, string | null | undefined>}
1222
- */
1223
- export function aggregateGroups(match) {
1224
- return {
1225
- ...match.hash.groups,
1226
- ...match.hostname.groups,
1227
- ...match.password.groups,
1228
- ...match.pathname.groups,
1229
- ...match.port.groups,
1230
- ...match.protocol.groups,
1231
- ...match.search.groups,
1232
- ...match.username.groups,
1233
- };
1234
- }
1235
-
1236
- /**
1237
- * @typedef {object} ShouldRedirectServerInput
1238
- * @property {Request} request
1239
- * @property {string | URL} [url]
1240
- * @property {ReturnType<typeof assertIsLocale>} [locale]
1241
- *
1242
- * @typedef {object} ShouldRedirectClientInput
1243
- * @property {undefined} [request]
1244
- * @property {string | URL} [url]
1245
- * @property {ReturnType<typeof assertIsLocale>} [locale]
1246
- *
1247
- * @typedef {ShouldRedirectServerInput | ShouldRedirectClientInput} ShouldRedirectInput
1248
- *
1249
- * @typedef {object} ShouldRedirectResult
1250
- * @property {boolean} shouldRedirect - Indicates whether the consumer should perform a redirect.
1251
- * @property {ReturnType<typeof assertIsLocale>} locale - Locale resolved using the configured strategies.
1252
- * @property {URL | undefined} redirectUrl - Destination URL when a redirect is required.
1253
- */
1254
- /**
1255
- * Determines whether a redirect is required to align the current URL with the active locale.
1256
- *
1257
- * This helper mirrors the logic that powers `paraglideMiddleware`, but works in both server
1258
- * and client environments. It evaluates the configured strategies in order, computes the
1259
- * canonical localized URL, and reports when the current URL does not match.
1260
- *
1261
- * When called in the browser without arguments, the current `window.location.href` is used.
1262
- *
1263
- * @see https://inlang.com/m/gerre34r/library-inlang-paraglideJs/i18n-routing#client-side-redirects
1264
- *
1265
- * @example
1266
- * // Client side usage (e.g. TanStack Router beforeLoad hook)
1267
- * async function beforeLoad({ location }) {
1268
- * const decision = await shouldRedirect({ url: location.href });
1269
- *
1270
- * if (decision.shouldRedirect) {
1271
- * throw redirect({ to: decision.redirectUrl.href });
1272
- * }
1273
- * }
1274
- *
1275
- * @example
1276
- * // Server side usage with a Request
1277
- * export async function handle(request) {
1278
- * const decision = await shouldRedirect({ request });
1279
- *
1280
- * if (decision.shouldRedirect) {
1281
- * return Response.redirect(decision.redirectUrl, 307);
1282
- * }
1283
- *
1284
- * return render(request, decision.locale);
1285
- * }
1286
- *
1287
- * @param {ShouldRedirectInput} [input]
1288
- * @returns {Promise<ShouldRedirectResult>}
1289
- */
1290
- export async function shouldRedirect(input = {}) {
1291
- const currentUrl = resolveUrl(input);
1292
- const locale = /** @type {ReturnType<typeof assertIsLocale>} */ (await resolveLocale(input, currentUrl));
1293
- const strategy = getStrategyForUrl(currentUrl.href);
1294
- if (isExcludedByRouteStrategy(currentUrl.href) || !strategy.includes("url")) {
1295
- return { shouldRedirect: false, locale, redirectUrl: undefined };
1296
- }
1297
- const localizedUrl = localizeUrl(currentUrl.href, { locale });
1298
- const shouldRedirectToLocalizedUrl = normalizeUrl(localizedUrl.href) !== normalizeUrl(currentUrl.href);
1299
- return {
1300
- shouldRedirect: shouldRedirectToLocalizedUrl,
1301
- locale,
1302
- redirectUrl: shouldRedirectToLocalizedUrl ? localizedUrl : undefined,
1303
- };
1304
- }
1305
- /**
1306
- * Resolves the locale either from the provided input or by using the configured strategies.
1307
- *
1308
- * @param {ShouldRedirectInput} input
1309
- * @param {URL} currentUrl
1310
- * @returns {Promise<ReturnType<typeof assertIsLocale>>}
1311
- */
1312
- async function resolveLocale(input, currentUrl) {
1313
- if (input.locale) {
1314
- return assertIsLocale(input.locale);
1315
- }
1316
- if (input.request) {
1317
- return extractLocaleFromRequestAsync(input.request);
1318
- }
1319
- if (typeof input.url !== "undefined") {
1320
- return getLocaleForUrl(currentUrl.href);
1321
- }
1322
- return getLocale();
1323
- }
1324
- /**
1325
- * Resolves the current URL from the provided input or runtime context.
1326
- *
1327
- * @param {ShouldRedirectInput} input
1328
- * @returns {URL}
1329
- */
1330
- function resolveUrl(input) {
1331
- if (input.request) {
1332
- return new URL(input.request.url);
1333
- }
1334
- if (input.url instanceof URL) {
1335
- return new URL(input.url.href);
1336
- }
1337
- if (typeof input.url === "string") {
1338
- return new URL(input.url, getUrlOrigin());
1339
- }
1340
- if (typeof window !== "undefined" && window?.location?.href) {
1341
- return new URL(window.location.href);
1342
- }
1343
- throw new Error("shouldRedirect() requires either a request, an absolute URL, or must run in a browser environment.");
1344
- }
1345
- /**
1346
- * Normalize url for comparison by stripping the trailing slash.
1347
- *
1348
- * @param {string} url
1349
- * @returns {string}
1350
- */
1351
- function normalizeUrl(url) {
1352
- const urlObj = new URL(url);
1353
- urlObj.pathname = urlObj.pathname.replace(/\/$/, "");
1354
- return urlObj.href;
1355
- }
1356
-
1357
- /**
1358
- * High-level URL localization function optimized for client-side UI usage.
1359
- *
1360
- * This is a convenience wrapper around `localizeUrl()` that provides features
1361
- * needed in UI:
1362
- *
1363
- * - Accepts relative paths (e.g., "/about")
1364
- * - Returns relative paths when possible
1365
- * - Automatically detects current locale if not specified
1366
- * - Handles string input/output instead of URL objects
1367
- *
1368
- * @see https://inlang.com/m/gerre34r/library-inlang-paraglideJs/i18n-routing
1369
- *
1370
- * @example
1371
- * ```typescript
1372
- * // In a React/Vue/Svelte component
1373
- * const NavLink = ({ href }) => {
1374
- * // Automatically uses current locale, keeps path relative
1375
- * return <a href={localizeHref(href)}>...</a>;
1376
- * };
1377
- *
1378
- * // Examples:
1379
- * localizeHref("/about")
1380
- * // => "/de/about" (if current locale is "de")
1381
- * localizeHref("/store", { locale: "fr" })
1382
- * // => "/fr/store" (explicit locale)
1383
- *
1384
- * // Cross-origin links remain absolute
1385
- * localizeHref("https://other-site.com/about")
1386
- * // => "https://other-site.com/de/about"
1387
- * ```
1388
- *
1389
- * For server-side URL localization (e.g., in middleware), use `localizeUrl()`
1390
- * which provides more precise control over URL handling.
1391
- *
1392
- * @param {string} href - The href to localize (can be relative or absolute)
1393
- * @param {Object} [options] - Options for localization
1394
- * @param {string} [options.locale] - Target locale. If not provided, uses `getLocale()`
1395
- * @returns {string} The localized href, relative if input was relative
1396
- */
1397
- export function localizeHref(href, options) {
1398
- const currentLocale = getLocale();
1399
- const locale = options?.locale ?? currentLocale;
1400
- const url = new URL(href, getUrlOrigin());
1401
- const localized = localizeUrl(url, { locale });
1402
- // if the origin is identical and the href is relative,
1403
- // return the relative path
1404
- if (href.startsWith("/") && url.origin === localized.origin) {
1405
- // check for cross origin localization in which case an absolute URL must be returned.
1406
- if (locale !== currentLocale) {
1407
- const localizedCurrentLocale = localizeUrl(url, {
1408
- locale: currentLocale,
1409
- });
1410
- if (localizedCurrentLocale.origin !== localized.origin) {
1411
- return localized.href;
1412
- }
1413
- }
1414
- return localized.pathname + localized.search + localized.hash;
1415
- }
1416
- return localized.href;
1417
- }
1418
- /**
1419
- * High-level URL de-localization function optimized for client-side UI usage.
1420
- *
1421
- * This is a convenience wrapper around `deLocalizeUrl()` that provides features
1422
- * needed in the UI:
1423
- *
1424
- * - Accepts relative paths (e.g., "/de/about")
1425
- * - Returns relative paths when possible
1426
- * - Handles string input/output instead of URL objects
1427
- *
1428
- * @see https://inlang.com/m/gerre34r/library-inlang-paraglideJs/i18n-routing
1429
- *
1430
- * @example
1431
- * ```typescript
1432
- * // In a React/Vue/Svelte component
1433
- * const LocaleSwitcher = ({ href }) => {
1434
- * // Remove locale prefix before switching
1435
- * const baseHref = deLocalizeHref(href);
1436
- * return locales.map(locale =>
1437
- * <a href={localizeHref(baseHref, { locale })}>
1438
- * Switch to {locale}
1439
- * </a>
1440
- * );
1441
- * };
1442
- *
1443
- * // Examples:
1444
- * deLocalizeHref("/de/about") // => "/about"
1445
- * deLocalizeHref("/fr/store") // => "/store"
1446
- *
1447
- * // Cross-origin links remain absolute
1448
- * deLocalizeHref("https://example.com/de/about")
1449
- * // => "https://example.com/about"
1450
- * ```
1451
- *
1452
- * For server-side URL de-localization (e.g., in middleware), use `deLocalizeUrl()`
1453
- * which provides more precise control over URL handling.
1454
- *
1455
- * @param {string} href - The href to de-localize (can be relative or absolute)
1456
- * @returns {string} The de-localized href, relative if input was relative
1457
- */
1458
- export function deLocalizeHref(href) {
1459
- const url = new URL(href, getUrlOrigin());
1460
- const deLocalized = deLocalizeUrl(url);
1461
- // If the origin is identical and the href is relative,
1462
- // return the relative path instead of the full URL.
1463
- if (href.startsWith("/") && url.origin === deLocalized.origin) {
1464
- return deLocalized.pathname + deLocalized.search + deLocalized.hash;
1465
- }
1466
- return deLocalized.href;
1467
- }
1468
-
1469
- /**
1470
- * @param {string} safeModuleId
1471
- * @param {Locale} locale
1472
- */
1473
- export function trackMessageCall(safeModuleId, locale) {
1474
- if (isServer === false)
1475
- return;
1476
- const store = serverAsyncLocalStorage?.getStore();
1477
- if (store) {
1478
- store.messageCalls?.add(`${safeModuleId}:${locale}`);
1479
- }
1480
- }
1481
-
1482
- /**
1483
- * Generates localized URL variants for all provided URLs based on your configured locales and URL patterns.
1484
- *
1485
- * This function is essential for Static Site Generation (SSG) where you need to tell your framework
1486
- * which pages to pre-render at build time. It's also useful for generating sitemaps and
1487
- * `<link rel="alternate" hreflang>` tags for SEO.
1488
- *
1489
- * The function respects your `urlPatterns` configuration - if you have translated pathnames
1490
- * (e.g., `/about` → `/ueber-uns` for German), it will generate the correct localized paths.
1491
- *
1492
- * @see https://inlang.com/m/gerre34r/library-inlang-paraglideJs/static-site-generation
1493
- *
1494
- * @example
1495
- * // Basic usage - generate all locale variants for a list of paths
1496
- * const localizedUrls = generateStaticLocalizedUrls([
1497
- * "/",
1498
- * "/about",
1499
- * "/blog/post-1",
1500
- * ]);
1501
- * // Returns URL objects for each locale:
1502
- * // ["/en/", "/de/", "/en/about", "/de/about", "/en/blog/post-1", "/de/blog/post-1"]
1503
- *
1504
- * @example
1505
- * // Use with framework SSG APIs
1506
- * // SvelteKit
1507
- * export function entries() {
1508
- * const paths = ["/", "/about", "/contact"];
1509
- * return generateStaticLocalizedUrls(paths).map(url => ({
1510
- * locale: extractLocaleFromUrl(url)
1511
- * }));
1512
- * }
1513
- *
1514
- * @example
1515
- * // Sitemap generation
1516
- * const allPages = ["/", "/about", "/blog"];
1517
- * const sitemapUrls = generateStaticLocalizedUrls(allPages);
1518
- *
1519
- * @param {(string | URL)[]} urls - List of canonical URLs or paths to generate localized versions for.
1520
- * Can be absolute URLs (`https://example.com/about`) or paths (`/about`).
1521
- * Paths are resolved against `http://localhost` internally.
1522
- * @returns {URL[]} Array of URL objects representing all localized variants.
1523
- * The order follows each input URL with all its locale variants before moving to the next URL.
1524
- */
1525
- export function generateStaticLocalizedUrls(urls) {
1526
- const localizedUrls = new Set();
1527
- // For default URL pattern, we can optimize the generation
1528
- if (TREE_SHAKE_DEFAULT_URL_PATTERN_USED) {
1529
- for (const urlInput of urls) {
1530
- const url = urlInput instanceof URL
1531
- ? urlInput
1532
- : new URL(urlInput, "http://localhost");
1533
- // Base locale doesn't get a prefix
1534
- localizedUrls.add(url);
1535
- // Other locales get their code as prefix
1536
- for (const locale of locales) {
1537
- if (locale !== baseLocale) {
1538
- const localizedPath = `/${locale}${url.pathname}${url.search}${url.hash}`;
1539
- const localizedUrl = new URL(localizedPath, url.origin);
1540
- localizedUrls.add(localizedUrl);
1541
- }
1542
- }
1543
- }
1544
- return Array.from(localizedUrls);
1545
- }
1546
- // For custom URL patterns, we need to use localizeUrl for each URL and locale
1547
- for (const urlInput of urls) {
1548
- const url = urlInput instanceof URL
1549
- ? urlInput
1550
- : new URL(urlInput, "http://localhost");
1551
- // Try each URL pattern to find one that matches
1552
- let patternFound = false;
1553
- for (const pattern of urlPatterns) {
1554
- try {
1555
- // Try to match the unlocalized pattern
1556
- const unlocalizedMatch = new URLPattern(pattern.pattern, url.href).exec(url.href);
1557
- if (!unlocalizedMatch)
1558
- continue;
1559
- patternFound = true;
1560
- // Track unique localized URLs to avoid duplicates when patterns are the same
1561
- const seenUrls = new Set();
1562
- // Generate localized URL for each locale
1563
- for (const [locale] of pattern.localized) {
1564
- try {
1565
- const localizedUrl = localizeUrl(url, { locale });
1566
- const urlString = localizedUrl.href;
1567
- // Only add if we haven't seen this exact URL before
1568
- if (!seenUrls.has(urlString)) {
1569
- seenUrls.add(urlString);
1570
- localizedUrls.add(localizedUrl);
1571
- }
1572
- }
1573
- catch {
1574
- // Skip if localization fails for this locale
1575
- continue;
1576
- }
1577
- }
1578
- break;
1579
- }
1580
- catch {
1581
- // Skip if pattern matching fails
1582
- continue;
1583
- }
1584
- }
1585
- // If no pattern matched, use the URL as is
1586
- if (!patternFound) {
1587
- localizedUrls.add(url);
1588
- }
1589
- }
1590
- return Array.from(localizedUrls);
1591
- }
1592
-
1593
- /**
1594
- * @typedef {"cookie" | "baseLocale" | "globalVariable" | "url" | "preferredLanguage" | "localStorage"} BuiltInStrategy
1595
- */
1596
- /**
1597
- * @typedef {`custom_${string}`} CustomStrategy
1598
- */
1599
- /**
1600
- * @typedef {BuiltInStrategy | CustomStrategy} Strategy
1601
- */
1602
- /**
1603
- * @typedef {Array<Strategy>} Strategies
1604
- */
1605
- /**
1606
- * @typedef {{ getLocale: (request?: Request) => Promise<string | undefined> | (string | undefined) }} CustomServerStrategyHandler
1607
- */
1608
- /**
1609
- * @typedef {{ getLocale: () => Promise<string|undefined> | (string | undefined), setLocale: (locale: string) => Promise<void> | void }} CustomClientStrategyHandler
1610
- */
1611
- /** @type {Map<string, CustomServerStrategyHandler>} */
1612
- export const customServerStrategies = new Map();
1613
- /** @type {Map<string, CustomClientStrategyHandler>} */
1614
- export const customClientStrategies = new Map();
1615
- /**
1616
- * Checks if the given strategy is a custom strategy.
1617
- *
1618
- * @param {any} strategy The name of the custom strategy to validate.
1619
- * Must be a string that starts with "custom-" followed by alphanumeric characters, hyphens, or underscores.
1620
- * @returns {boolean} Returns true if it is a custom strategy, false otherwise.
1621
- */
1622
- export function isCustomStrategy(strategy) {
1623
- return (typeof strategy === "string" && /^custom-[A-Za-z0-9_-]+$/.test(strategy));
1624
- }
1625
- /**
1626
- * Defines a custom strategy that is executed on the server.
1627
- *
1628
- * @see https://inlang.com/m/gerre34r/library-inlang-paraglideJs/strategy#write-your-own-strategy
1629
- *
1630
- * @param {any} strategy The name of the custom strategy to define. Must follow the pattern custom-name with alphanumeric characters, hyphens, or underscores.
1631
- * @param {CustomServerStrategyHandler} handler The handler for the custom strategy, which should implement
1632
- * the method getLocale.
1633
- * @returns {void}
1634
- */
1635
- export function defineCustomServerStrategy(strategy, handler) {
1636
- if (!isCustomStrategy(strategy)) {
1637
- throw new Error(`Invalid custom strategy: "${strategy}". Must be a custom strategy following the pattern custom-name.`);
1638
- }
1639
- customServerStrategies.set(strategy, handler);
1640
- }
1641
- /**
1642
- * Defines a custom strategy that is executed on the client.
1643
- *
1644
- * @see https://inlang.com/m/gerre34r/library-inlang-paraglideJs/strategy#write-your-own-strategy
1645
- *
1646
- * @param {any} strategy The name of the custom strategy to define. Must follow the pattern custom-name with alphanumeric characters, hyphens, or underscores.
1647
- * @param {CustomClientStrategyHandler} handler The handler for the custom strategy, which should implement the
1648
- * methods getLocale and setLocale.
1649
- * @returns {void}
1650
- */
1651
- export function defineCustomClientStrategy(strategy, handler) {
1652
- if (!isCustomStrategy(strategy)) {
1653
- throw new Error(`Invalid custom strategy: "${strategy}". Must be a custom strategy following the pattern custom-name.`);
1654
- }
1655
- customClientStrategies.set(strategy, handler);
1656
- }
1657
-
1658
- // ------ TYPES ------
1659
-
1660
- /**
1661
- * A locale that is available in the project.
1662
- *
1663
- * @example
1664
- * setLocale(request.locale as Locale)
1665
- *
1666
- * @typedef {(typeof locales)[number]} Locale
1667
- */
1668
-
1669
- /**
1670
- * A branded type representing a localized string.
1671
- *
1672
- * Message functions return this type instead of `string`, enabling TypeScript
1673
- * to distinguish translated strings from regular strings at compile time.
1674
- * This allows you to enforce that only properly localized content is used
1675
- * in your UI components.
1676
- *
1677
- * Since `LocalizedString` is a branded subtype of `string`, it remains fully
1678
- * backward compatible—you can pass it anywhere a `string` is expected.
1679
- *
1680
- * @example
1681
- * // Enforce localized strings in your components
1682
- * function PageTitle(props: { title: LocalizedString }) {
1683
- * return <h1>{props.title}</h1>
1684
- * }
1685
- *
1686
- * // ✅ Correct: using a message function
1687
- * <PageTitle title={m.welcome_title()} />
1688
- *
1689
- * // ❌ Type error: raw strings are not LocalizedString
1690
- * <PageTitle title="Welcome" />
1691
- *
1692
- * @example
1693
- * // LocalizedString is assignable to string (backward compatible)
1694
- * const localized: LocalizedString = m.greeting()
1695
- * const str: string = localized // ✅ works fine
1696
- *
1697
- * // But string is not assignable to LocalizedString
1698
- * const raw: LocalizedString = "Hello" // ❌ Type error
1699
- *
1700
- * @example
1701
- * // Catches accidental string concatenation
1702
- * function showMessage(msg: LocalizedString) { ... }
1703
- *
1704
- * showMessage(m.hello()) // ✅
1705
- * showMessage("Hello " + userName) // ❌ Type error
1706
- * showMessage(m.hello_user({ name: userName })) // ✅ use params instead
1707
- *
1708
- * @typedef {string & { readonly __brand: 'LocalizedString' }} LocalizedString
1709
- */
1710
-
1711
- /**
1712
- * Record of markup options for a tag instance.
1713
- *
1714
- * @typedef {Record<string, unknown>} MessageMarkupOptions
1715
- */
1716
-
1717
- /**
1718
- * Record of markup attributes for a tag instance.
1719
- *
1720
- * @typedef {Record<string, string | true>} MessageMarkupAttributes
1721
- */
1722
-
1723
- /**
1724
- * Type-level schema for a single markup tag.
1725
- *
1726
- * @typedef {{
1727
- * options: MessageMarkupOptions;
1728
- * attributes: MessageMarkupAttributes;
1729
- * children: boolean;
1730
- * }} MessageMarkupTag
1731
- */
1732
-
1733
- /**
1734
- * Type-level schema for all markup tags in a message.
1735
- *
1736
- * @typedef {Record<string, MessageMarkupTag>} MessageMarkupSchema
1737
- */
1738
-
1739
- /**
1740
- * Type-only metadata attached to compiled message functions.
1741
- *
1742
- * @template Inputs
1743
- * @template Options
1744
- * @template {MessageMarkupSchema} Markup
1745
- * @typedef {{
1746
- * readonly __paraglide?: {
1747
- * inputs: Inputs;
1748
- * options: Options;
1749
- * markup: Markup;
1750
- * };
1751
- * }} MessageMetadata
1752
- */
1753
-
1754
- /**
1755
- * A compiled, framework-neutral message part.
1756
- *
1757
- * @typedef {{
1758
- * type: "text";
1759
- * value: string;
1760
- * } | {
1761
- * type: "markup-start";
1762
- * name: string;
1763
- * options: MessageMarkupOptions;
1764
- * attributes: MessageMarkupAttributes;
1765
- * } | {
1766
- * type: "markup-end";
1767
- * name: string;
1768
- * options: MessageMarkupOptions;
1769
- * attributes: MessageMarkupAttributes;
1770
- * } | {
1771
- * type: "markup-standalone";
1772
- * name: string;
1773
- * options: MessageMarkupOptions;
1774
- * attributes: MessageMarkupAttributes;
1775
- * }} MessagePart
1776
- */
1777
-