rune-lab 0.0.19 → 0.0.21
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/README.md +28 -6
- package/dist/actions/portal.js +6 -1
- package/dist/components/Icon.svelte +13 -2
- package/dist/components/RuneProvider.svelte +81 -0
- package/dist/components/RuneProvider.svelte.d.ts +9 -0
- package/dist/composables/usePersistence.d.ts +2 -0
- package/dist/composables/usePersistence.js +5 -0
- package/dist/composables/useRuneLab.d.ts +17 -0
- package/dist/composables/useRuneLab.js +19 -0
- package/dist/config.d.ts +4 -85
- package/dist/config.js +6 -32
- package/dist/context.d.ts +12 -0
- package/dist/context.js +13 -0
- package/dist/devtools/API_Monitor.svelte +5 -2
- package/dist/devtools/Toaster.svelte +14 -12
- package/dist/devtools/createConfigStore.svelte.d.ts +11 -1
- package/dist/devtools/createConfigStore.svelte.js +13 -11
- package/dist/features/command-palette/CommandPalette.svelte +12 -4
- package/dist/features/config/components/AppSettingSelector.svelte +85 -3
- package/dist/features/config/components/AppSettingSelector.svelte.d.ts +2 -0
- package/dist/features/config/components/CurrencySelector.svelte +27 -9
- package/dist/features/config/components/CurrencySelector.svelte.d.ts +3 -1
- package/dist/features/config/components/LanguageSelector.svelte +21 -6
- package/dist/features/config/components/LanguageSelector.svelte.d.ts +3 -1
- package/dist/features/config/components/ThemeSelector.svelte +23 -8
- package/dist/features/config/components/ThemeSelector.svelte.d.ts +3 -1
- package/dist/features/detail-panels/DashboardPanel.svelte +34 -17
- package/dist/features/detail-panels/ShortcutsPanel.svelte +185 -88
- package/dist/features/detail-panels/ShowcasePanel.svelte +20 -6
- package/dist/features/layout/smart/ConnectedNavigationPanel.svelte +30 -0
- package/dist/features/layout/smart/ConnectedNavigationPanel.svelte.d.ts +10 -0
- package/dist/features/layout/smart/ConnectedWorkspaceStrip.svelte +23 -0
- package/dist/features/layout/smart/ConnectedWorkspaceStrip.svelte.d.ts +9 -0
- package/dist/features/shortcuts/ShortcutPalette.svelte +14 -6
- package/dist/index.d.ts +14 -2
- package/dist/index.js +19 -8
- package/dist/layout/NavigationPanel.svelte +97 -121
- package/dist/layout/NavigationPanel.svelte.d.ts +5 -1
- package/dist/layout/WorkspaceLayout.svelte +109 -30
- package/dist/layout/WorkspaceLayout.svelte.d.ts +2 -2
- package/dist/layout/WorkspaceStrip.svelte +17 -12
- package/dist/layout/WorkspaceStrip.svelte.d.ts +3 -1
- package/dist/layout/index.d.ts +1 -1
- package/dist/layout/index.js +1 -1
- package/dist/paraglide/runtime.d.ts +43 -2
- package/dist/paraglide/runtime.js +143 -23
- package/dist/paraglide/server.js +37 -14
- package/dist/persistence/drivers.d.ts +10 -0
- package/dist/persistence/drivers.js +71 -0
- package/dist/persistence/types.d.ts +17 -0
- package/dist/persistence/types.js +2 -0
- package/dist/showcase/AppStateInspector.svelte +26 -11
- package/dist/showcase/Showcase.svelte +4 -1
- package/dist/state/api.svelte.d.ts +3 -3
- package/dist/state/api.svelte.js +9 -2
- package/dist/state/app.svelte.d.ts +3 -3
- package/dist/state/app.svelte.js +14 -4
- package/dist/state/commands.svelte.d.ts +21 -4
- package/dist/state/commands.svelte.js +22 -94
- package/dist/state/currency.svelte.d.ts +4 -1
- package/dist/state/currency.svelte.js +16 -8
- package/dist/state/index.d.ts +9 -9
- package/dist/state/index.js +10 -9
- package/dist/state/language.svelte.d.ts +4 -1
- package/dist/state/language.svelte.js +27 -8
- package/dist/state/layout.svelte.d.ts +10 -3
- package/dist/state/layout.svelte.js +56 -14
- package/dist/state/shortcuts.svelte.d.ts +56 -6
- package/dist/state/shortcuts.svelte.js +21 -69
- package/dist/state/theme.svelte.d.ts +4 -1
- package/dist/state/theme.svelte.js +16 -8
- package/dist/state/toast.svelte.d.ts +3 -3
- package/dist/state/toast.svelte.js +9 -2
- package/package.json +9 -9
- package/dist/features/command-palette/commands.svelte.d.ts +0 -8
- package/dist/features/command-palette/commands.svelte.js +0 -5
- package/dist/features/config/stores/api.svelte.d.ts +0 -13
- package/dist/features/config/stores/api.svelte.js +0 -5
- package/dist/features/config/stores/app.svelte.d.ts +0 -13
- package/dist/features/config/stores/app.svelte.js +0 -5
- package/dist/features/config/stores/currency.svelte.d.ts +0 -8
- package/dist/features/config/stores/currency.svelte.js +0 -5
- package/dist/features/config/stores/language.svelte.d.ts +0 -8
- package/dist/features/config/stores/language.svelte.js +0 -5
- package/dist/features/config/stores/theme.svelte.d.ts +0 -8
- package/dist/features/config/stores/theme.svelte.js +0 -5
- package/dist/features/config/stores/toast.svelte.d.ts +0 -9
- package/dist/features/config/stores/toast.svelte.js +0 -5
|
@@ -37,6 +37,18 @@ export const strategy = [
|
|
|
37
37
|
"globalVariable",
|
|
38
38
|
"baseLocale"
|
|
39
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 = [];
|
|
40
52
|
/**
|
|
41
53
|
* The used URL patterns.
|
|
42
54
|
*
|
|
@@ -101,6 +113,63 @@ export const urlPatterns = [
|
|
|
101
113
|
]
|
|
102
114
|
}
|
|
103
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
|
+
}
|
|
104
173
|
/**
|
|
105
174
|
* @typedef {{
|
|
106
175
|
* getStore(): {
|
|
@@ -182,8 +251,6 @@ export let getLocale = () => {
|
|
|
182
251
|
if (experimentalStaticLocale !== undefined) {
|
|
183
252
|
return assertIsLocale(experimentalStaticLocale);
|
|
184
253
|
}
|
|
185
|
-
/** @type {string | undefined} */
|
|
186
|
-
let locale;
|
|
187
254
|
// if running in a server-side rendering context
|
|
188
255
|
// retrieve the locale from the async local storage
|
|
189
256
|
if (serverAsyncLocalStorage) {
|
|
@@ -192,7 +259,48 @@ export let getLocale = () => {
|
|
|
192
259
|
return locale;
|
|
193
260
|
}
|
|
194
261
|
}
|
|
195
|
-
|
|
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) {
|
|
196
304
|
if (TREE_SHAKE_COOKIE_STRATEGY_USED && strat === "cookie") {
|
|
197
305
|
locale = extractLocaleFromCookie();
|
|
198
306
|
}
|
|
@@ -202,8 +310,8 @@ export let getLocale = () => {
|
|
|
202
310
|
else if (TREE_SHAKE_URL_STRATEGY_USED &&
|
|
203
311
|
strat === "url" &&
|
|
204
312
|
!isServer &&
|
|
205
|
-
typeof
|
|
206
|
-
locale = extractLocaleFromUrl(
|
|
313
|
+
typeof urlForUrlStrategy === "string") {
|
|
314
|
+
locale = extractLocaleFromUrl(urlForUrlStrategy);
|
|
207
315
|
}
|
|
208
316
|
else if (TREE_SHAKE_GLOBAL_VARIABLE_STRATEGY_USED &&
|
|
209
317
|
strat === "globalVariable" &&
|
|
@@ -232,20 +340,12 @@ export let getLocale = () => {
|
|
|
232
340
|
locale = result;
|
|
233
341
|
}
|
|
234
342
|
}
|
|
235
|
-
// check if match, else continue loop
|
|
236
343
|
if (locale !== undefined) {
|
|
237
|
-
|
|
238
|
-
if (!localeInitiallySet) {
|
|
239
|
-
_locale = asserted;
|
|
240
|
-
// https://github.com/opral/inlang-paraglide-js/issues/455
|
|
241
|
-
localeInitiallySet = true;
|
|
242
|
-
setLocale(asserted, { reload: false });
|
|
243
|
-
}
|
|
244
|
-
return asserted;
|
|
344
|
+
return assertIsLocale(locale);
|
|
245
345
|
}
|
|
246
346
|
}
|
|
247
|
-
|
|
248
|
-
}
|
|
347
|
+
return undefined;
|
|
348
|
+
}
|
|
249
349
|
/**
|
|
250
350
|
* Overwrite the `getLocale()` function.
|
|
251
351
|
*
|
|
@@ -324,7 +424,11 @@ export let setLocale = (newLocale, options) => {
|
|
|
324
424
|
const customSetLocalePromises = [];
|
|
325
425
|
/** @type {string | undefined} */
|
|
326
426
|
let newLocation = undefined;
|
|
327
|
-
|
|
427
|
+
let strategyToUse = strategy;
|
|
428
|
+
if (!isServer && typeof window !== "undefined" && window.location?.href) {
|
|
429
|
+
strategyToUse = getStrategyForUrl(window.location.href);
|
|
430
|
+
}
|
|
431
|
+
for (const strat of strategyToUse) {
|
|
328
432
|
if (TREE_SHAKE_GLOBAL_VARIABLE_STRATEGY_USED &&
|
|
329
433
|
strat === "globalVariable") {
|
|
330
434
|
// a default for a custom strategy to get started quickly
|
|
@@ -507,9 +611,19 @@ export function assertIsLocale(input) {
|
|
|
507
611
|
* @type {(request: Request) => Locale}
|
|
508
612
|
*/
|
|
509
613
|
export const extractLocaleFromRequest = (request) => {
|
|
614
|
+
return extractLocaleFromRequestWithStrategies(request, getStrategyForUrl(request.url));
|
|
615
|
+
};
|
|
616
|
+
/**
|
|
617
|
+
* Extracts a locale from a request using the provided strategy order.
|
|
618
|
+
*
|
|
619
|
+
* @param {Request} request
|
|
620
|
+
* @param {typeof strategy} strategies
|
|
621
|
+
* @returns {Locale}
|
|
622
|
+
*/
|
|
623
|
+
export const extractLocaleFromRequestWithStrategies = (request, strategies) => {
|
|
510
624
|
/** @type {string|undefined} */
|
|
511
625
|
let locale;
|
|
512
|
-
for (const strat of
|
|
626
|
+
for (const strat of strategies) {
|
|
513
627
|
if (TREE_SHAKE_COOKIE_STRATEGY_USED && strat === "cookie") {
|
|
514
628
|
locale = request.headers
|
|
515
629
|
.get("cookie")
|
|
@@ -582,6 +696,7 @@ export const extractLocaleFromRequest = (request) => {
|
|
|
582
696
|
export const extractLocaleFromRequestAsync = async (request) => {
|
|
583
697
|
/** @type {string|undefined} */
|
|
584
698
|
let locale;
|
|
699
|
+
const strategy = getStrategyForUrl(request.url);
|
|
585
700
|
// Process custom strategies first, in order
|
|
586
701
|
for (const strat of strategy) {
|
|
587
702
|
if (isCustomStrategy(strat) && customServerStrategies.has(strat)) {
|
|
@@ -597,7 +712,7 @@ export const extractLocaleFromRequestAsync = async (request) => {
|
|
|
597
712
|
}
|
|
598
713
|
}
|
|
599
714
|
// If no custom strategy provided a valid locale, fall back to sync version
|
|
600
|
-
locale =
|
|
715
|
+
locale = extractLocaleFromRequestWithStrategies(request, strategy);
|
|
601
716
|
return assertIsLocale(locale);
|
|
602
717
|
};
|
|
603
718
|
|
|
@@ -1128,11 +1243,12 @@ export function aggregateGroups(match) {
|
|
|
1128
1243
|
* @returns {Promise<ShouldRedirectResult>}
|
|
1129
1244
|
*/
|
|
1130
1245
|
export async function shouldRedirect(input = {}) {
|
|
1131
|
-
const
|
|
1132
|
-
|
|
1246
|
+
const currentUrl = resolveUrl(input);
|
|
1247
|
+
const locale = /** @type {ReturnType<typeof assertIsLocale>} */ (await resolveLocale(input, currentUrl));
|
|
1248
|
+
const strategy = getStrategyForUrl(currentUrl.href);
|
|
1249
|
+
if (isExcludedByRouteStrategy(currentUrl.href) || !strategy.includes("url")) {
|
|
1133
1250
|
return { shouldRedirect: false, locale, redirectUrl: undefined };
|
|
1134
1251
|
}
|
|
1135
|
-
const currentUrl = resolveUrl(input);
|
|
1136
1252
|
const localizedUrl = localizeUrl(currentUrl.href, { locale });
|
|
1137
1253
|
const shouldRedirectToLocalizedUrl = normalizeUrl(localizedUrl.href) !== normalizeUrl(currentUrl.href);
|
|
1138
1254
|
return {
|
|
@@ -1145,15 +1261,19 @@ export async function shouldRedirect(input = {}) {
|
|
|
1145
1261
|
* Resolves the locale either from the provided input or by using the configured strategies.
|
|
1146
1262
|
*
|
|
1147
1263
|
* @param {ShouldRedirectInput} input
|
|
1264
|
+
* @param {URL} currentUrl
|
|
1148
1265
|
* @returns {Promise<ReturnType<typeof assertIsLocale>>}
|
|
1149
1266
|
*/
|
|
1150
|
-
async function resolveLocale(input) {
|
|
1267
|
+
async function resolveLocale(input, currentUrl) {
|
|
1151
1268
|
if (input.locale) {
|
|
1152
1269
|
return assertIsLocale(input.locale);
|
|
1153
1270
|
}
|
|
1154
1271
|
if (input.request) {
|
|
1155
1272
|
return extractLocaleFromRequestAsync(input.request);
|
|
1156
1273
|
}
|
|
1274
|
+
if (typeof input.url !== "undefined") {
|
|
1275
|
+
return getLocaleForUrl(currentUrl.href);
|
|
1276
|
+
}
|
|
1157
1277
|
return getLocale();
|
|
1158
1278
|
}
|
|
1159
1279
|
/**
|
package/dist/paraglide/server.js
CHANGED
|
@@ -106,6 +106,15 @@ export async function paraglideMiddleware(request, resolve, callbacks) {
|
|
|
106
106
|
} else if (!runtime.serverAsyncLocalStorage) {
|
|
107
107
|
runtime.overwriteServerAsyncLocalStorage(createMockAsyncLocalStorage());
|
|
108
108
|
}
|
|
109
|
+
if (runtime.isExcludedByRouteStrategy(request.url)) {
|
|
110
|
+
const locale = runtime.baseLocale;
|
|
111
|
+
const origin = new URL(request.url).origin;
|
|
112
|
+
const newRequest = cloneRequestWithFallback(request);
|
|
113
|
+
/** @type {Set<string>} */
|
|
114
|
+
const messageCalls = new Set();
|
|
115
|
+
return /** @type {Response} */ (await runtime.serverAsyncLocalStorage?.run({ locale, origin, messageCalls }, () => resolve({ locale, request: newRequest })));
|
|
116
|
+
}
|
|
117
|
+
const strategy = runtime.getStrategyForUrl(request.url);
|
|
109
118
|
const decision = await runtime.shouldRedirect({ request });
|
|
110
119
|
const locale = decision.locale;
|
|
111
120
|
const origin = new URL(request.url).origin;
|
|
@@ -117,7 +126,7 @@ export async function paraglideMiddleware(request, resolve, callbacks) {
|
|
|
117
126
|
// Create headers object with Vary header if preferredLanguage strategy is used
|
|
118
127
|
/** @type {Record<string, string>} */
|
|
119
128
|
const headers = {};
|
|
120
|
-
if (
|
|
129
|
+
if (strategy.includes("preferredLanguage")) {
|
|
121
130
|
headers["Vary"] = "Accept-Language";
|
|
122
131
|
}
|
|
123
132
|
const response = new Response(null, {
|
|
@@ -137,22 +146,11 @@ export async function paraglideMiddleware(request, resolve, callbacks) {
|
|
|
137
146
|
// de-localized URL e.g. `/en/about` to `/about`. Otherwise,
|
|
138
147
|
// the server can't render the correct page.
|
|
139
148
|
let newRequest;
|
|
140
|
-
if (
|
|
149
|
+
if (strategy.includes("url")) {
|
|
141
150
|
newRequest = new Request(runtime.deLocalizeUrl(request.url), request);
|
|
142
151
|
}
|
|
143
152
|
else {
|
|
144
|
-
|
|
145
|
-
// https://github.com/opral/inlang-paraglide-js/issues/411
|
|
146
|
-
// However, some frameworks (TanStack Start 1.143+) use custom Request
|
|
147
|
-
// implementations that cannot be cloned with `new Request(request)`
|
|
148
|
-
// https://github.com/opral/paraglide-js/issues/573
|
|
149
|
-
// Try to clone the request, but fall back to the original if cloning fails
|
|
150
|
-
try {
|
|
151
|
-
newRequest = new Request(request);
|
|
152
|
-
}
|
|
153
|
-
catch {
|
|
154
|
-
newRequest = request;
|
|
155
|
-
}
|
|
153
|
+
newRequest = cloneRequestWithFallback(request);
|
|
156
154
|
}
|
|
157
155
|
// the message functions that have been called in this request
|
|
158
156
|
/** @type {Set<string>} */
|
|
@@ -185,6 +183,31 @@ export async function paraglideMiddleware(request, resolve, callbacks) {
|
|
|
185
183
|
}
|
|
186
184
|
return response;
|
|
187
185
|
}
|
|
186
|
+
/**
|
|
187
|
+
* Some metaframeworks (NextJS) require a new Request object.
|
|
188
|
+
* https://github.com/opral/inlang-paraglide-js/issues/411
|
|
189
|
+
*
|
|
190
|
+
* However, some frameworks (TanStack Start 1.143+) use custom Request
|
|
191
|
+
* implementations that cannot be cloned with `new Request(request)`.
|
|
192
|
+
* https://github.com/opral/paraglide-js/issues/573
|
|
193
|
+
*
|
|
194
|
+
* @param {Request} request
|
|
195
|
+
* @returns {Request}
|
|
196
|
+
*/
|
|
197
|
+
function cloneRequestWithFallback(request) {
|
|
198
|
+
try {
|
|
199
|
+
// Clone first so building a new Request does not consume the original body stream.
|
|
200
|
+
return new Request(request.clone());
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
try {
|
|
204
|
+
return new Request(request);
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
return request;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
188
211
|
/**
|
|
189
212
|
* Creates a mock AsyncLocalStorage implementation for environments where
|
|
190
213
|
* native AsyncLocalStorage is not available or disabled.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { PersistenceDriver } from "./types";
|
|
2
|
+
export declare function createInMemoryDriver(): PersistenceDriver;
|
|
3
|
+
export declare const inMemoryDriver: PersistenceDriver;
|
|
4
|
+
export declare const localStorageDriver: PersistenceDriver;
|
|
5
|
+
export declare const sessionStorageDriver: PersistenceDriver;
|
|
6
|
+
export declare const cookieDriver: (options?: {
|
|
7
|
+
path?: string;
|
|
8
|
+
maxAge?: number;
|
|
9
|
+
sameSite?: "Lax" | "Strict" | "None";
|
|
10
|
+
}) => PersistenceDriver;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
export function createInMemoryDriver() {
|
|
2
|
+
const store = new Map();
|
|
3
|
+
return {
|
|
4
|
+
get: (key) => store.get(key) ?? null,
|
|
5
|
+
set: (key, value) => store.set(key, value),
|
|
6
|
+
remove: (key) => store.delete(key),
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
export const inMemoryDriver = createInMemoryDriver();
|
|
10
|
+
export const localStorageDriver = {
|
|
11
|
+
get: (key) => {
|
|
12
|
+
if (typeof window === "undefined")
|
|
13
|
+
return null;
|
|
14
|
+
return window.localStorage.getItem(key);
|
|
15
|
+
},
|
|
16
|
+
set: (key, value) => {
|
|
17
|
+
if (typeof window === "undefined")
|
|
18
|
+
return;
|
|
19
|
+
window.localStorage.setItem(key, value);
|
|
20
|
+
},
|
|
21
|
+
remove: (key) => {
|
|
22
|
+
if (typeof window === "undefined")
|
|
23
|
+
return;
|
|
24
|
+
window.localStorage.removeItem(key);
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
export const sessionStorageDriver = {
|
|
28
|
+
get: (key) => {
|
|
29
|
+
if (typeof window === "undefined")
|
|
30
|
+
return null;
|
|
31
|
+
return window.sessionStorage.getItem(key);
|
|
32
|
+
},
|
|
33
|
+
set: (key, value) => {
|
|
34
|
+
if (typeof window === "undefined")
|
|
35
|
+
return;
|
|
36
|
+
window.sessionStorage.setItem(key, value);
|
|
37
|
+
},
|
|
38
|
+
remove: (key) => {
|
|
39
|
+
if (typeof window === "undefined")
|
|
40
|
+
return;
|
|
41
|
+
window.sessionStorage.removeItem(key);
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
export const cookieDriver = (options = {}) => ({
|
|
45
|
+
get: (key) => {
|
|
46
|
+
if (typeof window === "undefined" || typeof document === "undefined") {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
const match = document.cookie.match(new RegExp(`(^| )${key}=([^;]+)`));
|
|
50
|
+
return match ? decodeURIComponent(match[2]) : null;
|
|
51
|
+
},
|
|
52
|
+
set: (key, value) => {
|
|
53
|
+
if (typeof window === "undefined" || typeof document === "undefined") {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
let cookie = `${key}=${encodeURIComponent(value)}`;
|
|
57
|
+
if (options.path)
|
|
58
|
+
cookie += `; path=${options.path}`;
|
|
59
|
+
if (options.maxAge)
|
|
60
|
+
cookie += `; max-age=${options.maxAge}`;
|
|
61
|
+
if (options.sameSite)
|
|
62
|
+
cookie += `; samesite=${options.sameSite}`;
|
|
63
|
+
document.cookie = cookie;
|
|
64
|
+
},
|
|
65
|
+
remove: (key) => {
|
|
66
|
+
if (typeof window === "undefined" || typeof document === "undefined") {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
document.cookie = `${key}=; max-age=0; path=${options.path || "/"}`;
|
|
70
|
+
},
|
|
71
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Synchronous persistence driver interface for Svelte stores
|
|
3
|
+
*/
|
|
4
|
+
export interface PersistenceDriver {
|
|
5
|
+
/**
|
|
6
|
+
* Get a value by key. Should return null if not found.
|
|
7
|
+
*/
|
|
8
|
+
get(key: string): string | null;
|
|
9
|
+
/**
|
|
10
|
+
* Set a value by key.
|
|
11
|
+
*/
|
|
12
|
+
set(key: string, value: string): void;
|
|
13
|
+
/**
|
|
14
|
+
* Remove a value by key.
|
|
15
|
+
*/
|
|
16
|
+
remove(key: string): void;
|
|
17
|
+
}
|
|
@@ -1,7 +1,22 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { appConfig } from "../config";
|
|
3
2
|
import * as m from "../paraglide/messages.js";
|
|
4
3
|
import StoreDetailCard from "./StoreDetailCard.svelte";
|
|
4
|
+
import { getAppStore } from "../state/app.svelte";
|
|
5
|
+
import { getApiStore } from "../state/api.svelte";
|
|
6
|
+
import { getThemeStore } from "../state/theme.svelte";
|
|
7
|
+
import { getLanguageStore } from "../state/language.svelte";
|
|
8
|
+
import { getCurrencyStore } from "../state/currency.svelte";
|
|
9
|
+
import { getToastStore } from "../state/toast.svelte";
|
|
10
|
+
import { getCommandStore } from "../state/commands.svelte";
|
|
11
|
+
|
|
12
|
+
// Inject context stores
|
|
13
|
+
const appStore = getAppStore();
|
|
14
|
+
const apiStore = getApiStore();
|
|
15
|
+
const themeStore = getThemeStore();
|
|
16
|
+
const languageStore = getLanguageStore();
|
|
17
|
+
const currencyStore = getCurrencyStore();
|
|
18
|
+
const toastStore = getToastStore();
|
|
19
|
+
const commandStore = getCommandStore();
|
|
5
20
|
|
|
6
21
|
const storeState = $derived([
|
|
7
22
|
{
|
|
@@ -13,12 +28,12 @@
|
|
|
13
28
|
{
|
|
14
29
|
key: "Name",
|
|
15
30
|
labelKey: "name_label",
|
|
16
|
-
value:
|
|
31
|
+
value: appStore.name,
|
|
17
32
|
},
|
|
18
33
|
{
|
|
19
34
|
key: "Version",
|
|
20
35
|
labelKey: "version_label",
|
|
21
|
-
value:
|
|
36
|
+
value: appStore.version,
|
|
22
37
|
},
|
|
23
38
|
],
|
|
24
39
|
},
|
|
@@ -31,7 +46,7 @@
|
|
|
31
46
|
{
|
|
32
47
|
key: "Theme",
|
|
33
48
|
labelKey: "current_theme",
|
|
34
|
-
value: `${
|
|
49
|
+
value: `${themeStore.getProp("icon") || ""} ${themeStore.current}`,
|
|
35
50
|
},
|
|
36
51
|
],
|
|
37
52
|
},
|
|
@@ -44,12 +59,12 @@
|
|
|
44
59
|
{
|
|
45
60
|
key: "Language",
|
|
46
61
|
labelKey: "current_language",
|
|
47
|
-
value: `${
|
|
62
|
+
value: `${languageStore.getProp("flag") || ""} ${languageStore.current}`,
|
|
48
63
|
},
|
|
49
64
|
{
|
|
50
65
|
key: "Currency",
|
|
51
66
|
labelKey: "current_currency",
|
|
52
|
-
value: `${
|
|
67
|
+
value: `${currencyStore.getProp("symbol") || ""} ${currencyStore.current}`,
|
|
53
68
|
},
|
|
54
69
|
],
|
|
55
70
|
},
|
|
@@ -101,15 +116,15 @@
|
|
|
101
116
|
</div>
|
|
102
117
|
<div
|
|
103
118
|
class="stat-value capitalize {getStatusClass(
|
|
104
|
-
|
|
119
|
+
apiStore.connectionState,
|
|
105
120
|
)} text-2xl"
|
|
106
121
|
>
|
|
107
|
-
{
|
|
122
|
+
{apiStore.connectionState}
|
|
108
123
|
</div>
|
|
109
124
|
<div
|
|
110
125
|
class="stat-desc font-mono text-[10px] opacity-50 truncate max-w-[200px]"
|
|
111
126
|
>
|
|
112
|
-
{
|
|
127
|
+
{apiStore.URL}
|
|
113
128
|
</div>
|
|
114
129
|
</div>
|
|
115
130
|
|
|
@@ -120,7 +135,7 @@
|
|
|
120
135
|
{t(m.active_toasts, "Active Toasts")}
|
|
121
136
|
</div>
|
|
122
137
|
<div class="stat-value text-2xl">
|
|
123
|
-
{
|
|
138
|
+
{toastStore.toasts.length}
|
|
124
139
|
</div>
|
|
125
140
|
<div class="stat-desc font-mono text-[10px] opacity-50">
|
|
126
141
|
{t(m.currently_in_queue, "Currently in queue")}
|
|
@@ -134,7 +149,7 @@
|
|
|
134
149
|
{t(m.commands_label, "Commands")}
|
|
135
150
|
</div>
|
|
136
151
|
<div class="stat-value text-2xl">
|
|
137
|
-
{
|
|
152
|
+
{commandStore.commands.length}
|
|
138
153
|
</div>
|
|
139
154
|
<div class="stat-desc font-mono text-[10px] opacity-50">
|
|
140
155
|
{t(m.registered_in_registry, "Registered in registry")}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import { getLayoutStore } from "../state/layout.svelte";
|
|
3
|
+
|
|
4
|
+
const layoutStore = getLayoutStore();
|
|
5
|
+
|
|
3
6
|
import Actions from "./tabs/Actions.svelte";
|
|
4
7
|
import DataInput from "./tabs/DataInput.svelte";
|
|
5
8
|
import Display from "./tabs/Display.svelte";
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* API connection state and configuration
|
|
3
3
|
*/
|
|
4
4
|
export type ConnectionState = "connected" | "connecting" | "disconnected";
|
|
5
|
-
declare class ApiStore {
|
|
5
|
+
export declare class ApiStore {
|
|
6
6
|
url: string;
|
|
7
7
|
version: string;
|
|
8
8
|
connectionState: ConnectionState;
|
|
@@ -21,5 +21,5 @@ declare class ApiStore {
|
|
|
21
21
|
*/
|
|
22
22
|
init(url: string, version?: string): void;
|
|
23
23
|
}
|
|
24
|
-
export declare
|
|
25
|
-
export
|
|
24
|
+
export declare function createApiStore(): ApiStore;
|
|
25
|
+
export declare function getApiStore(): ApiStore;
|
package/dist/state/api.svelte.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import { getContext } from "svelte";
|
|
2
|
+
import { RUNE_LAB_CONTEXT } from "../context";
|
|
3
|
+
export class ApiStore {
|
|
2
4
|
// State
|
|
3
5
|
url = $state("http://localhost:8000");
|
|
4
6
|
version = $state("v1");
|
|
@@ -42,4 +44,9 @@ class ApiStore {
|
|
|
42
44
|
this.reconnect();
|
|
43
45
|
}
|
|
44
46
|
}
|
|
45
|
-
export
|
|
47
|
+
export function createApiStore() {
|
|
48
|
+
return new ApiStore();
|
|
49
|
+
}
|
|
50
|
+
export function getApiStore() {
|
|
51
|
+
return getContext(RUNE_LAB_CONTEXT.api);
|
|
52
|
+
}
|
|
@@ -14,7 +14,7 @@ export interface AppData {
|
|
|
14
14
|
* App Store
|
|
15
15
|
* Manages application metadata and identity
|
|
16
16
|
*/
|
|
17
|
-
declare class AppStore {
|
|
17
|
+
export declare class AppStore {
|
|
18
18
|
#private;
|
|
19
19
|
name: string;
|
|
20
20
|
version: string;
|
|
@@ -32,5 +32,5 @@ declare class AppStore {
|
|
|
32
32
|
*/
|
|
33
33
|
get info(): AppData;
|
|
34
34
|
}
|
|
35
|
-
export declare
|
|
36
|
-
export
|
|
35
|
+
export declare function createAppStore(): AppStore;
|
|
36
|
+
export declare function getAppStore(): AppStore;
|
package/dist/state/app.svelte.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
// src/lib/stores/app-config.svelte.ts
|
|
2
|
-
|
|
2
|
+
import { getContext } from "svelte";
|
|
3
|
+
import { RUNE_LAB_CONTEXT } from "../context";
|
|
3
4
|
/**
|
|
4
5
|
* App Store
|
|
5
6
|
* Manages application metadata and identity
|
|
6
7
|
*/
|
|
7
|
-
class AppStore {
|
|
8
|
+
export class AppStore {
|
|
8
9
|
// State
|
|
9
10
|
name = $state("Rune Lab");
|
|
10
11
|
version = $state("0.0.1");
|
|
@@ -18,8 +19,12 @@ class AppStore {
|
|
|
18
19
|
* Initialize app store with metadata
|
|
19
20
|
*/
|
|
20
21
|
init(data) {
|
|
21
|
-
if (this.#initialized)
|
|
22
|
+
if (this.#initialized) {
|
|
23
|
+
if (import.meta.env?.DEV) {
|
|
24
|
+
console.warn("AppStore.init() called multiple times. Ignoring subsequent calls.", "Overwritten properties would have been:", data);
|
|
25
|
+
}
|
|
22
26
|
return;
|
|
27
|
+
}
|
|
23
28
|
if (data.name)
|
|
24
29
|
this.name = data.name;
|
|
25
30
|
if (data.version)
|
|
@@ -52,4 +57,9 @@ class AppStore {
|
|
|
52
57
|
}
|
|
53
58
|
}
|
|
54
59
|
// Export singleton instance
|
|
55
|
-
export
|
|
60
|
+
export function createAppStore() {
|
|
61
|
+
return new AppStore();
|
|
62
|
+
}
|
|
63
|
+
export function getAppStore() {
|
|
64
|
+
return getContext(RUNE_LAB_CONTEXT.app);
|
|
65
|
+
}
|