refactor: transition to opaque workspace identifiers

This commit is contained in:
Mohamed Boudra
2026-04-15 17:59:09 +07:00
parent beeb315c8d
commit e6044264e2
34 changed files with 462 additions and 382 deletions
+19 -23
View File
@@ -1,6 +1,7 @@
import { Buffer } from "buffer";
type NullableString = string | null | undefined;
const BASE64_WORKSPACE_ID_PREFIX = "b64_";
function stripSearchAndHash(pathname: string): string {
const hashIndex = pathname.indexOf("#");
@@ -87,12 +88,16 @@ function tryDecodeBase64UrlNoPadUtf8(input: string): string | null {
return decoded;
}
function isPathLikeWorkspaceIdentity(value: string): boolean {
return value.includes("/") || value.includes("\\") || /^[A-Za-z]:[\\/]/.test(value);
function normalizeWorkspaceId(value: string): string {
return value.trim();
}
function normalizeWorkspaceId(value: string): string {
return value.trim().replace(/\\/g, "/").replace(/\/+$/, "");
function isUrlSafeWorkspaceId(value: string): boolean {
return /^[A-Za-z0-9._~-]+$/.test(value);
}
function isLegacyPathLikeWorkspaceValue(value: string): boolean {
return value.includes("/") || value.includes("\\") || /^[A-Za-z]:[\\/]/.test(value);
}
export type WorkspaceOpenIntent =
@@ -163,13 +168,11 @@ export function encodeWorkspaceIdForPathSegment(workspaceId: string): string {
if (!normalized) {
return "";
}
// Numeric string IDs are URL-safe and don't need encoding.
// Legacy path-based IDs still get base64-encoded for safety.
const id = normalizeWorkspaceId(normalized);
if (isPathLikeWorkspaceIdentity(id)) {
return toBase64UrlNoPad(id);
if (isUrlSafeWorkspaceId(id)) {
return id;
}
return encodeURIComponent(id);
return `${BASE64_WORKSPACE_ID_PREFIX}${toBase64UrlNoPad(id)}`;
}
export function decodeWorkspaceIdFromPathSegment(workspaceIdSegment: string): string | null {
@@ -178,32 +181,25 @@ export function decodeWorkspaceIdFromPathSegment(workspaceIdSegment: string): st
return null;
}
// Decode %2F etc first (legacy scheme), but keep the raw segment to decide if base64 applies.
const decoded = trimNonEmpty(decodeSegment(normalizedSegment));
if (!decoded) {
return null;
}
// Legacy: if it already looks like a path after decoding, keep it.
if (decoded.includes("/") || decoded.includes("\\")) {
return normalizeWorkspaceId(decoded);
}
// If the segment looks like a plain numeric ID, return it directly.
// Do NOT attempt base64 decode on short alphanumeric strings.
if (/^\d+$/.test(decoded)) {
return decoded;
if (decoded.startsWith(BASE64_WORKSPACE_ID_PREFIX)) {
const encodedPayload = decoded.slice(BASE64_WORKSPACE_ID_PREFIX.length);
const prefixedDecoded =
tryDecodeBase64UrlNoPadUtf8(encodedPayload) ?? decodeBase64UrlNoPadUtf8(encodedPayload);
return prefixedDecoded ? normalizeWorkspaceId(prefixedDecoded) : null;
}
const base64Decoded = tryDecodeBase64UrlNoPadUtf8(decoded);
if (base64Decoded) {
if (base64Decoded && isLegacyPathLikeWorkspaceValue(base64Decoded)) {
return normalizeWorkspaceId(base64Decoded);
}
// Some older links use non-canonical base64url (non-zero pad bits). Accept
// decoded values only when they clearly represent filesystem paths.
const relaxedBase64Decoded = decodeBase64UrlNoPadUtf8(decoded);
if (relaxedBase64Decoded && isPathLikeWorkspaceIdentity(relaxedBase64Decoded)) {
if (relaxedBase64Decoded && isLegacyPathLikeWorkspaceValue(relaxedBase64Decoded)) {
return normalizeWorkspaceId(relaxedBase64Decoded);
}