File size: 3,271 Bytes
be47857
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
/**
 * 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;
  }
}