visualize_dataset / src /utils /localDataset.ts
alexis779
feat: local dataset paths, API route, and versioned fetch routing
be47857
raw
history blame
3.27 kB
/**
* Local LeRobot dataset roots (absolute filesystem paths) are encoded in URLs as
* `/local/<base64url(utf8 path)>/episode_0`. File bytes are served via
* `/api/local-dataset/file?root=...&rel=...`.
*/
export const LOCAL_ORG = "local";
export const LOCAL_REPO_PREFIX = `${LOCAL_ORG}/`;
function utf8ToBase64Url(s: string): string {
const bytes = new TextEncoder().encode(s);
let bin = "";
for (let i = 0; i < bytes.length; i++) {
bin += String.fromCharCode(bytes[i]!);
}
const b64 =
typeof btoa !== "undefined"
? btoa(bin)
: Buffer.from(bytes).toString("base64");
return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
}
function base64UrlToUtf8(s: string): string {
const pad = s.length % 4 === 0 ? "" : "=".repeat(4 - (s.length % 4));
const b64 = s.replace(/-/g, "+").replace(/_/g, "/") + pad;
let bin: string;
if (typeof atob !== "undefined") {
bin = atob(b64);
} else {
bin = Buffer.from(b64, "base64").toString("binary");
}
const bytes = new Uint8Array(bin.length);
for (let i = 0; i < bin.length; i++) {
bytes[i] = bin.charCodeAt(i);
}
return new TextDecoder().decode(bytes);
}
export function normalizeUserLocalPathInput(input: string): string {
const t = input.trim();
if (t.startsWith("file://")) {
try {
const u = new URL(t);
return decodeURIComponent(u.pathname);
} catch {
return t;
}
}
return t;
}
export function looksLikeAbsoluteFilesystemPath(input: string): boolean {
const t = normalizeUserLocalPathInput(input);
if (!t) return false;
if (t.startsWith("/")) return true;
return /^[A-Za-z]:[\\/]/.test(t);
}
export function encodeLocalDatasetRoot(absolutePath: string): string {
return utf8ToBase64Url(absolutePath);
}
export function decodeLocalDatasetSegment(
encodedSegment: string,
): string | null {
if (!encodedSegment) return null;
try {
return base64UrlToUtf8(encodedSegment);
} catch {
return null;
}
}
export function localRepoIdFromAbsolutePath(absolutePath: string): string {
return `${LOCAL_REPO_PREFIX}${encodeLocalDatasetRoot(absolutePath)}`;
}
export function isLocalRepoId(repoId: string): boolean {
return (
repoId.startsWith(LOCAL_REPO_PREFIX) &&
repoId.length > LOCAL_REPO_PREFIX.length
);
}
export function localDatasetFileApiPath(
rootEncoded: string,
relPath: string,
): string {
const q = new URLSearchParams({
root: rootEncoded,
rel: relPath,
});
return `/api/local-dataset/file?${q.toString()}`;
}
/** Parse our local file API URL (absolute or relative) for server-side fs shortcut. */
export function tryParseLocalDatasetFileApiUrl(
url: string,
): { rootEncoded: string; relPath: string } | null {
const marker = "/api/local-dataset/file";
const idx = url.indexOf(marker);
if (idx === -1) return null;
const after = url.slice(idx + marker.length);
const queryStart = after.indexOf("?");
if (queryStart === -1) return null;
const search = after.slice(queryStart + 1);
try {
const params = new URLSearchParams(search);
const root = params.get("root");
const rel = params.get("rel");
if (!root || !rel) return null;
return { rootEncoded: root, relPath: rel };
} catch {
return null;
}
}