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.
- package/dist/RuneProvider.svelte.d.ts +12 -20
- package/dist/i18n/message-resolver.test.d.ts +1 -0
- package/dist/i18n/messages.d.ts +2 -0
- package/dist/i18n/messages.js +7 -0
- package/dist/i18n/paraglide/README.md +29 -12
- package/dist/i18n/paraglide/messages/_index.js +0 -18
- package/dist/i18n/paraglide/runtime.js +98 -12
- package/dist/i18n/paraglide/server.js +57 -18
- package/dist/kernel/src/persistence/createConfigStore.svelte.js +14 -7
- package/dist/kernel/src/persistence/drivers.test.d.ts +1 -0
- package/dist/kernel/src/persistence/provider.test.d.ts +1 -0
- package/dist/kernel/src/registry/registry.test.d.ts +1 -0
- package/dist/mod.js +1 -1
- package/dist/runes/layout/src/AppSettingSelector.svelte +5 -10
- package/dist/runes/layout/src/AppSettingSelector.svelte.d.ts +35 -21
- package/dist/runes/layout/src/ConnectedNavigationPanel.svelte +4 -2
- package/dist/runes/layout/src/ConnectedNavigationPanel.svelte.d.ts +13 -19
- package/dist/runes/layout/src/ConnectedWorkspaceStrip.svelte +4 -2
- package/dist/runes/layout/src/ConnectedWorkspaceStrip.svelte.d.ts +12 -19
- package/dist/runes/layout/src/ContentArea.svelte.d.ts +7 -19
- package/dist/runes/layout/src/DetailPanel.svelte.d.ts +6 -19
- package/dist/runes/layout/src/Icon.svelte +5 -1
- package/dist/runes/layout/src/Icon.svelte.d.ts +6 -16
- package/dist/runes/layout/src/LanguageSelector.svelte +8 -4
- package/dist/runes/layout/src/LanguageSelector.svelte.d.ts +11 -19
- package/dist/runes/layout/src/NavigationPanel.svelte +11 -9
- package/dist/runes/layout/src/NavigationPanel.svelte.d.ts +16 -19
- package/dist/runes/layout/src/ResourceSelector.svelte +22 -21
- package/dist/runes/layout/src/ResourceSelector.svelte.d.ts +52 -18
- package/dist/runes/layout/src/ThemeSelector.svelte +9 -6
- package/dist/runes/layout/src/ThemeSelector.svelte.d.ts +11 -19
- package/dist/runes/layout/src/WorkspaceLayout.svelte.d.ts +7 -20
- package/dist/runes/layout/src/WorkspaceStrip.svelte +8 -6
- package/dist/runes/layout/src/WorkspaceStrip.svelte.d.ts +10 -19
- package/dist/runes/layout/src/language.svelte.d.ts +4 -0
- package/dist/runes/layout/src/language.svelte.js +8 -0
- package/dist/runes/layout/src/theme.svelte.d.ts +4 -0
- package/dist/runes/layout/src/theme.svelte.js +8 -0
- package/dist/runes/palettes/src/commands/CommandPalette.svelte +5 -1
- package/dist/runes/palettes/src/commands/CommandPalette.svelte.d.ts +5 -19
- package/dist/runes/palettes/src/notifications/NotificationBell.svelte.d.ts +7 -20
- package/dist/runes/palettes/src/notifications/Toaster.svelte.d.ts +30 -15
- package/dist/runes/palettes/src/notifications/bridge.d.ts +1 -1
- package/dist/runes/palettes/src/notifications/bridge.js +1 -1
- package/dist/runes/palettes/src/shortcuts/ShortcutPalette.svelte.d.ts +10 -25
- package/dist/runes/plugins/money/src/CurrencySelector.svelte +8 -4
- package/dist/runes/plugins/money/src/CurrencySelector.svelte.d.ts +11 -19
- package/dist/runes/plugins/money/src/MoneyDisplay.svelte.d.ts +36 -19
- package/dist/runes/plugins/money/src/MoneyInput.svelte.d.ts +7 -20
- package/dist/runes/plugins/money/src/currency.svelte.d.ts +4 -0
- package/dist/runes/plugins/money/src/currency.svelte.js +13 -1
- package/dist/runes/plugins/money/src/currency.test.d.ts +1 -0
- package/dist/runes/plugins/money/src/exchange-rate.test.d.ts +1 -0
- package/dist/runes/plugins/money/src/money-primitive.test.d.ts +1 -0
- package/dist/runes/plugins/money/src/money.test.d.ts +1 -0
- package/dist/runes/plugins/money/src/strategies.test.d.ts +1 -0
- package/package.json +6 -15
- package/LICENSE +0 -21
- package/README.md +0 -314
- package/dist/i18n/paraglide/messages/active_toasts.js +0 -101
- package/dist/i18n/paraglide/messages/api_status.js +0 -101
- package/dist/i18n/paraglide/messages/app_info.js +0 -101
- package/dist/i18n/paraglide/messages/appearance.js +0 -101
- package/dist/i18n/paraglide/messages/commands_label.js +0 -101
- package/dist/i18n/paraglide/messages/current_currency.js +0 -101
- package/dist/i18n/paraglide/messages/current_language.js +0 -101
- package/dist/i18n/paraglide/messages/current_theme.js +0 -101
- package/dist/i18n/paraglide/messages/currently_in_queue.js +0 -101
- package/dist/i18n/paraglide/messages/extended_controls.js +0 -101
- package/dist/i18n/paraglide/messages/live_store_dashboard.js +0 -101
- package/dist/i18n/paraglide/messages/localization.js +0 -101
- package/dist/i18n/paraglide/messages/name_label.js +0 -101
- package/dist/i18n/paraglide/messages/real_time_monitor_desc.js +0 -101
- package/dist/i18n/paraglide/messages/registered_in_registry.js +0 -101
- package/dist/i18n/paraglide/messages/state_label.js +0 -101
- package/dist/i18n/paraglide/messages/url_label.js +0 -101
- package/dist/i18n/paraglide/messages/version_label.js +0 -101
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
export
|
|
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,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
|
-
"
|
|
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
|
-
-
|
|
115
|
-
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
150
|
-
inputs={{}}
|
|
161
|
+
message={m.welcome}
|
|
162
|
+
inputs={{ name: "Ada" }}
|
|
151
163
|
markup={{
|
|
152
|
-
|
|
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(
|
|
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 = (
|
|
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(
|
|
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
|
|
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(
|
|
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} [
|
|
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 {{
|
|
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,
|
|
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
|
-
|
|
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
|
|
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(
|
|
111
|
-
const decision = await runtime.shouldRedirect({
|
|
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
|
-
|
|
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 =
|
|
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
|
|
174
|
-
|
|
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 (
|
|
26
|
-
|
|
27
|
-
|
|
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
|
@@ -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 (
|
|
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():
|
|
14
|
-
events():
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
46
|
+
declare const AppSettingSelector: $$IsomorphicComponent;
|
|
47
|
+
type AppSettingSelector<T> = InstanceType<typeof AppSettingSelector<T>>;
|
|
48
|
+
export default AppSettingSelector;
|