rune-lab 0.4.5 → 0.4.7

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 (77) hide show
  1. package/dist/RuneProvider.svelte.d.ts +12 -20
  2. package/dist/i18n/message-resolver.test.d.ts +1 -0
  3. package/dist/i18n/messages.d.ts +2 -0
  4. package/dist/i18n/messages.js +7 -0
  5. package/dist/i18n/paraglide/README.md +29 -12
  6. package/dist/i18n/paraglide/messages/_index.js +0 -18
  7. package/dist/i18n/paraglide/runtime.js +98 -12
  8. package/dist/i18n/paraglide/server.js +57 -18
  9. package/dist/kernel/src/persistence/createConfigStore.svelte.js +14 -7
  10. package/dist/kernel/src/persistence/drivers.test.d.ts +1 -0
  11. package/dist/kernel/src/persistence/provider.test.d.ts +1 -0
  12. package/dist/kernel/src/registry/registry.test.d.ts +1 -0
  13. package/dist/mod.js +1 -1
  14. package/dist/runes/layout/src/AppSettingSelector.svelte +5 -10
  15. package/dist/runes/layout/src/AppSettingSelector.svelte.d.ts +35 -21
  16. package/dist/runes/layout/src/ConnectedNavigationPanel.svelte +4 -2
  17. package/dist/runes/layout/src/ConnectedNavigationPanel.svelte.d.ts +13 -19
  18. package/dist/runes/layout/src/ConnectedWorkspaceStrip.svelte +4 -2
  19. package/dist/runes/layout/src/ConnectedWorkspaceStrip.svelte.d.ts +12 -19
  20. package/dist/runes/layout/src/ContentArea.svelte.d.ts +7 -19
  21. package/dist/runes/layout/src/DetailPanel.svelte.d.ts +6 -19
  22. package/dist/runes/layout/src/Icon.svelte +5 -1
  23. package/dist/runes/layout/src/Icon.svelte.d.ts +6 -16
  24. package/dist/runes/layout/src/LanguageSelector.svelte +8 -4
  25. package/dist/runes/layout/src/LanguageSelector.svelte.d.ts +11 -19
  26. package/dist/runes/layout/src/NavigationPanel.svelte +11 -9
  27. package/dist/runes/layout/src/NavigationPanel.svelte.d.ts +16 -19
  28. package/dist/runes/layout/src/ResourceSelector.svelte +22 -21
  29. package/dist/runes/layout/src/ResourceSelector.svelte.d.ts +52 -18
  30. package/dist/runes/layout/src/ThemeSelector.svelte +9 -6
  31. package/dist/runes/layout/src/ThemeSelector.svelte.d.ts +11 -19
  32. package/dist/runes/layout/src/WorkspaceLayout.svelte.d.ts +7 -20
  33. package/dist/runes/layout/src/WorkspaceStrip.svelte +8 -6
  34. package/dist/runes/layout/src/WorkspaceStrip.svelte.d.ts +10 -19
  35. package/dist/runes/layout/src/language.svelte.d.ts +4 -0
  36. package/dist/runes/layout/src/language.svelte.js +8 -0
  37. package/dist/runes/layout/src/theme.svelte.d.ts +4 -0
  38. package/dist/runes/layout/src/theme.svelte.js +8 -0
  39. package/dist/runes/palettes/src/commands/CommandPalette.svelte +5 -1
  40. package/dist/runes/palettes/src/commands/CommandPalette.svelte.d.ts +5 -19
  41. package/dist/runes/palettes/src/notifications/NotificationBell.svelte.d.ts +7 -20
  42. package/dist/runes/palettes/src/notifications/Toaster.svelte.d.ts +30 -15
  43. package/dist/runes/palettes/src/notifications/bridge.d.ts +1 -1
  44. package/dist/runes/palettes/src/notifications/bridge.js +1 -1
  45. package/dist/runes/palettes/src/shortcuts/ShortcutPalette.svelte.d.ts +10 -25
  46. package/dist/runes/plugins/money/src/CurrencySelector.svelte +8 -4
  47. package/dist/runes/plugins/money/src/CurrencySelector.svelte.d.ts +11 -19
  48. package/dist/runes/plugins/money/src/MoneyDisplay.svelte.d.ts +36 -19
  49. package/dist/runes/plugins/money/src/MoneyInput.svelte.d.ts +7 -20
  50. package/dist/runes/plugins/money/src/currency.svelte.d.ts +4 -0
  51. package/dist/runes/plugins/money/src/currency.svelte.js +13 -1
  52. package/dist/runes/plugins/money/src/currency.test.d.ts +1 -0
  53. package/dist/runes/plugins/money/src/exchange-rate.test.d.ts +1 -0
  54. package/dist/runes/plugins/money/src/money-primitive.test.d.ts +1 -0
  55. package/dist/runes/plugins/money/src/money.test.d.ts +1 -0
  56. package/dist/runes/plugins/money/src/strategies.test.d.ts +1 -0
  57. package/package.json +6 -15
  58. package/LICENSE +0 -21
  59. package/README.md +0 -314
  60. package/dist/i18n/paraglide/messages/active_toasts.js +0 -101
  61. package/dist/i18n/paraglide/messages/api_status.js +0 -101
  62. package/dist/i18n/paraglide/messages/app_info.js +0 -101
  63. package/dist/i18n/paraglide/messages/appearance.js +0 -101
  64. package/dist/i18n/paraglide/messages/commands_label.js +0 -101
  65. package/dist/i18n/paraglide/messages/current_currency.js +0 -101
  66. package/dist/i18n/paraglide/messages/current_language.js +0 -101
  67. package/dist/i18n/paraglide/messages/current_theme.js +0 -101
  68. package/dist/i18n/paraglide/messages/currently_in_queue.js +0 -101
  69. package/dist/i18n/paraglide/messages/extended_controls.js +0 -101
  70. package/dist/i18n/paraglide/messages/live_store_dashboard.js +0 -101
  71. package/dist/i18n/paraglide/messages/localization.js +0 -101
  72. package/dist/i18n/paraglide/messages/name_label.js +0 -101
  73. package/dist/i18n/paraglide/messages/real_time_monitor_desc.js +0 -101
  74. package/dist/i18n/paraglide/messages/registered_in_registry.js +0 -101
  75. package/dist/i18n/paraglide/messages/state_label.js +0 -101
  76. package/dist/i18n/paraglide/messages/url_label.js +0 -101
  77. package/dist/i18n/paraglide/messages/version_label.js +0 -101
@@ -1,5 +1,5 @@
1
- import { SvelteComponentTyped } from "svelte";
2
- import type { PersistenceDriver } from "./kernel/src/mod.js";
1
+ import { type Component, type Snippet } from "svelte";
2
+ import type { PersistenceDriver, RunePlugin } from "./kernel/src/mod.js";
3
3
  import type { AppData } from "./kernel/src/mod.js";
4
4
  /**
5
5
  * Namespaced configuration for Rune Lab plugins.
@@ -16,22 +16,14 @@ export interface RuneLabConfig {
16
16
  /** Namespaced config for plugins */
17
17
  [pluginId: string]: unknown;
18
18
  }
19
- declare const __propDef: {
20
- props: Record<string, never>;
21
- events: {
22
- [evt: string]: CustomEvent<any>;
23
- };
24
- slots: {};
25
- exports?: {};
26
- bindings?: string;
19
+ type $$ComponentProps = {
20
+ children: Snippet;
21
+ config?: RuneLabConfig;
22
+ plugins?: RunePlugin[];
23
+ onThemeChange?: (newTheme: any, oldTheme: any) => void;
24
+ onLanguageChange?: (newLang: any, oldLang: any) => void;
25
+ onCurrencyChange?: (newCurrency: any, oldCurrency: any) => void;
27
26
  };
28
- export type RuneProviderProps = typeof __propDef.props;
29
- export type RuneProviderEvents = typeof __propDef.events;
30
- export type RuneProviderSlots = typeof __propDef.slots;
31
- export default class RuneProvider extends SvelteComponentTyped<
32
- RuneProviderProps,
33
- RuneProviderEvents,
34
- RuneProviderSlots
35
- > {
36
- }
37
- export {};
27
+ declare const RuneProvider: Component<$$ComponentProps, {}, "">;
28
+ type RuneProvider = ReturnType<typeof RuneProvider>;
29
+ export default RuneProvider;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ import * as m from "./paraglide/messages.js";
2
+ export { m };
@@ -0,0 +1,7 @@
1
+ // @ts-ignore: paraglide-js generated files are hidden by .gitignore and missing declarations during type-check
2
+ import * as m from "./paraglide/messages.js";
3
+ // re-export all the messages as m
4
+ export { m };
5
+ // * Note: This is a re-export of the messages from the paraglide-js package.
6
+ // * This is to reduce the `Could not find a declaration file for module` error.
7
+ // This error always happens because the generated `paraglide` directory contains it's own `.gitignore` file, so, the typecheck doesn't see the generated files and throws an error, but the build still works. :)
@@ -4,7 +4,7 @@
4
4
  > strings.
5
5
 
6
6
  Compiled from:
7
- `/home/yrrrrrf/docs/lab/code/typescript/rune-lab/src/lib/i18n/project.inlang`
7
+ `/home/yrrrrrf/docs/lab/code/typescript/rune-lab/src/package/src/lib/i18n/project.inlang`
8
8
 
9
9
  ## What is this folder?
10
10
 
@@ -101,18 +101,25 @@ for details.
101
101
  Messages can contain markup tags for bold, links, and other inline elements.
102
102
  Translators control where tags appear; developers control how they render.
103
103
 
104
+ Important:
105
+
106
+ - Tag names are app-defined. There is no built-in list of HTML tags.
107
+ - `{#b}...{/b}` does not automatically render as `<b>...</b>`.
108
+ - Renderers/snippets are looked up by the same tag name used in the message.
109
+
104
110
  ### Message syntax
105
111
 
106
112
  ```json
107
113
  {
108
- "cta": "{#link to=|/docs|}Read the docs{/link}",
109
- "bold_text": "This is {#bold}important{/bold}"
114
+ "cta": "{#link to=|/docs| rel=$relationship @track}Read the docs{/link}",
115
+ "welcome": "{#b}Hi {name}{/b}{#icon/}"
110
116
  }
111
117
  ```
112
118
 
113
119
  - `{#tagName}` opens a tag, `{/tagName}` closes it.
114
- - Options: `to=|/docs|` (accessed via `options.to`).
115
- - Attributes: `@track` (boolean, accessed via `attributes.track`).
120
+ - `{#tagName/}` creates a standalone tag.
121
+ - Options: `to=|/docs|` or `rel=$relationship` (accessed via `options.*`).
122
+ - Attributes: `@track` or `@variant=|hero|` (accessed via `attributes.*`).
116
123
 
117
124
  This is the default inlang message syntax. Paraglide's message format is
118
125
  plugin-based — you can use
@@ -122,13 +129,18 @@ plugin-based — you can use
122
129
 
123
130
  ### Rendering markup
124
131
 
125
- Calling `m.cta()` returns **plain text** (markup stripped). To render markup,
126
- use the framework adapter or the low-level `parts()` API:
132
+ Calling the message function still returns **plain text** (markup stripped):
127
133
 
128
134
  ```js
129
- const parts = m.cta.parts({});
135
+ m.cta({ relationship: "noopener" }); // "Read the docs"
136
+ ```
137
+
138
+ To render markup, use the framework adapter or the low-level `parts()` API:
139
+
140
+ ```js
141
+ const parts = m.cta.parts({ relationship: "noopener" });
130
142
  // [
131
- // { type: "markup-start", name: "link", options: { to: "/docs" }, attributes: {} },
143
+ // { type: "markup-start", name: "link", options: { to: "/docs", rel: "noopener" }, attributes: { track: true } },
132
144
  // { type: "text", value: "Read the docs" },
133
145
  // { type: "markup-end", name: "link" }
134
146
  // ]
@@ -146,14 +158,19 @@ renderers:
146
158
  import { ParaglideMessage } from "@inlang/paraglide-js-react"; // or -vue, -svelte, -solid
147
159
 
148
160
  <ParaglideMessage
149
- message={m.cta}
150
- inputs={{}}
161
+ message={m.welcome}
162
+ inputs={{ name: "Ada" }}
151
163
  markup={{
152
- link: ({ children, options }) => <a href={options.to}>{children}</a>,
164
+ b: ({ children }) => <b>{children}</b>,
165
+ icon: () => <span aria-hidden="true" className="icon-wave" />,
153
166
  }}
154
167
  />;
155
168
  ```
156
169
 
170
+ The available renderer/snippet names come from the message itself. You can
171
+ inspect them through `message.parts()`, and TypeScript uses the same names to
172
+ type-check your markup renderers.
173
+
157
174
  See the
158
175
  [markup documentation](https://inlang.com/m/gerre34r/library-inlang-paraglideJs/markup)
159
176
  for details.
@@ -32,7 +32,6 @@ export * from "./themes.js";
32
32
  export * from "./all_themes.js";
33
33
  export * from "./all_currencies.js";
34
34
  export * from "./all_languages.js";
35
- export * from "./extended_controls.js";
36
35
  export * from "./light.js";
37
36
  export * from "./dark.js";
38
37
  export * from "./system.js";
@@ -69,20 +68,3 @@ export * from "./sunset.js";
69
68
  export * from "./caramellatte.js";
70
69
  export * from "./abyss.js";
71
70
  export * from "./silk.js";
72
- export * from "./live_store_dashboard.js";
73
- export * from "./real_time_monitor_desc.js";
74
- export * from "./api_status.js";
75
- export * from "./active_toasts.js";
76
- export * from "./commands_label.js";
77
- export * from "./app_info.js";
78
- export * from "./appearance.js";
79
- export * from "./localization.js";
80
- export * from "./name_label.js";
81
- export * from "./version_label.js";
82
- export * from "./current_theme.js";
83
- export * from "./current_language.js";
84
- export * from "./current_currency.js";
85
- export * from "./state_label.js";
86
- export * from "./url_label.js";
87
- export * from "./currently_in_queue.js";
88
- export * from "./registered_in_registry.js";
@@ -685,6 +685,10 @@ export function assertIsLocale(input) {
685
685
  );
686
686
  }
687
687
 
688
+ /**
689
+ * @typedef {object} ExtractLocaleFromRequestOptions
690
+ * @property {string | URL} [effectiveRequestUrl] - Effective request URL to use for route matching and locale detection with the URL strategy.
691
+ */
688
692
  /**
689
693
  * Extracts a locale from a request.
690
694
  *
@@ -702,12 +706,18 @@ export function assertIsLocale(input) {
702
706
  * const locale = extractLocaleFromRequest(request);
703
707
  *
704
708
  * @param {Request} request
709
+ * @param {ExtractLocaleFromRequestOptions} [options]
705
710
  * @returns {Locale}
706
711
  */
707
- export const extractLocaleFromRequest = (request) => {
712
+ export const extractLocaleFromRequest = (request, options = {}) => {
713
+ const effectiveRequestUrl = resolveEffectiveRequestUrl(
714
+ request,
715
+ options.effectiveRequestUrl,
716
+ );
708
717
  return extractLocaleFromRequestWithStrategies(
709
718
  request,
710
- getStrategyForUrl(request.url),
719
+ getStrategyForUrl(effectiveRequestUrl),
720
+ effectiveRequestUrl,
711
721
  );
712
722
  };
713
723
  /**
@@ -715,9 +725,15 @@ export const extractLocaleFromRequest = (request) => {
715
725
  *
716
726
  * @param {Request} request
717
727
  * @param {typeof strategy} strategies
728
+ * @param {string | URL} [url]
718
729
  * @returns {Locale}
719
730
  */
720
- export const extractLocaleFromRequestWithStrategies = (request, strategies) => {
731
+ export const extractLocaleFromRequestWithStrategies = (
732
+ request,
733
+ strategies,
734
+ url = request.url,
735
+ ) => {
736
+ const effectiveRequestUrl = resolveEffectiveRequestUrl(request, url);
721
737
  /** @type {string|undefined} */
722
738
  let locale;
723
739
  for (const strat of strategies) {
@@ -728,7 +744,7 @@ export const extractLocaleFromRequestWithStrategies = (request, strategies) => {
728
744
  .find((c) => c.startsWith(cookieName + "="))
729
745
  ?.split("=")[1];
730
746
  } else if (TREE_SHAKE_URL_STRATEGY_USED && strat === "url") {
731
- locale = extractLocaleFromUrl(request.url);
747
+ locale = extractLocaleFromUrl(effectiveRequestUrl);
732
748
  } else if (
733
749
  TREE_SHAKE_PREFERRED_LANGUAGE_STRATEGY_USED &&
734
750
  strat === "preferredLanguage"
@@ -754,6 +770,20 @@ export const extractLocaleFromRequestWithStrategies = (request, strategies) => {
754
770
  "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",
755
771
  );
756
772
  };
773
+ /**
774
+ * @param {Request} request
775
+ * @param {string | URL | undefined} effectiveRequestUrl
776
+ * @returns {URL}
777
+ */
778
+ function resolveEffectiveRequestUrl(
779
+ request,
780
+ effectiveRequestUrl = request.url,
781
+ ) {
782
+ if (effectiveRequestUrl instanceof URL) {
783
+ return new URL(effectiveRequestUrl.href);
784
+ }
785
+ return new URL(effectiveRequestUrl, request.url);
786
+ }
757
787
 
758
788
  /**
759
789
  * Asynchronously extracts a locale from a request.
@@ -783,12 +813,17 @@ export const extractLocaleFromRequestWithStrategies = (request, strategies) => {
783
813
  * const locale = await extractLocaleFromRequestAsync(request);
784
814
  *
785
815
  * @param {Request} request - The request object to extract the locale from.
816
+ * @param {{ effectiveRequestUrl?: string | URL }} [options] - Effective request URL to use for route matching and locale detection with the URL strategy.
786
817
  * @returns {Promise<Locale>} The extracted locale.
787
818
  */
788
- export const extractLocaleFromRequestAsync = async (request) => {
819
+ export const extractLocaleFromRequestAsync = async (request, options = {}) => {
789
820
  /** @type {string|undefined} */
790
821
  let locale;
791
- const strategy = getStrategyForUrl(request.url);
822
+ const effectiveRequestUrl = resolveEffectiveRequestUrlFromRequestAsync(
823
+ request,
824
+ options.effectiveRequestUrl,
825
+ );
826
+ const strategy = getStrategyForUrl(effectiveRequestUrl);
792
827
  // Process custom strategies first, in order
793
828
  for (const strat of strategy) {
794
829
  if (isCustomStrategy(strat) && customServerStrategies.has(strat)) {
@@ -805,8 +840,26 @@ export const extractLocaleFromRequestAsync = async (request) => {
805
840
  }
806
841
  }
807
842
  // If no custom strategy provided a valid locale, fall back to sync version
808
- return extractLocaleFromRequestWithStrategies(request, strategy);
843
+ return extractLocaleFromRequestWithStrategies(
844
+ request,
845
+ strategy,
846
+ effectiveRequestUrl,
847
+ );
809
848
  };
849
+ /**
850
+ * @param {Request} request
851
+ * @param {string | URL | undefined} effectiveRequestUrl
852
+ * @returns {URL}
853
+ */
854
+ function resolveEffectiveRequestUrlFromRequestAsync(
855
+ request,
856
+ effectiveRequestUrl = request.url,
857
+ ) {
858
+ if (effectiveRequestUrl instanceof URL) {
859
+ return new URL(effectiveRequestUrl.href);
860
+ }
861
+ return new URL(effectiveRequestUrl, request.url);
862
+ }
810
863
 
811
864
  /**
812
865
  * Extracts a cookie from the document.
@@ -1314,7 +1367,7 @@ export function aggregateGroups(match) {
1314
1367
  /**
1315
1368
  * @typedef {object} ShouldRedirectServerInput
1316
1369
  * @property {Request} request
1317
- * @property {string | URL} [url]
1370
+ * @property {string | URL} [effectiveRequestUrl] - Effective request URL to use for route matching, locale detection with the URL strategy, and redirect targets.
1318
1371
  * @property {Locale} [locale]
1319
1372
  *
1320
1373
  * @typedef {object} ShouldRedirectClientInput
@@ -1362,6 +1415,23 @@ export function aggregateGroups(match) {
1362
1415
  * return render(request, decision.locale);
1363
1416
  * }
1364
1417
  *
1418
+ * @example
1419
+ * // Server side usage behind a proxy where request.url is not public-facing
1420
+ * export async function handle(request) {
1421
+ * const effectiveRequestUrl = new URL(request.url);
1422
+ * effectiveRequestUrl.protocol = "https:";
1423
+ * effectiveRequestUrl.host = "example.com";
1424
+ *
1425
+ * const decision = await shouldRedirect({
1426
+ * request,
1427
+ * effectiveRequestUrl,
1428
+ * });
1429
+ *
1430
+ * if (decision.shouldRedirect) {
1431
+ * return Response.redirect(decision.redirectUrl, 307);
1432
+ * }
1433
+ * }
1434
+ *
1365
1435
  * @param {ShouldRedirectInput} [input]
1366
1436
  * @returns {Promise<ShouldRedirectResult>}
1367
1437
  */
@@ -1394,9 +1464,11 @@ async function resolveLocale(input, currentUrl) {
1394
1464
  return locale;
1395
1465
  }
1396
1466
  if (input.request) {
1397
- return extractLocaleFromRequestAsync(input.request);
1467
+ return extractLocaleFromRequestAsync(input.request, {
1468
+ effectiveRequestUrl: currentUrl,
1469
+ });
1398
1470
  }
1399
- if (typeof input.url !== "undefined") {
1471
+ if ("url" in input && typeof input.url !== "undefined") {
1400
1472
  return getLocaleForUrl(currentUrl.href);
1401
1473
  }
1402
1474
  return getLocale();
@@ -1408,13 +1480,27 @@ async function resolveLocale(input, currentUrl) {
1408
1480
  * @returns {URL}
1409
1481
  */
1410
1482
  function resolveUrl(input) {
1483
+ if (
1484
+ "effectiveRequestUrl" in input && input.effectiveRequestUrl instanceof URL
1485
+ ) {
1486
+ return new URL(input.effectiveRequestUrl.href);
1487
+ }
1488
+ if (
1489
+ "effectiveRequestUrl" in input &&
1490
+ typeof input.effectiveRequestUrl === "string"
1491
+ ) {
1492
+ return new URL(
1493
+ input.effectiveRequestUrl,
1494
+ input.request ? input.request.url : getUrlOrigin(),
1495
+ );
1496
+ }
1411
1497
  if (input.request) {
1412
1498
  return new URL(input.request.url);
1413
1499
  }
1414
- if (input.url instanceof URL) {
1500
+ if ("url" in input && input.url instanceof URL) {
1415
1501
  return new URL(input.url.href);
1416
1502
  }
1417
- if (typeof input.url === "string") {
1503
+ if ("url" in input && typeof input.url === "string") {
1418
1504
  return new URL(input.url, getUrlOrigin());
1419
1505
  }
1420
1506
  if (typeof window !== "undefined" && window?.location?.href) {
@@ -33,7 +33,10 @@ import * as runtime from "./runtime.js";
33
33
  * If your framework handles URL localization itself (e.g., TanStack Router's `rewrite` option), use the original
34
34
  * request instead to avoid redirect loops.
35
35
  * - `locale`: The determined locale for this request.
36
- * @param {{ onRedirect:(response: Response) => void }} [callbacks] - Callbacks to handle events from middleware
36
+ * @param {{
37
+ * effectiveRequestUrl?: string | URL | ((request: Request) => string | URL),
38
+ * onRedirect?: (response: Response) => void
39
+ * }} [options] - Options to control middleware behavior. `effectiveRequestUrl` sets the effective request URL used for route matching, URL-based locale detection, redirects, and `getUrlOrigin()`.
37
40
  * @returns {Promise<Response>}
38
41
  *
39
42
  * @example
@@ -88,17 +91,18 @@ import * as runtime from "./runtime.js";
88
91
  * }
89
92
  * ```
90
93
  */
91
- export async function paraglideMiddleware(request, resolve, callbacks) {
94
+ export async function paraglideMiddleware(request, resolve, options) {
92
95
  if (!runtime.disableAsyncLocalStorage && !runtime.serverAsyncLocalStorage) {
93
96
  const { AsyncLocalStorage } = await import("async_hooks");
94
97
  runtime.overwriteServerAsyncLocalStorage(new AsyncLocalStorage());
95
98
  } else if (!runtime.serverAsyncLocalStorage) {
96
99
  runtime.overwriteServerAsyncLocalStorage(createMockAsyncLocalStorage());
97
100
  }
98
- if (runtime.isExcludedByRouteStrategy(request.url)) {
101
+ const url = resolveMiddlewareUrl(request, options?.effectiveRequestUrl);
102
+ const origin = url.origin;
103
+ if (runtime.isExcludedByRouteStrategy(url.href)) {
99
104
  const locale = runtime.baseLocale;
100
- const origin = new URL(request.url).origin;
101
- const newRequest = cloneRequestWithFallback(request);
105
+ const newRequest = cloneRequestWithFallback(request, url);
102
106
  /** @type {Set<string>} */
103
107
  const messageCalls = new Set();
104
108
  return /** @type {Response} */ (await runtime.serverAsyncLocalStorage?.run({
@@ -107,10 +111,12 @@ export async function paraglideMiddleware(request, resolve, callbacks) {
107
111
  messageCalls,
108
112
  }, () => resolve({ locale, request: newRequest })));
109
113
  }
110
- const strategy = runtime.getStrategyForUrl(request.url);
111
- const decision = await runtime.shouldRedirect({ request });
114
+ const strategy = runtime.getStrategyForUrl(url.href);
115
+ const decision = await runtime.shouldRedirect({
116
+ request,
117
+ effectiveRequestUrl: url,
118
+ });
112
119
  const locale = decision.locale;
113
- const origin = new URL(request.url).origin;
114
120
  // if the client makes a request to a URL that doesn't match
115
121
  // the localizedUrl, redirect the client to the localized URL
116
122
  if (
@@ -131,7 +137,7 @@ export async function paraglideMiddleware(request, resolve, callbacks) {
131
137
  ...headers,
132
138
  },
133
139
  });
134
- callbacks?.onRedirect(response);
140
+ options?.onRedirect?.(response);
135
141
  return response;
136
142
  }
137
143
  // If the strategy includes "url", we need to de-localize the URL
@@ -142,9 +148,9 @@ export async function paraglideMiddleware(request, resolve, callbacks) {
142
148
  // the server can't render the correct page.
143
149
  let newRequest;
144
150
  if (strategy.includes("url")) {
145
- newRequest = new Request(runtime.deLocalizeUrl(request.url), request);
151
+ newRequest = cloneRequestWithFallback(request, runtime.deLocalizeUrl(url));
146
152
  } else {
147
- newRequest = cloneRequestWithFallback(request);
153
+ newRequest = cloneRequestWithFallback(request, url);
148
154
  }
149
155
  // the message functions that have been called in this request
150
156
  /** @type {Set<string>} */
@@ -170,10 +176,9 @@ export async function paraglideMiddleware(request, resolve, callbacks) {
170
176
  messages.push(`${id}: ${compiledBundles[id]?.[locale]}`);
171
177
  }
172
178
  // Prevent translated content from terminating the inline script tag.
173
- const escapedMessages = messages.join(",").replace(
174
- /<\/(script)/gi,
175
- "<\\/$1",
176
- );
179
+ const escapedMessages = messages
180
+ .join(",")
181
+ .replace(/<\/(script)/gi, "<\\/$1");
177
182
  const script =
178
183
  `<script>globalThis.__paraglide = globalThis.__paraglide ?? {}; globalThis.__paraglide.ssr = { ${escapedMessages} }</script>`;
179
184
  // Insert the script before the closing head tag
@@ -190,6 +195,23 @@ export async function paraglideMiddleware(request, resolve, callbacks) {
190
195
  }
191
196
  return response;
192
197
  }
198
+ /**
199
+ * @param {Request} request
200
+ * @param {string | URL | ((request: Request) => string | URL) | undefined} effectiveRequestUrl
201
+ * @returns {URL}
202
+ */
203
+ function resolveMiddlewareUrl(request, effectiveRequestUrl) {
204
+ if (typeof effectiveRequestUrl === "function") {
205
+ return new URL(effectiveRequestUrl(request), request.url);
206
+ }
207
+ if (
208
+ typeof effectiveRequestUrl === "string" ||
209
+ effectiveRequestUrl instanceof URL
210
+ ) {
211
+ return new URL(effectiveRequestUrl, request.url);
212
+ }
213
+ return new URL(request.url);
214
+ }
193
215
  /**
194
216
  * Some metaframeworks (NextJS) require a new Request object.
195
217
  * https://github.com/opral/inlang-paraglide-js/issues/411
@@ -198,16 +220,33 @@ export async function paraglideMiddleware(request, resolve, callbacks) {
198
220
  * implementations that cannot be cloned with `new Request(request)`.
199
221
  * https://github.com/opral/paraglide-js/issues/573
200
222
  *
223
+ * Effective request URL overrides behind proxies:
224
+ * https://github.com/opral/paraglide-js/issues/652
225
+ *
201
226
  * @param {Request} request
227
+ * @param {string | URL} [url]
202
228
  * @returns {Request}
203
229
  */
204
- function cloneRequestWithFallback(request) {
230
+ function cloneRequestWithFallback(request, url = request.url) {
231
+ const targetUrl = typeof url === "string" ? url : url.href;
232
+ if (targetUrl === request.url) {
233
+ try {
234
+ // Clone first so building a new Request does not consume the original body stream.
235
+ return new Request(request.clone());
236
+ } catch {
237
+ try {
238
+ return new Request(request);
239
+ } catch {
240
+ return request;
241
+ }
242
+ }
243
+ }
205
244
  try {
206
245
  // Clone first so building a new Request does not consume the original body stream.
207
- return new Request(request.clone());
246
+ return new Request(targetUrl, request.clone());
208
247
  } catch {
209
248
  try {
210
- return new Request(request);
249
+ return new Request(targetUrl, request);
211
250
  } catch {
212
251
  return request;
213
252
  }
@@ -22,15 +22,19 @@ class ConfigStoreImpl {
22
22
  if (saved && this.get(saved)) {
23
23
  this.current = saved;
24
24
  }
25
- if (DEV) {
26
- console.log(
27
- `${options.icon ?? "⚙️"} ${options.displayName} configured:`,
28
- {
29
- current: this.current,
30
- },
31
- );
25
+ // Only log here if we were initialized with a real driver (not the default in-memory)
26
+ if (DEV && options.driver) {
27
+ this.#logConfig();
32
28
  }
33
29
  }
30
+ #logConfig() {
31
+ console.log(
32
+ `${this.#options.icon ?? "⚙️"} ${this.#options.displayName} configured:`,
33
+ {
34
+ current: this.current,
35
+ },
36
+ );
37
+ }
34
38
  /**
35
39
  * Replace the persistence driver and immediately re-read any saved value.
36
40
  *
@@ -46,6 +50,9 @@ class ConfigStoreImpl {
46
50
  if (saved && this.get(saved)) {
47
51
  this.current = saved;
48
52
  }
53
+ if (DEV) {
54
+ this.#logConfig();
55
+ }
49
56
  }
50
57
  /**
51
58
  * Set current item with validation
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
package/dist/mod.js CHANGED
@@ -4,4 +4,4 @@ export * from "./kernel/src/mod.js";
4
4
  export * from "./runes/layout/src/mod.js";
5
5
  export * from "./runes/palettes/src/mod.js";
6
6
  export * from "./runes/plugins/money/src/mod.js";
7
- export const version = () => "0.4.5";
7
+ export const version = () => "0.4.7";
@@ -23,15 +23,7 @@
23
23
  tooltip,
24
24
  direction = "bottom",
25
25
  responsive = true,
26
- } = $props<{
27
- options: T[];
28
- value: T;
29
- item: Snippet<[T]>;
30
- triggerLabel: Snippet<[T]>;
31
- tooltip?: string;
32
- direction?: "top" | "bottom" | "left" | "right" | "end" | "auto";
33
- responsive?: boolean;
34
- }>();
26
+ }: AppSettingSelectorProps<T> = $props();
35
27
 
36
28
  let modal = $state<HTMLDialogElement>();
37
29
 
@@ -150,7 +142,10 @@
150
142
  class="w-full text-left py-3 cursor-pointer hover:bg-base-200 px-4 transition-colors"
151
143
  onclick={() => modal?.close()}
152
144
  onkeydown={(e) => {
153
- if (e.key === "Enter" || e.key === " ") {
145
+ if (
146
+ e.key === "Enter" ||
147
+ e.key === " "
148
+ ) {
154
149
  e.preventDefault();
155
150
  modal?.close();
156
151
  }
@@ -1,4 +1,3 @@
1
- import { SvelteComponentTyped } from "svelte";
2
1
  import { type Snippet } from "svelte";
3
2
  export interface AppSettingSelectorProps<T> {
4
3
  options: T[];
@@ -9,26 +8,41 @@ export interface AppSettingSelectorProps<T> {
9
8
  direction?: "top" | "bottom" | "left" | "right" | "end" | "auto";
10
9
  responsive?: boolean;
11
10
  }
11
+ declare function $$render<T>(): {
12
+ props: AppSettingSelectorProps<T>;
13
+ exports: {};
14
+ bindings: "";
15
+ slots: {};
16
+ events: {};
17
+ };
12
18
  declare class __sveltets_Render<T> {
13
- props(): Record<string, never>;
14
- events(): {} & {
15
- [evt: string]: CustomEvent<any>;
16
- };
17
- slots(): {};
19
+ props(): ReturnType<typeof $$render<T>>["props"];
20
+ events(): ReturnType<typeof $$render<T>>["events"];
21
+ slots(): ReturnType<typeof $$render<T>>["slots"];
22
+ bindings(): "";
23
+ exports(): {};
18
24
  }
19
- export type AppSettingSelectorProps_<T> = ReturnType<
20
- __sveltets_Render<T>["props"]
21
- >;
22
- export type AppSettingSelectorEvents<T> = ReturnType<
23
- __sveltets_Render<T>["events"]
24
- >;
25
- export type AppSettingSelectorSlots<T> = ReturnType<
26
- __sveltets_Render<T>["slots"]
27
- >;
28
- export default class AppSettingSelector<T> extends SvelteComponentTyped<
29
- AppSettingSelectorProps_<T>,
30
- AppSettingSelectorEvents<T>,
31
- AppSettingSelectorSlots<T>
32
- > {
25
+ interface $$IsomorphicComponent {
26
+ new <T>(
27
+ options: import("svelte").ComponentConstructorOptions<
28
+ ReturnType<__sveltets_Render<T>["props"]>
29
+ >,
30
+ ):
31
+ & import("svelte").SvelteComponent<
32
+ ReturnType<__sveltets_Render<T>["props"]>,
33
+ ReturnType<__sveltets_Render<T>["events"]>,
34
+ ReturnType<__sveltets_Render<T>["slots"]>
35
+ >
36
+ & {
37
+ $$bindings?: ReturnType<__sveltets_Render<T>["bindings"]>;
38
+ }
39
+ & ReturnType<__sveltets_Render<T>["exports"]>;
40
+ <T>(
41
+ internal: unknown,
42
+ props: ReturnType<__sveltets_Render<T>["props"]> & {},
43
+ ): ReturnType<__sveltets_Render<T>["exports"]>;
44
+ z_$$bindings?: ReturnType<__sveltets_Render<any>["bindings"]>;
33
45
  }
34
- export {};
46
+ declare const AppSettingSelector: $$IsomorphicComponent;
47
+ type AppSettingSelector<T> = InstanceType<typeof AppSettingSelector<T>>;
48
+ export default AppSettingSelector;