@devframes/hub
Advanced tools
| import { REMOTE_CONNECTION_KEY } from "devframe/constants"; | ||
| //#region src/remote-url.ts | ||
| function base64UrlEncode(value) { | ||
| const bytes = new TextEncoder().encode(value); | ||
| let binary = ""; | ||
| for (const byte of bytes) binary += String.fromCharCode(byte); | ||
| return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); | ||
| } | ||
| function setRemoteConnectionParam(value, param) { | ||
| const parts = value ? value.split("&") : []; | ||
| const existingIdx = parts.findIndex((part) => part.split("=")[0] === REMOTE_CONNECTION_KEY); | ||
| if (existingIdx >= 0) parts[existingIdx] = param; | ||
| else parts.push(param); | ||
| return parts.join("&"); | ||
| } | ||
| /** Encode a remote connection descriptor into an external viewer URL. */ | ||
| function buildRemoteConnectionUrl(baseUrl, payload, transport = "fragment") { | ||
| const param = `${REMOTE_CONNECTION_KEY}=${base64UrlEncode(JSON.stringify(payload))}`; | ||
| if (transport === "fragment") { | ||
| const hashIdx = baseUrl.indexOf("#"); | ||
| if (hashIdx === -1) return `${baseUrl}#${param}`; | ||
| const beforeHash = baseUrl.slice(0, hashIdx); | ||
| const rawHash = baseUrl.slice(hashIdx + 1); | ||
| if (!rawHash) return `${beforeHash}#${param}`; | ||
| const routeQueryIdx = rawHash.indexOf("?"); | ||
| if (routeQueryIdx !== -1) return `${beforeHash}#${rawHash.slice(0, routeQueryIdx + 1)}${setRemoteConnectionParam(rawHash.slice(routeQueryIdx + 1), param)}`; | ||
| return `${beforeHash}#${setRemoteConnectionParam(rawHash, param)}`; | ||
| } | ||
| const hashIdx = baseUrl.indexOf("#"); | ||
| const hash = hashIdx === -1 ? "" : baseUrl.slice(hashIdx); | ||
| const beforeHash = hashIdx === -1 ? baseUrl : baseUrl.slice(0, hashIdx); | ||
| return `${beforeHash}${beforeHash.includes("?") ? "&" : "?"}${param}${hash}`; | ||
| } | ||
| /** Remove the remote connection descriptor from a URL before displaying or copying it. */ | ||
| function stripRemoteConnectionFromUrl(input) { | ||
| function stripParamList(value) { | ||
| const parts = value.split("&"); | ||
| const filtered = parts.filter((part) => part.split("=")[0] !== REMOTE_CONNECTION_KEY); | ||
| return [filtered.join("&"), filtered.length !== parts.length]; | ||
| } | ||
| const hashIdx = input.indexOf("#"); | ||
| let beforeHash = hashIdx === -1 ? input : input.slice(0, hashIdx); | ||
| let hash = hashIdx === -1 ? void 0 : input.slice(hashIdx + 1); | ||
| let changed = false; | ||
| if (hash !== void 0) { | ||
| const routeQueryIdx = hash.indexOf("?"); | ||
| if (routeQueryIdx === -1) { | ||
| const [nextHash, didChange] = stripParamList(hash); | ||
| hash = nextHash; | ||
| changed ||= didChange; | ||
| } else { | ||
| const route = hash.slice(0, routeQueryIdx); | ||
| const [query, didChange] = stripParamList(hash.slice(routeQueryIdx + 1)); | ||
| if (didChange) { | ||
| hash = query ? `${route}?${query}` : route; | ||
| changed = true; | ||
| } | ||
| } | ||
| } | ||
| const queryIdx = beforeHash.indexOf("?"); | ||
| if (queryIdx !== -1) { | ||
| const path = beforeHash.slice(0, queryIdx); | ||
| const [query, didChange] = stripParamList(beforeHash.slice(queryIdx + 1)); | ||
| if (didChange) { | ||
| beforeHash = query ? `${path}?${query}` : path; | ||
| changed = true; | ||
| } | ||
| } | ||
| if (!changed) return input; | ||
| return hash ? `${beforeHash}#${hash}` : beforeHash; | ||
| } | ||
| //#endregion | ||
| export { stripRemoteConnectionFromUrl as n, buildRemoteConnectionUrl as t }; |
@@ -5,3 +5,3 @@ import { N as NavTarget, O as DevframeViewIframe, P as RemoteConnectionInfo, a as DevframeCommandEntry, g as DevframeDockEntry, h as DevframeDockEntriesGrouped, n as DevframeClientCommand, s as DevframeCommandKeybinding, t as DevframeDocksUserSettings, x as DevframeDockUserEntry, y as DevframeDockEntryIcon } from "../settings-Byh48aCd.mjs"; | ||
| import "../index-Bx3T-R7i.mjs"; | ||
| import { DevframeClientRpcHost, DevframeConnectionStatus, DevframeRpcClient, DevframeRpcClientOptions, DevframeRpcContext, RpcClientEvents, RpcClientEvents as RpcClientEvents$1 } from "devframe/client"; | ||
| import { DevframeClientRpcHost, DevframeConnection, DevframeConnectionStatus, DevframeRpcClient, DevframeRpcClientOptions, DevframeRpcContext, RpcClientEvents, RpcClientEvents as RpcClientEvents$1 } from "devframe/client"; | ||
| import { EventEmitter } from "devframe/types"; | ||
@@ -260,2 +260,12 @@ import { SharedState } from "devframe/utils/shared-state"; | ||
| //#endregion | ||
| //#region src/client/dock-resources.d.ts | ||
| /** | ||
| * Resolve a dock iframe URL relative to the Devframe server that provided the | ||
| * dock entry. This keeps root-relative and dot-relative paths on the host | ||
| * server when an external viewer renders the hub. | ||
| */ | ||
| declare function resolveDockUrl(url: string, connection: DevframeConnection): string; | ||
| /** Resolve URL-backed dock icons while preserving Iconify names and data URLs. */ | ||
| declare function resolveDockIcon(icon: DevframeDockEntryIcon, connection: DevframeConnection): DevframeDockEntryIcon; | ||
| //#endregion | ||
| //#region src/client/frame-nav.d.ts | ||
@@ -468,5 +478,15 @@ /** | ||
| //#endregion | ||
| //#region src/remote-url.d.ts | ||
| /** Remove the remote connection descriptor from a URL before displaying or copying it. */ | ||
| declare function stripRemoteConnectionFromUrl(input: string): string; | ||
| //#endregion | ||
| //#region src/client/remote.d.ts | ||
| type ConnectRemoteDevframeOptions = Omit<DevframeRpcClientOptions, 'connectionMeta' | 'authToken'>; | ||
| /** | ||
| * Build an external viewer URL from an existing trusted Devframe connection. | ||
| * Returns the original URL if the connection has no auth token, does not use | ||
| * WebSockets, or has an invalid metadata URL. | ||
| */ | ||
| declare function buildRemoteDevframeUrl(url: string, connection: DevframeConnection): string; | ||
| /** | ||
| * Parse a {@link RemoteConnectionInfo} descriptor from the current page's URL | ||
@@ -490,2 +510,2 @@ * (or a provided URL/string). Checks the URL fragment first, then the query. | ||
| //#endregion | ||
| export { CLIENT_CONTEXT_KEY, CommandsContext, ConnectRemoteDevframeOptions, DEFAULT_CATEGORIES_ORDER, DevframeClientContext, DevframeClientHost, DevframeClientHostOptions, type DevframeClientRpcHost, DockClientScriptContext, DockClientType, DockEntryState, DockEntryStateEvents, DockPanelStorage, DockRegistration, DockRenderer, DockRendererInstance, DockRendererMountOptions, DockRenderersContext, DocksConnectionContext, DocksContext, DocksEntriesContext, DocksPanelContext, FRAME_NAV_CHANNEL, FRAME_NAV_VERSION, FrameNavClient, FrameNavClientOptions, FrameNavEnvelope, FrameNavFrameMessage, FrameNavHostMessage, FrameNavHostPayload, FrameNavListenTarget, FrameTab, MessagesClientOptions, type RpcClientEvents, WhenClauseContext, attachFrameNavClient, connectRemoteDevframe, createDevframeClientHost, createMessagesClient, getDevframeClientContext, parseRemoteConnection, setDevframeClientContext }; | ||
| export { CLIENT_CONTEXT_KEY, CommandsContext, ConnectRemoteDevframeOptions, DEFAULT_CATEGORIES_ORDER, DevframeClientContext, DevframeClientHost, DevframeClientHostOptions, type DevframeClientRpcHost, DockClientScriptContext, DockClientType, DockEntryState, DockEntryStateEvents, DockPanelStorage, DockRegistration, DockRenderer, DockRendererInstance, DockRendererMountOptions, DockRenderersContext, DocksConnectionContext, DocksContext, DocksEntriesContext, DocksPanelContext, FRAME_NAV_CHANNEL, FRAME_NAV_VERSION, FrameNavClient, FrameNavClientOptions, FrameNavEnvelope, FrameNavFrameMessage, FrameNavHostMessage, FrameNavHostPayload, FrameNavListenTarget, FrameTab, MessagesClientOptions, type RpcClientEvents, WhenClauseContext, attachFrameNavClient, buildRemoteDevframeUrl, connectRemoteDevframe, createDevframeClientHost, createMessagesClient, getDevframeClientContext, parseRemoteConnection, resolveDockIcon, resolveDockUrl, setDevframeClientContext, stripRemoteConnectionFromUrl }; |
| import { DEFAULT_CATEGORIES_ORDER, DEFAULT_STATE_USER_SETTINGS } from "../constants.mjs"; | ||
| import { n as stripRemoteConnectionFromUrl, t as buildRemoteConnectionUrl } from "../remote-url-KVXtKP47.mjs"; | ||
| import { REMOTE_CONNECTION_KEY } from "devframe/constants"; | ||
| import { connectDevframe, getDevframeRpcClient } from "devframe/client"; | ||
| import { connectDevframe, getDevframeRpcClient, resolveWsUrl } from "devframe/client"; | ||
| import { createEventEmitter } from "devframe/utils/events"; | ||
@@ -24,2 +25,49 @@ import { destr } from "destr"; | ||
| //#endregion | ||
| //#region src/client/dock-resources.ts | ||
| const URL_SCHEME_RE = /^[a-z][a-z\d+.-]*:/i; | ||
| function resolveResourceUrl(value, connection) { | ||
| const url = value.trim(); | ||
| if (!url || URL_SCHEME_RE.test(url) || url.startsWith("//")) return url; | ||
| if (!url.startsWith("/") && !url.startsWith("./") && !url.startsWith("../")) return url; | ||
| try { | ||
| return new URL(url, connection.metaBaseUrl).href; | ||
| } catch { | ||
| return url; | ||
| } | ||
| } | ||
| function resolveIconUrl(value, connection) { | ||
| const url = value.trim(); | ||
| if (!url || URL_SCHEME_RE.test(url) || url.startsWith("//")) return url; | ||
| if (!url.includes("/") && !url.includes(".")) return url; | ||
| try { | ||
| return new URL(url, connection.metaBaseUrl).href; | ||
| } catch { | ||
| return url; | ||
| } | ||
| } | ||
| /** | ||
| * Resolve a dock iframe URL relative to the Devframe server that provided the | ||
| * dock entry. This keeps root-relative and dot-relative paths on the host | ||
| * server when an external viewer renders the hub. | ||
| */ | ||
| function resolveDockUrl(url, connection) { | ||
| const resolved = resolveResourceUrl(url, connection); | ||
| if (resolved !== url.trim()) return resolved; | ||
| const value = url.trim(); | ||
| if (!value || URL_SCHEME_RE.test(value) || value.startsWith("//")) return /^localhost:\d/i.test(value) ? `http://${value}` : value; | ||
| try { | ||
| return new URL(`http://${value}`).href; | ||
| } catch { | ||
| return value; | ||
| } | ||
| } | ||
| /** Resolve URL-backed dock icons while preserving Iconify names and data URLs. */ | ||
| function resolveDockIcon(icon, connection) { | ||
| if (typeof icon === "string") return resolveIconUrl(icon, connection); | ||
| return { | ||
| light: resolveIconUrl(icon.light, connection), | ||
| dark: resolveIconUrl(icon.dark, connection) | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region src/client/frame-nav.ts | ||
@@ -654,2 +702,23 @@ /** | ||
| } | ||
| /** | ||
| * Build an external viewer URL from an existing trusted Devframe connection. | ||
| * Returns the original URL if the connection has no auth token, does not use | ||
| * WebSockets, or has an invalid metadata URL. | ||
| */ | ||
| function buildRemoteDevframeUrl(url, connection) { | ||
| if (!connection.authToken || connection.connectionMeta.backend !== "websocket") return url; | ||
| let base; | ||
| try { | ||
| base = new URL(connection.metaBaseUrl); | ||
| } catch { | ||
| return url; | ||
| } | ||
| return buildRemoteConnectionUrl(url, { | ||
| v: 1, | ||
| backend: "websocket", | ||
| websocket: resolveWsUrl(connection.connectionMeta.websocket, connection.metaBaseUrl, base), | ||
| authToken: connection.authToken, | ||
| origin: base.origin | ||
| }); | ||
| } | ||
| function extractKeyFromFragment(hash) { | ||
@@ -731,2 +800,2 @@ if (!hash) return null; | ||
| //#endregion | ||
| export { CLIENT_CONTEXT_KEY, DEFAULT_CATEGORIES_ORDER, FRAME_NAV_CHANNEL, FRAME_NAV_VERSION, attachFrameNavClient, connectRemoteDevframe, createDevframeClientHost, createMessagesClient, getDevframeClientContext, parseRemoteConnection, setDevframeClientContext }; | ||
| export { CLIENT_CONTEXT_KEY, DEFAULT_CATEGORIES_ORDER, FRAME_NAV_CHANNEL, FRAME_NAV_VERSION, attachFrameNavClient, buildRemoteDevframeUrl, connectRemoteDevframe, createDevframeClientHost, createMessagesClient, getDevframeClientContext, parseRemoteConnection, resolveDockIcon, resolveDockUrl, setDevframeClientContext, stripRemoteConnectionFromUrl }; |
+3
-3
| { | ||
| "name": "@devframes/hub", | ||
| "type": "module", | ||
| "version": "0.8.0", | ||
| "version": "0.8.1", | ||
| "description": "Framework-neutral hub layer for devframe — docks, terminals, messages, commands.", | ||
@@ -36,3 +36,3 @@ "author": "Anthony Fu <anthonyfu117@hotmail.com>", | ||
| "peerDependencies": { | ||
| "devframe": "0.8.0" | ||
| "devframe": "0.8.1" | ||
| }, | ||
@@ -53,3 +53,3 @@ "dependencies": { | ||
| "valibot": "^1.4.2", | ||
| "devframe": "0.8.0" | ||
| "devframe": "0.8.1" | ||
| }, | ||
@@ -56,0 +56,0 @@ "scripts": { |
Sorry, the diff of this file is too big to display
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
365299
1.44%17
6.25%7600
1.39%