sh3-core 0.17.2 → 0.19.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/dist/Sh3.svelte +59 -4
  2. package/dist/actions/CommandPalette.svelte +1 -2
  3. package/dist/actions/listeners.js +12 -1
  4. package/dist/api.d.ts +4 -0
  5. package/dist/app/store/storeShard.svelte.js +1 -21
  6. package/dist/app/store/version.d.ts +11 -0
  7. package/dist/app/store/version.js +39 -0
  8. package/dist/app/store/version.test.d.ts +1 -0
  9. package/dist/app/store/version.test.js +44 -0
  10. package/dist/apps/lifecycle.d.ts +6 -0
  11. package/dist/apps/lifecycle.js +5 -2
  12. package/dist/apps/lifecycle.test.js +30 -0
  13. package/dist/apps/types.d.ts +12 -0
  14. package/dist/assets/iconIds.generated.d.ts +1 -1
  15. package/dist/assets/iconIds.generated.js +5 -0
  16. package/dist/assets/icons.svg +31 -0
  17. package/dist/auth/auth.svelte.js +18 -8
  18. package/dist/auth/types.d.ts +6 -0
  19. package/dist/chrome/CompactChrome.svelte +54 -20
  20. package/dist/chrome/CompactChrome.svelte.test.js +112 -5
  21. package/dist/createShell.d.ts +9 -0
  22. package/dist/createShell.js +20 -7
  23. package/dist/createShell.remoteAuth.test.d.ts +1 -0
  24. package/dist/createShell.remoteAuth.test.js +71 -0
  25. package/dist/documents/http-backend.js +12 -11
  26. package/dist/env/client.js +11 -5
  27. package/dist/files/types.d.ts +106 -0
  28. package/dist/files/types.js +1 -0
  29. package/dist/gestures/gestureRegistry.d.ts +6 -0
  30. package/dist/gestures/gestureRegistry.js +190 -0
  31. package/dist/gestures/gestureRegistry.test.d.ts +1 -0
  32. package/dist/gestures/gestureRegistry.test.js +120 -0
  33. package/dist/gestures/index.d.ts +6 -0
  34. package/dist/gestures/index.js +12 -0
  35. package/dist/gestures/pointerClaim.d.ts +7 -0
  36. package/dist/gestures/pointerClaim.js +36 -0
  37. package/dist/gestures/pointerClaim.test.d.ts +1 -0
  38. package/dist/gestures/pointerClaim.test.js +64 -0
  39. package/dist/gestures/types.d.ts +83 -0
  40. package/dist/gestures/types.js +1 -0
  41. package/dist/host-entry.d.ts +1 -0
  42. package/dist/host-entry.js +1 -0
  43. package/dist/layout/LayoutRenderer.browser.test.js +15 -3
  44. package/dist/layout/LayoutRenderer.svelte +16 -3
  45. package/dist/layout/LayoutRenderer.svelte.d.ts +2 -0
  46. package/dist/layout/__screenshots__/LayoutRenderer.browser.test.ts/LayoutRenderer-browser---E-3-splitter-drag-updates-split-sizes-when-the-splitter-handle-is-dragged-1.png +0 -0
  47. package/dist/layout/__screenshots__/LayoutRenderer.browser.test.ts/LayoutRenderer-browser---E-5-splitter-collapse-toggle-toggles-collapsed-i--on-double-click-1.png +0 -0
  48. package/dist/layout/__screenshots__/LayoutRenderer.browser.test.ts/LayoutRenderer-browser---E-6-fixed-slots-hides-the-collapse-widget-on-a-fixed-pane-but-keeps-it-on-panes-with-a-non-fixed-neighbor-1.png +0 -0
  49. package/dist/layout/compact/CarouselTabs.svelte +362 -0
  50. package/dist/layout/compact/CarouselTabs.svelte.d.ts +10 -0
  51. package/dist/layout/compact/CarouselTabs.svelte.test.d.ts +1 -0
  52. package/dist/layout/compact/CarouselTabs.svelte.test.js +300 -0
  53. package/dist/layout/compact/CompactRenderer.svelte +1 -1
  54. package/dist/layout/compact/CompactRenderer.svelte.test.js +49 -0
  55. package/dist/layout/compact/derive.js +2 -0
  56. package/dist/layout/compact/derive.test.js +37 -0
  57. package/dist/layout/compact/enrichCarousels.d.ts +8 -0
  58. package/dist/layout/compact/enrichCarousels.js +44 -0
  59. package/dist/layout/compact/enrichCarousels.test.d.ts +1 -0
  60. package/dist/layout/compact/enrichCarousels.test.js +88 -0
  61. package/dist/layout/compact/types.d.ts +3 -0
  62. package/dist/layout/drag.svelte.js +13 -0
  63. package/dist/layout/store.schemaVersion.test.js +2 -2
  64. package/dist/layout/types.d.ts +9 -1
  65. package/dist/layout/types.js +1 -1
  66. package/dist/layout/types.test.d.ts +1 -0
  67. package/dist/layout/types.test.js +26 -0
  68. package/dist/overlays/ModalFrame.svelte +3 -1
  69. package/dist/overlays/ModalFrame.svelte.d.ts +1 -0
  70. package/dist/overlays/floatDismiss.js +5 -0
  71. package/dist/overlays/focusTrap.d.ts +11 -1
  72. package/dist/overlays/focusTrap.js +11 -9
  73. package/dist/overlays/modal.js +1 -0
  74. package/dist/overlays/popup.js +4 -0
  75. package/dist/overlays/types.d.ts +9 -0
  76. package/dist/primitives/Button.svelte +18 -0
  77. package/dist/primitives/Button.svelte.d.ts +6 -0
  78. package/dist/primitives/ResizableSplitter.svelte +71 -11
  79. package/dist/primitives/ResizableSplitter.svelte.d.ts +8 -0
  80. package/dist/primitives/ResizableSplitter.svelte.test.d.ts +1 -0
  81. package/dist/primitives/ResizableSplitter.svelte.test.js +74 -0
  82. package/dist/server-shard/types.d.ts +2 -1
  83. package/dist/shards/activate.svelte.js +16 -0
  84. package/dist/shards/ctx-fetch.test.d.ts +1 -0
  85. package/dist/shards/ctx-fetch.test.js +136 -0
  86. package/dist/shards/types.d.ts +29 -0
  87. package/dist/transport/apiFetch.d.ts +1 -0
  88. package/dist/transport/apiFetch.js +65 -0
  89. package/dist/transport/apiFetch.test.d.ts +1 -0
  90. package/dist/transport/apiFetch.test.js +37 -0
  91. package/dist/transport/authToken.d.ts +2 -0
  92. package/dist/transport/authToken.js +53 -0
  93. package/dist/transport/authToken.test.d.ts +1 -0
  94. package/dist/transport/authToken.test.js +33 -0
  95. package/dist/version.d.ts +1 -1
  96. package/dist/version.js +1 -1
  97. package/package.json +1 -1
@@ -205,6 +205,35 @@ export interface ShardContext {
205
205
  registerVerb(verb: Verb): void;
206
206
  /** Obtain a file-oriented document handle scoped to this shard. */
207
207
  documents(options: DocumentHandleOptions): DocumentHandle;
208
+ /**
209
+ * Cross-origin-safe HTTP helper. Resolves relative `/api/...` paths
210
+ * against the configured serverUrl. In Tauri, routes through
211
+ * @tauri-apps/plugin-http (no browser CORS, cookies via reqwest).
212
+ * On web, behaves like `fetch` with `credentials: 'include'`.
213
+ *
214
+ * Server-shards must use this instead of global `fetch` to remain
215
+ * compatible with cross-origin clients (e.g. Tauri Android).
216
+ *
217
+ * @param path - Relative `/api/...` path or fully-qualified URL.
218
+ * @param init - Standard RequestInit.
219
+ */
220
+ fetch(path: string, init?: RequestInit): Promise<Response>;
221
+ /**
222
+ * The configured server base URL (e.g. `https://my-sh3.example.com`).
223
+ * Empty string when running local-only (no remote server).
224
+ * Use this as the `BaseAddress` for non-fetch HTTP clients (e.g. WASM .NET
225
+ * `HttpClient`) that cannot go through `ctx.fetch`.
226
+ */
227
+ readonly serverUrl: string;
228
+ /**
229
+ * Resolve a path to an absolute URL against the configured server, using
230
+ * the same rules as `ctx.fetch` but without making a request. Useful for
231
+ * WebSocket URLs and any transport that bypasses `ctx.fetch`.
232
+ *
233
+ * @param path - Relative `/api/...` path or fully-qualified URL.
234
+ * @returns Absolute URL string.
235
+ */
236
+ resolveUrl(path: string): string;
208
237
  /**
209
238
  * Declare environment state for this shard and receive a hydrated snapshot.
210
239
  * Env state is server-authoritative, fetched once at activation, and
@@ -0,0 +1 @@
1
+ export declare function apiFetch(url: string, init?: RequestInit): Promise<Response>;
@@ -0,0 +1,65 @@
1
+ /*
2
+ * apiFetch — single source of truth for HTTP calls against the
3
+ * configured sh3-server, transparently using Tauri's plugin-http
4
+ * when running inside a Tauri webview.
5
+ *
6
+ * Browser fetch enforces CORS and respects SameSite cookie rules,
7
+ * which breaks cross-origin requests from a Tauri webview at e.g.
8
+ * `tauri://localhost` to a remote sh3-server. plugin-http (reqwest
9
+ * under the hood) is not subject to browser CORS and uses a
10
+ * per-host cookie store, so existing same-origin auth keeps working
11
+ * across origins.
12
+ *
13
+ * The plugin-http import is dynamic and behind try/catch — same
14
+ * defensive pattern as `platform/index.ts`. Vite code-splits it
15
+ * into a Tauri-only chunk that never loads in web builds.
16
+ */
17
+ import { getAuthToken } from './authToken';
18
+ let tauriFetch = null;
19
+ let tauriProbed = false;
20
+ function inTauriRuntime() {
21
+ // The plugin's fetch implementation calls into IPC via window.__TAURI__,
22
+ // so the *package being installed* is not enough — we have to be running
23
+ // inside a Tauri webview where these globals are injected.
24
+ if (typeof window === 'undefined')
25
+ return false;
26
+ return '__TAURI_INTERNALS__' in window || '__TAURI__' in window;
27
+ }
28
+ async function getTauriFetch() {
29
+ if (tauriProbed)
30
+ return tauriFetch;
31
+ tauriProbed = true;
32
+ if (!inTauriRuntime())
33
+ return null;
34
+ try {
35
+ // Static analysis is skipped so Vite/svelte-package don't try to bundle
36
+ // the optional Tauri-only dependency in pure-web builds.
37
+ const specifier = '@tauri-apps/plugin-http';
38
+ const mod = await import(/* @vite-ignore */ specifier);
39
+ tauriFetch = mod.fetch;
40
+ }
41
+ catch (_a) {
42
+ tauriFetch = null;
43
+ }
44
+ return tauriFetch;
45
+ }
46
+ export async function apiFetch(url, init) {
47
+ var _a;
48
+ // Inject Authorization: Bearer <session-token> if a session is active
49
+ // and the caller didn't already supply one. Cookies don't survive the
50
+ // cross-origin hop (SameSite=Lax + plugin-http has no cookie store),
51
+ // so the header carries the session for both transports uniformly.
52
+ const token = getAuthToken();
53
+ let finalInit = init !== null && init !== void 0 ? init : {};
54
+ if (token) {
55
+ const headers = new Headers((_a = finalInit.headers) !== null && _a !== void 0 ? _a : {});
56
+ if (!headers.has('Authorization')) {
57
+ headers.set('Authorization', `Bearer ${token}`);
58
+ finalInit = Object.assign(Object.assign({}, finalInit), { headers });
59
+ }
60
+ }
61
+ const tf = await getTauriFetch();
62
+ if (tf)
63
+ return tf(url, finalInit);
64
+ return fetch(url, Object.assign({ credentials: 'include' }, finalInit));
65
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,37 @@
1
+ import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest';
2
+ describe('apiFetch', () => {
3
+ let originalFetch;
4
+ beforeEach(() => {
5
+ originalFetch = globalThis.fetch;
6
+ vi.resetModules();
7
+ });
8
+ afterEach(() => {
9
+ globalThis.fetch = originalFetch;
10
+ });
11
+ it('uses global fetch with credentials:include when Tauri plugin-http is unavailable', async () => {
12
+ const calls = [];
13
+ globalThis.fetch = vi.fn(async (input, init) => {
14
+ calls.push([input, init]);
15
+ return new Response('ok');
16
+ });
17
+ const { apiFetch } = await import('./apiFetch');
18
+ const res = await apiFetch('https://example.com/api/foo', { method: 'GET' });
19
+ expect(await res.text()).toBe('ok');
20
+ expect(calls).toHaveLength(1);
21
+ const [input, init] = calls[0];
22
+ expect(input).toBe('https://example.com/api/foo');
23
+ expect(init.credentials).toBe('include');
24
+ expect(init.method).toBe('GET');
25
+ });
26
+ it('lets caller-provided credentials override the default', async () => {
27
+ const calls = [];
28
+ globalThis.fetch = vi.fn(async (input, init) => {
29
+ calls.push([input, init]);
30
+ return new Response('ok');
31
+ });
32
+ const { apiFetch } = await import('./apiFetch');
33
+ await apiFetch('https://example.com/api/foo', { credentials: 'omit' });
34
+ const [, init] = calls[0];
35
+ expect(init.credentials).toBe('omit');
36
+ });
37
+ });
@@ -0,0 +1,2 @@
1
+ export declare function setAuthToken(value: string | null): void;
2
+ export declare function getAuthToken(): string | null;
@@ -0,0 +1,53 @@
1
+ /*
2
+ * Auth-token store used by `apiFetch` to attach `Authorization: Bearer
3
+ * <token>` to every request when a session exists.
4
+ *
5
+ * The browser auth flow relies on Set-Cookie + same-origin requests to
6
+ * keep the session alive. That breaks down for cross-origin Tauri
7
+ * clients (e.g. Android): the WebView's origin is `tauri://localhost`
8
+ * and the sh3-server lives on a different domain, so the session
9
+ * cookie is treated as cross-site and dropped under SameSite=Lax. On
10
+ * top of that, `@tauri-apps/plugin-http` (reqwest under the hood)
11
+ * doesn't enable a persistent cookie store by default, so even if
12
+ * SameSite weren't an issue cookies wouldn't survive between calls.
13
+ *
14
+ * Carrying the session token in a header sidesteps both problems.
15
+ * `auth.svelte.ts` populates this store after login/register/initFromBoot
16
+ * and clears it on logout. The token is mirrored to localStorage so an
17
+ * app restart doesn't dump the user back at the sign-in wall.
18
+ */
19
+ const KEY = 'sh3:authToken';
20
+ let token = null;
21
+ let restored = false;
22
+ function restore() {
23
+ if (restored)
24
+ return;
25
+ restored = true;
26
+ if (typeof localStorage === 'undefined')
27
+ return;
28
+ try {
29
+ token = localStorage.getItem(KEY);
30
+ }
31
+ catch (_a) {
32
+ token = null;
33
+ }
34
+ }
35
+ export function setAuthToken(value) {
36
+ restored = true;
37
+ token = value;
38
+ if (typeof localStorage === 'undefined')
39
+ return;
40
+ try {
41
+ if (value)
42
+ localStorage.setItem(KEY, value);
43
+ else
44
+ localStorage.removeItem(KEY);
45
+ }
46
+ catch (_a) {
47
+ // localStorage may be disabled (private browsing); in-memory copy still works.
48
+ }
49
+ }
50
+ export function getAuthToken() {
51
+ restore();
52
+ return token;
53
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,33 @@
1
+ import { describe, expect, it, beforeEach, vi } from 'vitest';
2
+ describe('authToken', () => {
3
+ beforeEach(() => {
4
+ vi.resetModules();
5
+ if (typeof localStorage !== 'undefined')
6
+ localStorage.clear();
7
+ });
8
+ it('returns null when nothing stored', async () => {
9
+ const { getAuthToken } = await import('./authToken');
10
+ expect(getAuthToken()).toBeNull();
11
+ });
12
+ it('round-trips set/get', async () => {
13
+ const { setAuthToken, getAuthToken } = await import('./authToken');
14
+ setAuthToken('sh3s_deadbeef');
15
+ expect(getAuthToken()).toBe('sh3s_deadbeef');
16
+ });
17
+ it('persists across module re-imports via localStorage', async () => {
18
+ const first = await import('./authToken');
19
+ first.setAuthToken('sh3s_persisted');
20
+ vi.resetModules();
21
+ const second = await import('./authToken');
22
+ expect(second.getAuthToken()).toBe('sh3s_persisted');
23
+ });
24
+ it('clear removes from storage', async () => {
25
+ const { setAuthToken, getAuthToken } = await import('./authToken');
26
+ setAuthToken('sh3s_x');
27
+ setAuthToken(null);
28
+ expect(getAuthToken()).toBeNull();
29
+ vi.resetModules();
30
+ const reloaded = await import('./authToken');
31
+ expect(reloaded.getAuthToken()).toBeNull();
32
+ });
33
+ });
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  /** Auto-generated from package.json — do not edit manually. */
2
- export declare const VERSION = "0.17.2";
2
+ export declare const VERSION = "0.19.1";
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  /** Auto-generated from package.json — do not edit manually. */
2
- export const VERSION = '0.17.2';
2
+ export const VERSION = '0.19.1';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sh3-core",
3
- "version": "0.17.2",
3
+ "version": "0.19.1",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist"