File size: 2,571 Bytes
21dd449
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import { HUB_URL } from "../consts";
import { createApiError } from "../error";
import type { ApiIndexTreeEntry } from "../types/api/api-index-tree";
import type { CredentialsParams, RepoDesignation } from "../types/public";
import { checkCredentials } from "../utils/checkCredentials";
import { parseLinkHeader } from "../utils/parseLinkHeader";
import { toRepoId } from "../utils/toRepoId";

export interface ListFileEntry {
	type: "file" | "directory" | "unknown";
	size: number;
	path: string;
	oid: string;
	lfs?: {
		oid: string;
		size: number;
		/** Size of the raw pointer file, 100~200 bytes */
		pointerSize: number;
	};
	/**
	 * Xet-backed hash, a new protocol replacing LFS for big files.
	 */
	xetHash?: string;
	/**
	 * Only fetched if `expand` is set to `true` in the `listFiles` call.
	 */
	lastCommit?: {
		date: string;
		id: string;
		title: string;
	};
	/**
	 * Only fetched if `expand` is set to `true` in the `listFiles` call.
	 */
	securityFileStatus?: unknown;
}

/**
 * List files in a folder. To list ALL files in the directory, call it
 * with {@link params.recursive} set to `true`.
 */
export async function* listFiles(
	params: {
		repo: RepoDesignation;
		/**
		 * Do we want to list files in subdirectories?
		 */
		recursive?: boolean;
		/**
		 * Eg 'data' for listing all files in the 'data' folder. Leave it empty to list all
		 * files in the repo.
		 */
		path?: string;
		/**
		 * Fetch `lastCommit` and `securityFileStatus` for each file.
		 */
		expand?: boolean;
		revision?: string;
		hubUrl?: string;
		/**
		 * Custom fetch function to use instead of the default one, for example to use a proxy or edit headers.
		 */
		fetch?: typeof fetch;
	} & Partial<CredentialsParams>
): AsyncGenerator<ListFileEntry> {
	const accessToken = checkCredentials(params);
	const repoId = toRepoId(params.repo);
	let url: string | undefined = `${params.hubUrl || HUB_URL}/api/${repoId.type}s/${repoId.name}/tree/${
		params.revision || "main"
	}${params.path ? "/" + params.path : ""}?recursive=${!!params.recursive}&expand=${!!params.expand}`;

	while (url) {
		const res: Response = await (params.fetch ?? fetch)(url, {
			headers: {
				accept: "application/json",
				...(accessToken ? { Authorization: `Bearer ${accessToken}` } : undefined),
			},
		});

		if (!res.ok) {
			throw await createApiError(res);
		}

		const items: ApiIndexTreeEntry[] = await res.json();

		for (const item of items) {
			yield item;
		}

		const linkHeader = res.headers.get("Link");

		url = linkHeader ? parseLinkHeader(linkHeader).next : undefined;
	}
}