diff --git a/packages/workshop-backend/src/server.ts b/packages/workshop-backend/src/server.ts index cf7d19aeb..042b8bcd7 100644 --- a/packages/workshop-backend/src/server.ts +++ b/packages/workshop-backend/src/server.ts @@ -643,6 +643,8 @@ class PublicApiImpl extends RpcTarget implements PublicApi { this.users = this.ctx.exports.UserDurableObject; } + async ping(): Promise {} + async getServerConfig(): Promise { return getServerConfig(this.env); } diff --git a/packages/workshop-frontend/src/GadgetEditor.tsx b/packages/workshop-frontend/src/GadgetEditor.tsx index a9ba6dbdd..b123b64e9 100644 --- a/packages/workshop-frontend/src/GadgetEditor.tsx +++ b/packages/workshop-frontend/src/GadgetEditor.tsx @@ -16,6 +16,7 @@ import { } from '@phosphor-icons/react' import { RpcStub, RpcTarget } from 'capnweb' import { useAuthenticatedApi } from './AuthContext' +import { useConnectionLost } from './RpcContext' import UserMenu from './components/UserMenu' import SiteLogo from './components/SiteLogo' @@ -449,6 +450,7 @@ export default function GadgetEditor() { isEditingTitleRef.current = isEditingTitle const [titleInput, setTitleInput] = useState('') + const rpcConnectionLost = useConnectionLost() const { overseer, metadata, @@ -473,6 +475,10 @@ export default function GadgetEditor() { }) const [userInfo, setUserInfo] = useState(null) + // The workspace-level flag covers reopen failures; the socket-level flag covers the outage + // window itself, during which the dead stub stays published and no reopen is attempted yet. + const showReconnecting = connectionLost || rpcConnectionLost + // ── role gating ──────────────────────────────────────────────────────────────── // "use"-role collaborators receive a restricted overseer that only permits rendering and // interacting with the gadget's deployed UI. We render the minimal use-only view for them (see @@ -1434,7 +1440,7 @@ export default function GadgetEditor() { onViewActivity={openActivity} /> - {connectionLost && } + {showReconnecting && } setShareModalOpen(true)} @@ -1466,7 +1472,7 @@ export default function GadgetEditor() {
- {connectionLost && } + {showReconnecting && } {/* Desktop reaches Export from the gadget pane's tab bar, which is hidden on phones. */} ): Promise { // // Anyway, I pulled the connection management out into these globals instead. let lastConnectTime: number = 0; -let backoff: number = 1000; + +const INITIAL_BACKOFF_MS = 1000; +const MAX_BACKOFF_MS = 10000; +// Generous probe deadlines let a slow-but-alive backend settle instead of connect/dispose looping +// (or, on wake, tearing down a healthy socket under load). +const RECONNECT_PROBE_TIMEOUT_MS = 20000; +const WAKE_PROBE_TIMEOUT_MS = 10000; +const WAKE_PROBE_MIN_IDLE_MS = 15000; + +// Callbacks to call whenever `currentStub` or connection state is updated. +const subscribers = new Set<() => void>(); +const notifySubscribers = () => subscribers.forEach(cb => cb()); +let isConnectionLost = false; +let probing = false; +let lastProvenAt = Date.now(); + +const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); + +const withTimeout = (promise: Promise, ms: number): Promise => { + let timer: ReturnType | undefined; + const timeout = new Promise((_, reject) => { + timer = setTimeout(() => reject(new Error(`timed out after ${ms}ms`)), ms); + }); + return Promise.race([promise, timeout]).finally(() => clearTimeout(timer)); +}; function getBackendHost(): string { // Only the Vite dev server is hosted separately from the backend. Built assets are served from @@ -71,52 +95,92 @@ function startConnection(): RpcStub { lastConnectTime = Date.now(); const apiHost = getBackendHost(); const wsUrl = (window.location.protocol === 'https:' ? 'wss:' : 'ws:') + '//' + apiHost + '/api'; - return newWebSocketRpcSession(wsUrl); + const stub = newWebSocketRpcSession(wsUrl); + stub.onRpcBroken(handleBroken); + return stub; } -async function handleBroken(error: any) { - console.warn('RPC connection lost:', error); +const disposeQuietly = (stub: RpcStub) => { + try { stub[Symbol.dispose](); } catch { /* already broken */ } +}; - isConnectionLost = true; - for (let cb of notifyCurrentStubUpdated) { cb(); } - - let timeSinceConnect = Date.now() - lastConnectTime; - if (timeSinceConnect < backoff) { - let waitTime = backoff - timeSinceConnect; - console.warn(`Will try again in ${Math.round(waitTime / 1000)} seconds...`) - await new Promise(resolve => setTimeout(resolve, waitTime)); - console.warn(`Retrying connection...`); - backoff = Math.min(backoff * 2, 10000); - } else { - backoff = 1000; - } +// Connects with jittered backoff until a candidate answers a probe, and resolves only to that +// proven connection: capnweb queues sends while a socket is still CONNECTING, so an unproven stub +// looks fine right up until everything pipelined onto it fails at once. +async function reconnect(): Promise> { + // Fast recovery from one-off blips: skip the first backoff if the dying connection was up a while. + let skipSleep = Date.now() - lastConnectTime >= INITIAL_BACKOFF_MS; + let backoff = INITIAL_BACKOFF_MS; + for (;;) { + if (!skipSleep) { + await sleep(backoff * (0.85 + 0.3 * Math.random())); // jittered against stampedes + backoff = Math.min(backoff * 2, MAX_BACKOFF_MS); + } + skipSleep = false; - currentStub = startConnection(); - currentStub.onRpcBroken(handleBroken); + const candidate = startConnection(); + try { + await withTimeout(candidate.ping(), RECONNECT_PROBE_TIMEOUT_MS); + } catch (probeError) { + console.debug('Reconnect attempt failed:', probeError); + disposeQuietly(candidate); + continue; + } - // Don't clear isConnectionLost here — the new connection hasn't proven - // it works yet. It gets cleared by markConnectionRestored() once the - // app successfully communicates with the backend. - for (let cb of notifyCurrentStubUpdated) { - cb(); + lastProvenAt = Date.now(); + isConnectionLost = false; + console.warn('RPC connection restored.'); + notifySubscribers(); + return candidate; } } -// Callbacks to call whenever `currentStub` or connection state is updated. -let notifyCurrentStubUpdated: Set<() => void> = new Set(); -let isConnectionLost = false; +// Subscribers hear exactly twice per outage — lost here, restored in `reconnect` — because +// `currentStub` is replaced once, by a promise, rather than once per attempt. +function handleBroken(error: unknown) { + if (isConnectionLost) return; // stale/disposed stub, or recovery already underway + isConnectionLost = true; + + console.warn('RPC connection lost:', error); -/** Called externally (e.g., by auth) to indicate the connection is alive. */ -export function markConnectionRestored() { - if (!isConnectionLost) return; - isConnectionLost = false; - for (let cb of notifyCurrentStubUpdated) { cb(); } + // Publish a stub for the connection we have not made yet, so the dead one stops being reachable + // immediately. capnweb queues calls pipelined onto an unresolved `RpcPromise` and delivers them, + // in order, once it resolves — so work issued during the outage waits for the replacement + // instead of failing against a socket known to be gone. The `RpcPromise` takes ownership of its + // resolution, keeping the proven stub on a single disposal path. + currentStub = new RpcPromise(reconnect()); + notifySubscribers(); } +// Passive close detection misses sockets killed during laptop sleep or tab throttling, so on +// tab-visible / network-online signals probe the connection instead of letting the user's next +// action hang on a zombie socket. +async function probeOnWake() { + if (isConnectionLost || probing || Date.now() - lastProvenAt < WAKE_PROBE_MIN_IDLE_MS) return; + probing = true; + const suspect = currentStub; + try { + await withTimeout(suspect.ping(), WAKE_PROBE_TIMEOUT_MS); + lastProvenAt = Date.now(); + } catch (error) { + if (currentStub !== suspect || isConnectionLost) return; // a real broken event won the race + console.warn('Connection unresponsive after wake:', error); + // Disposal fires onRpcBroken → handleBroken recovers. Its skip-first-backoff path retries + // immediately — right for "the network just came back". + disposeQuietly(suspect); + } finally { + probing = false; + } +} + +document.addEventListener('visibilitychange', () => { + if (document.visibilityState === 'visible') void probeOnWake(); +}); +window.addEventListener('online', () => void probeOnWake()); + // Current stub. handleBroken() will replace this on disconnect. installWorkshopErrorReporting() let currentStub = startConnection(); -currentStub.onRpcBroken(handleBroken); const router = createRouter() applyStoredThemeMode() @@ -153,9 +217,9 @@ function AppWithConnection() { }, []); useEffect(() => { - let cb = () => setRpcState({ stub: currentStub, connectionLost: isConnectionLost }); - notifyCurrentStubUpdated.add(cb); - return () => { notifyCurrentStubUpdated.delete(cb); }; + const cb = () => setRpcState({ stub: currentStub, connectionLost: isConnectionLost }); + subscribers.add(cb); + return () => { subscribers.delete(cb); }; }, []); // Fetch deployment config once the (re)connected stub is available. Re-fetch on reconnect so a diff --git a/packages/workshop-frontend/src/routes/__root.tsx b/packages/workshop-frontend/src/routes/__root.tsx index 07f789cfe..004dd1300 100644 --- a/packages/workshop-frontend/src/routes/__root.tsx +++ b/packages/workshop-frontend/src/routes/__root.tsx @@ -5,7 +5,6 @@ import { TooltipProvider, Toasty } from '@cloudflare/kumo' import { RpcStub } from 'capnweb' import { AuthenticatedApi } from '@gadgets/workshop-shared/api' import { useRpcStub, useConnectionLost } from '../RpcContext' -import { markConnectionRestored } from '../main' import { useAuth, CF_ACCESS_MODE } from '../useAuth' import { AuthProvider } from '../AuthContext' import { FeatureFlagsProvider } from '../FeatureFlagsContext' @@ -25,11 +24,6 @@ function RootComponent() { const { isAuthenticated, authenticatedApi, isLoading, error, logout, login } = useAuth(rpcStub) const pathname = useRouterState({ select: (s) => s.location.pathname }) - // When authenticatedApi becomes available, the connection is proven alive. - useEffect(() => { - if (authenticatedApi) markConnectionRestored() - }, [authenticatedApi]) - // Routes that don't require auth (public routes) const isSignup = pathname === '/signup' const isBlueprint = pathname.startsWith('/blueprint/') diff --git a/packages/workshop-shared/src/api.ts b/packages/workshop-shared/src/api.ts index d858dd2d5..35afb6dbb 100644 --- a/packages/workshop-shared/src/api.ts +++ b/packages/workshop-shared/src/api.ts @@ -46,6 +46,9 @@ export interface LoginAttempt extends RpcTarget { /** Public API exposed to the internet. */ export interface PublicApi extends RpcTarget { + /** Confirms that the RPC connection can round-trip without performing application work. */ + ping(): Promise; + /** * Returns deployment-level configuration the client needs at boot (auth mode, available sign-in * vendors, whether the Cloudflare limits flow is enabled). Contains no secrets. diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 794f196a3..686c8034e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,11 +10,11 @@ catalogs: specifier: ^0.20.2 version: 0.20.3 capnweb: - specifier: ^0.11.1 - version: 0.11.1 + specifier: ^0.12.0 + version: 0.12.0 capnweb-validate: - specifier: 0.2.4 - version: 0.2.4 + specifier: 0.3.0 + version: 0.3.0 typescript: specifier: 7.0.2 version: 7.0.2 @@ -109,10 +109,10 @@ importers: version: link:../workshop-shared capnweb: specifier: 'catalog:' - version: 0.11.1 + version: 0.12.0 capnweb-validate: specifier: 'catalog:' - version: 0.2.4(capnweb@0.11.1)(esbuild@0.28.1)(rollup@4.62.3)(vite@7.3.6(@types/node@26.1.0)(jiti@2.7.0)(lightningcss@1.33.0)(terser@5.49.2)(yaml@2.9.0)) + version: 0.3.0(capnweb@0.12.0)(esbuild@0.28.1)(rollup@4.62.3)(vite@7.3.6(@types/node@26.1.0)(jiti@2.7.0)(lightningcss@1.33.0)(terser@5.49.2)(yaml@2.9.0)) devDependencies: '@cloudflare/vitest-pool-workers': specifier: 'catalog:' @@ -140,10 +140,10 @@ importers: version: link:../workshop-shared capnweb: specifier: 'catalog:' - version: 0.11.1 + version: 0.12.0 capnweb-validate: specifier: 'catalog:' - version: 0.2.4(capnweb@0.11.1)(esbuild@0.28.1)(rollup@4.62.3)(vite@7.3.6(@types/node@26.1.0)(jiti@2.7.0)(lightningcss@1.33.0)(terser@5.49.2)(yaml@2.9.0)) + version: 0.3.0(capnweb@0.12.0)(esbuild@0.28.1)(rollup@4.62.3)(vite@7.3.6(@types/node@26.1.0)(jiti@2.7.0)(lightningcss@1.33.0)(terser@5.49.2)(yaml@2.9.0)) node-html-parser: specifier: ^7.1.0 version: 7.1.0 @@ -174,10 +174,10 @@ importers: version: link:../workshop-shared capnweb: specifier: 'catalog:' - version: 0.11.1 + version: 0.12.0 capnweb-validate: specifier: 'catalog:' - version: 0.2.4(capnweb@0.11.1)(esbuild@0.28.1)(rollup@4.62.3)(vite@7.3.6(@types/node@26.1.0)(jiti@2.7.0)(lightningcss@1.33.0)(terser@5.49.2)(yaml@2.9.0)) + version: 0.3.0(capnweb@0.12.0)(esbuild@0.28.1)(rollup@4.62.3)(vite@7.3.6(@types/node@26.1.0)(jiti@2.7.0)(lightningcss@1.33.0)(terser@5.49.2)(yaml@2.9.0)) isomorphic-git: specifier: ^1.40.5 version: 1.40.5 @@ -295,10 +295,10 @@ importers: version: link:../workshop-shared capnweb: specifier: 'catalog:' - version: 0.11.1 + version: 0.12.0 capnweb-validate: specifier: 'catalog:' - version: 0.2.4(capnweb@0.11.1)(esbuild@0.28.1)(rollup@4.62.3)(vite@7.3.6(@types/node@26.1.0)(jiti@2.7.0)(lightningcss@1.33.0)(terser@5.49.2)(yaml@2.9.0)) + version: 0.3.0(capnweb@0.12.0)(esbuild@0.28.1)(rollup@4.62.3)(vite@7.3.6(@types/node@26.1.0)(jiti@2.7.0)(lightningcss@1.33.0)(terser@5.49.2)(yaml@2.9.0)) postal-mime: specifier: ^2.7.6 version: 2.7.6 @@ -323,10 +323,10 @@ importers: version: link:../workshop-shared capnweb: specifier: 'catalog:' - version: 0.11.1 + version: 0.12.0 capnweb-validate: specifier: 'catalog:' - version: 0.2.4(capnweb@0.11.1)(esbuild@0.28.1)(rollup@4.62.3)(vite@7.3.6(@types/node@26.1.0)(jiti@2.7.0)(lightningcss@1.33.0)(terser@5.49.2)(yaml@2.9.0)) + version: 0.3.0(capnweb@0.12.0)(esbuild@0.28.1)(rollup@4.62.3)(vite@7.3.6(@types/node@26.1.0)(jiti@2.7.0)(lightningcss@1.33.0)(terser@5.49.2)(yaml@2.9.0)) devDependencies: typescript: specifier: 'catalog:' @@ -351,10 +351,10 @@ importers: version: link:../workshop-shared capnweb: specifier: 'catalog:' - version: 0.11.1 + version: 0.12.0 capnweb-validate: specifier: 'catalog:' - version: 0.2.4(capnweb@0.11.1)(esbuild@0.28.1)(rollup@4.62.3)(vite@7.3.6(@types/node@26.1.0)(jiti@2.7.0)(lightningcss@1.33.0)(terser@5.49.2)(yaml@2.9.0)) + version: 0.3.0(capnweb@0.12.0)(esbuild@0.28.1)(rollup@4.62.3)(vite@7.3.6(@types/node@26.1.0)(jiti@2.7.0)(lightningcss@1.33.0)(terser@5.49.2)(yaml@2.9.0)) mimetext: specifier: ^3.0.28 version: 3.0.28 @@ -379,10 +379,10 @@ importers: version: link:../workshop-shared capnweb: specifier: 'catalog:' - version: 0.11.1 + version: 0.12.0 capnweb-validate: specifier: 'catalog:' - version: 0.2.4(capnweb@0.11.1)(esbuild@0.28.1)(rollup@4.62.3)(vite@7.3.6(@types/node@26.1.0)(jiti@2.7.0)(lightningcss@1.33.0)(terser@5.49.2)(yaml@2.9.0)) + version: 0.3.0(capnweb@0.12.0)(esbuild@0.28.1)(rollup@4.62.3)(vite@7.3.6(@types/node@26.1.0)(jiti@2.7.0)(lightningcss@1.33.0)(terser@5.49.2)(yaml@2.9.0)) devDependencies: typescript: specifier: 'catalog:' @@ -404,10 +404,10 @@ importers: version: link:../workshop-shared capnweb: specifier: 'catalog:' - version: 0.11.1 + version: 0.12.0 capnweb-validate: specifier: 'catalog:' - version: 0.2.4(capnweb@0.11.1)(esbuild@0.28.1)(rollup@4.62.3)(vite@7.3.6(@types/node@26.1.0)(jiti@2.7.0)(lightningcss@1.33.0)(terser@5.49.2)(yaml@2.9.0)) + version: 0.3.0(capnweb@0.12.0)(esbuild@0.28.1)(rollup@4.62.3)(vite@7.3.6(@types/node@26.1.0)(jiti@2.7.0)(lightningcss@1.33.0)(terser@5.49.2)(yaml@2.9.0)) devDependencies: typescript: specifier: 'catalog:' @@ -432,10 +432,10 @@ importers: version: link:../workshop-shared capnweb: specifier: 'catalog:' - version: 0.11.1 + version: 0.12.0 capnweb-validate: specifier: 'catalog:' - version: 0.2.4(capnweb@0.11.1)(esbuild@0.28.1)(rollup@4.62.3)(vite@7.3.6(@types/node@26.1.0)(jiti@2.7.0)(lightningcss@1.33.0)(terser@5.49.2)(yaml@2.9.0)) + version: 0.3.0(capnweb@0.12.0)(esbuild@0.28.1)(rollup@4.62.3)(vite@7.3.6(@types/node@26.1.0)(jiti@2.7.0)(lightningcss@1.33.0)(terser@5.49.2)(yaml@2.9.0)) devDependencies: typescript: specifier: 'catalog:' @@ -463,10 +463,10 @@ importers: version: link:../workshop-shared capnweb: specifier: 'catalog:' - version: 0.11.1 + version: 0.12.0 capnweb-validate: specifier: 'catalog:' - version: 0.2.4(capnweb@0.11.1)(esbuild@0.28.1)(rollup@4.62.3)(vite@7.3.6(@types/node@26.1.0)(jiti@2.7.0)(lightningcss@1.33.0)(terser@5.49.2)(yaml@2.9.0)) + version: 0.3.0(capnweb@0.12.0)(esbuild@0.28.1)(rollup@4.62.3)(vite@7.3.6(@types/node@26.1.0)(jiti@2.7.0)(lightningcss@1.33.0)(terser@5.49.2)(yaml@2.9.0)) devDependencies: typescript: specifier: 'catalog:' @@ -488,10 +488,10 @@ importers: version: link:../workshop-shared capnweb: specifier: 'catalog:' - version: 0.11.1 + version: 0.12.0 capnweb-validate: specifier: 'catalog:' - version: 0.2.4(capnweb@0.11.1)(esbuild@0.28.1)(rollup@4.62.3)(vite@7.3.6(@types/node@26.1.0)(jiti@2.7.0)(lightningcss@1.33.0)(terser@5.49.2)(yaml@2.9.0)) + version: 0.3.0(capnweb@0.12.0)(esbuild@0.28.1)(rollup@4.62.3)(vite@7.3.6(@types/node@26.1.0)(jiti@2.7.0)(lightningcss@1.33.0)(terser@5.49.2)(yaml@2.9.0)) devDependencies: typescript: specifier: 'catalog:' @@ -516,10 +516,10 @@ importers: version: link:../workshop-shared capnweb: specifier: 'catalog:' - version: 0.11.1 + version: 0.12.0 capnweb-validate: specifier: 'catalog:' - version: 0.2.4(capnweb@0.11.1)(esbuild@0.28.1)(rollup@4.62.3)(vite@7.3.6(@types/node@26.1.0)(jiti@2.7.0)(lightningcss@1.33.0)(terser@5.49.2)(yaml@2.9.0)) + version: 0.3.0(capnweb@0.12.0)(esbuild@0.28.1)(rollup@4.62.3)(vite@7.3.6(@types/node@26.1.0)(jiti@2.7.0)(lightningcss@1.33.0)(terser@5.49.2)(yaml@2.9.0)) temporal-polyfill: specifier: 1.0.2 version: 1.0.2 @@ -598,10 +598,10 @@ importers: version: link:../workshop-shared capnweb: specifier: 'catalog:' - version: 0.11.1 + version: 0.12.0 capnweb-validate: specifier: 'catalog:' - version: 0.2.4(capnweb@0.11.1)(esbuild@0.28.1)(rollup@4.62.3)(vite@7.3.6(@types/node@26.1.0)(jiti@2.7.0)(lightningcss@1.33.0)(terser@5.49.2)(yaml@2.9.0)) + version: 0.3.0(capnweb@0.12.0)(esbuild@0.28.1)(rollup@4.62.3)(vite@7.3.6(@types/node@26.1.0)(jiti@2.7.0)(lightningcss@1.33.0)(terser@5.49.2)(yaml@2.9.0)) devDependencies: typescript: specifier: 'catalog:' @@ -620,10 +620,10 @@ importers: version: link:../workshop-shared capnweb: specifier: 'catalog:' - version: 0.11.1 + version: 0.12.0 capnweb-validate: specifier: 'catalog:' - version: 0.2.4(capnweb@0.11.1)(esbuild@0.28.1)(rollup@4.62.3)(vite@7.3.6(@types/node@26.1.0)(jiti@2.7.0)(lightningcss@1.33.0)(terser@5.49.2)(yaml@2.9.0)) + version: 0.3.0(capnweb@0.12.0)(esbuild@0.28.1)(rollup@4.62.3)(vite@7.3.6(@types/node@26.1.0)(jiti@2.7.0)(lightningcss@1.33.0)(terser@5.49.2)(yaml@2.9.0)) devDependencies: typescript: specifier: 'catalog:' @@ -645,10 +645,10 @@ importers: version: link:../workshop-shared capnweb: specifier: 'catalog:' - version: 0.11.1 + version: 0.12.0 capnweb-validate: specifier: 'catalog:' - version: 0.2.4(capnweb@0.11.1)(esbuild@0.28.1)(rollup@4.62.3)(vite@7.3.6(@types/node@26.1.0)(jiti@2.7.0)(lightningcss@1.33.0)(terser@5.49.2)(yaml@2.9.0)) + version: 0.3.0(capnweb@0.12.0)(esbuild@0.28.1)(rollup@4.62.3)(vite@7.3.6(@types/node@26.1.0)(jiti@2.7.0)(lightningcss@1.33.0)(terser@5.49.2)(yaml@2.9.0)) devDependencies: typescript: specifier: 'catalog:' @@ -667,10 +667,10 @@ importers: version: link:../workshop-shared capnweb: specifier: 'catalog:' - version: 0.11.1 + version: 0.12.0 capnweb-validate: specifier: 'catalog:' - version: 0.2.4(capnweb@0.11.1)(esbuild@0.28.1)(rollup@4.62.3)(vite@7.3.6(@types/node@26.1.0)(jiti@2.7.0)(lightningcss@1.33.0)(terser@5.49.2)(yaml@2.9.0)) + version: 0.3.0(capnweb@0.12.0)(esbuild@0.28.1)(rollup@4.62.3)(vite@7.3.6(@types/node@26.1.0)(jiti@2.7.0)(lightningcss@1.33.0)(terser@5.49.2)(yaml@2.9.0)) devDependencies: typescript: specifier: 'catalog:' @@ -686,7 +686,7 @@ importers: version: link:../workshop-shared capnweb: specifier: 'catalog:' - version: 0.11.1 + version: 0.12.0 jsonc-parser: specifier: ^3.3.1 version: 3.3.1 @@ -790,10 +790,10 @@ importers: version: link:../workshop-shared capnweb: specifier: 'catalog:' - version: 0.11.1 + version: 0.12.0 capnweb-validate: specifier: 'catalog:' - version: 0.2.4(capnweb@0.11.1)(esbuild@0.28.1)(rollup@4.62.3)(vite@7.3.6(@types/node@26.1.0)(jiti@2.7.0)(lightningcss@1.33.0)(terser@5.49.2)(yaml@2.9.0)) + version: 0.3.0(capnweb@0.12.0)(esbuild@0.28.1)(rollup@4.62.3)(vite@7.3.6(@types/node@26.1.0)(jiti@2.7.0)(lightningcss@1.33.0)(terser@5.49.2)(yaml@2.9.0)) diff: specifier: ^8.0.4 version: 8.0.4 @@ -851,7 +851,7 @@ importers: version: 1.170.23(react-dom@19.2.8(react@19.2.8))(react@19.2.8) capnweb: specifier: 'catalog:' - version: 0.11.1 + version: 0.12.0 hash-wasm: specifier: ^4.12.0 version: 4.12.0 @@ -918,7 +918,7 @@ importers: version: 5.20260808.1 capnweb: specifier: 'catalog:' - version: 0.11.1 + version: 0.12.0 devDependencies: typescript: specifier: 'catalog:' @@ -3115,8 +3115,8 @@ packages: caniuse-lite@1.0.30001806: resolution: {integrity: sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==} - capnweb-validate@0.2.4: - resolution: {integrity: sha512-1E1OqJZ96P/YE1WbhnPHGs8hvZ1g2Da7f/DJSgxGQ6aKy17bjkJa9sEhTixQKJ0VsNRIdmeEjFE3brBlXcF/Uw==} + capnweb-validate@0.3.0: + resolution: {integrity: sha512-YjYu37+WE/cQWNM39Hv9sjov9fDgHIT/142ZD5pdt4iXk0YQqXg3FGgn5I3bg2ytQMJMsQQsnfooEKwnqc2E3A==} hasBin: true peerDependencies: capnweb: '>=0.7.0' @@ -3124,8 +3124,8 @@ packages: capnweb: optional: true - capnweb@0.11.1: - resolution: {integrity: sha512-7LyJhH96Ak7qyM3nUShVP5noXRMav2TvnhZ+hDxANrNqkFaPcu9MkbMIKTvBGV8+HN44kKTDlNMHzjPDRr36KQ==} + capnweb@0.12.0: + resolution: {integrity: sha512-jgZ/LMtMVTi+RVlooIslH78Gg23XbDTdvcLghWx3oz17D6u5GuJARt+uhZk/wcTo+f81eeabfnow4d3zLBj+JQ==} ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -6951,12 +6951,12 @@ snapshots: caniuse-lite@1.0.30001806: {} - capnweb-validate@0.2.4(capnweb@0.11.1)(esbuild@0.28.1)(rollup@4.62.3)(vite@7.3.6(@types/node@26.1.0)(jiti@2.7.0)(lightningcss@1.33.0)(terser@5.49.2)(yaml@2.9.0)): + capnweb-validate@0.3.0(capnweb@0.12.0)(esbuild@0.28.1)(rollup@4.62.3)(vite@7.3.6(@types/node@26.1.0)(jiti@2.7.0)(lightningcss@1.33.0)(terser@5.49.2)(yaml@2.9.0)): dependencies: typescript: 6.0.3 unplugin: 3.3.0(esbuild@0.28.1)(rollup@4.62.3)(vite@7.3.6(@types/node@26.1.0)(jiti@2.7.0)(lightningcss@1.33.0)(terser@5.49.2)(yaml@2.9.0)) optionalDependencies: - capnweb: 0.11.1 + capnweb: 0.12.0 transitivePeerDependencies: - '@farmfe/core' - '@rspack/core' @@ -6968,7 +6968,7 @@ snapshots: - vite - webpack - capnweb@0.11.1: {} + capnweb@0.12.0: {} ccount@2.0.1: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index aa1de4595..2f0acd77c 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -5,9 +5,9 @@ packages: # needing to hand edit a bunch of different package.json files. catalog: '@cloudflare/vitest-pool-workers': ^0.20.2 - capnweb: ^0.11.1 + capnweb: ^0.12.0 # Exact: pinned in lockstep with capnweb (declared as a >=0.7.0 peer) - capnweb-validate: 0.2.4 + capnweb-validate: 0.3.0 # Exact: TypeScript 7 (tsgo). Type-checking runs on the native compiler. Build-time # transpilers that need the JS compiler API (scripts/build-gatekeeper-configurator.ts) # use the root "typescript6" npm alias instead; capnweb-validate ships its own