Skip to content

Commit c593695

Browse files
authored
Merge pull request #442 from ucdjs/feat/improve-wildcard-api
2 parents 5367375 + 2b6d01b commit c593695

File tree

10 files changed

+1242
-691
lines changed

10 files changed

+1242
-691
lines changed

apps/api/src/lib/files.ts

Lines changed: 103 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { Entry } from "apache-autoindex-parse";
2-
import { trimTrailingSlash } from "@luxass/utils";
2+
import { trimLeadingSlash, trimTrailingSlash } from "@luxass/utils";
3+
import { createGlobMatcher } from "@ucdjs-internal/shared";
34
import { parse } from "apache-autoindex-parse";
45

56
/**
@@ -20,13 +21,105 @@ import { parse } from "apache-autoindex-parse";
2021
* console.log(entries); // [{ type: 'directory', name: 'UNIDATA', path: '/UNIDATA', ... }]
2122
* ```
2223
*/
23-
export async function parseUnicodeDirectory(html: string): Promise<Entry[]> {
24-
const files = parse(html, "F2");
25-
26-
return files.map(({ type, name, path, lastModified }) => ({
27-
type,
28-
name: trimTrailingSlash(name),
29-
path: trimTrailingSlash(path),
30-
lastModified,
31-
}));
24+
export async function parseUnicodeDirectory(html: string, basePath = ""): Promise<Entry[]> {
25+
const files = parse(html, {
26+
format: "F2",
27+
basePath,
28+
});
29+
30+
return files.map((entry) => {
31+
entry.name = trimLeadingSlash(trimTrailingSlash(entry.name));
32+
return entry;
33+
});
34+
}
35+
36+
export interface DirectoryFilterOptions {
37+
/**
38+
* A string to filter file/directory names that start with this query (case-insensitive).
39+
*/
40+
query?: string;
41+
42+
/**
43+
* A glob pattern to filter file/directory names.
44+
*/
45+
pattern?: string;
46+
47+
/**
48+
* Type of entries to include: "all" (default), "files", or "directories".
49+
*/
50+
type?: string;
51+
52+
/**
53+
* Field to sort by: "name" (default) or "lastModified".
54+
*/
55+
sort?: string;
56+
57+
/**
58+
* Sort order: "asc" (default) or "desc".
59+
*/
60+
order?: string;
61+
}
62+
63+
/**
64+
* Applies filtering and sorting to directory entries based on query parameters.
65+
*
66+
* @param {Entry[]} files - Array of directory entries to filter and sort
67+
* @param {DirectoryFilterOptions} options - Filter and sort options
68+
* @returns {Entry[]} Filtered and sorted array of entries
69+
*/
70+
export function applyDirectoryFiltersAndSort(
71+
files: Entry[],
72+
options: DirectoryFilterOptions,
73+
): Entry[] {
74+
let filtered = [...files];
75+
76+
// Apply query filter (prefix search, case-insensitive)
77+
if (options.query) {
78+
// eslint-disable-next-line no-console
79+
console.info(`[v1_files]: applying query filter: ${options.query}`);
80+
const queryLower = options.query.toLowerCase();
81+
filtered = filtered.filter((entry) => entry.name.toLowerCase().startsWith(queryLower));
82+
}
83+
84+
// Apply pattern filter if provided
85+
if (options.pattern) {
86+
// eslint-disable-next-line no-console
87+
console.info(`[v1_files]: applying glob pattern filter: ${options.pattern}`);
88+
const matcher = createGlobMatcher(options.pattern);
89+
filtered = filtered.filter((entry) => matcher(entry.name));
90+
}
91+
92+
// Apply type filter
93+
const type = options.type || "all";
94+
if (type === "files") {
95+
filtered = filtered.filter((entry) => entry.type === "file");
96+
} else if (type === "directories") {
97+
filtered = filtered.filter((entry) => entry.type === "directory");
98+
}
99+
100+
// Apply sorting (directories always first, like Windows File Explorer)
101+
const sort = options.sort || "name";
102+
const order = options.order || "asc";
103+
104+
filtered = filtered.toSorted((a, b) => {
105+
// Directories always come first
106+
if (a.type !== b.type) {
107+
return a.type === "directory" ? -1 : 1;
108+
}
109+
110+
// Within same type, apply the requested sort
111+
let comparison: number;
112+
113+
if (sort === "lastModified") {
114+
// lastModified is always available from parseUnicodeDirectory
115+
comparison = (a.lastModified ?? 0) - (b.lastModified ?? 0);
116+
} else {
117+
// Natural name sorting (numeric aware) so 2.0.0 < 10.0.0
118+
comparison = a.name.localeCompare(b.name, undefined, { numeric: true, sensitivity: "base" });
119+
}
120+
121+
return order === "desc" ? -comparison : comparison;
122+
});
123+
124+
return filtered;
32125
}

0 commit comments

Comments
 (0)