sh3-core 0.19.0 → 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.
@@ -1,3 +1,4 @@
1
+ // @vitest-environment jsdom
1
2
  import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
3
  import { createGestureRegistry } from './gestureRegistry';
3
4
  import { __resetForTest } from './pointerClaim';
@@ -18,7 +18,7 @@
18
18
  import type { TabsNode, TabEntry, TreeRootRef } from '../types';
19
19
  import SlotContainer from '../SlotContainer.svelte';
20
20
  import SlotDropZone from '../SlotDropZone.svelte';
21
- import { claim, revoke } from '../../gestures/pointerClaim';
21
+ import { claim, revoke, isOwner } from '../../gestures/pointerClaim';
22
22
  import { ancestorCount } from '../../gestures';
23
23
 
24
24
  let {
@@ -125,7 +125,7 @@
125
125
  // Multi-touch (pinch-zoom etc.) is not a swipe — bail.
126
126
  if (ev.isPrimary === false) return;
127
127
  const depth = containerEl ? ancestorCount(containerEl) : 0;
128
- const claimGranted = claim(ev.pointerId, { ownerId: 'sh3:carousel', axis: 'x', priority: 'edge', depth });
128
+ const claimGranted = claim(ev.pointerId, { ownerId: 'sh3:carousel', axis: 'x', priority: 'normal', depth });
129
129
  if (!claimGranted) return;
130
130
  activePointerId = ev.pointerId;
131
131
  downSnap = { id: ev.pointerId, x: ev.clientX, y: ev.clientY, t: performance.now() };
@@ -140,6 +140,7 @@
140
140
 
141
141
  function onPointerMove(ev: PointerEvent) {
142
142
  if (!downSnap || ev.pointerId !== downSnap.id) return;
143
+ if (!isOwner(ev.pointerId, 'sh3:carousel')) { endGesture(); return; }
143
144
  const dx = ev.clientX - downSnap.x;
144
145
  const dy = ev.clientY - downSnap.y;
145
146
  if (!claimed) {
@@ -160,12 +160,18 @@ export async function activateShard(id, opts) {
160
160
  return handle;
161
161
  },
162
162
  fetch(path, init) {
163
+ return apiFetch(this.resolveUrl(path), init);
164
+ },
165
+ get serverUrl() {
166
+ return getEnvServerUrl();
167
+ },
168
+ resolveUrl(path) {
163
169
  const isAbsolute = path.startsWith('http://') || path.startsWith('https://');
164
170
  if (isAbsolute)
165
- return apiFetch(path, init);
171
+ return path;
166
172
  const base = getEnvServerUrl();
167
173
  const sep = path.startsWith('/') ? '' : '/';
168
- return apiFetch(`${base}${sep}${path}`, init);
174
+ return `${base}${sep}${path}`;
169
175
  },
170
176
  env(defaults) {
171
177
  if (envState.proxy) {
@@ -64,3 +64,73 @@ describe('ctx.fetch', () => {
64
64
  expect(calls[0]).toBe('https://example.com/api/baz');
65
65
  });
66
66
  });
67
+ describe('ctx.serverUrl', () => {
68
+ beforeEach(() => {
69
+ __resetShardRegistryForTest();
70
+ __resetViewRegistryForTest();
71
+ __setDocumentBackend(new MemoryDocumentBackend());
72
+ __setTenantId('tenant-test');
73
+ __setEnvServerUrl('https://example.com');
74
+ });
75
+ afterEach(() => {
76
+ __setEnvServerUrl('');
77
+ });
78
+ it('returns the configured server base URL', async () => {
79
+ let captured = null;
80
+ registerShard({
81
+ manifest: { id: 'test', label: 'test', version: '0.0.0', views: [] },
82
+ activate(ctx) { captured = ctx; },
83
+ });
84
+ await activateShard('test');
85
+ expect(captured.serverUrl).toBe('https://example.com');
86
+ });
87
+ it('returns empty string when no server URL is configured', async () => {
88
+ __setEnvServerUrl('');
89
+ let captured = null;
90
+ registerShard({
91
+ manifest: { id: 'test', label: 'test', version: '0.0.0', views: [] },
92
+ activate(ctx) { captured = ctx; },
93
+ });
94
+ await activateShard('test');
95
+ expect(captured.serverUrl).toBe('');
96
+ });
97
+ });
98
+ describe('ctx.resolveUrl', () => {
99
+ beforeEach(() => {
100
+ __resetShardRegistryForTest();
101
+ __resetViewRegistryForTest();
102
+ __setDocumentBackend(new MemoryDocumentBackend());
103
+ __setTenantId('tenant-test');
104
+ __setEnvServerUrl('https://example.com');
105
+ });
106
+ afterEach(() => {
107
+ __setEnvServerUrl('');
108
+ });
109
+ it('resolves a relative path to an absolute URL', async () => {
110
+ let captured = null;
111
+ registerShard({
112
+ manifest: { id: 'test', label: 'test', version: '0.0.0', views: [] },
113
+ activate(ctx) { captured = ctx; },
114
+ });
115
+ await activateShard('test');
116
+ expect(captured.resolveUrl('/api/foo')).toBe('https://example.com/api/foo');
117
+ });
118
+ it('passes absolute URLs through unchanged', async () => {
119
+ let captured = null;
120
+ registerShard({
121
+ manifest: { id: 'test', label: 'test', version: '0.0.0', views: [] },
122
+ activate(ctx) { captured = ctx; },
123
+ });
124
+ await activateShard('test');
125
+ expect(captured.resolveUrl('https://other.example.com/ws')).toBe('https://other.example.com/ws');
126
+ });
127
+ it('prepends a slash to bare relative paths', async () => {
128
+ let captured = null;
129
+ registerShard({
130
+ manifest: { id: 'test', label: 'test', version: '0.0.0', views: [] },
131
+ activate(ctx) { captured = ctx; },
132
+ });
133
+ await activateShard('test');
134
+ expect(captured.resolveUrl('api/ws')).toBe('https://example.com/api/ws');
135
+ });
136
+ });
@@ -218,6 +218,22 @@ export interface ShardContext {
218
218
  * @param init - Standard RequestInit.
219
219
  */
220
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;
221
237
  /**
222
238
  * Declare environment state for this shard and receive a hydrated snapshot.
223
239
  * Env state is server-authoritative, fetched once at activation, and
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.19.0";
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.19.0';
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.19.0",
3
+ "version": "0.19.1",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist"