@dirwell/core 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -1
- package/dist/client.d.mts +1 -1
- package/dist/index.d.mts +17 -6
- package/dist/index.mjs +144 -50
- package/dist/index.mjs.map +1 -1
- package/dist/{listing-B_6TbWuz.d.mts → listing-BrPLLSiX.d.mts} +29 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @dirwell/core
|
|
2
2
|
|
|
3
|
-
Framework-neutral filesystem, listing and static-publication SDK. Requires Node.js 22.12 or newer. For this quick start, install `npm install @dirwell/core@0.0.
|
|
3
|
+
Framework-neutral filesystem, listing and static-publication SDK. Requires Node.js 22.12 or newer. For this quick start, install `npm install @dirwell/core@0.0.3 @dirwell/theme-plain@0.0.3`.
|
|
4
4
|
|
|
5
5
|
Core owns filesystem selection, safe navigation, public listing projection and static publication. It requires an explicit theme and does not load a default theme, image renderer, frontend framework, config loader or watcher. Runtime matching uses picomatch for globs and ignore for Git-style ignore files.
|
|
6
6
|
|
|
@@ -32,3 +32,13 @@ JavaScript callers receive the same recognized-option checks as typed consumers:
|
|
|
32
32
|
Custom theme objects may carry extra private fields. Their required name/render and known hooks must match the theme contract. Metadata and annotation callbacks remain trusted executable host code and are awaited where the public contract is asynchronous. These controls are included in `0.0.1`.
|
|
33
33
|
|
|
34
34
|
The 0.0.x API may evolve. Pin your qualified version and upgrade the official family together. See [support and verification](https://vp-tw.github.io/dirwell/support/) for host, browser and device boundaries.
|
|
35
|
+
|
|
36
|
+
## Directory URLs
|
|
37
|
+
|
|
38
|
+
Version 0.0.3 uses Core `routing: { directoryUrls: "explicit" }` by default
|
|
39
|
+
for every theme. Choose `slash` for directory-index hosts or `clean` for a
|
|
40
|
+
qualified extensionless host. Themes do not rewrite links. Existing source
|
|
41
|
+
homepages and download names are preserved; output conflicts fail. CLI config
|
|
42
|
+
puts routing at the top level, and `--directory-urls` overrides it.
|
|
43
|
+
See [directory URL guidance](https://vp-tw.github.io/dirwell/urls/) for output
|
|
44
|
+
placement, host requirements, native Plain file navigation and migration.
|
package/dist/client.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as ListingManifest, r as ListingEntry, t as ListingBlock } from "./listing-
|
|
1
|
+
import { i as ListingManifest, r as ListingEntry, t as ListingBlock } from "./listing-BrPLLSiX.mjs";
|
|
2
2
|
//#region src/client.d.ts
|
|
3
3
|
export declare class ListingDataError extends Error {
|
|
4
4
|
readonly name = "ListingDataError";
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
|
-
import { A as SymlinkMetadata, B as
|
|
1
|
+
import { A as SymlinkMetadata, B as MetadataImage, C as MetadataProviderFactory, D as SortDirection, E as RenderedPage, F as encodeLogicalPath, G as ResolvedMetadataContext, H as MetadataOptions, I as resolveDirectoryUrls, J as JsonValue, K as ThemeMetadataDefaults, L as ContentCounts, M as DirectoryUrls, N as RoutingOptions, O as SortField, P as directoryRoute, R as ImageSource, S as MetadataProvider, T as OutputNameResolver, U as MetadataValue, V as MetadataImageValue, W as PageMetadata, Y as copyJsonObject, _ as FileMetadata, a as ListingPage, b as GenerateOptions, c as ListingPolicyContext, d as projectListingEntry, f as BuildReport, g as ExplorerTheme, h as EntryKind, i as ListingManifest, j as ThemeContext, k as SortOptions, l as entryDataFacts, m as EntryDataProvider, n as ListingContext, o as ListingPlan, p as DirectoryData, q as JsonObject, r as ListingEntry, s as ListingPolicy, t as ListingBlock, u as listingRevision, v as FileSystemEntry, w as NameSortMode, x as IgnoreFile, y as FileTimes, z as MetadataContext } from "./listing-BrPLLSiX.mjs";
|
|
2
2
|
import { FileHandle } from "node:fs/promises";
|
|
3
|
+
//#region src/filters.d.ts
|
|
4
|
+
/** Resolve configuration paths once, before a host changes working directories. */
|
|
5
|
+
export declare function resolveIgnoreFiles(value?: unknown, cwd?: string): readonly Exclude<IgnoreFile, string>[];
|
|
6
|
+
interface SelectionBudget {
|
|
7
|
+
readonly maxEntries: number;
|
|
8
|
+
readonly maxDirectories: number;
|
|
9
|
+
readonly signal: AbortSignal;
|
|
10
|
+
readonly allowPath: (logicalPath: string) => Promise<boolean>;
|
|
11
|
+
}
|
|
12
|
+
export declare class SelectionBudgetError extends Error {
|
|
13
|
+
readonly name = "SelectionBudgetError";
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
3
16
|
//#region src/service.d.ts
|
|
4
17
|
interface DirectoryServiceOptions {
|
|
5
18
|
readonly entryData?: EntryDataProvider;
|
|
@@ -10,6 +23,8 @@ interface DirectoryServiceOptions {
|
|
|
10
23
|
readonly sort?: SortOptions;
|
|
11
24
|
}
|
|
12
25
|
interface DirectoryService {
|
|
26
|
+
/** Bounded fresh enumeration; authorization runs before directory traversal. */
|
|
27
|
+
readonly scan: (budget: SelectionBudget) => Promise<readonly FileSystemEntry[]>;
|
|
13
28
|
readonly resolvePath: (logicalPath: string) => Promise<string>;
|
|
14
29
|
readonly openFile: (logicalPath: string) => Promise<{
|
|
15
30
|
readonly entry: FileSystemEntry;
|
|
@@ -40,9 +55,5 @@ export declare function readGeneratedPage(filePath: string): Promise<string>;
|
|
|
40
55
|
/** Escape untrusted text for HTML/XML text nodes and double-quoted attributes. */
|
|
41
56
|
export declare function escapeHtml(value: string): string;
|
|
42
57
|
//#endregion
|
|
43
|
-
|
|
44
|
-
/** Resolve configuration paths once, before a host changes working directories. */
|
|
45
|
-
export declare function resolveIgnoreFiles(value?: unknown, cwd?: string): readonly Exclude<IgnoreFile, string>[];
|
|
46
|
-
//#endregion
|
|
47
|
-
export { type BuildReport, type ContentCounts, type DirectoryData, type DirectoryService, type DirectoryServiceOptions, type EntryDataProvider, type EntryKind, type ExplorerTheme, type FileMetadata, type FileSystemEntry, type FileTimes, type GenerateOptions, type IgnoreFile, type ImageSource, type JsonObject, type JsonValue, type ListingBlock, type ListingContext, type ListingEntry, type ListingManifest, type ListingPage, type ListingPlan, type ListingPolicy, type ListingPolicyContext, type MetadataContext, type MetadataImage, type MetadataImageValue, type MetadataOptions, type MetadataProvider, type MetadataProviderFactory, type MetadataValue, type NameSortMode, type OutputNameResolver, type PageMetadata, type RenderedPage, type ResolvedMetadataContext, type SortDirection, type SortField, type SortOptions, type SymlinkMetadata, type ThemeContext, type ThemeMetadataDefaults, copyJsonObject, type entryDataFacts, listingRevision, projectListingEntry };
|
|
58
|
+
export { type BuildReport, type ContentCounts, type DirectoryData, type DirectoryService, type DirectoryServiceOptions, type DirectoryUrls, type EntryDataProvider, type EntryKind, type ExplorerTheme, type FileMetadata, type FileSystemEntry, type FileTimes, type GenerateOptions, type IgnoreFile, type ImageSource, type JsonObject, type JsonValue, type ListingBlock, type ListingContext, type ListingEntry, type ListingManifest, type ListingPage, type ListingPlan, type ListingPolicy, type ListingPolicyContext, type MetadataContext, type MetadataImage, type MetadataImageValue, type MetadataOptions, type MetadataProvider, type MetadataProviderFactory, type MetadataValue, type NameSortMode, type OutputNameResolver, type PageMetadata, type RenderedPage, type ResolvedMetadataContext, type RoutingOptions, type SelectionBudget, type SortDirection, type SortField, type SortOptions, type SymlinkMetadata, type ThemeContext, type ThemeMetadataDefaults, copyJsonObject, directoryRoute, encodeLogicalPath, type entryDataFacts, listingRevision, projectListingEntry, resolveDirectoryUrls };
|
|
48
59
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as copyJsonObject } from "./json-Bvj6xQLq.mjs";
|
|
2
|
-
import { cp, lstat, mkdir, mkdtemp, open, readFile, readdir, readlink, realpath, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
2
|
+
import { cp, lstat, mkdir, mkdtemp, open, opendir, readFile, readdir, readlink, realpath, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import picomatch from "picomatch";
|
|
5
5
|
import ignore from "ignore";
|
|
@@ -77,8 +77,11 @@ function isWithin(parent, candidate) {
|
|
|
77
77
|
const relative = path.relative(parent, candidate);
|
|
78
78
|
return relative === "" || relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
|
|
79
79
|
}
|
|
80
|
+
var SelectionBudgetError = class extends Error {
|
|
81
|
+
name = "SelectionBudgetError";
|
|
82
|
+
};
|
|
80
83
|
/** Select physical source paths; parent directories remain when a descendant matches. */
|
|
81
|
-
async function selectSourcePaths(sourceDir, outputDir, include, exclude, ignoredPaths = [], ignoreFiles = [], preparedIgnore) {
|
|
84
|
+
async function selectSourcePaths(sourceDir, outputDir, include, exclude, ignoredPaths = [], ignoreFiles = [], preparedIgnore, budget) {
|
|
82
85
|
const isIgnored = preparedIgnore ?? await createIgnoreMatcher(ignoreFiles);
|
|
83
86
|
const canonicalRoot = await realpath(sourceDir);
|
|
84
87
|
const ignored = ignoredPaths.map((value) => path.resolve(value));
|
|
@@ -92,12 +95,25 @@ async function selectSourcePaths(sourceDir, outputDir, include, exclude, ignored
|
|
|
92
95
|
}));
|
|
93
96
|
const selected = /* @__PURE__ */ new Set([""]);
|
|
94
97
|
const symlinks = /* @__PURE__ */ new Map();
|
|
98
|
+
let entries = 0;
|
|
99
|
+
let directories = 0;
|
|
95
100
|
async function visit(directory, relativeDir, inheritedInclude) {
|
|
101
|
+
budget?.signal.throwIfAborted();
|
|
102
|
+
if (budget) {
|
|
103
|
+
const physical = await realpath(directory);
|
|
104
|
+
if (!isWithin(canonicalRoot, physical)) return false;
|
|
105
|
+
if (path.relative(canonicalRoot, physical).split(path.sep).join("/") !== relativeDir) return false;
|
|
106
|
+
}
|
|
107
|
+
if (budget && !await budget.allowPath(relativeDir)) return false;
|
|
108
|
+
if (budget && ++directories > budget.maxDirectories) throw new SelectionBudgetError("Directory scan budget exceeded");
|
|
96
109
|
let hasSelected = false;
|
|
97
|
-
for (const entry of await
|
|
110
|
+
for await (const entry of await opendir(directory)) {
|
|
111
|
+
budget?.signal.throwIfAborted();
|
|
112
|
+
if (budget && ++entries > budget.maxEntries) throw new SelectionBudgetError("Entry scan budget exceeded");
|
|
98
113
|
const absolutePath = path.join(directory, entry.name);
|
|
99
114
|
if (isWithin(outputDir, absolutePath) || ignored.some((value) => isWithin(value, absolutePath))) continue;
|
|
100
115
|
const relativePath = path.posix.join(relativeDir, entry.name);
|
|
116
|
+
if (budget && !await budget.allowPath(relativePath)) continue;
|
|
101
117
|
const directoryLike = entry.isDirectory();
|
|
102
118
|
const matches = (matcher) => matcher(relativePath) || directoryLike && matcher(`${relativePath}/`);
|
|
103
119
|
if (excluded.some(matches) || isIgnored(path.join(canonicalRoot, relativePath), directoryLike)) continue;
|
|
@@ -132,6 +148,48 @@ async function selectSourcePaths(sourceDir, outputDir, include, exclude, ignored
|
|
|
132
148
|
};
|
|
133
149
|
}
|
|
134
150
|
//#endregion
|
|
151
|
+
//#region src/routing.ts
|
|
152
|
+
/** Validate the shared policy before publication or server startup. */
|
|
153
|
+
function resolveDirectoryUrls(value, name = "routing") {
|
|
154
|
+
if (value === void 0) return "explicit";
|
|
155
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) throw new TypeError(`${name} must be an object`);
|
|
156
|
+
for (const key of Object.keys(value)) if (key !== "directoryUrls") throw new TypeError(`Unsupported option "${name}.${key}"`);
|
|
157
|
+
const style = "directoryUrls" in value ? value.directoryUrls : void 0;
|
|
158
|
+
if (style === void 0) return "explicit";
|
|
159
|
+
if (style !== "explicit" && style !== "slash" && style !== "clean") throw new TypeError(`${name}.directoryUrls must be explicit, slash or clean`);
|
|
160
|
+
return style;
|
|
161
|
+
}
|
|
162
|
+
function encodeLogicalPath(value) {
|
|
163
|
+
return value.split("/").map(encodeURIComponent).join("/");
|
|
164
|
+
}
|
|
165
|
+
/** Output placement and public URL are planned together, before theme rendering. */
|
|
166
|
+
function directoryRoute(directory, filename, style, page = 1) {
|
|
167
|
+
if (style === "clean" && (filename === "index.html" || page > 1)) {
|
|
168
|
+
const stem = page === 1 ? directory : directory ? `${directory}.${filename.slice(0, -5)}` : filename.slice(0, -5);
|
|
169
|
+
return {
|
|
170
|
+
outputPath: stem ? `${stem}.html` : "index.html",
|
|
171
|
+
publicPath: stem
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
const outputPath = path.posix.join(directory, filename);
|
|
175
|
+
return {
|
|
176
|
+
outputPath,
|
|
177
|
+
publicPath: style === "slash" && filename === "index.html" ? directory ? `${directory}/` : "" : outputPath
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
/** Resolve references from the public document, never from its logical directory. */
|
|
181
|
+
function createHrefResolver(documentPath, strategy, base) {
|
|
182
|
+
const documentDirectory = documentPath.endsWith("/") ? documentPath : path.posix.dirname(documentPath || ".");
|
|
183
|
+
return (target) => {
|
|
184
|
+
const directory = target === "" || target.endsWith("/");
|
|
185
|
+
if (strategy !== "relative") {
|
|
186
|
+
const suffix = encodeLogicalPath(target);
|
|
187
|
+
return strategy === "base" ? `${base}${suffix}` : suffix || "./";
|
|
188
|
+
}
|
|
189
|
+
return `${(path.posix.relative(documentDirectory || ".", target || ".") || (directory ? "" : `../${path.posix.basename(target)}`)).split("/").map((segment) => segment === ".." || segment === "." ? segment : encodeURIComponent(segment)).join("/") || "."}${directory ? "/" : ""}`;
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
//#endregion
|
|
135
193
|
//#region src/validation.ts
|
|
136
194
|
function optionObject(value, name) {
|
|
137
195
|
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new TypeError(`${name} must be an object`);
|
|
@@ -226,6 +284,7 @@ function validateTheme(value, name = "options.theme") {
|
|
|
226
284
|
}
|
|
227
285
|
}
|
|
228
286
|
const generateKeys = {
|
|
287
|
+
routing: true,
|
|
229
288
|
sourceDir: true,
|
|
230
289
|
outputDir: true,
|
|
231
290
|
theme: true,
|
|
@@ -249,6 +308,7 @@ const generateKeys = {
|
|
|
249
308
|
function validateGenerateOptions(value) {
|
|
250
309
|
const options = optionObject(value, "options");
|
|
251
310
|
optionKeys(options, Object.keys(generateKeys), "options");
|
|
311
|
+
resolveDirectoryUrls(options.routing, "options.routing");
|
|
252
312
|
requiredString(options.sourceDir, "options.sourceDir");
|
|
253
313
|
requiredString(options.outputDir, "options.outputDir");
|
|
254
314
|
validateTheme(options.theme);
|
|
@@ -491,9 +551,9 @@ const defaultSort = {
|
|
|
491
551
|
field: "name",
|
|
492
552
|
nameMode: "natural"
|
|
493
553
|
};
|
|
494
|
-
const defaultOutputName = ({ entries }) => entries.some((entry) =>
|
|
554
|
+
const defaultOutputName = ({ entries }) => entries.some((entry) => indexPattern.test(entry.name)) ? entries.some((entry) => entry.name.toLowerCase() === "_dirwell.html") ? null : "_dirwell.html" : "index.html";
|
|
495
555
|
function assertSafeOutputName(name) {
|
|
496
|
-
if (name.length === 0 || path.basename(name) !== name || name === "." || name === "..") throw new Error(`Output name must be a single safe filename: ${name}`);
|
|
556
|
+
if (name.length === 0 || name.includes("\\") || Array.from(name).some((character) => character.charCodeAt(0) < 32) || path.basename(name) !== name || name === "." || name === "..") throw new Error(`Output name must be a single safe filename: ${name}`);
|
|
497
557
|
}
|
|
498
558
|
function normalizeBase(value) {
|
|
499
559
|
if (value.includes("?") || value.includes("#")) throw new TypeError("base must not contain a query string or fragment");
|
|
@@ -505,9 +565,6 @@ function normalizeBase(value) {
|
|
|
505
565
|
if (!value.startsWith("/")) throw new TypeError("base must start with \"/\" or be an absolute HTTP(S) URL");
|
|
506
566
|
return `${value.replace(/\/+$/, "")}/`;
|
|
507
567
|
}
|
|
508
|
-
function encodeLogicalPath(value) {
|
|
509
|
-
return value.split("/").filter((segment) => segment.length > 0).map((segment) => encodeURIComponent(segment)).join("/");
|
|
510
|
-
}
|
|
511
568
|
function isDirectoryLike(entry) {
|
|
512
569
|
return entry.kind === "directory" || entry.symlink?.targetKind === "directory";
|
|
513
570
|
}
|
|
@@ -578,6 +635,7 @@ async function generateExplorerUnlocked(options, ignoredPaths) {
|
|
|
578
635
|
const resolveOutputName = typeof outputName === "string" ? () => outputName : outputName;
|
|
579
636
|
const theme = options.theme;
|
|
580
637
|
if (theme === void 0) throw new TypeError("Core requires an explicit theme");
|
|
638
|
+
const directoryUrls = resolveDirectoryUrls(options.routing);
|
|
581
639
|
const mode = options.mode ?? "ssg";
|
|
582
640
|
const urlStrategy = options.urlStrategy ?? "relative";
|
|
583
641
|
const base = normalizeBase(options.base ?? "/");
|
|
@@ -587,12 +645,7 @@ async function generateExplorerUnlocked(options, ignoredPaths) {
|
|
|
587
645
|
const filtersActive = options.include !== void 0 || options.exclude !== void 0 || (options.ignoreFiles?.length ?? 0) > 0 || ignoredSourcePaths.length > 0;
|
|
588
646
|
const plans = [];
|
|
589
647
|
if (isInsideRoot(outputDir, sourceDir)) throw new Error("outputDir must not contain the sourceDir");
|
|
590
|
-
|
|
591
|
-
await lstat(path.join(sourceDir, "__dirwell"));
|
|
592
|
-
throw new Error("Dirwell reserves the source-root path \"__dirwell\"");
|
|
593
|
-
} catch (error) {
|
|
594
|
-
if (error instanceof Error && "code" in error && error.code === "ENOENT") {} else throw error;
|
|
595
|
-
}
|
|
648
|
+
if ((await readdir(sourceDir)).some((name) => name.normalize("NFC").toLowerCase() === "__dirwell")) throw new Error("Dirwell reserves the source-root path \"__dirwell\"");
|
|
596
649
|
const outputIsWithinSource = isInsideRoot(sourceDir, outputDir);
|
|
597
650
|
const selection = await selectSourcePaths(sourceDir, outputDir, options.include, options.exclude, ignoredSourcePaths, options.ignoreFiles);
|
|
598
651
|
const stageParent = outputIsWithinSource ? path.dirname(sourceDir) : path.dirname(outputDir);
|
|
@@ -690,7 +743,10 @@ async function generateExplorerUnlocked(options, ignoredPaths) {
|
|
|
690
743
|
entries
|
|
691
744
|
};
|
|
692
745
|
const name = await resolveOutputName(directory);
|
|
693
|
-
if (name !== null)
|
|
746
|
+
if (name !== null) {
|
|
747
|
+
assertSafeOutputName(name);
|
|
748
|
+
if (options.outputName !== void 0 && directoryUrls !== "explicit" && name !== "index.html") throw new TypeError("Custom outputName requires routing.directoryUrls: explicit");
|
|
749
|
+
}
|
|
694
750
|
plans.push({
|
|
695
751
|
directory,
|
|
696
752
|
logicalDir,
|
|
@@ -717,6 +773,19 @@ async function generateExplorerUnlocked(options, ignoredPaths) {
|
|
|
717
773
|
}
|
|
718
774
|
try {
|
|
719
775
|
await visit("", sourceDir, /* @__PURE__ */ new Set());
|
|
776
|
+
const generatedPages = new Map(plans.filter((plan) => plan.outputName !== null).map((plan) => [plan.logicalDir, directoryRoute(plan.logicalDir, plan.outputName, directoryUrls)]));
|
|
777
|
+
const sourcePaths = new Set([...selection.selected].map((value) => value.normalize("NFC").toLowerCase()));
|
|
778
|
+
const claimedPages = /* @__PURE__ */ new Set();
|
|
779
|
+
const claimedAssets = /* @__PURE__ */ new Set();
|
|
780
|
+
const claimPage = (outputPath) => {
|
|
781
|
+
const key = outputPath.normalize("NFC").toLowerCase();
|
|
782
|
+
if (key === "__dirwell" || key.startsWith("__dirwell/") || sourcePaths.has(key) || claimedPages.has(key) || claimedAssets.has(key)) throw new Error(`Explorer page conflicts with source or generated output: ${outputPath}`);
|
|
783
|
+
claimedPages.add(key);
|
|
784
|
+
};
|
|
785
|
+
for (const route of generatedPages.values()) claimPage(route.outputPath);
|
|
786
|
+
const ownedDirectories = new Set(plans.filter((plan) => plan.outputName === null && plan.directory.entries.some((entry) => entry.kind === "file" && indexPattern.test(entry.name))).map((plan) => plan.logicalDir));
|
|
787
|
+
const generatedDirectories = new Set(generatedPages.keys());
|
|
788
|
+
const pagePathForDirectory = (logicalPath) => generatedPages.get(logicalPath)?.publicPath ?? (ownedDirectories.has(logicalPath) ? logicalPath ? `${logicalPath}/` : "" : null);
|
|
720
789
|
const report = {
|
|
721
790
|
sourceDir,
|
|
722
791
|
outputDir,
|
|
@@ -727,16 +796,12 @@ async function generateExplorerUnlocked(options, ignoredPaths) {
|
|
|
727
796
|
selectedPaths: [...selection.selected].filter(Boolean).sort(),
|
|
728
797
|
pages: plans.map((plan) => ({
|
|
729
798
|
directory: plan.logicalDir,
|
|
730
|
-
outputName: plan.outputName
|
|
799
|
+
outputName: plan.outputName,
|
|
800
|
+
outputPath: generatedPages.get(plan.logicalDir)?.outputPath ?? null,
|
|
801
|
+
publicPath: generatedPages.get(plan.logicalDir)?.publicPath ?? null
|
|
731
802
|
}))
|
|
732
803
|
};
|
|
733
804
|
if (options.dryRun) return report;
|
|
734
|
-
const generatedPages = new Map(plans.filter((plan) => plan.outputName !== null).map((plan) => [plan.logicalDir, plan.outputName]));
|
|
735
|
-
const generatedDirectories = new Set(generatedPages.keys());
|
|
736
|
-
const pagePathForDirectory = (logicalPath) => {
|
|
737
|
-
const pageName = generatedPages.get(logicalPath);
|
|
738
|
-
return pageName === void 0 || pageName === "index.html" ? logicalPath : path.posix.join(logicalPath, pageName);
|
|
739
|
-
};
|
|
740
805
|
const sourceKinds = /* @__PURE__ */ new Map();
|
|
741
806
|
for (const { directory } of plans) for (const entry of directory.entries) {
|
|
742
807
|
const sourcePath = path.relative(sourceDir, entry.absolutePath).split(path.sep).join("/");
|
|
@@ -769,7 +834,7 @@ async function generateExplorerUnlocked(options, ignoredPaths) {
|
|
|
769
834
|
rawLinkFiles.set(filename, entry.symlink.target);
|
|
770
835
|
href = `raw-links/${filename}`;
|
|
771
836
|
} else if (linkedPath !== null) {
|
|
772
|
-
href =
|
|
837
|
+
href = createHrefResolver("__dirwell/search-index.json", "relative", base)(linkedPath);
|
|
773
838
|
if (!directoryLike && options.fileHref !== void 0) href = mappedFileHref(linkedPath);
|
|
774
839
|
}
|
|
775
840
|
searchEntries.set(entry.relativePath, {
|
|
@@ -790,33 +855,38 @@ async function generateExplorerUnlocked(options, ignoredPaths) {
|
|
|
790
855
|
}
|
|
791
856
|
for (const plan of plans) {
|
|
792
857
|
const { directory, logicalDir, outputName: name } = plan;
|
|
793
|
-
const sourceEntryNames = new Set(directory.entries.map((entry) => entry.name.toLowerCase()));
|
|
794
858
|
if (name === null) continue;
|
|
795
859
|
const targetLogicalPathFor = (entry) => {
|
|
796
860
|
if (isUnavailableSymlink(entry)) return null;
|
|
797
861
|
if (entry.symlink?.targetRelativePath !== null && entry.symlink !== null) return entry.symlink.targetRelativePath;
|
|
798
862
|
return path.posix.join(logicalDir, entry.name);
|
|
799
863
|
};
|
|
800
|
-
const
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
if (encodedTarget === "") return urlStrategy === "base" ? base : "./";
|
|
806
|
-
const suffix = `${encodedTarget}${linkToDirectory ? "/" : ""}`;
|
|
807
|
-
return urlStrategy === "base" ? `${base}${suffix}` : suffix;
|
|
864
|
+
const currentRoute = generatedPages.get(logicalDir);
|
|
865
|
+
const hrefForPath = createHrefResolver(currentRoute.publicPath, urlStrategy, base);
|
|
866
|
+
const hrefForDirectory = (targetPath) => {
|
|
867
|
+
const target = pagePathForDirectory(targetPath);
|
|
868
|
+
return target === null ? null : hrefForPath(target);
|
|
808
869
|
};
|
|
870
|
+
const assetHrefForDirectory = (targetPath, assetName) => {
|
|
871
|
+
assertSafeOutputName(assetName);
|
|
872
|
+
return hrefForPath(path.posix.join(mode === "ssg" ? targetPath : "__dirwell", assetName));
|
|
873
|
+
};
|
|
874
|
+
const directoryTargets = new Map(directory.entries.filter((entry) => isDirectoryLike(entry) && !isUnavailableSymlink(entry)).flatMap((entry) => {
|
|
875
|
+
const target = targetLogicalPathFor(entry);
|
|
876
|
+
return target !== null && generatedDirectories.has(target) ? [[entry.relativePath, target]] : [];
|
|
877
|
+
}));
|
|
878
|
+
const directoryPathForEntry = (entryId) => directoryTargets.get(entryId) ?? null;
|
|
809
879
|
const hrefFor = (entry) => {
|
|
810
880
|
if (entry.symlink !== null && isUnavailableSymlink(entry)) {
|
|
811
881
|
const filename = rawLinkFilename(entry);
|
|
812
882
|
rawLinkFiles.set(filename, entry.symlink.target);
|
|
813
|
-
return
|
|
883
|
+
return hrefForPath(path.posix.join("__dirwell", "raw-links", filename));
|
|
814
884
|
}
|
|
815
885
|
const targetLogicalPath = targetLogicalPathFor(entry);
|
|
816
886
|
if (targetLogicalPath === null) return null;
|
|
817
887
|
const directoryLike = entry.kind === "directory" || entry.symlink?.targetKind === "directory";
|
|
818
888
|
if (!directoryLike && options.fileHref !== void 0) return mappedFileHref(targetLogicalPath);
|
|
819
|
-
return
|
|
889
|
+
return directoryLike ? hrefForDirectory(targetLogicalPath) : hrefForPath(targetLogicalPath);
|
|
820
890
|
};
|
|
821
891
|
const baseContext = {
|
|
822
892
|
operation: "build",
|
|
@@ -824,17 +894,16 @@ async function generateExplorerUnlocked(options, ignoredPaths) {
|
|
|
824
894
|
documentBaseHref: urlStrategy === "html-base" ? base : null,
|
|
825
895
|
outputName: name,
|
|
826
896
|
mode,
|
|
827
|
-
searchIndexHref:
|
|
897
|
+
searchIndexHref: hrefForPath("__dirwell/search-index.json"),
|
|
828
898
|
sort: {
|
|
829
899
|
...defaultSort,
|
|
830
900
|
...options.sort
|
|
831
901
|
},
|
|
832
|
-
assetHref: (assetName) =>
|
|
833
|
-
assertSafeOutputName(assetName);
|
|
834
|
-
return hrefForLogicalPath(mode === "ssg" ? path.posix.join(logicalDir, assetName) : path.posix.join("__dirwell", assetName), false);
|
|
835
|
-
},
|
|
902
|
+
assetHref: (assetName) => assetHrefForDirectory(logicalDir, assetName),
|
|
836
903
|
hrefFor,
|
|
837
|
-
hrefForDirectory
|
|
904
|
+
hrefForDirectory,
|
|
905
|
+
assetHrefForDirectory,
|
|
906
|
+
directoryPathForEntry,
|
|
838
907
|
hasPageForDirectory: (relativePath) => generatedDirectories.has(relativePath),
|
|
839
908
|
exitsExplorerFor: (entry) => {
|
|
840
909
|
if (isUnavailableSymlink(entry)) return true;
|
|
@@ -859,6 +928,8 @@ async function generateExplorerUnlocked(options, ignoredPaths) {
|
|
|
859
928
|
outputName: baseContext.outputName,
|
|
860
929
|
assetHref: baseContext.assetHref,
|
|
861
930
|
hrefForDirectory: baseContext.hrefForDirectory,
|
|
931
|
+
assetHrefForDirectory,
|
|
932
|
+
directoryPathForEntry,
|
|
862
933
|
documentBaseHref: baseContext.documentBaseHref,
|
|
863
934
|
directoryPath: logicalDir
|
|
864
935
|
}))
|
|
@@ -879,8 +950,9 @@ async function generateExplorerUnlocked(options, ignoredPaths) {
|
|
|
879
950
|
}
|
|
880
951
|
const directoryId = createHash("sha256").update(logicalDir).digest("hex");
|
|
881
952
|
const manifestDirectory = path.posix.join("__dirwell", "listing", directoryId);
|
|
882
|
-
const
|
|
883
|
-
|
|
953
|
+
const documentDirectory = currentRoute.publicPath.endsWith("/") ? currentRoute.publicPath : path.posix.dirname(currentRoute.publicPath || ".");
|
|
954
|
+
const hrefBase = urlStrategy === "relative" ? `../../../${documentDirectory === "." ? "" : `${encodeLogicalPath(documentDirectory.replace(/\/$/, ""))}/`}` : base;
|
|
955
|
+
manifestHref = hrefForPath(path.posix.join(manifestDirectory, "manifest.json"));
|
|
884
956
|
const blocks = [];
|
|
885
957
|
const blockSize = candidate.data.strategy === "blocks" ? candidate.data.blockSize : Math.max(1, records.length);
|
|
886
958
|
revision = listingRevision(records, sort, hrefBase, blockSize);
|
|
@@ -916,12 +988,13 @@ async function generateExplorerUnlocked(options, ignoredPaths) {
|
|
|
916
988
|
const pageCount = Math.max(1, Math.ceil(directory.entries.length / pageSize));
|
|
917
989
|
const pagePrefix = `__dirwell-page-${createHash("sha256").update(name).digest("hex").slice(0, 16)}`;
|
|
918
990
|
const pageNames = Array.from({ length: pageCount }, (_, index) => index === 0 ? name : `${pagePrefix}-${index + 1}.html`);
|
|
919
|
-
|
|
991
|
+
const pageRoutes = pageNames.map((pageName, index) => directoryRoute(logicalDir, pageName, name === "index.html" ? directoryUrls : "explicit", index + 1));
|
|
992
|
+
for (const route of pageRoutes.slice(1)) claimPage(route.outputPath);
|
|
920
993
|
const pagedAssets = /* @__PURE__ */ new Map();
|
|
921
994
|
for (const [index, pageName] of pageNames.entries()) {
|
|
922
|
-
const
|
|
923
|
-
const metadata = await metadataResolver?.resolve(directory, (outputPath) =>
|
|
924
|
-
const pageHref = (number) =>
|
|
995
|
+
const pageRoute = pageRoutes[index];
|
|
996
|
+
const metadata = await metadataResolver?.resolve(directory, (outputPath) => hrefForPath(outputPath), pageRoute.publicPath);
|
|
997
|
+
const pageHref = (number) => hrefForPath(pageRoutes[number]?.publicPath ?? currentRoute.publicPath);
|
|
925
998
|
const page = await theme.render({
|
|
926
999
|
...baseContext,
|
|
927
1000
|
...metadata === void 0 ? {} : { metadata },
|
|
@@ -944,11 +1017,15 @@ async function generateExplorerUnlocked(options, ignoredPaths) {
|
|
|
944
1017
|
}
|
|
945
1018
|
} }
|
|
946
1019
|
});
|
|
947
|
-
|
|
1020
|
+
const pageDestination = path.join(buildOutputDir, pageRoute.outputPath);
|
|
1021
|
+
await mkdir(path.dirname(pageDestination), { recursive: true });
|
|
1022
|
+
await writeFile(pageDestination, page.html);
|
|
948
1023
|
for (const [assetName, contents] of Object.entries(page.assets ?? {})) {
|
|
949
1024
|
assertSafeOutputName(assetName);
|
|
950
|
-
|
|
951
|
-
if (
|
|
1025
|
+
const assetPathKey = path.posix.join(mode === "ssg" ? logicalDir : "__dirwell", assetName).normalize("NFC").toLowerCase();
|
|
1026
|
+
if (sourcePaths.has(assetPathKey)) throw new Error(`Theme asset conflicts with source entry: ${assetName}`);
|
|
1027
|
+
if (claimedPages.has(assetPathKey)) throw new Error(`Theme asset conflicts with listing page: ${assetName}`);
|
|
1028
|
+
claimedAssets.add(assetPathKey);
|
|
952
1029
|
if (options.listing !== void 0 && pageCount > 1) {
|
|
953
1030
|
const bytes = Buffer.from(contents);
|
|
954
1031
|
const existing = pagedAssets.get(assetName);
|
|
@@ -1078,6 +1155,23 @@ async function createDirectoryService(options) {
|
|
|
1078
1155
|
});
|
|
1079
1156
|
}
|
|
1080
1157
|
return {
|
|
1158
|
+
async scan(budget) {
|
|
1159
|
+
if (!budget || !Number.isSafeInteger(budget.maxEntries) || budget.maxEntries < 1 || !Number.isSafeInteger(budget.maxDirectories) || budget.maxDirectories < 1 || !(budget.signal instanceof AbortSignal) || typeof budget.allowPath !== "function") throw new TypeError("scan requires positive entry/directory limits, an AbortSignal and allowPath");
|
|
1160
|
+
const selection = (await selectSourcePaths(root, path.join(root, "__dirwell"), options.include, options.exclude, [], ignoreFiles, void 0, budget)).selected;
|
|
1161
|
+
const entries = [];
|
|
1162
|
+
for (const logical of [...selection].sort()) {
|
|
1163
|
+
budget.signal.throwIfAborted();
|
|
1164
|
+
if (!logical) continue;
|
|
1165
|
+
try {
|
|
1166
|
+
const entry = await describe(logical, selection);
|
|
1167
|
+
if (entry.kind === "file" || entry.kind === "directory") entries.push(entry);
|
|
1168
|
+
} catch (error) {
|
|
1169
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") continue;
|
|
1170
|
+
throw error;
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
return entries;
|
|
1174
|
+
},
|
|
1081
1175
|
async resolvePath(logicalPath) {
|
|
1082
1176
|
const selection = await selected();
|
|
1083
1177
|
const entry = await describe(logicalPath, selection);
|
|
@@ -1155,6 +1249,6 @@ async function createDirectoryService(options) {
|
|
|
1155
1249
|
};
|
|
1156
1250
|
}
|
|
1157
1251
|
//#endregion
|
|
1158
|
-
export { DirectoryAccessError, compareEntries, copyJsonObject, createDirectoryService, defaultOutputName, escapeHtml, generateExplorer, generateExplorerSkippingPaths, listingRevision, projectListingEntry, readGeneratedPage, resolveIgnoreFiles, validateListingPlan };
|
|
1252
|
+
export { DirectoryAccessError, SelectionBudgetError, compareEntries, copyJsonObject, createDirectoryService, defaultOutputName, directoryRoute, encodeLogicalPath, escapeHtml, generateExplorer, generateExplorerSkippingPaths, listingRevision, projectListingEntry, readGeneratedPage, resolveDirectoryUrls, resolveIgnoreFiles, validateListingPlan };
|
|
1159
1253
|
|
|
1160
1254
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/filters.ts","../src/validation.ts","../src/filesystem.ts","../src/listing.ts","../src/publication.ts","../src/generator.ts","../src/html.ts","../src/file-identity.ts","../src/service.ts"],"sourcesContent":["import { readdir, readlink, readFile, realpath } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport picomatch from \"picomatch\";\nimport ignore from \"ignore\";\nimport type { IgnoreFile } from \"./model.ts\";\n\n/** Resolve configuration paths once, before a host changes working directories. */\nexport function resolveIgnoreFiles(\n value: unknown = [],\n cwd = process.cwd(),\n): readonly Exclude<IgnoreFile, string>[] {\n if (!Array.isArray(value)) throw new TypeError(\"ignoreFiles must be an array\");\n return value.map((input) => {\n const file = typeof input === \"string\" ? { path: input } : input;\n if (typeof file !== \"object\" || file === null || Array.isArray(file))\n throw new TypeError(\"ignoreFiles entries must be paths or objects\");\n for (const key of Object.keys(file))\n if (![\"path\", \"baseDir\", \"optional\"].includes(key))\n throw new TypeError(`Unsupported ignoreFiles key \"${key}\"`);\n for (const key of [\"path\", \"baseDir\"] as const)\n if (\n (key === \"path\" || file[key] !== undefined) &&\n (typeof file[key] !== \"string\" || file[key].length === 0)\n )\n throw new TypeError(`ignoreFiles.${key} must be a non-empty string`);\n if (file.optional !== undefined && typeof file.optional !== \"boolean\")\n throw new TypeError(\"ignoreFiles.optional must be a boolean\");\n const filename = path.resolve(cwd, file.path);\n return {\n path: filename,\n baseDir: path.resolve(cwd, file.baseDir ?? path.dirname(filename)),\n optional: file.optional ?? false,\n };\n });\n}\n\nexport async function createIgnoreMatcher(files: readonly IgnoreFile[]) {\n const rules: { base: string; matcher: ReturnType<typeof ignore> }[] = [];\n const scopes = new Map<string, ReturnType<typeof ignore>>();\n let hasRules = false;\n for (const file of resolveIgnoreFiles(files)) {\n let contents: string;\n try {\n contents = await readFile(file.path, \"utf8\");\n } catch (error) {\n if (file.optional && error instanceof Error && \"code\" in error && error.code === \"ENOENT\")\n continue;\n throw error;\n }\n const base = await realpath(file.baseDir!);\n if (contents.split(/\\r?\\n/).some((line) => line.trim() !== \"\" && !line.startsWith(\"#\")))\n hasRules = true;\n let matcher = scopes.get(base);\n if (matcher === undefined) {\n matcher = ignore();\n scopes.set(base, matcher);\n rules.push({ base, matcher });\n }\n matcher.add(contents);\n }\n // Preserve Git's parent-directory negation behavior across files in one scope.\n // Explicit deeper scopes override applicable parent-scope rules.\n rules.sort((left, right) => left.base.split(path.sep).length - right.base.split(path.sep).length);\n const matches = (filename: string, directory: boolean): boolean => {\n let ignored = false;\n for (const rule of rules) {\n if (!isWithin(rule.base, filename)) continue;\n const relative = path.relative(rule.base, filename).split(path.sep).join(\"/\");\n if (relative === \"\") continue;\n const result = rule.matcher.test(relative + (directory ? \"/\" : \"\"));\n if (result.ignored) ignored = true;\n else if (result.unignored) ignored = false;\n }\n return ignored;\n };\n return Object.assign(matches, { hasRules });\n}\n\nexport function normalizePathPatterns(value: unknown, name: string): readonly string[] {\n if (value === undefined) return [];\n const patterns = typeof value === \"string\" ? [value] : value;\n if (\n !Array.isArray(patterns) ||\n patterns.some(\n (pattern) =>\n typeof pattern !== \"string\" ||\n pattern.length === 0 ||\n pattern.startsWith(\"/\") ||\n /^[A-Za-z]:/.test(pattern) ||\n pattern.startsWith(\"!\") ||\n pattern.includes(\"\\\\\") ||\n pattern.split(\"/\").some((segment) => segment === \".\" || segment === \"..\"),\n )\n ) {\n throw new TypeError(`${name} must contain root-relative glob patterns using forward slashes`);\n }\n return patterns as readonly string[];\n}\n\nfunction isWithin(parent: string, candidate: string): boolean {\n const relative = path.relative(parent, candidate);\n return (\n relative === \"\" ||\n (relative !== \"..\" && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative))\n );\n}\n\nexport interface SourceSelection {\n readonly selected: ReadonlySet<string>;\n readonly symlinks: ReadonlySet<string>;\n readonly mirroredSymlinks: ReadonlySet<string>;\n}\n\n/** Select physical source paths; parent directories remain when a descendant matches. */\nexport async function selectSourcePaths(\n sourceDir: string,\n outputDir: string,\n include: unknown,\n exclude: unknown,\n ignoredPaths: readonly string[] = [],\n ignoreFiles: readonly IgnoreFile[] = [],\n preparedIgnore?: Awaited<ReturnType<typeof createIgnoreMatcher>>,\n): Promise<SourceSelection> {\n const isIgnored = preparedIgnore ?? (await createIgnoreMatcher(ignoreFiles));\n const canonicalRoot = await realpath(sourceDir);\n const ignored = ignoredPaths.map((value) => path.resolve(value));\n const included = normalizePathPatterns(include, \"include\").map((pattern) =>\n picomatch(pattern, { dot: true, nonegate: true }),\n );\n const excluded = normalizePathPatterns(exclude, \"exclude\").map((pattern) =>\n picomatch(pattern, { dot: true, nonegate: true }),\n );\n const selected = new Set<string>([\"\"]);\n const symlinks = new Map<string, string>();\n\n async function visit(\n directory: string,\n relativeDir: string,\n inheritedInclude: boolean,\n ): Promise<boolean> {\n let hasSelected = false;\n for (const entry of await readdir(directory, { withFileTypes: true })) {\n const absolutePath = path.join(directory, entry.name);\n if (\n isWithin(outputDir, absolutePath) ||\n ignored.some((value) => isWithin(value, absolutePath))\n )\n continue;\n const relativePath = path.posix.join(relativeDir, entry.name);\n const directoryLike = entry.isDirectory();\n const matches = (matcher: (value: string) => boolean) =>\n matcher(relativePath) || (directoryLike && matcher(`${relativePath}/`));\n if (\n excluded.some(matches) ||\n isIgnored(path.join(canonicalRoot, relativePath), directoryLike)\n )\n continue;\n const includedHere = inheritedInclude || included.some(matches);\n if (directoryLike) {\n const hasSelectedChild = await visit(absolutePath, relativePath, includedHere);\n if (included.length === 0 || includedHere || hasSelectedChild) {\n selected.add(relativePath);\n hasSelected = true;\n }\n } else if (included.length === 0 || includedHere) {\n selected.add(relativePath);\n hasSelected = true;\n if (entry.isSymbolicLink()) symlinks.set(relativePath, await readlink(absolutePath));\n }\n }\n return hasSelected;\n }\n\n if (!isIgnored(canonicalRoot, true)) await visit(sourceDir, \"\", false);\n // Absolute, escaping, and filtered targets cannot remain valid inside the copied tree.\n const mirroredSymlinks = new Set<string>();\n for (const [relativePath, target] of symlinks) {\n if (path.isAbsolute(target)) continue;\n const targetPath = path.resolve(sourceDir, path.dirname(relativePath), target);\n if (!isWithin(sourceDir, targetPath)) continue;\n const targetRelativePath = path.relative(sourceDir, targetPath).split(path.sep).join(\"/\");\n if (selected.has(targetRelativePath)) mirroredSymlinks.add(relativePath);\n }\n return { selected, symlinks: new Set(symlinks.keys()), mirroredSymlinks };\n}\n","import type { GenerateOptions, ExplorerTheme } from \"./model.ts\";\nimport type { DirectoryServiceOptions } from \"./service.ts\";\nimport type { ListingPlan } from \"./listing.ts\";\nimport { resolveIgnoreFiles } from \"./filters.ts\";\n\ntype Options = Record<string, unknown>;\nexport function optionObject(value: unknown, name: string): Options {\n if (typeof value !== \"object\" || value === null || Array.isArray(value))\n throw new TypeError(`${name} must be an object`);\n return value as Options;\n}\nfunction distance(left: string, right: string): number {\n let previous = Array.from({ length: right.length + 1 }, (_, index) => index);\n for (let i = 1; i <= left.length; i++) {\n const current = [i];\n for (let j = 1; j <= right.length; j++)\n current[j] = Math.min(\n current[j - 1]! + 1,\n previous[j]! + 1,\n previous[j - 1]! + (left[i - 1] === right[j - 1] ? 0 : 1),\n );\n previous = current;\n }\n return previous[right.length]!;\n}\nexport function optionKeys(value: Options, allowed: readonly string[], name: string): void {\n for (const key of Object.keys(value)) {\n if (allowed.includes(key)) continue;\n const suggestion = allowed\n .map((candidate) => ({ candidate, distance: distance(key, candidate) }))\n .sort((left, right) => left.distance - right.distance)\n .find((item) => item.distance <= 2)?.candidate;\n throw new TypeError(\n `Unsupported option \"${name}.${key}\".${suggestion ? ` Did you mean \"${name}.${suggestion}\"?` : ` Supported keys: ${allowed.join(\", \")}.`}`,\n );\n }\n}\nfunction scalar(value: unknown, type: \"boolean\" | \"string\" | \"function\", name: string): void {\n if (value !== undefined && typeof value !== type)\n throw new TypeError(`${name} must be a ${type}`);\n}\nfunction choice(value: unknown, allowed: readonly unknown[], name: string): void {\n if (value !== undefined && !allowed.includes(value))\n throw new TypeError(`${name} must be one of ${allowed.join(\", \")}`);\n}\nfunction positiveInteger(value: unknown, name: string): void {\n if (typeof value !== \"number\" || !Number.isSafeInteger(value) || value < 1)\n throw new TypeError(`${name} must be a positive safe integer`);\n}\nexport function validateListingPlan(value: unknown): asserts value is ListingPlan {\n const plan = optionObject(value, \"listing\");\n optionKeys(plan, [\"html\", \"data\"], \"listing\");\n for (const [key, strategy, size] of [\n [\"html\", \"paged\", \"pageSize\"],\n [\"data\", \"blocks\", \"blockSize\"],\n ] as const) {\n const section = optionObject(plan[key], `listing.${key}`);\n optionKeys(\n section,\n section.strategy === strategy ? [\"strategy\", size] : [\"strategy\"],\n `listing.${key}`,\n );\n if (section.strategy !== \"all\" && section.strategy !== strategy)\n throw new TypeError(`listing.${key}.strategy must be all or ${strategy}`);\n if (section.strategy === strategy) positiveInteger(section[size], `listing.${key}.${size}`);\n }\n}\nfunction selection(value: Options, name: string): void {\n for (const key of [\"include\", \"exclude\"])\n if (\n value[key] !== undefined &&\n typeof value[key] !== \"string\" &&\n (!Array.isArray(value[key]) || value[key].some((item: unknown) => typeof item !== \"string\"))\n )\n throw new TypeError(`${name}.${key} must be a string or array of strings`);\n resolveIgnoreFiles(value.ignoreFiles);\n scalar(value.entryData, \"function\", `${name}.entryData`);\n if (value.sort !== undefined) {\n const sort = optionObject(value.sort, `${name}.sort`);\n optionKeys(sort, [\"field\", \"direction\", \"directoriesFirst\", \"nameMode\"], `${name}.sort`);\n scalar(sort.directoriesFirst, \"boolean\", `${name}.sort.directoriesFirst`);\n choice(sort.field, [\"name\", \"size\", \"modified\"], `${name}.sort.field`);\n choice(sort.direction, [\"asc\", \"desc\"], `${name}.sort.direction`);\n choice(sort.nameMode, [\"natural\", \"locale\", \"unicode\"], `${name}.sort.nameMode`);\n }\n}\nfunction requiredString(value: unknown, name: string): void {\n if (typeof value !== \"string\" || !value.length)\n throw new TypeError(`${name} must be a non-empty string`);\n}\nexport function validateTheme(\n value: unknown,\n name = \"options.theme\",\n): asserts value is ExplorerTheme {\n if (value === undefined) throw new TypeError(\"Core requires an explicit theme\");\n const theme = optionObject(value, name);\n requiredString(theme.name, `${name}.name`);\n if (typeof theme.render !== \"function\") throw new TypeError(`${name}.render must be a function`);\n scalar(theme.entryData, \"function\", `${name}.entryData`);\n scalar(theme.searchIndex, \"boolean\", `${name}.searchIndex`);\n if (theme.listing !== undefined) {\n const listing = optionObject(theme.listing, `${name}.listing`);\n optionKeys(listing, [\"pagination\"], `${name}.listing`);\n if (typeof listing.pagination !== \"boolean\")\n throw new TypeError(`${name}.listing.pagination must be a boolean`);\n }\n}\nconst generateKeys = {\n sourceDir: true,\n outputDir: true,\n theme: true,\n include: true,\n exclude: true,\n ignoreFiles: true,\n sort: true,\n entryData: true,\n fileHref: true,\n dryRun: true,\n outputPolicy: true,\n listing: true,\n metadata: true,\n base: true,\n mode: true,\n mirror: true,\n outputName: true,\n urlStrategy: true,\n symlinks: true,\n} satisfies Record<keyof GenerateOptions, true>;\nexport function validateGenerateOptions(value: unknown): asserts value is GenerateOptions {\n const options = optionObject(value, \"options\");\n optionKeys(options, Object.keys(generateKeys), \"options\");\n requiredString(options.sourceDir, \"options.sourceDir\");\n requiredString(options.outputDir, \"options.outputDir\");\n validateTheme(options.theme);\n selection(options, \"options\");\n for (const key of [\"fileHref\", \"metadata\"]) scalar(options[key], \"function\", `options.${key}`);\n for (const key of [\"dryRun\", \"mirror\"]) scalar(options[key], \"boolean\", `options.${key}`);\n scalar(options.base, \"string\", \"options.base\");\n choice(options.mode, [\"ssg\", \"mpa\"], \"options.mode\");\n choice(\n options.urlStrategy,\n Object.keys({\n base: true,\n \"html-base\": true,\n relative: true,\n } satisfies Record<NonNullable<GenerateOptions[\"urlStrategy\"]>, true>),\n \"options.urlStrategy\",\n );\n choice(options.outputPolicy, [\"owned\", \"replace\"], \"options.outputPolicy\");\n if (\n options.outputName !== undefined &&\n typeof options.outputName !== \"string\" &&\n typeof options.outputName !== \"function\"\n )\n throw new TypeError(\"options.outputName must be a string or function\");\n if (options.listing !== undefined && typeof options.listing !== \"function\")\n validateListingPlan(options.listing);\n if (options.symlinks !== undefined) {\n const links = optionObject(options.symlinks, \"options.symlinks\");\n optionKeys(links, [\"follow\", \"boundary\", \"onCycle\"], \"options.symlinks\");\n scalar(links.follow, \"boolean\", \"options.symlinks.follow\");\n choice(links.boundary, [\"root\", \"anywhere\"], \"options.symlinks.boundary\");\n choice(links.onCycle, [\"error\", \"skip\"], \"options.symlinks.onCycle\");\n }\n}\nconst serviceKeys = {\n root: true,\n include: true,\n exclude: true,\n ignoreFiles: true,\n sort: true,\n entryData: true,\n} satisfies Record<keyof DirectoryServiceOptions, true>;\nexport function validateDirectoryOptions(value: unknown): asserts value is DirectoryServiceOptions {\n const options = optionObject(value, \"options\");\n optionKeys(options, Object.keys(serviceKeys), \"options\");\n requiredString(options.root, \"options.root\");\n selection(options, \"options\");\n}\n","import { lstat, readlink, realpath, stat } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { Stats } from \"node:fs\";\nimport type { EntryKind, FileMetadata, FileSystemEntry, SymlinkMetadata } from \"./model.ts\";\nfunction entryKind(stats: Stats): EntryKind {\n if (stats.isDirectory()) return \"directory\";\n if (stats.isFile()) return \"file\";\n if (stats.isSymbolicLink()) return \"symlink\";\n return \"other\";\n}\n\nfunction metadata(stats: Stats): FileMetadata {\n return {\n device: stats.dev,\n groupId: stats.gid,\n hardLinkCount: stats.nlink,\n inode: stats.ino,\n mode: stats.mode,\n ownerId: stats.uid,\n size: stats.size,\n times: {\n accessedAt: stats.atime.toISOString(),\n changedAt: stats.ctime.toISOString(),\n createdAt: stats.birthtime.toISOString(),\n modifiedAt: stats.mtime.toISOString(),\n },\n };\n}\n\nexport function isInsideRoot(rootPath: string, candidatePath: string): boolean {\n const relative = path.relative(rootPath, candidatePath);\n return (\n relative === \"\" ||\n (relative !== \"..\" && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative))\n );\n}\n\nexport async function describeEntry({\n absolutePath,\n relativePath,\n rootRealPath,\n ancestors,\n followSymlinks,\n selectedPaths,\n}: {\n absolutePath: string;\n relativePath: string;\n rootRealPath: string;\n ancestors: ReadonlySet<string>;\n followSymlinks: boolean;\n selectedPaths: ReadonlySet<string> | null;\n}): Promise<FileSystemEntry> {\n const linkStats = await lstat(absolutePath);\n const kind = entryKind(linkStats);\n let symlink: SymlinkMetadata | null = null;\n\n if (kind === \"symlink\") {\n const target = await readlink(absolutePath);\n let resolvedPath: string | null = null;\n let targetKind: Exclude<EntryKind, \"symlink\"> | null = null;\n let targetSize: number | null = null;\n try {\n resolvedPath = await realpath(absolutePath);\n const targetStats = await stat(absolutePath);\n targetKind = entryKind(targetStats) as Exclude<EntryKind, \"symlink\">;\n targetSize = targetStats.size;\n } catch {\n // Broken and inaccessible links are represented as data.\n }\n const isOutsideRoot = resolvedPath === null || !isInsideRoot(rootRealPath, resolvedPath);\n const isCycle = resolvedPath !== null && ancestors.has(resolvedPath);\n const targetRelativePath =\n resolvedPath !== null && !isOutsideRoot\n ? path.relative(rootRealPath, resolvedPath).split(path.sep).join(\"/\")\n : null;\n const isTargetExcluded =\n targetRelativePath !== null &&\n selectedPaths !== null &&\n !selectedPaths.has(targetRelativePath);\n symlink = {\n target,\n resolvedPath,\n targetRelativePath,\n targetKind,\n targetSize:\n isOutsideRoot || isTargetExcluded || targetKind === \"directory\" ? null : targetSize,\n isBroken: resolvedPath === null,\n isCycle,\n isOutsideRoot,\n isTargetExcluded,\n wasFollowed:\n followSymlinks &&\n targetKind === \"directory\" &&\n !isCycle &&\n !isOutsideRoot &&\n !isTargetExcluded,\n };\n }\n\n return {\n name: path.basename(absolutePath),\n absolutePath,\n relativePath,\n kind,\n metadata: metadata(linkStats),\n symlink,\n };\n}\n","import type { JsonObject } from \"./json.ts\";\nimport { createHash } from \"node:crypto\";\nimport type { EntryKind, FileSystemEntry, ThemeContext, SortOptions } from \"./model.ts\";\nimport { copyJsonObject } from \"./json.ts\";\n\n/** Public transport fields only. Internal filesystem metadata must never be spread here. */\nexport interface ListingEntry {\n readonly data?: JsonObject;\n readonly presentation?: JsonObject;\n readonly id: string;\n readonly name: string;\n readonly kind: EntryKind;\n readonly href: string | null;\n readonly exitsExplorer: boolean;\n readonly size: number;\n readonly modifiedAt: string;\n readonly link: null | {\n readonly target: string;\n readonly targetKind: Exclude<EntryKind, \"symlink\"> | null;\n readonly targetSize: number | null;\n readonly unavailable: boolean;\n readonly cycle: boolean;\n };\n}\nexport interface ListingPlan {\n readonly html:\n | { readonly strategy: \"all\" }\n | { readonly strategy: \"paged\"; readonly pageSize: number };\n readonly data:\n | { readonly strategy: \"all\" }\n | { readonly strategy: \"blocks\"; readonly blockSize: number };\n}\nexport interface ListingPolicyContext {\n readonly operation?: \"build\" | \"server\";\n readonly directoryPath: string;\n readonly visibleCount: number;\n readonly dataBytes: number;\n readonly mode: \"ssg\" | \"mpa\";\n readonly supportsPagination: boolean;\n}\nexport type ListingPolicy = ListingPlan | ((context: ListingPolicyContext) => ListingPlan);\nexport interface ListingPage {\n readonly number: number;\n readonly count: number;\n readonly offset: number;\n readonly total: number;\n readonly href: string;\n readonly previousHref: string | null;\n readonly nextHref: string | null;\n}\nexport interface ListingContext {\n readonly revision: string;\n readonly manifestHref: string;\n readonly page: ListingPage;\n}\nexport interface ListingBlock {\n readonly version: 1;\n readonly revision: string;\n readonly offset: number;\n readonly total: number;\n readonly entries: readonly ListingEntry[];\n}\nexport interface ListingManifest {\n readonly version: 1;\n readonly revision: string;\n readonly directory: string;\n /** Resolve entry hrefs against this URL, resolved relative to the manifest URL. */\n readonly hrefBase: string;\n readonly total: number;\n readonly sort: Required<SortOptions>;\n /** Block hrefs are relative to the manifest URL. */\n readonly blocks: readonly {\n readonly href: string;\n readonly offset: number;\n readonly count: number;\n }[];\n}\n\nexport function projectListingEntry(\n entry: FileSystemEntry,\n context: Pick<ThemeContext, \"hrefFor\" | \"exitsExplorerFor\">,\n): ListingEntry {\n const link = entry.symlink;\n return {\n ...(entry.publicData === undefined ? {} : { data: copyJsonObject(entry.publicData) }),\n id: entry.relativePath,\n name: entry.name,\n kind: entry.kind,\n href: context.hrefFor(entry),\n exitsExplorer: context.exitsExplorerFor(entry),\n size: entry.metadata.size,\n modifiedAt: entry.metadata.times.modifiedAt,\n link:\n link === null\n ? null\n : {\n target: link.target,\n targetKind: link.targetKind,\n targetSize: link.targetSize ?? null,\n unavailable: link.isBroken || link.isOutsideRoot || Boolean(link.isTargetExcluded),\n cycle: link.isCycle,\n },\n };\n}\n\n/** Annotation providers receive public facts, not transport URLs or raw stat objects. */\nexport function entryDataFacts(\n entry: FileSystemEntry,\n): Omit<ListingEntry, \"href\" | \"exitsExplorer\" | \"presentation\" | \"data\"> {\n const {\n href: _href,\n exitsExplorer: _exits,\n data: _data,\n ...facts\n } = projectListingEntry(entry, { hrefFor: () => null, exitsExplorerFor: () => false });\n return facts;\n}\nexport { validateListingPlan } from \"./validation.ts\";\nexport function listingRevision(\n entries: readonly ListingEntry[],\n sort: Required<SortOptions>,\n hrefBase: string,\n blockSize: number,\n): string {\n return createHash(\"sha256\")\n .update(JSON.stringify({ version: 1, entries, sort, hrefBase, blockSize }))\n .digest(\"hex\");\n}\n","import { lstat, readdir, readFile, realpath, rename, rm, mkdir, open } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { randomUUID } from \"node:crypto\";\nimport { isInsideRoot } from \"./filesystem.ts\";\n\nexport const outputMarker = \"__dirwell/ownership.json\";\nexport const outputMarkerContents = '{\"type\":\"dirwell-output\",\"version\":1}\\n';\n\nexport async function lockOutput(output: string) {\n const canonical = await canonicalPath(output);\n const lockPath = path.join(path.dirname(canonical), `.${path.basename(canonical)}-dirwell.lock`);\n await mkdir(path.dirname(lockPath), { recursive: true });\n let handle;\n try {\n handle = await open(lockPath, \"wx\");\n } catch (error) {\n if (error instanceof Error && \"code\" in error && error.code === \"EEXIST\")\n throw new Error(\n `Output is locked by another build: ${lockPath}. Wait for it to finish. Remove a stale lock only after confirming its owner has stopped; --force does not bypass locks.`,\n );\n throw error;\n }\n try {\n await handle.writeFile(JSON.stringify({ pid: process.pid }));\n } catch (error) {\n await handle.close();\n await rm(lockPath);\n throw error;\n }\n return {\n lockPath,\n async release() {\n await handle.close();\n await rm(lockPath);\n },\n };\n}\n\nexport async function canonicalPath(filename: string): Promise<string> {\n try {\n return await realpath(filename);\n } catch (error) {\n if (!(error instanceof Error && \"code\" in error && error.code === \"ENOENT\")) throw error;\n const parent = path.dirname(filename);\n if (parent === filename) throw error;\n return path.join(await canonicalPath(parent), path.basename(filename));\n }\n}\n\n/** Force permits output ownership replacement, never source containment violations. */\nexport async function checkOutput(\n source: string,\n output: string,\n policy: \"owned\" | \"replace\" = \"owned\",\n): Promise<boolean> {\n if (policy !== \"owned\" && policy !== \"replace\")\n throw new TypeError(\"outputPolicy must be owned or replace\");\n if (isInsideRoot(await canonicalPath(output), await realpath(source)))\n throw new Error(\"outputDir must not contain the sourceDir\");\n let stat;\n try {\n stat = await lstat(output);\n } catch (error) {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") return false;\n throw error;\n }\n if (stat.isSymbolicLink() || !stat.isDirectory())\n throw new Error(\"outputDir must be a directory, not a file or symlink\");\n if ((await readdir(output)).length === 0 || policy === \"replace\") return true;\n try {\n const marker = path.join(output, outputMarker);\n if (\n !(await lstat(path.dirname(marker))).isSymbolicLink() &&\n !(await lstat(marker)).isSymbolicLink() &&\n (await readFile(marker, \"utf8\")) === outputMarkerContents\n )\n return true;\n } catch (error) {\n if (!(error instanceof Error && \"code\" in error && error.code === \"ENOENT\")) throw error;\n }\n throw new Error(\n \"Output is nonempty and not owned by Dirwell. Choose another directory or explicitly replace it with --force (-f) / outputPolicy: 'replace'.\",\n );\n}\n\n/** Keep a recoverable previous tree until the new tree is installed. */\nexport async function publishOutput(\n staged: string,\n output: string,\n source: string,\n policy: \"owned\" | \"replace\" = \"owned\",\n): Promise<void> {\n const exists = await checkOutput(source, output, policy);\n await mkdir(path.dirname(output), { recursive: true });\n const backup = path.join(\n path.dirname(output),\n `.${path.basename(output)}-previous-${randomUUID()}`,\n );\n if (exists) await rename(output, backup);\n try {\n await rename(staged, output);\n } catch (error) {\n if (exists) {\n try {\n await rename(backup, output);\n } catch (restoreError) {\n throw new AggregateError(\n [error, restoreError],\n `Publication failed; previous output is retained at ${backup}`,\n );\n }\n }\n throw error;\n }\n if (exists) await rm(backup, { recursive: true, force: true });\n}\n","import { validateGenerateOptions } from \"./validation.ts\";\nimport { copyJsonObject } from \"./json.ts\";\nimport { describeEntry, isInsideRoot } from \"./filesystem.ts\";\nimport {\n listingRevision,\n projectListingEntry,\n entryDataFacts,\n validateListingPlan,\n type ListingManifest,\n} from \"./listing.ts\";\nimport {\n cp,\n lstat,\n mkdtemp,\n mkdir,\n readFile,\n readdir,\n realpath,\n rm,\n writeFile,\n} from \"node:fs/promises\";\nimport { createHash } from \"node:crypto\";\nimport path from \"node:path\";\nimport type {\n DirectoryData,\n EntryKind,\n ExplorerTheme,\n FileSystemEntry,\n GenerateOptions,\n NameSortMode,\n OutputNameResolver,\n SortOptions,\n ThemeContext,\n} from \"./model.ts\";\nimport { selectSourcePaths } from \"./filters.ts\";\nimport {\n checkOutput,\n publishOutput,\n outputMarker,\n outputMarkerContents,\n lockOutput,\n canonicalPath,\n} from \"./publication.ts\";\nimport type { BuildReport } from \"./model.ts\";\n\nconst indexPattern = /^index\\.html?$/i;\n\nconst defaultSort = {\n direction: \"asc\",\n directoriesFirst: true,\n field: \"name\",\n nameMode: \"natural\",\n} as const satisfies Required<SortOptions>;\n\nexport const defaultOutputName: OutputNameResolver = ({ entries }) =>\n entries.some((entry) => entry.kind === \"file\" && indexPattern.test(entry.name))\n ? entries.some((entry) => entry.name.toLowerCase() === \"_dirwell.html\")\n ? null\n : \"_dirwell.html\"\n : \"index.html\";\n\nfunction assertSafeOutputName(name: string): void {\n if (name.length === 0 || path.basename(name) !== name || name === \".\" || name === \"..\") {\n throw new Error(`Output name must be a single safe filename: ${name}`);\n }\n}\n\nfunction normalizeBase(value: string): string {\n if (value.includes(\"?\") || value.includes(\"#\")) {\n throw new TypeError(\"base must not contain a query string or fragment\");\n }\n if (/^https?:\\/\\//i.test(value)) {\n const url = new URL(value);\n url.pathname = `${url.pathname.replace(/\\/+$/, \"\")}/`;\n return url.href;\n }\n if (!value.startsWith(\"/\")) {\n throw new TypeError('base must start with \"/\" or be an absolute HTTP(S) URL');\n }\n return `${value.replace(/\\/+$/, \"\")}/`;\n}\n\nfunction encodeLogicalPath(value: string): string {\n return value\n .split(\"/\")\n .filter((segment) => segment.length > 0)\n .map((segment) => encodeURIComponent(segment))\n .join(\"/\");\n}\n\nfunction isDirectoryLike(entry: FileSystemEntry): boolean {\n return entry.kind === \"directory\" || entry.symlink?.targetKind === \"directory\";\n}\n\nfunction isUnavailableSymlink(entry: FileSystemEntry): boolean {\n return Boolean(\n entry.symlink?.isBroken || entry.symlink?.isOutsideRoot || entry.symlink?.isTargetExcluded,\n );\n}\n\nfunction compareUnicode(left: string, right: string): number {\n const leftPoints = [...left];\n const rightPoints = [...right];\n const length = Math.min(leftPoints.length, rightPoints.length);\n for (let index = 0; index < length; index += 1) {\n const difference =\n (leftPoints[index]?.codePointAt(0) ?? 0) - (rightPoints[index]?.codePointAt(0) ?? 0);\n if (difference !== 0) return difference;\n }\n return leftPoints.length - rightPoints.length;\n}\n\nfunction compareNames(left: string, right: string, mode: NameSortMode): number {\n if (mode === \"unicode\") return compareUnicode(left, right);\n return left.localeCompare(right, undefined, {\n numeric: mode === \"natural\",\n sensitivity: \"base\",\n });\n}\n\nexport function compareEntries(\n left: FileSystemEntry,\n right: FileSystemEntry,\n options: SortOptions = {},\n): number {\n const resolved = { ...defaultSort, ...options };\n if (resolved.directoriesFirst && isDirectoryLike(left) !== isDirectoryLike(right)) {\n return isDirectoryLike(left) ? -1 : 1;\n }\n let result: number;\n if (resolved.field === \"modified\") {\n result = left.metadata.times.modifiedAt.localeCompare(right.metadata.times.modifiedAt);\n } else if (resolved.field === \"size\") {\n result =\n (left.kind === \"directory\" ? 0 : left.metadata.size) -\n (right.kind === \"directory\" ? 0 : right.metadata.size);\n } else {\n result = compareNames(left.name, right.name, resolved.nameMode);\n }\n if (result === 0) result = compareNames(left.name, right.name, resolved.nameMode);\n return resolved.direction === \"asc\" ? result : -result;\n}\n\nfunction rawLinkFilename(entry: FileSystemEntry): string {\n return `${createHash(\"sha256\")\n .update(entry.relativePath)\n .update(\"\\0\")\n .update(entry.symlink?.target ?? \"\")\n .digest(\"hex\")\n .slice(0, 16)}.txt`;\n}\n\nexport async function generateExplorer(options: GenerateOptions): Promise<BuildReport> {\n return generateExplorerSkippingPaths(options, []);\n}\n\n/** Internal entry point for hosts that stage previews outside their published output. */\nexport async function generateExplorerSkippingPaths(\n options: GenerateOptions,\n ignoredPaths: readonly string[],\n): Promise<BuildReport> {\n validateGenerateOptions(options);\n const source = path.resolve(options.sourceDir),\n output = path.resolve(options.outputDir);\n await checkOutput(source, output, options.outputPolicy);\n if (options.dryRun) return generateExplorerUnlocked(options, ignoredPaths);\n const lock = await lockOutput(output);\n const lexicalLock = path.join(path.dirname(output), path.basename(lock.lockPath));\n try {\n return await generateExplorerUnlocked(options, [...ignoredPaths, lock.lockPath, lexicalLock]);\n } finally {\n await lock.release();\n }\n}\n\nasync function generateExplorerUnlocked(\n options: GenerateOptions,\n ignoredPaths: readonly string[],\n): Promise<BuildReport> {\n const sourceDir = await realpath(path.resolve(options.sourceDir));\n const outputDir = await canonicalPath(path.resolve(options.outputDir));\n const ignoredSourcePaths = ignoredPaths.map((value) => path.resolve(value));\n const rootRealPath = await realpath(sourceDir);\n const replacesOutput = await checkOutput(sourceDir, outputDir, options.outputPolicy);\n const outputName = options.outputName ?? defaultOutputName;\n const resolveOutputName: OutputNameResolver =\n typeof outputName === \"string\" ? () => outputName : outputName;\n const theme: ExplorerTheme = options.theme;\n if (theme === undefined) throw new TypeError(\"Core requires an explicit theme\");\n const mode = options.mode ?? \"ssg\";\n const urlStrategy = options.urlStrategy ?? \"relative\";\n const base = normalizeBase(options.base ?? \"/\");\n const followSymlinks = options.symlinks?.follow ?? false;\n const boundary = options.symlinks?.boundary ?? \"root\";\n const onCycle = options.symlinks?.onCycle ?? \"skip\";\n const filtersActive =\n options.include !== undefined ||\n options.exclude !== undefined ||\n (options.ignoreFiles?.length ?? 0) > 0 ||\n ignoredSourcePaths.length > 0;\n const plans: Array<{\n readonly directory: DirectoryData;\n readonly logicalDir: string;\n readonly outputName: string | null;\n }> = [];\n if (isInsideRoot(outputDir, sourceDir)) {\n throw new Error(\"outputDir must not contain the sourceDir\");\n }\n try {\n await lstat(path.join(sourceDir, \"__dirwell\"));\n throw new Error('Dirwell reserves the source-root path \"__dirwell\"');\n } catch (error) {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") {\n // The reserved asset path is available.\n } else {\n throw error;\n }\n }\n const outputIsWithinSource = isInsideRoot(sourceDir, outputDir);\n const selection = await selectSourcePaths(\n sourceDir,\n outputDir,\n options.include,\n options.exclude,\n ignoredSourcePaths,\n options.ignoreFiles,\n );\n const stageParent = outputIsWithinSource ? path.dirname(sourceDir) : path.dirname(outputDir);\n if (!options.dryRun) await mkdir(stageParent, { recursive: true });\n const stagedOutputDir = options.dryRun\n ? null\n : await mkdtemp(path.join(stageParent, `.${path.basename(outputDir)}-dirwell-`));\n const buildOutputDir = stagedOutputDir!;\n\n try {\n if (!options.dryRun && (options.mirror ?? true)) {\n await rm(buildOutputDir, { recursive: true, force: true });\n await mkdir(buildOutputDir, { recursive: true });\n await cp(sourceDir, buildOutputDir, {\n recursive: true,\n dereference: false,\n preserveTimestamps: true,\n filter: (sourcePath) => {\n const relativePath = path.relative(sourceDir, sourcePath).split(path.sep).join(\"/\");\n return (\n selection.selected.has(relativePath) &&\n (!selection.symlinks.has(relativePath) || selection.mirroredSymlinks.has(relativePath))\n );\n },\n });\n }\n } catch (error) {\n if (stagedOutputDir !== null) await rm(stagedOutputDir, { recursive: true, force: true });\n throw error;\n }\n\n async function visit(\n logicalDir: string,\n physicalDir: string,\n ancestors: ReadonlySet<string>,\n ): Promise<void> {\n const physicalRealPath = await realpath(physicalDir);\n if (ancestors.has(physicalRealPath)) {\n if (onCycle === \"error\") throw new Error(`Symlink cycle at ${logicalDir}`);\n return;\n }\n\n const nextAncestors = new Set(ancestors).add(physicalRealPath);\n const names = (await readdir(physicalDir)).filter((name) => {\n if (outputIsWithinSource && isInsideRoot(outputDir, path.join(physicalDir, name)))\n return false;\n if (ignoredSourcePaths.some((value) => isInsideRoot(value, path.join(physicalDir, name))))\n return false;\n if (!filtersActive) return true;\n const relativePath = path.relative(rootRealPath, path.join(physicalRealPath, name));\n return selection.selected.has(relativePath.split(path.sep).join(\"/\"));\n });\n const entries = await Promise.all(\n names.map(async (name) => {\n const entry = await describeEntry({\n absolutePath: path.join(physicalDir, name),\n relativePath: path.posix.join(logicalDir, name),\n rootRealPath,\n ancestors: nextAncestors,\n followSymlinks,\n selectedPaths: filtersActive ? selection.selected : null,\n });\n return options.entryData === undefined\n ? entry\n : {\n ...entry,\n publicData: copyJsonObject(\n await options.entryData(entryDataFacts(entry), {\n operation: \"build\",\n directoryPath: logicalDir,\n }),\n ),\n };\n }),\n );\n entries.sort(\n (left, right) =>\n compareEntries(left, right, options.sort) ||\n (options.listing === undefined ? 0 : compareUnicode(left.relativePath, right.relativePath)),\n );\n\n const describedCurrent = await describeEntry({\n absolutePath: physicalDir,\n relativePath: logicalDir,\n rootRealPath,\n ancestors,\n followSymlinks,\n selectedPaths: filtersActive ? selection.selected : null,\n });\n if (describedCurrent.kind !== \"directory\") {\n throw new Error(`${physicalDir} is not a directory`);\n }\n const current: DirectoryData[\"current\"] = {\n ...describedCurrent,\n kind: \"directory\",\n };\n\n const root = await describeEntry({\n absolutePath: sourceDir,\n relativePath: \"\",\n rootRealPath,\n ancestors: new Set(),\n followSymlinks,\n selectedPaths: filtersActive ? selection.selected : null,\n });\n const parent =\n logicalDir === \"\"\n ? null\n : await describeEntry({\n absolutePath: path.dirname(physicalDir),\n relativePath:\n path.posix.dirname(logicalDir) === \".\" ? \"\" : path.posix.dirname(logicalDir),\n rootRealPath,\n ancestors,\n followSymlinks,\n selectedPaths: filtersActive ? selection.selected : null,\n });\n const directory: DirectoryData = {\n root,\n current,\n parent,\n depth: logicalDir === \"\" ? 0 : logicalDir.split(\"/\").length,\n entries,\n };\n\n const name = await resolveOutputName(directory);\n if (name !== null) assertSafeOutputName(name);\n plans.push({ directory, logicalDir, outputName: name });\n\n for (const entry of entries) {\n if (entry.kind === \"directory\") {\n await visit(path.posix.join(logicalDir, entry.name), entry.absolutePath, nextAncestors);\n continue;\n }\n if (!followSymlinks || entry.symlink?.targetKind !== \"directory\") continue;\n const physicalLinkPath = path\n .relative(rootRealPath, path.join(physicalRealPath, entry.name))\n .split(path.sep)\n .join(\"/\");\n if ((options.mirror ?? true) && !selection.mirroredSymlinks.has(physicalLinkPath)) continue;\n if (entry.symlink.isCycle) {\n if (onCycle === \"error\") throw new Error(`Symlink cycle at ${entry.relativePath}`);\n continue;\n }\n if (entry.symlink.resolvedPath === null) continue;\n if (boundary === \"root\" && entry.symlink.isOutsideRoot) continue;\n if (\n filtersActive &&\n (entry.symlink.targetRelativePath === null ||\n !selection.selected.has(entry.symlink.targetRelativePath))\n )\n continue;\n // Mirrored internal aliases already resolve to the target's generated page.\n // Writing an alias page through the copied symlink would overwrite that page.\n if (\n (options.mirror ?? true) &&\n entry.symlink.targetRelativePath !== null &&\n !entry.symlink.isOutsideRoot\n )\n continue;\n await visit(\n path.posix.join(logicalDir, entry.name),\n entry.symlink.resolvedPath,\n nextAncestors,\n );\n }\n }\n\n try {\n await visit(\"\", sourceDir, new Set());\n\n const report: BuildReport = {\n sourceDir,\n outputDir,\n theme: theme.name,\n dryRun: options.dryRun ?? false,\n mirror: options.mirror ?? true,\n replacesOutput,\n selectedPaths: [...selection.selected].filter(Boolean).sort(),\n pages: plans.map((plan) => ({ directory: plan.logicalDir, outputName: plan.outputName })),\n };\n if (options.dryRun) return report;\n\n const generatedPages = new Map(\n plans\n .filter(\n (plan): plan is typeof plan & { readonly outputName: string } => plan.outputName !== null,\n )\n .map((plan) => [plan.logicalDir, plan.outputName]),\n );\n const generatedDirectories = new Set(generatedPages.keys());\n const pagePathForDirectory = (logicalPath: string): string => {\n const pageName = generatedPages.get(logicalPath);\n return pageName === undefined || pageName === \"index.html\"\n ? logicalPath\n : path.posix.join(logicalPath, pageName);\n };\n const sourceKinds = new Map<string, string>();\n for (const { directory } of plans)\n for (const entry of directory.entries) {\n const sourcePath = path.relative(sourceDir, entry.absolutePath).split(path.sep).join(\"/\");\n if (selection.selected.has(sourcePath)) sourceKinds.set(sourcePath, entry.kind);\n }\n const metadataResolver = await options.metadata?.({ sourceDir, sourceKinds });\n const sharedAssets = new Map<string, string | Uint8Array>();\n const rawLinkFiles = new Map<string, string>();\n const searchEntries = new Map<\n string,\n {\n href: string | null;\n exitsExplorer: boolean;\n isLink: boolean;\n kind: EntryKind;\n modifiedAt: string;\n name: string;\n path: string;\n size: number;\n target: string | null;\n targetKind: Exclude<EntryKind, \"symlink\"> | null;\n targetSize: number | null;\n isTargetUnavailable: boolean;\n isCycle: boolean;\n }\n >();\n\n const fileHrefs = new Map<string, string | null>();\n const mappedFileHref = (logicalPath: string): string | null => {\n if (!fileHrefs.has(logicalPath)) {\n const href = options.fileHref!(logicalPath);\n if (\n href !== null &&\n (typeof href !== \"string\" ||\n href.length === 0 ||\n href.includes(\"\\\\\") ||\n Array.from(href).some((character) => character.charCodeAt(0) < 32) ||\n ![\"http:\", \"https:\"].includes(new URL(href, \"https://dirwell.invalid/\").protocol))\n )\n throw new TypeError(\n \"fileHref must return a synchronous HTTP(S), root-relative or relative URL, or null\",\n );\n fileHrefs.set(logicalPath, href);\n }\n return fileHrefs.get(logicalPath)!;\n };\n\n for (const { directory, logicalDir } of theme.searchIndex === false ? [] : plans) {\n for (const entry of directory.entries) {\n if (searchEntries.has(entry.relativePath)) continue;\n const targetLogicalPath = isUnavailableSymlink(entry)\n ? null\n : entry.symlink?.targetRelativePath !== null && entry.symlink !== null\n ? entry.symlink.targetRelativePath\n : path.posix.join(logicalDir, entry.name);\n const directoryLike = isDirectoryLike(entry);\n const linkedPath =\n directoryLike && targetLogicalPath !== null\n ? pagePathForDirectory(targetLogicalPath)\n : targetLogicalPath;\n let href: string | null = null;\n if (entry.symlink !== null && isUnavailableSymlink(entry)) {\n const filename = rawLinkFilename(entry);\n rawLinkFiles.set(filename, entry.symlink.target);\n href = `raw-links/${filename}`;\n } else if (linkedPath !== null) {\n href =\n linkedPath === \"\"\n ? \"../\"\n : `../${encodeLogicalPath(linkedPath)}${directoryLike && linkedPath === targetLogicalPath ? \"/\" : \"\"}`;\n if (!directoryLike && options.fileHref !== undefined) href = mappedFileHref(linkedPath);\n }\n searchEntries.set(entry.relativePath, {\n exitsExplorer:\n isUnavailableSymlink(entry) ||\n (targetLogicalPath !== null &&\n (!directoryLike || !generatedDirectories.has(targetLogicalPath))),\n href,\n isLink: entry.kind === \"symlink\",\n kind: entry.kind,\n modifiedAt: entry.metadata.times.modifiedAt,\n name: entry.name,\n path: entry.relativePath,\n size: entry.metadata.size,\n target: entry.symlink?.target ?? null,\n targetKind: entry.symlink?.targetKind ?? null,\n targetSize: entry.symlink?.targetSize ?? null,\n isTargetUnavailable: isUnavailableSymlink(entry),\n isCycle: entry.symlink?.isCycle ?? false,\n });\n }\n }\n\n for (const plan of plans) {\n const { directory, logicalDir, outputName: name } = plan;\n const sourceEntryNames = new Set(directory.entries.map((entry) => entry.name.toLowerCase()));\n if (name === null) continue;\n\n const targetLogicalPathFor = (entry: FileSystemEntry): string | null => {\n if (isUnavailableSymlink(entry)) return null;\n if (entry.symlink?.targetRelativePath !== null && entry.symlink !== null) {\n return entry.symlink.targetRelativePath;\n }\n return path.posix.join(logicalDir, entry.name);\n };\n const hrefForLogicalPath = (targetPath: string, directoryLike: boolean): string => {\n const linkedPath = directoryLike ? pagePathForDirectory(targetPath) : targetPath;\n const linkToDirectory = directoryLike && linkedPath === targetPath;\n if (urlStrategy === \"relative\") {\n const relativeTarget = path.posix.relative(\n logicalDir === \"\" ? \".\" : logicalDir,\n linkedPath === \"\" ? \".\" : linkedPath,\n );\n const encodedTarget = relativeTarget\n .split(\"/\")\n .map((segment) =>\n segment === \".\" || segment === \"..\" ? segment : encodeURIComponent(segment),\n )\n .join(\"/\");\n return `${encodedTarget || \".\"}${linkToDirectory ? \"/\" : \"\"}`;\n }\n const encodedTarget = encodeLogicalPath(linkedPath);\n if (encodedTarget === \"\") {\n return urlStrategy === \"base\" ? base : \"./\";\n }\n const suffix = `${encodedTarget}${linkToDirectory ? \"/\" : \"\"}`;\n return urlStrategy === \"base\" ? `${base}${suffix}` : suffix;\n };\n const hrefFor = (entry: FileSystemEntry): string | null => {\n if (entry.symlink !== null && isUnavailableSymlink(entry)) {\n const filename = rawLinkFilename(entry);\n rawLinkFiles.set(filename, entry.symlink.target);\n return hrefForLogicalPath(path.posix.join(\"__dirwell\", \"raw-links\", filename), false);\n }\n const targetLogicalPath = targetLogicalPathFor(entry);\n if (targetLogicalPath === null) return null;\n const directoryLike =\n entry.kind === \"directory\" || entry.symlink?.targetKind === \"directory\";\n if (!directoryLike && options.fileHref !== undefined)\n return mappedFileHref(targetLogicalPath);\n return hrefForLogicalPath(targetLogicalPath, directoryLike);\n };\n const baseContext: ThemeContext = {\n operation: \"build\",\n directory,\n documentBaseHref: urlStrategy === \"html-base\" ? base : null,\n outputName: name,\n mode,\n searchIndexHref: hrefForLogicalPath(\n path.posix.join(\"__dirwell\", \"search-index.json\"),\n false,\n ),\n sort: { ...defaultSort, ...options.sort },\n assetHref: (assetName) => {\n assertSafeOutputName(assetName);\n return hrefForLogicalPath(\n mode === \"ssg\"\n ? path.posix.join(logicalDir, assetName)\n : path.posix.join(\"__dirwell\", assetName),\n false,\n );\n },\n hrefFor,\n hrefForDirectory: (relativePath) => hrefForLogicalPath(relativePath, true),\n hasPageForDirectory: (relativePath) => generatedDirectories.has(relativePath),\n exitsExplorerFor: (entry) => {\n if (isUnavailableSymlink(entry)) return true;\n const targetLogicalPath = targetLogicalPathFor(entry);\n const directoryLike =\n entry.kind === \"directory\" || entry.symlink?.targetKind === \"directory\";\n return (\n targetLogicalPath !== null &&\n (!directoryLike || !generatedDirectories.has(targetLogicalPath))\n );\n },\n };\n const targetDir = path.join(buildOutputDir, logicalDir);\n await mkdir(targetDir, { recursive: true });\n const sort = baseContext.sort;\n let revision = \"\";\n let manifestHref = \"\";\n let pageSize = Math.max(1, directory.entries.length);\n if (options.listing !== undefined) {\n const records = directory.entries.map((entry) => {\n const projected = projectListingEntry(entry, baseContext);\n return theme.entryData === undefined\n ? projected\n : {\n ...projected,\n presentation: copyJsonObject(\n theme.entryData(projected, {\n mode: baseContext.mode,\n outputName: baseContext.outputName,\n assetHref: baseContext.assetHref,\n hrefForDirectory: baseContext.hrefForDirectory,\n documentBaseHref: baseContext.documentBaseHref,\n directoryPath: logicalDir,\n }),\n ),\n };\n });\n const candidate =\n typeof options.listing === \"function\"\n ? options.listing({\n directoryPath: logicalDir,\n visibleCount: records.length,\n dataBytes: Buffer.byteLength(JSON.stringify(records)),\n mode,\n operation: \"build\",\n supportsPagination: theme.listing?.pagination === true,\n })\n : options.listing;\n validateListingPlan(candidate);\n if (candidate.html.strategy === \"paged\") {\n if (theme.listing?.pagination !== true)\n throw new Error(`Theme ${theme.name} does not support listing pagination`);\n pageSize = candidate.html.pageSize;\n }\n const directoryId = createHash(\"sha256\").update(logicalDir).digest(\"hex\");\n const manifestDirectory = path.posix.join(\"__dirwell\", \"listing\", directoryId);\n // Relative records belong to the document directory, not to the JSON asset directory.\n const hrefBase =\n urlStrategy === \"relative\"\n ? `../../../${encodeLogicalPath(logicalDir)}${logicalDir ? \"/\" : \"\"}`\n : base;\n const manifestPath = path.posix.join(manifestDirectory, \"manifest.json\");\n manifestHref = hrefForLogicalPath(manifestPath, false);\n const blocks: Array<ListingManifest[\"blocks\"][number]> = [];\n const blockSize =\n candidate.data.strategy === \"blocks\"\n ? candidate.data.blockSize\n : Math.max(1, records.length);\n revision = listingRevision(records, sort, hrefBase, blockSize);\n const outputListingDir = path.join(buildOutputDir, manifestDirectory);\n await mkdir(outputListingDir, { recursive: true });\n for (let offset = 0; offset < records.length || offset === 0; offset += blockSize) {\n const entries = records.slice(offset, offset + blockSize);\n const filename = `${revision}-${blocks.length}.json`;\n blocks.push({ href: filename, offset, count: entries.length });\n await writeFile(\n path.join(outputListingDir, filename),\n JSON.stringify({ version: 1, revision, offset, total: records.length, entries }),\n );\n }\n const manifest: ListingManifest = {\n version: 1,\n revision,\n directory: logicalDir,\n hrefBase,\n total: records.length,\n sort,\n blocks,\n };\n await writeFile(path.join(outputListingDir, \"manifest.json\"), JSON.stringify(manifest));\n }\n const pageCount = Math.max(1, Math.ceil(directory.entries.length / pageSize));\n const pagePrefix = `__dirwell-page-${createHash(\"sha256\").update(name).digest(\"hex\").slice(0, 16)}`;\n const pageNames = Array.from({ length: pageCount }, (_, index) =>\n index === 0 ? name : `${pagePrefix}-${index + 1}.html`,\n );\n for (const pageName of pageNames.slice(1)) {\n if (directory.entries.some((entry) => entry.name.toLowerCase() === pageName.toLowerCase()))\n throw new Error(\n `Listing page conflicts with source entry: ${path.posix.join(logicalDir, pageName)}`,\n );\n }\n const pagedAssets = new Map<string, Buffer>();\n for (const [index, pageName] of pageNames.entries()) {\n const pagePath = path.posix.join(logicalDir, pageName);\n const metadata = await metadataResolver?.resolve(\n directory,\n (outputPath) => hrefForLogicalPath(outputPath, false),\n pageName === \"index.html\" ? (logicalDir ? `${logicalDir}/` : \"\") : pagePath,\n );\n const pageHref = (number: number): string =>\n hrefForLogicalPath(path.posix.join(logicalDir, pageNames[number] ?? name), false);\n const page = await theme.render({\n ...baseContext,\n ...(metadata === undefined ? {} : { metadata }),\n outputName: pageName,\n directory:\n options.listing === undefined\n ? directory\n : {\n ...directory,\n entries: directory.entries.slice(index * pageSize, (index + 1) * pageSize),\n },\n ...(options.listing === undefined\n ? {}\n : {\n listing: {\n revision,\n manifestHref,\n page: {\n number: index + 1,\n count: pageCount,\n offset: index * pageSize,\n total: directory.entries.length,\n href: pageHref(index),\n previousHref: index === 0 ? null : pageHref(index - 1),\n nextHref: index + 1 === pageCount ? null : pageHref(index + 1),\n },\n },\n }),\n });\n await writeFile(path.join(targetDir, pageName), page.html);\n for (const [assetName, contents] of Object.entries(page.assets ?? {})) {\n assertSafeOutputName(assetName);\n if (\n (options.listing !== undefined || theme.listing?.pagination === true) &&\n mode === \"ssg\" &&\n sourceEntryNames.has(assetName.toLowerCase())\n )\n throw new Error(`Theme asset conflicts with source entry: ${assetName}`);\n if (\n options.listing !== undefined &&\n pageNames.some((pageName) => pageName.toLowerCase() === assetName.toLowerCase())\n )\n throw new Error(`Theme asset conflicts with listing page: ${assetName}`);\n if (options.listing !== undefined && pageCount > 1) {\n const bytes = Buffer.from(contents);\n const existing = pagedAssets.get(assetName);\n if (existing !== undefined && !existing.equals(bytes))\n throw new Error(`Theme emitted conflicting paginated asset: ${assetName}`);\n pagedAssets.set(assetName, bytes);\n }\n if (mode === \"ssg\") {\n await writeFile(path.join(targetDir, assetName), contents);\n } else {\n const bytes = Buffer.from(contents);\n const existing = sharedAssets.get(assetName);\n if (existing !== undefined && !Buffer.from(existing).equals(bytes)) {\n throw new Error(`Theme emitted conflicting shared asset: ${assetName}`);\n }\n sharedAssets.set(assetName, bytes);\n }\n }\n }\n }\n\n if (mode === \"mpa\" && sharedAssets.size > 0) {\n const assetDirectory = path.join(buildOutputDir, \"__dirwell\");\n await mkdir(assetDirectory, { recursive: true });\n for (const [assetName, contents] of sharedAssets) {\n await writeFile(path.join(assetDirectory, assetName), contents);\n }\n }\n\n for (const [filename, bytes] of metadataResolver?.assets ?? []) {\n const parts = filename.split(\"/\");\n for (let index = 1; index <= parts.length; index++) {\n const prefix = parts.slice(0, index).join(\"/\");\n if (selection.selected.has(prefix))\n throw new Error(`Metadata image conflicts with source entry: ${filename}`);\n const candidate = path.join(buildOutputDir, prefix);\n try {\n const info = await lstat(candidate);\n if (info.isSymbolicLink() || index === parts.length || !info.isDirectory())\n throw new Error(`Metadata image conflicts with generated output: ${filename}`);\n } catch (error) {\n if (!(error instanceof Error && \"code\" in error && error.code === \"ENOENT\")) throw error;\n }\n }\n const destination = path.join(buildOutputDir, filename);\n await mkdir(path.dirname(destination), { recursive: true });\n await writeFile(destination, bytes);\n }\n\n if (rawLinkFiles.size > 0) {\n const rawLinkDirectory = path.join(buildOutputDir, \"__dirwell\", \"raw-links\");\n await mkdir(rawLinkDirectory, { recursive: true });\n for (const [filename, target] of rawLinkFiles) {\n await writeFile(path.join(rawLinkDirectory, filename), target);\n }\n }\n if (theme.searchIndex !== false) {\n const generatedAssetDirectory = path.join(buildOutputDir, \"__dirwell\");\n await mkdir(generatedAssetDirectory, { recursive: true });\n const indexedEntries = [...searchEntries.values()];\n const shards: string[] = [];\n for (let offset = 0; offset < indexedEntries.length; offset += 512) {\n const filename = `search-${String(shards.length).padStart(5, \"0\")}.json`;\n shards.push(filename);\n await writeFile(\n path.join(generatedAssetDirectory, filename),\n JSON.stringify({ entries: indexedEntries.slice(offset, offset + 512) }),\n );\n }\n await writeFile(\n path.join(generatedAssetDirectory, \"search-index.json\"),\n JSON.stringify({ count: indexedEntries.length, shards, version: 3 }),\n );\n }\n\n await mkdir(path.join(buildOutputDir, \"__dirwell\"), { recursive: true });\n await writeFile(path.join(buildOutputDir, outputMarker), outputMarkerContents);\n await publishOutput(buildOutputDir, outputDir, sourceDir, options.outputPolicy);\n return report;\n } finally {\n if (stagedOutputDir !== null) await rm(stagedOutputDir, { recursive: true, force: true });\n }\n}\n\nexport async function readGeneratedPage(filePath: string): Promise<string> {\n return readFile(filePath, \"utf8\");\n}\n","/** Escape untrusted text for HTML/XML text nodes and double-quoted attributes. */\nexport function escapeHtml(value: string): string {\n return value\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replaceAll('\"', \""\");\n}\n","/** Some Windows Node/libuv versions omit the path-stat device ID but retain the file ID. */\nexport function fileIdentityMatches(\n before: { readonly device: number; readonly inode: number },\n descriptor: { readonly dev: number; readonly ino: number },\n platform = process.platform,\n): boolean {\n if (before.inode !== descriptor.ino) return false;\n if (before.device === descriptor.dev) return true;\n return platform === \"win32\" && before.device === 0 && before.inode !== 0;\n}\n","import { validateDirectoryOptions } from \"./validation.ts\";\nimport { readdir, realpath, open, type FileHandle } from \"node:fs/promises\";\nimport { constants } from \"node:fs\";\nimport { fileIdentityMatches } from \"./file-identity.ts\";\nimport path from \"node:path\";\nimport { describeEntry, isInsideRoot } from \"./filesystem.ts\";\nimport {\n selectSourcePaths,\n resolveIgnoreFiles,\n createIgnoreMatcher,\n normalizePathPatterns,\n} from \"./filters.ts\";\nimport { compareEntries } from \"./generator.ts\";\nimport { entryDataFacts } from \"./listing.ts\";\nimport { copyJsonObject } from \"./json.ts\";\nimport type {\n DirectoryData,\n FileSystemEntry,\n SortOptions,\n IgnoreFile,\n EntryDataProvider,\n} from \"./model.ts\";\n\nexport interface DirectoryServiceOptions {\n readonly entryData?: EntryDataProvider;\n readonly root: string;\n readonly include?: string | readonly string[];\n readonly exclude?: string | readonly string[];\n readonly ignoreFiles?: readonly IgnoreFile[];\n readonly sort?: SortOptions;\n}\nexport interface DirectoryService {\n readonly resolvePath: (logicalPath: string) => Promise<string>;\n readonly openFile: (logicalPath: string) => Promise<{\n readonly entry: FileSystemEntry;\n readonly handle: FileHandle;\n readonly canonicalPath: string;\n }>;\n readonly getEntry: (logicalPath: string) => Promise<FileSystemEntry>;\n readonly listDirectory: (logicalPath: string) => Promise<DirectoryData>;\n}\nexport class DirectoryAccessError extends Error {\n override readonly name = \"DirectoryAccessError\";\n}\nfunction assertLogicalPath(value: string): void {\n if (\n value.startsWith(\"/\") ||\n value.includes(\"\\\\\") ||\n value.includes(\"\\0\") ||\n value\n .split(\"/\")\n .some((segment) => segment === \".\" || segment === \"..\" || (segment === \"\" && value !== \"\")) ||\n /^[a-z]:/i.test(value) ||\n value === \"__dirwell\" ||\n value.startsWith(\"__dirwell/\")\n )\n throw new DirectoryAccessError(\"Unavailable path\");\n}\n/** Live reads; no watcher, recursive stat snapshot, output tree or implicit cache. */\nexport async function createDirectoryService(\n options: DirectoryServiceOptions,\n): Promise<DirectoryService> {\n validateDirectoryOptions(options);\n const root = await realpath(options.root);\n const ignoreFiles = resolveIgnoreFiles(options.ignoreFiles);\n // Validate explicit sources before an HTTP host starts accepting requests.\n await createIgnoreMatcher(ignoreFiles);\n normalizePathPatterns(options.include, \"include\");\n normalizePathPatterns(options.exclude, \"exclude\");\n const selected = async () => {\n const matcher = await createIgnoreMatcher(ignoreFiles);\n if (\n normalizePathPatterns(options.include, \"include\").length === 0 &&\n normalizePathPatterns(options.exclude, \"exclude\").length === 0 &&\n !matcher.hasRules\n )\n return null;\n return (\n await selectSourcePaths(\n root,\n path.join(root, \"__dirwell\"),\n options.include,\n options.exclude,\n [],\n ignoreFiles,\n matcher,\n )\n ).selected;\n };\n async function describe(\n logicalPath: string,\n selection: ReadonlySet<string> | null,\n ): Promise<FileSystemEntry> {\n assertLogicalPath(logicalPath);\n if (selection !== null && !selection.has(logicalPath))\n throw new DirectoryAccessError(\"Unavailable path\");\n const absolutePath = path.join(root, logicalPath);\n const parent = logicalPath === \"\" ? root : await realpath(path.dirname(absolutePath));\n if (!isInsideRoot(root, parent)) throw new DirectoryAccessError(\"Unavailable path\");\n const ancestors = new Set<string>([root]);\n if (logicalPath !== \"\") {\n const segments = logicalPath.split(\"/\").slice(0, -1);\n for (let length = 1; length <= segments.length; length++)\n ancestors.add(await realpath(path.join(root, ...segments.slice(0, length))));\n }\n return describeEntry({\n absolutePath,\n relativePath: logicalPath,\n rootRealPath: root,\n ancestors,\n followSymlinks: false,\n selectedPaths: selection,\n });\n }\n return {\n async resolvePath(logicalPath) {\n const selection = await selected();\n const entry = await describe(logicalPath, selection);\n if (\n entry.symlink &&\n (entry.symlink.isBroken || entry.symlink.isOutsideRoot || entry.symlink.isTargetExcluded)\n )\n throw new DirectoryAccessError(\"Unavailable target\");\n const resolved = await realpath(entry.absolutePath);\n if (!isInsideRoot(root, resolved)) throw new DirectoryAccessError(\"Unavailable target\");\n const canonical = path.relative(root, resolved).split(path.sep).join(\"/\");\n if (selection !== null && !selection.has(canonical))\n throw new DirectoryAccessError(\"Unavailable target\");\n return canonical;\n },\n async openFile(logicalPath) {\n assertLogicalPath(logicalPath);\n const selection = await selected();\n let entry = await describe(logicalPath, selection);\n if (entry.symlink !== null) {\n if (\n entry.symlink.isBroken ||\n entry.symlink.isOutsideRoot ||\n entry.symlink.isTargetExcluded ||\n entry.symlink.targetRelativePath === null\n )\n throw new DirectoryAccessError(\"Unavailable file\");\n entry = await describe(entry.symlink.targetRelativePath, selection);\n }\n if (entry.kind !== \"file\") throw new DirectoryAccessError(\"Unavailable file\");\n const handle = await open(\n entry.absolutePath,\n constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0),\n );\n try {\n const stats = await handle.stat();\n const resolved = await realpath(entry.absolutePath);\n if (\n !stats.isFile() ||\n !isInsideRoot(root, resolved) ||\n !fileIdentityMatches(entry.metadata, stats)\n )\n throw new DirectoryAccessError(\"File changed before opening\");\n return {\n entry,\n handle,\n canonicalPath: path.relative(root, resolved).split(path.sep).join(\"/\"),\n };\n } catch (error) {\n await handle.close();\n throw error;\n }\n },\n async getEntry(logicalPath) {\n assertLogicalPath(logicalPath);\n return describe(logicalPath, await selected());\n },\n async listDirectory(logicalPath) {\n assertLogicalPath(logicalPath);\n const selection = await selected();\n const entry = await describe(logicalPath, selection);\n if (entry.kind !== \"directory\") throw new DirectoryAccessError(\"Unavailable directory\");\n const physical = await realpath(entry.absolutePath);\n if (!isInsideRoot(root, physical)) throw new DirectoryAccessError(\"Unavailable directory\");\n const names = (await readdir(physical)).filter(\n (name) => logicalPath !== \"\" || name !== \"__dirwell\",\n );\n const described = await Promise.all(\n names\n .filter((name) => selection === null || selection.has(path.posix.join(logicalPath, name)))\n .map(async (name) => {\n let entry: FileSystemEntry;\n try {\n entry = await describe(path.posix.join(logicalPath, name), selection);\n } catch (error) {\n // An enumerated child may disappear before stat; the directory is still available.\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") return null;\n throw error;\n }\n return options.entryData === undefined\n ? entry\n : {\n ...entry,\n publicData: copyJsonObject(\n await options.entryData(entryDataFacts(entry), {\n operation: \"server\",\n directoryPath: logicalPath,\n }),\n ),\n };\n }),\n );\n const entries = described.filter((entry): entry is FileSystemEntry => entry !== null);\n entries.sort(\n (left, right) =>\n compareEntries(left, right, options.sort) ||\n (left.relativePath < right.relativePath\n ? -1\n : left.relativePath > right.relativePath\n ? 1\n : 0),\n );\n return {\n root: await describe(\"\", selection),\n current: { ...entry, kind: \"directory\" },\n parent:\n logicalPath === \"\"\n ? null\n : await describe(\n path.posix.dirname(logicalPath) === \".\" ? \"\" : path.posix.dirname(logicalPath),\n selection,\n ),\n depth: logicalPath === \"\" ? 0 : logicalPath.split(\"/\").length,\n entries,\n };\n },\n };\n}\n"],"mappings":";;;;;;;;;AAOA,SAAgB,mBACd,QAAiB,CAAC,GAClB,MAAM,QAAQ,IAAI,GACsB;CACxC,IAAI,CAAC,MAAM,QAAQ,KAAK,GAAG,MAAM,IAAI,UAAU,8BAA8B;CAC7E,OAAO,MAAM,KAAK,UAAU;EAC1B,MAAM,OAAO,OAAO,UAAU,WAAW,EAAE,MAAM,MAAM,IAAI;EAC3D,IAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,MAAM,QAAQ,IAAI,GACjE,MAAM,IAAI,UAAU,8CAA8C;EACpE,KAAK,MAAM,OAAO,OAAO,KAAK,IAAI,GAChC,IAAI,CAAC;GAAC;GAAQ;GAAW;EAAU,CAAC,CAAC,SAAS,GAAG,GAC/C,MAAM,IAAI,UAAU,gCAAgC,IAAI,EAAE;EAC9D,KAAK,MAAM,OAAO,CAAC,QAAQ,SAAS,GAClC,KACG,QAAQ,UAAU,KAAK,SAAS,KAAA,OAChC,OAAO,KAAK,SAAS,YAAY,KAAK,IAAI,CAAC,WAAW,IAEvD,MAAM,IAAI,UAAU,eAAe,IAAI,4BAA4B;EACvE,IAAI,KAAK,aAAa,KAAA,KAAa,OAAO,KAAK,aAAa,WAC1D,MAAM,IAAI,UAAU,wCAAwC;EAC9D,MAAM,WAAW,KAAK,QAAQ,KAAK,KAAK,IAAI;EAC5C,OAAO;GACL,MAAM;GACN,SAAS,KAAK,QAAQ,KAAK,KAAK,WAAW,KAAK,QAAQ,QAAQ,CAAC;GACjE,UAAU,KAAK,YAAY;EAC7B;CACF,CAAC;AACH;AAEA,eAAsB,oBAAoB,OAA8B;CACtE,MAAM,QAAgE,CAAC;CACvE,MAAM,yBAAS,IAAI,IAAuC;CAC1D,IAAI,WAAW;CACf,KAAK,MAAM,QAAQ,mBAAmB,KAAK,GAAG;EAC5C,IAAI;EACJ,IAAI;GACF,WAAW,MAAM,SAAS,KAAK,MAAM,MAAM;EAC7C,SAAS,OAAO;GACd,IAAI,KAAK,YAAY,iBAAiB,SAAS,UAAU,SAAS,MAAM,SAAS,UAC/E;GACF,MAAM;EACR;EACA,MAAM,OAAO,MAAM,SAAS,KAAK,OAAQ;EACzC,IAAI,SAAS,MAAM,OAAO,CAAC,CAAC,MAAM,SAAS,KAAK,KAAK,MAAM,MAAM,CAAC,KAAK,WAAW,GAAG,CAAC,GACpF,WAAW;EACb,IAAI,UAAU,OAAO,IAAI,IAAI;EAC7B,IAAI,YAAY,KAAA,GAAW;GACzB,UAAU,OAAO;GACjB,OAAO,IAAI,MAAM,OAAO;GACxB,MAAM,KAAK;IAAE;IAAM;GAAQ,CAAC;EAC9B;EACA,QAAQ,IAAI,QAAQ;CACtB;CAGA,MAAM,MAAM,MAAM,UAAU,KAAK,KAAK,MAAM,KAAK,GAAG,CAAC,CAAC,SAAS,MAAM,KAAK,MAAM,KAAK,GAAG,CAAC,CAAC,MAAM;CAChG,MAAM,WAAW,UAAkB,cAAgC;EACjE,IAAI,UAAU;EACd,KAAK,MAAM,QAAQ,OAAO;GACxB,IAAI,CAAC,SAAS,KAAK,MAAM,QAAQ,GAAG;GACpC,MAAM,WAAW,KAAK,SAAS,KAAK,MAAM,QAAQ,CAAC,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,GAAG;GAC5E,IAAI,aAAa,IAAI;GACrB,MAAM,SAAS,KAAK,QAAQ,KAAK,YAAY,YAAY,MAAM,GAAG;GAClE,IAAI,OAAO,SAAS,UAAU;QACzB,IAAI,OAAO,WAAW,UAAU;EACvC;EACA,OAAO;CACT;CACA,OAAO,OAAO,OAAO,SAAS,EAAE,SAAS,CAAC;AAC5C;AAEA,SAAgB,sBAAsB,OAAgB,MAAiC;CACrF,IAAI,UAAU,KAAA,GAAW,OAAO,CAAC;CACjC,MAAM,WAAW,OAAO,UAAU,WAAW,CAAC,KAAK,IAAI;CACvD,IACE,CAAC,MAAM,QAAQ,QAAQ,KACvB,SAAS,MACN,YACC,OAAO,YAAY,YACnB,QAAQ,WAAW,KACnB,QAAQ,WAAW,GAAG,KACtB,aAAa,KAAK,OAAO,KACzB,QAAQ,WAAW,GAAG,KACtB,QAAQ,SAAS,IAAI,KACrB,QAAQ,MAAM,GAAG,CAAC,CAAC,MAAM,YAAY,YAAY,OAAO,YAAY,IAAI,CAC5E,GAEA,MAAM,IAAI,UAAU,GAAG,KAAK,gEAAgE;CAE9F,OAAO;AACT;AAEA,SAAS,SAAS,QAAgB,WAA4B;CAC5D,MAAM,WAAW,KAAK,SAAS,QAAQ,SAAS;CAChD,OACE,aAAa,MACZ,aAAa,QAAQ,CAAC,SAAS,WAAW,KAAK,KAAK,KAAK,KAAK,CAAC,KAAK,WAAW,QAAQ;AAE5F;;AASA,eAAsB,kBACpB,WACA,WACA,SACA,SACA,eAAkC,CAAC,GACnC,cAAqC,CAAC,GACtC,gBAC0B;CAC1B,MAAM,YAAY,kBAAmB,MAAM,oBAAoB,WAAW;CAC1E,MAAM,gBAAgB,MAAM,SAAS,SAAS;CAC9C,MAAM,UAAU,aAAa,KAAK,UAAU,KAAK,QAAQ,KAAK,CAAC;CAC/D,MAAM,WAAW,sBAAsB,SAAS,SAAS,CAAC,CAAC,KAAK,YAC9D,UAAU,SAAS;EAAE,KAAK;EAAM,UAAU;CAAK,CAAC,CAClD;CACA,MAAM,WAAW,sBAAsB,SAAS,SAAS,CAAC,CAAC,KAAK,YAC9D,UAAU,SAAS;EAAE,KAAK;EAAM,UAAU;CAAK,CAAC,CAClD;CACA,MAAM,2BAAW,IAAI,IAAY,CAAC,EAAE,CAAC;CACrC,MAAM,2BAAW,IAAI,IAAoB;CAEzC,eAAe,MACb,WACA,aACA,kBACkB;EAClB,IAAI,cAAc;EAClB,KAAK,MAAM,SAAS,MAAM,QAAQ,WAAW,EAAE,eAAe,KAAK,CAAC,GAAG;GACrE,MAAM,eAAe,KAAK,KAAK,WAAW,MAAM,IAAI;GACpD,IACE,SAAS,WAAW,YAAY,KAChC,QAAQ,MAAM,UAAU,SAAS,OAAO,YAAY,CAAC,GAErD;GACF,MAAM,eAAe,KAAK,MAAM,KAAK,aAAa,MAAM,IAAI;GAC5D,MAAM,gBAAgB,MAAM,YAAY;GACxC,MAAM,WAAW,YACf,QAAQ,YAAY,KAAM,iBAAiB,QAAQ,GAAG,aAAa,EAAE;GACvE,IACE,SAAS,KAAK,OAAO,KACrB,UAAU,KAAK,KAAK,eAAe,YAAY,GAAG,aAAa,GAE/D;GACF,MAAM,eAAe,oBAAoB,SAAS,KAAK,OAAO;GAC9D,IAAI,eAAe;IACjB,MAAM,mBAAmB,MAAM,MAAM,cAAc,cAAc,YAAY;IAC7E,IAAI,SAAS,WAAW,KAAK,gBAAgB,kBAAkB;KAC7D,SAAS,IAAI,YAAY;KACzB,cAAc;IAChB;GACF,OAAO,IAAI,SAAS,WAAW,KAAK,cAAc;IAChD,SAAS,IAAI,YAAY;IACzB,cAAc;IACd,IAAI,MAAM,eAAe,GAAG,SAAS,IAAI,cAAc,MAAM,SAAS,YAAY,CAAC;GACrF;EACF;EACA,OAAO;CACT;CAEA,IAAI,CAAC,UAAU,eAAe,IAAI,GAAG,MAAM,MAAM,WAAW,IAAI,KAAK;CAErE,MAAM,mCAAmB,IAAI,IAAY;CACzC,KAAK,MAAM,CAAC,cAAc,WAAW,UAAU;EAC7C,IAAI,KAAK,WAAW,MAAM,GAAG;EAC7B,MAAM,aAAa,KAAK,QAAQ,WAAW,KAAK,QAAQ,YAAY,GAAG,MAAM;EAC7E,IAAI,CAAC,SAAS,WAAW,UAAU,GAAG;EACtC,MAAM,qBAAqB,KAAK,SAAS,WAAW,UAAU,CAAC,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,GAAG;EACxF,IAAI,SAAS,IAAI,kBAAkB,GAAG,iBAAiB,IAAI,YAAY;CACzE;CACA,OAAO;EAAE;EAAU,UAAU,IAAI,IAAI,SAAS,KAAK,CAAC;EAAG;CAAiB;AAC1E;;;AClLA,SAAgB,aAAa,OAAgB,MAAuB;CAClE,IAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,QAAQ,KAAK,GACpE,MAAM,IAAI,UAAU,GAAG,KAAK,mBAAmB;CACjD,OAAO;AACT;AACA,SAAS,SAAS,MAAc,OAAuB;CACrD,IAAI,WAAW,MAAM,KAAK,EAAE,QAAQ,MAAM,SAAS,EAAE,IAAI,GAAG,UAAU,KAAK;CAC3E,KAAK,IAAI,IAAI,GAAG,KAAK,KAAK,QAAQ,KAAK;EACrC,MAAM,UAAU,CAAC,CAAC;EAClB,KAAK,IAAI,IAAI,GAAG,KAAK,MAAM,QAAQ,KACjC,QAAQ,KAAK,KAAK,IAChB,QAAQ,IAAI,KAAM,GAClB,SAAS,KAAM,GACf,SAAS,IAAI,MAAO,KAAK,IAAI,OAAO,MAAM,IAAI,KAAK,IAAI,EACzD;EACF,WAAW;CACb;CACA,OAAO,SAAS,MAAM;AACxB;AACA,SAAgB,WAAW,OAAgB,SAA4B,MAAoB;CACzF,KAAK,MAAM,OAAO,OAAO,KAAK,KAAK,GAAG;EACpC,IAAI,QAAQ,SAAS,GAAG,GAAG;EAC3B,MAAM,aAAa,QAChB,KAAK,eAAe;GAAE;GAAW,UAAU,SAAS,KAAK,SAAS;EAAE,EAAE,CAAC,CACvE,MAAM,MAAM,UAAU,KAAK,WAAW,MAAM,QAAQ,CAAC,CACrD,MAAM,SAAS,KAAK,YAAY,CAAC,CAAC,EAAE;EACvC,MAAM,IAAI,UACR,uBAAuB,KAAK,GAAG,IAAI,IAAI,aAAa,kBAAkB,KAAK,GAAG,WAAW,MAAM,oBAAoB,QAAQ,KAAK,IAAI,EAAE,IACxI;CACF;AACF;AACA,SAAS,OAAO,OAAgB,MAAyC,MAAoB;CAC3F,IAAI,UAAU,KAAA,KAAa,OAAO,UAAU,MAC1C,MAAM,IAAI,UAAU,GAAG,KAAK,aAAa,MAAM;AACnD;AACA,SAAS,OAAO,OAAgB,SAA6B,MAAoB;CAC/E,IAAI,UAAU,KAAA,KAAa,CAAC,QAAQ,SAAS,KAAK,GAChD,MAAM,IAAI,UAAU,GAAG,KAAK,kBAAkB,QAAQ,KAAK,IAAI,GAAG;AACtE;AACA,SAAS,gBAAgB,OAAgB,MAAoB;CAC3D,IAAI,OAAO,UAAU,YAAY,CAAC,OAAO,cAAc,KAAK,KAAK,QAAQ,GACvE,MAAM,IAAI,UAAU,GAAG,KAAK,iCAAiC;AACjE;AACA,SAAgB,oBAAoB,OAA8C;CAChF,MAAM,OAAO,aAAa,OAAO,SAAS;CAC1C,WAAW,MAAM,CAAC,QAAQ,MAAM,GAAG,SAAS;CAC5C,KAAK,MAAM,CAAC,KAAK,UAAU,SAAS,CAClC;EAAC;EAAQ;EAAS;CAAU,GAC5B;EAAC;EAAQ;EAAU;CAAW,CAChC,GAAY;EACV,MAAM,UAAU,aAAa,KAAK,MAAM,WAAW,KAAK;EACxD,WACE,SACA,QAAQ,aAAa,WAAW,CAAC,YAAY,IAAI,IAAI,CAAC,UAAU,GAChE,WAAW,KACb;EACA,IAAI,QAAQ,aAAa,SAAS,QAAQ,aAAa,UACrD,MAAM,IAAI,UAAU,WAAW,IAAI,2BAA2B,UAAU;EAC1E,IAAI,QAAQ,aAAa,UAAU,gBAAgB,QAAQ,OAAO,WAAW,IAAI,GAAG,MAAM;CAC5F;AACF;AACA,SAAS,UAAU,OAAgB,MAAoB;CACrD,KAAK,MAAM,OAAO,CAAC,WAAW,SAAS,GACrC,IACE,MAAM,SAAS,KAAA,KACf,OAAO,MAAM,SAAS,aACrB,CAAC,MAAM,QAAQ,MAAM,IAAI,KAAK,MAAM,IAAI,CAAC,MAAM,SAAkB,OAAO,SAAS,QAAQ,IAE1F,MAAM,IAAI,UAAU,GAAG,KAAK,GAAG,IAAI,sCAAsC;CAC7E,mBAAmB,MAAM,WAAW;CACpC,OAAO,MAAM,WAAW,YAAY,GAAG,KAAK,WAAW;CACvD,IAAI,MAAM,SAAS,KAAA,GAAW;EAC5B,MAAM,OAAO,aAAa,MAAM,MAAM,GAAG,KAAK,MAAM;EACpD,WAAW,MAAM;GAAC;GAAS;GAAa;GAAoB;EAAU,GAAG,GAAG,KAAK,MAAM;EACvF,OAAO,KAAK,kBAAkB,WAAW,GAAG,KAAK,uBAAuB;EACxE,OAAO,KAAK,OAAO;GAAC;GAAQ;GAAQ;EAAU,GAAG,GAAG,KAAK,YAAY;EACrE,OAAO,KAAK,WAAW,CAAC,OAAO,MAAM,GAAG,GAAG,KAAK,gBAAgB;EAChE,OAAO,KAAK,UAAU;GAAC;GAAW;GAAU;EAAS,GAAG,GAAG,KAAK,eAAe;CACjF;AACF;AACA,SAAS,eAAe,OAAgB,MAAoB;CAC1D,IAAI,OAAO,UAAU,YAAY,CAAC,MAAM,QACtC,MAAM,IAAI,UAAU,GAAG,KAAK,4BAA4B;AAC5D;AACA,SAAgB,cACd,OACA,OAAO,iBACyB;CAChC,IAAI,UAAU,KAAA,GAAW,MAAM,IAAI,UAAU,iCAAiC;CAC9E,MAAM,QAAQ,aAAa,OAAO,IAAI;CACtC,eAAe,MAAM,MAAM,GAAG,KAAK,MAAM;CACzC,IAAI,OAAO,MAAM,WAAW,YAAY,MAAM,IAAI,UAAU,GAAG,KAAK,2BAA2B;CAC/F,OAAO,MAAM,WAAW,YAAY,GAAG,KAAK,WAAW;CACvD,OAAO,MAAM,aAAa,WAAW,GAAG,KAAK,aAAa;CAC1D,IAAI,MAAM,YAAY,KAAA,GAAW;EAC/B,MAAM,UAAU,aAAa,MAAM,SAAS,GAAG,KAAK,SAAS;EAC7D,WAAW,SAAS,CAAC,YAAY,GAAG,GAAG,KAAK,SAAS;EACrD,IAAI,OAAO,QAAQ,eAAe,WAChC,MAAM,IAAI,UAAU,GAAG,KAAK,sCAAsC;CACtE;AACF;AACA,MAAM,eAAe;CACnB,WAAW;CACX,WAAW;CACX,OAAO;CACP,SAAS;CACT,SAAS;CACT,aAAa;CACb,MAAM;CACN,WAAW;CACX,UAAU;CACV,QAAQ;CACR,cAAc;CACd,SAAS;CACT,UAAU;CACV,MAAM;CACN,MAAM;CACN,QAAQ;CACR,YAAY;CACZ,aAAa;CACb,UAAU;AACZ;AACA,SAAgB,wBAAwB,OAAkD;CACxF,MAAM,UAAU,aAAa,OAAO,SAAS;CAC7C,WAAW,SAAS,OAAO,KAAK,YAAY,GAAG,SAAS;CACxD,eAAe,QAAQ,WAAW,mBAAmB;CACrD,eAAe,QAAQ,WAAW,mBAAmB;CACrD,cAAc,QAAQ,KAAK;CAC3B,UAAU,SAAS,SAAS;CAC5B,KAAK,MAAM,OAAO,CAAC,YAAY,UAAU,GAAG,OAAO,QAAQ,MAAM,YAAY,WAAW,KAAK;CAC7F,KAAK,MAAM,OAAO,CAAC,UAAU,QAAQ,GAAG,OAAO,QAAQ,MAAM,WAAW,WAAW,KAAK;CACxF,OAAO,QAAQ,MAAM,UAAU,cAAc;CAC7C,OAAO,QAAQ,MAAM,CAAC,OAAO,KAAK,GAAG,cAAc;CACnD,OACE,QAAQ,aACR,OAAO,KAAK;EACV,MAAM;EACN,aAAa;EACb,UAAU;CACZ,CAAqE,GACrE,qBACF;CACA,OAAO,QAAQ,cAAc,CAAC,SAAS,SAAS,GAAG,sBAAsB;CACzE,IACE,QAAQ,eAAe,KAAA,KACvB,OAAO,QAAQ,eAAe,YAC9B,OAAO,QAAQ,eAAe,YAE9B,MAAM,IAAI,UAAU,iDAAiD;CACvE,IAAI,QAAQ,YAAY,KAAA,KAAa,OAAO,QAAQ,YAAY,YAC9D,oBAAoB,QAAQ,OAAO;CACrC,IAAI,QAAQ,aAAa,KAAA,GAAW;EAClC,MAAM,QAAQ,aAAa,QAAQ,UAAU,kBAAkB;EAC/D,WAAW,OAAO;GAAC;GAAU;GAAY;EAAS,GAAG,kBAAkB;EACvE,OAAO,MAAM,QAAQ,WAAW,yBAAyB;EACzD,OAAO,MAAM,UAAU,CAAC,QAAQ,UAAU,GAAG,2BAA2B;EACxE,OAAO,MAAM,SAAS,CAAC,SAAS,MAAM,GAAG,0BAA0B;CACrE;AACF;AACA,MAAM,cAAc;CAClB,MAAM;CACN,SAAS;CACT,SAAS;CACT,aAAa;CACb,MAAM;CACN,WAAW;AACb;AACA,SAAgB,yBAAyB,OAA0D;CACjG,MAAM,UAAU,aAAa,OAAO,SAAS;CAC7C,WAAW,SAAS,OAAO,KAAK,WAAW,GAAG,SAAS;CACvD,eAAe,QAAQ,MAAM,cAAc;CAC3C,UAAU,SAAS,SAAS;AAC9B;;;AC9KA,SAAS,UAAU,OAAyB;CAC1C,IAAI,MAAM,YAAY,GAAG,OAAO;CAChC,IAAI,MAAM,OAAO,GAAG,OAAO;CAC3B,IAAI,MAAM,eAAe,GAAG,OAAO;CACnC,OAAO;AACT;AAEA,SAAS,SAAS,OAA4B;CAC5C,OAAO;EACL,QAAQ,MAAM;EACd,SAAS,MAAM;EACf,eAAe,MAAM;EACrB,OAAO,MAAM;EACb,MAAM,MAAM;EACZ,SAAS,MAAM;EACf,MAAM,MAAM;EACZ,OAAO;GACL,YAAY,MAAM,MAAM,YAAY;GACpC,WAAW,MAAM,MAAM,YAAY;GACnC,WAAW,MAAM,UAAU,YAAY;GACvC,YAAY,MAAM,MAAM,YAAY;EACtC;CACF;AACF;AAEA,SAAgB,aAAa,UAAkB,eAAgC;CAC7E,MAAM,WAAW,KAAK,SAAS,UAAU,aAAa;CACtD,OACE,aAAa,MACZ,aAAa,QAAQ,CAAC,SAAS,WAAW,KAAK,KAAK,KAAK,KAAK,CAAC,KAAK,WAAW,QAAQ;AAE5F;AAEA,eAAsB,cAAc,EAClC,cACA,cACA,cACA,WACA,gBACA,iBAQ2B;CAC3B,MAAM,YAAY,MAAM,MAAM,YAAY;CAC1C,MAAM,OAAO,UAAU,SAAS;CAChC,IAAI,UAAkC;CAEtC,IAAI,SAAS,WAAW;EACtB,MAAM,SAAS,MAAM,SAAS,YAAY;EAC1C,IAAI,eAA8B;EAClC,IAAI,aAAmD;EACvD,IAAI,aAA4B;EAChC,IAAI;GACF,eAAe,MAAM,SAAS,YAAY;GAC1C,MAAM,cAAc,MAAM,KAAK,YAAY;GAC3C,aAAa,UAAU,WAAW;GAClC,aAAa,YAAY;EAC3B,QAAQ,CAER;EACA,MAAM,gBAAgB,iBAAiB,QAAQ,CAAC,aAAa,cAAc,YAAY;EACvF,MAAM,UAAU,iBAAiB,QAAQ,UAAU,IAAI,YAAY;EACnE,MAAM,qBACJ,iBAAiB,QAAQ,CAAC,gBACtB,KAAK,SAAS,cAAc,YAAY,CAAC,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,GAAG,IAClE;EACN,MAAM,mBACJ,uBAAuB,QACvB,kBAAkB,QAClB,CAAC,cAAc,IAAI,kBAAkB;EACvC,UAAU;GACR;GACA;GACA;GACA;GACA,YACE,iBAAiB,oBAAoB,eAAe,cAAc,OAAO;GAC3E,UAAU,iBAAiB;GAC3B;GACA;GACA;GACA,aACE,kBACA,eAAe,eACf,CAAC,WACD,CAAC,iBACD,CAAC;EACL;CACF;CAEA,OAAO;EACL,MAAM,KAAK,SAAS,YAAY;EAChC;EACA;EACA;EACA,UAAU,SAAS,SAAS;EAC5B;CACF;AACF;;;AC7BA,SAAgB,oBACd,OACA,SACc;CACd,MAAM,OAAO,MAAM;CACnB,OAAO;EACL,GAAI,MAAM,eAAe,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM,eAAe,MAAM,UAAU,EAAE;EACnF,IAAI,MAAM;EACV,MAAM,MAAM;EACZ,MAAM,MAAM;EACZ,MAAM,QAAQ,QAAQ,KAAK;EAC3B,eAAe,QAAQ,iBAAiB,KAAK;EAC7C,MAAM,MAAM,SAAS;EACrB,YAAY,MAAM,SAAS,MAAM;EACjC,MACE,SAAS,OACL,OACA;GACE,QAAQ,KAAK;GACb,YAAY,KAAK;GACjB,YAAY,KAAK,cAAc;GAC/B,aAAa,KAAK,YAAY,KAAK,iBAAiB,QAAQ,KAAK,gBAAgB;GACjF,OAAO,KAAK;EACd;CACR;AACF;;AAGA,SAAgB,eACd,OACwE;CACxE,MAAM,EACJ,MAAM,OACN,eAAe,QACf,MAAM,OACN,GAAG,UACD,oBAAoB,OAAO;EAAE,eAAe;EAAM,wBAAwB;CAAM,CAAC;CACrF,OAAO;AACT;AAEA,SAAgB,gBACd,SACA,MACA,UACA,WACQ;CACR,OAAO,WAAW,QAAQ,CAAC,CACxB,OAAO,KAAK,UAAU;EAAE,SAAS;EAAG;EAAS;EAAM;EAAU;CAAU,CAAC,CAAC,CAAC,CAC1E,OAAO,KAAK;AACjB;;;AC1HA,MAAa,eAAe;AAC5B,MAAa,uBAAuB;AAEpC,eAAsB,WAAW,QAAgB;CAC/C,MAAM,YAAY,MAAM,cAAc,MAAM;CAC5C,MAAM,WAAW,KAAK,KAAK,KAAK,QAAQ,SAAS,GAAG,IAAI,KAAK,SAAS,SAAS,EAAE,cAAc;CAC/F,MAAM,MAAM,KAAK,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;CACvD,IAAI;CACJ,IAAI;EACF,SAAS,MAAM,KAAK,UAAU,IAAI;CACpC,SAAS,OAAO;EACd,IAAI,iBAAiB,SAAS,UAAU,SAAS,MAAM,SAAS,UAC9D,MAAM,IAAI,MACR,sCAAsC,SAAS,yHACjD;EACF,MAAM;CACR;CACA,IAAI;EACF,MAAM,OAAO,UAAU,KAAK,UAAU,EAAE,KAAK,QAAQ,IAAI,CAAC,CAAC;CAC7D,SAAS,OAAO;EACd,MAAM,OAAO,MAAM;EACnB,MAAM,GAAG,QAAQ;EACjB,MAAM;CACR;CACA,OAAO;EACL;EACA,MAAM,UAAU;GACd,MAAM,OAAO,MAAM;GACnB,MAAM,GAAG,QAAQ;EACnB;CACF;AACF;AAEA,eAAsB,cAAc,UAAmC;CACrE,IAAI;EACF,OAAO,MAAM,SAAS,QAAQ;CAChC,SAAS,OAAO;EACd,IAAI,EAAE,iBAAiB,SAAS,UAAU,SAAS,MAAM,SAAS,WAAW,MAAM;EACnF,MAAM,SAAS,KAAK,QAAQ,QAAQ;EACpC,IAAI,WAAW,UAAU,MAAM;EAC/B,OAAO,KAAK,KAAK,MAAM,cAAc,MAAM,GAAG,KAAK,SAAS,QAAQ,CAAC;CACvE;AACF;;AAGA,eAAsB,YACpB,QACA,QACA,SAA8B,SACZ;CAClB,IAAI,WAAW,WAAW,WAAW,WACnC,MAAM,IAAI,UAAU,uCAAuC;CAC7D,IAAI,aAAa,MAAM,cAAc,MAAM,GAAG,MAAM,SAAS,MAAM,CAAC,GAClE,MAAM,IAAI,MAAM,0CAA0C;CAC5D,IAAI;CACJ,IAAI;EACF,OAAO,MAAM,MAAM,MAAM;CAC3B,SAAS,OAAO;EACd,IAAI,iBAAiB,SAAS,UAAU,SAAS,MAAM,SAAS,UAAU,OAAO;EACjF,MAAM;CACR;CACA,IAAI,KAAK,eAAe,KAAK,CAAC,KAAK,YAAY,GAC7C,MAAM,IAAI,MAAM,sDAAsD;CACxE,KAAK,MAAM,QAAQ,MAAM,EAAA,CAAG,WAAW,KAAK,WAAW,WAAW,OAAO;CACzE,IAAI;EACF,MAAM,SAAS,KAAK,KAAK,QAAQ,YAAY;EAC7C,IACE,EAAE,MAAM,MAAM,KAAK,QAAQ,MAAM,CAAC,EAAA,CAAG,eAAe,KACpD,EAAE,MAAM,MAAM,MAAM,EAAA,CAAG,eAAe,KACrC,MAAM,SAAS,QAAQ,MAAM,MAAA,iDAE9B,OAAO;CACX,SAAS,OAAO;EACd,IAAI,EAAE,iBAAiB,SAAS,UAAU,SAAS,MAAM,SAAS,WAAW,MAAM;CACrF;CACA,MAAM,IAAI,MACR,6IACF;AACF;;AAGA,eAAsB,cACpB,QACA,QACA,QACA,SAA8B,SACf;CACf,MAAM,SAAS,MAAM,YAAY,QAAQ,QAAQ,MAAM;CACvD,MAAM,MAAM,KAAK,QAAQ,MAAM,GAAG,EAAE,WAAW,KAAK,CAAC;CACrD,MAAM,SAAS,KAAK,KAClB,KAAK,QAAQ,MAAM,GACnB,IAAI,KAAK,SAAS,MAAM,EAAE,YAAY,WAAW,GACnD;CACA,IAAI,QAAQ,MAAM,OAAO,QAAQ,MAAM;CACvC,IAAI;EACF,MAAM,OAAO,QAAQ,MAAM;CAC7B,SAAS,OAAO;EACd,IAAI,QACF,IAAI;GACF,MAAM,OAAO,QAAQ,MAAM;EAC7B,SAAS,cAAc;GACrB,MAAM,IAAI,eACR,CAAC,OAAO,YAAY,GACpB,sDAAsD,QACxD;EACF;EAEF,MAAM;CACR;CACA,IAAI,QAAQ,MAAM,GAAG,QAAQ;EAAE,WAAW;EAAM,OAAO;CAAK,CAAC;AAC/D;;;ACtEA,MAAM,eAAe;AAErB,MAAM,cAAc;CAClB,WAAW;CACX,kBAAkB;CAClB,OAAO;CACP,UAAU;AACZ;AAEA,MAAa,qBAAyC,EAAE,cACtD,QAAQ,MAAM,UAAU,MAAM,SAAS,UAAU,aAAa,KAAK,MAAM,IAAI,CAAC,IAC1E,QAAQ,MAAM,UAAU,MAAM,KAAK,YAAY,MAAM,eAAe,IAClE,OACA,kBACF;AAEN,SAAS,qBAAqB,MAAoB;CAChD,IAAI,KAAK,WAAW,KAAK,KAAK,SAAS,IAAI,MAAM,QAAQ,SAAS,OAAO,SAAS,MAChF,MAAM,IAAI,MAAM,+CAA+C,MAAM;AAEzE;AAEA,SAAS,cAAc,OAAuB;CAC5C,IAAI,MAAM,SAAS,GAAG,KAAK,MAAM,SAAS,GAAG,GAC3C,MAAM,IAAI,UAAU,kDAAkD;CAExE,IAAI,gBAAgB,KAAK,KAAK,GAAG;EAC/B,MAAM,MAAM,IAAI,IAAI,KAAK;EACzB,IAAI,WAAW,GAAG,IAAI,SAAS,QAAQ,QAAQ,EAAE,EAAE;EACnD,OAAO,IAAI;CACb;CACA,IAAI,CAAC,MAAM,WAAW,GAAG,GACvB,MAAM,IAAI,UAAU,0DAAwD;CAE9E,OAAO,GAAG,MAAM,QAAQ,QAAQ,EAAE,EAAE;AACtC;AAEA,SAAS,kBAAkB,OAAuB;CAChD,OAAO,MACJ,MAAM,GAAG,CAAC,CACV,QAAQ,YAAY,QAAQ,SAAS,CAAC,CAAC,CACvC,KAAK,YAAY,mBAAmB,OAAO,CAAC,CAAC,CAC7C,KAAK,GAAG;AACb;AAEA,SAAS,gBAAgB,OAAiC;CACxD,OAAO,MAAM,SAAS,eAAe,MAAM,SAAS,eAAe;AACrE;AAEA,SAAS,qBAAqB,OAAiC;CAC7D,OAAO,QACL,MAAM,SAAS,YAAY,MAAM,SAAS,iBAAiB,MAAM,SAAS,gBAC5E;AACF;AAEA,SAAS,eAAe,MAAc,OAAuB;CAC3D,MAAM,aAAa,CAAC,GAAG,IAAI;CAC3B,MAAM,cAAc,CAAC,GAAG,KAAK;CAC7B,MAAM,SAAS,KAAK,IAAI,WAAW,QAAQ,YAAY,MAAM;CAC7D,KAAK,IAAI,QAAQ,GAAG,QAAQ,QAAQ,SAAS,GAAG;EAC9C,MAAM,cACH,WAAW,MAAM,EAAE,YAAY,CAAC,KAAK,MAAM,YAAY,MAAM,EAAE,YAAY,CAAC,KAAK;EACpF,IAAI,eAAe,GAAG,OAAO;CAC/B;CACA,OAAO,WAAW,SAAS,YAAY;AACzC;AAEA,SAAS,aAAa,MAAc,OAAe,MAA4B;CAC7E,IAAI,SAAS,WAAW,OAAO,eAAe,MAAM,KAAK;CACzD,OAAO,KAAK,cAAc,OAAO,KAAA,GAAW;EAC1C,SAAS,SAAS;EAClB,aAAa;CACf,CAAC;AACH;AAEA,SAAgB,eACd,MACA,OACA,UAAuB,CAAC,GAChB;CACR,MAAM,WAAW;EAAE,GAAG;EAAa,GAAG;CAAQ;CAC9C,IAAI,SAAS,oBAAoB,gBAAgB,IAAI,MAAM,gBAAgB,KAAK,GAC9E,OAAO,gBAAgB,IAAI,IAAI,KAAK;CAEtC,IAAI;CACJ,IAAI,SAAS,UAAU,YACrB,SAAS,KAAK,SAAS,MAAM,WAAW,cAAc,MAAM,SAAS,MAAM,UAAU;MAChF,IAAI,SAAS,UAAU,QAC5B,UACG,KAAK,SAAS,cAAc,IAAI,KAAK,SAAS,SAC9C,MAAM,SAAS,cAAc,IAAI,MAAM,SAAS;MAEnD,SAAS,aAAa,KAAK,MAAM,MAAM,MAAM,SAAS,QAAQ;CAEhE,IAAI,WAAW,GAAG,SAAS,aAAa,KAAK,MAAM,MAAM,MAAM,SAAS,QAAQ;CAChF,OAAO,SAAS,cAAc,QAAQ,SAAS,CAAC;AAClD;AAEA,SAAS,gBAAgB,OAAgC;CACvD,OAAO,GAAG,WAAW,QAAQ,CAAC,CAC3B,OAAO,MAAM,YAAY,CAAC,CAC1B,OAAO,IAAI,CAAC,CACZ,OAAO,MAAM,SAAS,UAAU,EAAE,CAAC,CACnC,OAAO,KAAK,CAAC,CACb,MAAM,GAAG,EAAE,EAAE;AAClB;AAEA,eAAsB,iBAAiB,SAAgD;CACrF,OAAO,8BAA8B,SAAS,CAAC,CAAC;AAClD;;AAGA,eAAsB,8BACpB,SACA,cACsB;CACtB,wBAAwB,OAAO;CAC/B,MAAM,SAAS,KAAK,QAAQ,QAAQ,SAAS,GAC3C,SAAS,KAAK,QAAQ,QAAQ,SAAS;CACzC,MAAM,YAAY,QAAQ,QAAQ,QAAQ,YAAY;CACtD,IAAI,QAAQ,QAAQ,OAAO,yBAAyB,SAAS,YAAY;CACzE,MAAM,OAAO,MAAM,WAAW,MAAM;CACpC,MAAM,cAAc,KAAK,KAAK,KAAK,QAAQ,MAAM,GAAG,KAAK,SAAS,KAAK,QAAQ,CAAC;CAChF,IAAI;EACF,OAAO,MAAM,yBAAyB,SAAS;GAAC,GAAG;GAAc,KAAK;GAAU;EAAW,CAAC;CAC9F,UAAU;EACR,MAAM,KAAK,QAAQ;CACrB;AACF;AAEA,eAAe,yBACb,SACA,cACsB;CACtB,MAAM,YAAY,MAAM,SAAS,KAAK,QAAQ,QAAQ,SAAS,CAAC;CAChE,MAAM,YAAY,MAAM,cAAc,KAAK,QAAQ,QAAQ,SAAS,CAAC;CACrE,MAAM,qBAAqB,aAAa,KAAK,UAAU,KAAK,QAAQ,KAAK,CAAC;CAC1E,MAAM,eAAe,MAAM,SAAS,SAAS;CAC7C,MAAM,iBAAiB,MAAM,YAAY,WAAW,WAAW,QAAQ,YAAY;CACnF,MAAM,aAAa,QAAQ,cAAc;CACzC,MAAM,oBACJ,OAAO,eAAe,iBAAiB,aAAa;CACtD,MAAM,QAAuB,QAAQ;CACrC,IAAI,UAAU,KAAA,GAAW,MAAM,IAAI,UAAU,iCAAiC;CAC9E,MAAM,OAAO,QAAQ,QAAQ;CAC7B,MAAM,cAAc,QAAQ,eAAe;CAC3C,MAAM,OAAO,cAAc,QAAQ,QAAQ,GAAG;CAC9C,MAAM,iBAAiB,QAAQ,UAAU,UAAU;CACnD,MAAM,WAAW,QAAQ,UAAU,YAAY;CAC/C,MAAM,UAAU,QAAQ,UAAU,WAAW;CAC7C,MAAM,gBACJ,QAAQ,YAAY,KAAA,KACpB,QAAQ,YAAY,KAAA,MACnB,QAAQ,aAAa,UAAU,KAAK,KACrC,mBAAmB,SAAS;CAC9B,MAAM,QAID,CAAC;CACN,IAAI,aAAa,WAAW,SAAS,GACnC,MAAM,IAAI,MAAM,0CAA0C;CAE5D,IAAI;EACF,MAAM,MAAM,KAAK,KAAK,WAAW,WAAW,CAAC;EAC7C,MAAM,IAAI,MAAM,qDAAmD;CACrE,SAAS,OAAO;EACd,IAAI,iBAAiB,SAAS,UAAU,SAAS,MAAM,SAAS,UAAU,CAE1E,OACE,MAAM;CAEV;CACA,MAAM,uBAAuB,aAAa,WAAW,SAAS;CAC9D,MAAM,YAAY,MAAM,kBACtB,WACA,WACA,QAAQ,SACR,QAAQ,SACR,oBACA,QAAQ,WACV;CACA,MAAM,cAAc,uBAAuB,KAAK,QAAQ,SAAS,IAAI,KAAK,QAAQ,SAAS;CAC3F,IAAI,CAAC,QAAQ,QAAQ,MAAM,MAAM,aAAa,EAAE,WAAW,KAAK,CAAC;CACjE,MAAM,kBAAkB,QAAQ,SAC5B,OACA,MAAM,QAAQ,KAAK,KAAK,aAAa,IAAI,KAAK,SAAS,SAAS,EAAE,UAAU,CAAC;CACjF,MAAM,iBAAiB;CAEvB,IAAI;EACF,IAAI,CAAC,QAAQ,WAAW,QAAQ,UAAU,OAAO;GAC/C,MAAM,GAAG,gBAAgB;IAAE,WAAW;IAAM,OAAO;GAAK,CAAC;GACzD,MAAM,MAAM,gBAAgB,EAAE,WAAW,KAAK,CAAC;GAC/C,MAAM,GAAG,WAAW,gBAAgB;IAClC,WAAW;IACX,aAAa;IACb,oBAAoB;IACpB,SAAS,eAAe;KACtB,MAAM,eAAe,KAAK,SAAS,WAAW,UAAU,CAAC,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,GAAG;KAClF,OACE,UAAU,SAAS,IAAI,YAAY,MAClC,CAAC,UAAU,SAAS,IAAI,YAAY,KAAK,UAAU,iBAAiB,IAAI,YAAY;IAEzF;GACF,CAAC;EACH;CACF,SAAS,OAAO;EACd,IAAI,oBAAoB,MAAM,MAAM,GAAG,iBAAiB;GAAE,WAAW;GAAM,OAAO;EAAK,CAAC;EACxF,MAAM;CACR;CAEA,eAAe,MACb,YACA,aACA,WACe;EACf,MAAM,mBAAmB,MAAM,SAAS,WAAW;EACnD,IAAI,UAAU,IAAI,gBAAgB,GAAG;GACnC,IAAI,YAAY,SAAS,MAAM,IAAI,MAAM,oBAAoB,YAAY;GACzE;EACF;EAEA,MAAM,gBAAgB,IAAI,IAAI,SAAS,CAAC,CAAC,IAAI,gBAAgB;EAC7D,MAAM,SAAS,MAAM,QAAQ,WAAW,EAAA,CAAG,QAAQ,SAAS;GAC1D,IAAI,wBAAwB,aAAa,WAAW,KAAK,KAAK,aAAa,IAAI,CAAC,GAC9E,OAAO;GACT,IAAI,mBAAmB,MAAM,UAAU,aAAa,OAAO,KAAK,KAAK,aAAa,IAAI,CAAC,CAAC,GACtF,OAAO;GACT,IAAI,CAAC,eAAe,OAAO;GAC3B,MAAM,eAAe,KAAK,SAAS,cAAc,KAAK,KAAK,kBAAkB,IAAI,CAAC;GAClF,OAAO,UAAU,SAAS,IAAI,aAAa,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,GAAG,CAAC;EACtE,CAAC;EACD,MAAM,UAAU,MAAM,QAAQ,IAC5B,MAAM,IAAI,OAAO,SAAS;GACxB,MAAM,QAAQ,MAAM,cAAc;IAChC,cAAc,KAAK,KAAK,aAAa,IAAI;IACzC,cAAc,KAAK,MAAM,KAAK,YAAY,IAAI;IAC9C;IACA,WAAW;IACX;IACA,eAAe,gBAAgB,UAAU,WAAW;GACtD,CAAC;GACD,OAAO,QAAQ,cAAc,KAAA,IACzB,QACA;IACE,GAAG;IACH,YAAY,eACV,MAAM,QAAQ,UAAU,eAAe,KAAK,GAAG;KAC7C,WAAW;KACX,eAAe;IACjB,CAAC,CACH;GACF;EACN,CAAC,CACH;EACA,QAAQ,MACL,MAAM,UACL,eAAe,MAAM,OAAO,QAAQ,IAAI,MACvC,QAAQ,YAAY,KAAA,IAAY,IAAI,eAAe,KAAK,cAAc,MAAM,YAAY,EAC7F;EAEA,MAAM,mBAAmB,MAAM,cAAc;GAC3C,cAAc;GACd,cAAc;GACd;GACA;GACA;GACA,eAAe,gBAAgB,UAAU,WAAW;EACtD,CAAC;EACD,IAAI,iBAAiB,SAAS,aAC5B,MAAM,IAAI,MAAM,GAAG,YAAY,oBAAoB;EAErD,MAAM,UAAoC;GACxC,GAAG;GACH,MAAM;EACR;EAsBA,MAAM,YAA2B;GAC/B,MAAA,MArBiB,cAAc;IAC/B,cAAc;IACd,cAAc;IACd;IACA,2BAAW,IAAI,IAAI;IACnB;IACA,eAAe,gBAAgB,UAAU,WAAW;GACtD,CAAC;GAeC;GACA,QAdA,eAAe,KACX,OACA,MAAM,cAAc;IAClB,cAAc,KAAK,QAAQ,WAAW;IACtC,cACE,KAAK,MAAM,QAAQ,UAAU,MAAM,MAAM,KAAK,KAAK,MAAM,QAAQ,UAAU;IAC7E;IACA;IACA;IACA,eAAe,gBAAgB,UAAU,WAAW;GACtD,CAAC;GAKL,OAAO,eAAe,KAAK,IAAI,WAAW,MAAM,GAAG,CAAC,CAAC;GACrD;EACF;EAEA,MAAM,OAAO,MAAM,kBAAkB,SAAS;EAC9C,IAAI,SAAS,MAAM,qBAAqB,IAAI;EAC5C,MAAM,KAAK;GAAE;GAAW;GAAY,YAAY;EAAK,CAAC;EAEtD,KAAK,MAAM,SAAS,SAAS;GAC3B,IAAI,MAAM,SAAS,aAAa;IAC9B,MAAM,MAAM,KAAK,MAAM,KAAK,YAAY,MAAM,IAAI,GAAG,MAAM,cAAc,aAAa;IACtF;GACF;GACA,IAAI,CAAC,kBAAkB,MAAM,SAAS,eAAe,aAAa;GAClE,MAAM,mBAAmB,KACtB,SAAS,cAAc,KAAK,KAAK,kBAAkB,MAAM,IAAI,CAAC,CAAC,CAC/D,MAAM,KAAK,GAAG,CAAC,CACf,KAAK,GAAG;GACX,KAAK,QAAQ,UAAU,SAAS,CAAC,UAAU,iBAAiB,IAAI,gBAAgB,GAAG;GACnF,IAAI,MAAM,QAAQ,SAAS;IACzB,IAAI,YAAY,SAAS,MAAM,IAAI,MAAM,oBAAoB,MAAM,cAAc;IACjF;GACF;GACA,IAAI,MAAM,QAAQ,iBAAiB,MAAM;GACzC,IAAI,aAAa,UAAU,MAAM,QAAQ,eAAe;GACxD,IACE,kBACC,MAAM,QAAQ,uBAAuB,QACpC,CAAC,UAAU,SAAS,IAAI,MAAM,QAAQ,kBAAkB,IAE1D;GAGF,KACG,QAAQ,UAAU,SACnB,MAAM,QAAQ,uBAAuB,QACrC,CAAC,MAAM,QAAQ,eAEf;GACF,MAAM,MACJ,KAAK,MAAM,KAAK,YAAY,MAAM,IAAI,GACtC,MAAM,QAAQ,cACd,aACF;EACF;CACF;CAEA,IAAI;EACF,MAAM,MAAM,IAAI,2BAAW,IAAI,IAAI,CAAC;EAEpC,MAAM,SAAsB;GAC1B;GACA;GACA,OAAO,MAAM;GACb,QAAQ,QAAQ,UAAU;GAC1B,QAAQ,QAAQ,UAAU;GAC1B;GACA,eAAe,CAAC,GAAG,UAAU,QAAQ,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK;GAC5D,OAAO,MAAM,KAAK,UAAU;IAAE,WAAW,KAAK;IAAY,YAAY,KAAK;GAAW,EAAE;EAC1F;EACA,IAAI,QAAQ,QAAQ,OAAO;EAE3B,MAAM,iBAAiB,IAAI,IACzB,MACG,QACE,SAAgE,KAAK,eAAe,IACvF,CAAC,CACA,KAAK,SAAS,CAAC,KAAK,YAAY,KAAK,UAAU,CAAC,CACrD;EACA,MAAM,uBAAuB,IAAI,IAAI,eAAe,KAAK,CAAC;EAC1D,MAAM,wBAAwB,gBAAgC;GAC5D,MAAM,WAAW,eAAe,IAAI,WAAW;GAC/C,OAAO,aAAa,KAAA,KAAa,aAAa,eAC1C,cACA,KAAK,MAAM,KAAK,aAAa,QAAQ;EAC3C;EACA,MAAM,8BAAc,IAAI,IAAoB;EAC5C,KAAK,MAAM,EAAE,eAAe,OAC1B,KAAK,MAAM,SAAS,UAAU,SAAS;GACrC,MAAM,aAAa,KAAK,SAAS,WAAW,MAAM,YAAY,CAAC,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,GAAG;GACxF,IAAI,UAAU,SAAS,IAAI,UAAU,GAAG,YAAY,IAAI,YAAY,MAAM,IAAI;EAChF;EACF,MAAM,mBAAmB,MAAM,QAAQ,WAAW;GAAE;GAAW;EAAY,CAAC;EAC5E,MAAM,+BAAe,IAAI,IAAiC;EAC1D,MAAM,+BAAe,IAAI,IAAoB;EAC7C,MAAM,gCAAgB,IAAI,IAiBxB;EAEF,MAAM,4BAAY,IAAI,IAA2B;EACjD,MAAM,kBAAkB,gBAAuC;GAC7D,IAAI,CAAC,UAAU,IAAI,WAAW,GAAG;IAC/B,MAAM,OAAO,QAAQ,SAAU,WAAW;IAC1C,IACE,SAAS,SACR,OAAO,SAAS,YACf,KAAK,WAAW,KAChB,KAAK,SAAS,IAAI,KAClB,MAAM,KAAK,IAAI,CAAC,CAAC,MAAM,cAAc,UAAU,WAAW,CAAC,IAAI,EAAE,KACjE,CAAC,CAAC,SAAS,QAAQ,CAAC,CAAC,SAAS,IAAI,IAAI,MAAM,0BAA0B,CAAC,CAAC,QAAQ,IAElF,MAAM,IAAI,UACR,oFACF;IACF,UAAU,IAAI,aAAa,IAAI;GACjC;GACA,OAAO,UAAU,IAAI,WAAW;EAClC;EAEA,KAAK,MAAM,EAAE,WAAW,gBAAgB,MAAM,gBAAgB,QAAQ,CAAC,IAAI,OACzE,KAAK,MAAM,SAAS,UAAU,SAAS;GACrC,IAAI,cAAc,IAAI,MAAM,YAAY,GAAG;GAC3C,MAAM,oBAAoB,qBAAqB,KAAK,IAChD,OACA,MAAM,SAAS,uBAAuB,QAAQ,MAAM,YAAY,OAC9D,MAAM,QAAQ,qBACd,KAAK,MAAM,KAAK,YAAY,MAAM,IAAI;GAC5C,MAAM,gBAAgB,gBAAgB,KAAK;GAC3C,MAAM,aACJ,iBAAiB,sBAAsB,OACnC,qBAAqB,iBAAiB,IACtC;GACN,IAAI,OAAsB;GAC1B,IAAI,MAAM,YAAY,QAAQ,qBAAqB,KAAK,GAAG;IACzD,MAAM,WAAW,gBAAgB,KAAK;IACtC,aAAa,IAAI,UAAU,MAAM,QAAQ,MAAM;IAC/C,OAAO,aAAa;GACtB,OAAO,IAAI,eAAe,MAAM;IAC9B,OACE,eAAe,KACX,QACA,MAAM,kBAAkB,UAAU,IAAI,iBAAiB,eAAe,oBAAoB,MAAM;IACtG,IAAI,CAAC,iBAAiB,QAAQ,aAAa,KAAA,GAAW,OAAO,eAAe,UAAU;GACxF;GACA,cAAc,IAAI,MAAM,cAAc;IACpC,eACE,qBAAqB,KAAK,KACzB,sBAAsB,SACpB,CAAC,iBAAiB,CAAC,qBAAqB,IAAI,iBAAiB;IAClE;IACA,QAAQ,MAAM,SAAS;IACvB,MAAM,MAAM;IACZ,YAAY,MAAM,SAAS,MAAM;IACjC,MAAM,MAAM;IACZ,MAAM,MAAM;IACZ,MAAM,MAAM,SAAS;IACrB,QAAQ,MAAM,SAAS,UAAU;IACjC,YAAY,MAAM,SAAS,cAAc;IACzC,YAAY,MAAM,SAAS,cAAc;IACzC,qBAAqB,qBAAqB,KAAK;IAC/C,SAAS,MAAM,SAAS,WAAW;GACrC,CAAC;EACH;EAGF,KAAK,MAAM,QAAQ,OAAO;GACxB,MAAM,EAAE,WAAW,YAAY,YAAY,SAAS;GACpD,MAAM,mBAAmB,IAAI,IAAI,UAAU,QAAQ,KAAK,UAAU,MAAM,KAAK,YAAY,CAAC,CAAC;GAC3F,IAAI,SAAS,MAAM;GAEnB,MAAM,wBAAwB,UAA0C;IACtE,IAAI,qBAAqB,KAAK,GAAG,OAAO;IACxC,IAAI,MAAM,SAAS,uBAAuB,QAAQ,MAAM,YAAY,MAClE,OAAO,MAAM,QAAQ;IAEvB,OAAO,KAAK,MAAM,KAAK,YAAY,MAAM,IAAI;GAC/C;GACA,MAAM,sBAAsB,YAAoB,kBAAmC;IACjF,MAAM,aAAa,gBAAgB,qBAAqB,UAAU,IAAI;IACtE,MAAM,kBAAkB,iBAAiB,eAAe;IACxD,IAAI,gBAAgB,YAWlB,OAAO,GAVgB,KAAK,MAAM,SAChC,eAAe,KAAK,MAAM,YAC1B,eAAe,KAAK,MAAM,UAEO,CAAC,CACjC,MAAM,GAAG,CAAC,CACV,KAAK,YACJ,YAAY,OAAO,YAAY,OAAO,UAAU,mBAAmB,OAAO,CAC5E,CAAC,CACA,KAAK,GACc,KAAK,MAAM,kBAAkB,MAAM;IAE3D,MAAM,gBAAgB,kBAAkB,UAAU;IAClD,IAAI,kBAAkB,IACpB,OAAO,gBAAgB,SAAS,OAAO;IAEzC,MAAM,SAAS,GAAG,gBAAgB,kBAAkB,MAAM;IAC1D,OAAO,gBAAgB,SAAS,GAAG,OAAO,WAAW;GACvD;GACA,MAAM,WAAW,UAA0C;IACzD,IAAI,MAAM,YAAY,QAAQ,qBAAqB,KAAK,GAAG;KACzD,MAAM,WAAW,gBAAgB,KAAK;KACtC,aAAa,IAAI,UAAU,MAAM,QAAQ,MAAM;KAC/C,OAAO,mBAAmB,KAAK,MAAM,KAAK,aAAa,aAAa,QAAQ,GAAG,KAAK;IACtF;IACA,MAAM,oBAAoB,qBAAqB,KAAK;IACpD,IAAI,sBAAsB,MAAM,OAAO;IACvC,MAAM,gBACJ,MAAM,SAAS,eAAe,MAAM,SAAS,eAAe;IAC9D,IAAI,CAAC,iBAAiB,QAAQ,aAAa,KAAA,GACzC,OAAO,eAAe,iBAAiB;IACzC,OAAO,mBAAmB,mBAAmB,aAAa;GAC5D;GACA,MAAM,cAA4B;IAChC,WAAW;IACX;IACA,kBAAkB,gBAAgB,cAAc,OAAO;IACvD,YAAY;IACZ;IACA,iBAAiB,mBACf,KAAK,MAAM,KAAK,aAAa,mBAAmB,GAChD,KACF;IACA,MAAM;KAAE,GAAG;KAAa,GAAG,QAAQ;IAAK;IACxC,YAAY,cAAc;KACxB,qBAAqB,SAAS;KAC9B,OAAO,mBACL,SAAS,QACL,KAAK,MAAM,KAAK,YAAY,SAAS,IACrC,KAAK,MAAM,KAAK,aAAa,SAAS,GAC1C,KACF;IACF;IACA;IACA,mBAAmB,iBAAiB,mBAAmB,cAAc,IAAI;IACzE,sBAAsB,iBAAiB,qBAAqB,IAAI,YAAY;IAC5E,mBAAmB,UAAU;KAC3B,IAAI,qBAAqB,KAAK,GAAG,OAAO;KACxC,MAAM,oBAAoB,qBAAqB,KAAK;KACpD,MAAM,gBACJ,MAAM,SAAS,eAAe,MAAM,SAAS,eAAe;KAC9D,OACE,sBAAsB,SACrB,CAAC,iBAAiB,CAAC,qBAAqB,IAAI,iBAAiB;IAElE;GACF;GACA,MAAM,YAAY,KAAK,KAAK,gBAAgB,UAAU;GACtD,MAAM,MAAM,WAAW,EAAE,WAAW,KAAK,CAAC;GAC1C,MAAM,OAAO,YAAY;GACzB,IAAI,WAAW;GACf,IAAI,eAAe;GACnB,IAAI,WAAW,KAAK,IAAI,GAAG,UAAU,QAAQ,MAAM;GACnD,IAAI,QAAQ,YAAY,KAAA,GAAW;IACjC,MAAM,UAAU,UAAU,QAAQ,KAAK,UAAU;KAC/C,MAAM,YAAY,oBAAoB,OAAO,WAAW;KACxD,OAAO,MAAM,cAAc,KAAA,IACvB,YACA;MACE,GAAG;MACH,cAAc,eACZ,MAAM,UAAU,WAAW;OACzB,MAAM,YAAY;OAClB,YAAY,YAAY;OACxB,WAAW,YAAY;OACvB,kBAAkB,YAAY;OAC9B,kBAAkB,YAAY;OAC9B,eAAe;MACjB,CAAC,CACH;KACF;IACN,CAAC;IACD,MAAM,YACJ,OAAO,QAAQ,YAAY,aACvB,QAAQ,QAAQ;KACd,eAAe;KACf,cAAc,QAAQ;KACtB,WAAW,OAAO,WAAW,KAAK,UAAU,OAAO,CAAC;KACpD;KACA,WAAW;KACX,oBAAoB,MAAM,SAAS,eAAe;IACpD,CAAC,IACD,QAAQ;IACd,oBAAoB,SAAS;IAC7B,IAAI,UAAU,KAAK,aAAa,SAAS;KACvC,IAAI,MAAM,SAAS,eAAe,MAChC,MAAM,IAAI,MAAM,SAAS,MAAM,KAAK,qCAAqC;KAC3E,WAAW,UAAU,KAAK;IAC5B;IACA,MAAM,cAAc,WAAW,QAAQ,CAAC,CAAC,OAAO,UAAU,CAAC,CAAC,OAAO,KAAK;IACxE,MAAM,oBAAoB,KAAK,MAAM,KAAK,aAAa,WAAW,WAAW;IAE7E,MAAM,WACJ,gBAAgB,aACZ,YAAY,kBAAkB,UAAU,IAAI,aAAa,MAAM,OAC/D;IAEN,eAAe,mBADM,KAAK,MAAM,KAAK,mBAAmB,eACX,GAAG,KAAK;IACrD,MAAM,SAAmD,CAAC;IAC1D,MAAM,YACJ,UAAU,KAAK,aAAa,WACxB,UAAU,KAAK,YACf,KAAK,IAAI,GAAG,QAAQ,MAAM;IAChC,WAAW,gBAAgB,SAAS,MAAM,UAAU,SAAS;IAC7D,MAAM,mBAAmB,KAAK,KAAK,gBAAgB,iBAAiB;IACpE,MAAM,MAAM,kBAAkB,EAAE,WAAW,KAAK,CAAC;IACjD,KAAK,IAAI,SAAS,GAAG,SAAS,QAAQ,UAAU,WAAW,GAAG,UAAU,WAAW;KACjF,MAAM,UAAU,QAAQ,MAAM,QAAQ,SAAS,SAAS;KACxD,MAAM,WAAW,GAAG,SAAS,GAAG,OAAO,OAAO;KAC9C,OAAO,KAAK;MAAE,MAAM;MAAU;MAAQ,OAAO,QAAQ;KAAO,CAAC;KAC7D,MAAM,UACJ,KAAK,KAAK,kBAAkB,QAAQ,GACpC,KAAK,UAAU;MAAE,SAAS;MAAG;MAAU;MAAQ,OAAO,QAAQ;MAAQ;KAAQ,CAAC,CACjF;IACF;IACA,MAAM,WAA4B;KAChC,SAAS;KACT;KACA,WAAW;KACX;KACA,OAAO,QAAQ;KACf;KACA;IACF;IACA,MAAM,UAAU,KAAK,KAAK,kBAAkB,eAAe,GAAG,KAAK,UAAU,QAAQ,CAAC;GACxF;GACA,MAAM,YAAY,KAAK,IAAI,GAAG,KAAK,KAAK,UAAU,QAAQ,SAAS,QAAQ,CAAC;GAC5E,MAAM,aAAa,kBAAkB,WAAW,QAAQ,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,GAAG,EAAE;GAChG,MAAM,YAAY,MAAM,KAAK,EAAE,QAAQ,UAAU,IAAI,GAAG,UACtD,UAAU,IAAI,OAAO,GAAG,WAAW,GAAG,QAAQ,EAAE,MAClD;GACA,KAAK,MAAM,YAAY,UAAU,MAAM,CAAC,GACtC,IAAI,UAAU,QAAQ,MAAM,UAAU,MAAM,KAAK,YAAY,MAAM,SAAS,YAAY,CAAC,GACvF,MAAM,IAAI,MACR,6CAA6C,KAAK,MAAM,KAAK,YAAY,QAAQ,GACnF;GAEJ,MAAM,8BAAc,IAAI,IAAoB;GAC5C,KAAK,MAAM,CAAC,OAAO,aAAa,UAAU,QAAQ,GAAG;IACnD,MAAM,WAAW,KAAK,MAAM,KAAK,YAAY,QAAQ;IACrD,MAAM,WAAW,MAAM,kBAAkB,QACvC,YACC,eAAe,mBAAmB,YAAY,KAAK,GACpD,aAAa,eAAgB,aAAa,GAAG,WAAW,KAAK,KAAM,QACrE;IACA,MAAM,YAAY,WAChB,mBAAmB,KAAK,MAAM,KAAK,YAAY,UAAU,WAAW,IAAI,GAAG,KAAK;IAClF,MAAM,OAAO,MAAM,MAAM,OAAO;KAC9B,GAAG;KACH,GAAI,aAAa,KAAA,IAAY,CAAC,IAAI,EAAE,SAAS;KAC7C,YAAY;KACZ,WACE,QAAQ,YAAY,KAAA,IAChB,YACA;MACE,GAAG;MACH,SAAS,UAAU,QAAQ,MAAM,QAAQ,WAAW,QAAQ,KAAK,QAAQ;KAC3E;KACN,GAAI,QAAQ,YAAY,KAAA,IACpB,CAAC,IACD,EACE,SAAS;MACP;MACA;MACA,MAAM;OACJ,QAAQ,QAAQ;OAChB,OAAO;OACP,QAAQ,QAAQ;OAChB,OAAO,UAAU,QAAQ;OACzB,MAAM,SAAS,KAAK;OACpB,cAAc,UAAU,IAAI,OAAO,SAAS,QAAQ,CAAC;OACrD,UAAU,QAAQ,MAAM,YAAY,OAAO,SAAS,QAAQ,CAAC;MAC/D;KACF,EACF;IACN,CAAC;IACD,MAAM,UAAU,KAAK,KAAK,WAAW,QAAQ,GAAG,KAAK,IAAI;IACzD,KAAK,MAAM,CAAC,WAAW,aAAa,OAAO,QAAQ,KAAK,UAAU,CAAC,CAAC,GAAG;KACrE,qBAAqB,SAAS;KAC9B,KACG,QAAQ,YAAY,KAAA,KAAa,MAAM,SAAS,eAAe,SAChE,SAAS,SACT,iBAAiB,IAAI,UAAU,YAAY,CAAC,GAE5C,MAAM,IAAI,MAAM,4CAA4C,WAAW;KACzE,IACE,QAAQ,YAAY,KAAA,KACpB,UAAU,MAAM,aAAa,SAAS,YAAY,MAAM,UAAU,YAAY,CAAC,GAE/E,MAAM,IAAI,MAAM,4CAA4C,WAAW;KACzE,IAAI,QAAQ,YAAY,KAAA,KAAa,YAAY,GAAG;MAClD,MAAM,QAAQ,OAAO,KAAK,QAAQ;MAClC,MAAM,WAAW,YAAY,IAAI,SAAS;MAC1C,IAAI,aAAa,KAAA,KAAa,CAAC,SAAS,OAAO,KAAK,GAClD,MAAM,IAAI,MAAM,8CAA8C,WAAW;MAC3E,YAAY,IAAI,WAAW,KAAK;KAClC;KACA,IAAI,SAAS,OACX,MAAM,UAAU,KAAK,KAAK,WAAW,SAAS,GAAG,QAAQ;UACpD;MACL,MAAM,QAAQ,OAAO,KAAK,QAAQ;MAClC,MAAM,WAAW,aAAa,IAAI,SAAS;MAC3C,IAAI,aAAa,KAAA,KAAa,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,OAAO,KAAK,GAC/D,MAAM,IAAI,MAAM,2CAA2C,WAAW;MAExE,aAAa,IAAI,WAAW,KAAK;KACnC;IACF;GACF;EACF;EAEA,IAAI,SAAS,SAAS,aAAa,OAAO,GAAG;GAC3C,MAAM,iBAAiB,KAAK,KAAK,gBAAgB,WAAW;GAC5D,MAAM,MAAM,gBAAgB,EAAE,WAAW,KAAK,CAAC;GAC/C,KAAK,MAAM,CAAC,WAAW,aAAa,cAClC,MAAM,UAAU,KAAK,KAAK,gBAAgB,SAAS,GAAG,QAAQ;EAElE;EAEA,KAAK,MAAM,CAAC,UAAU,UAAU,kBAAkB,UAAU,CAAC,GAAG;GAC9D,MAAM,QAAQ,SAAS,MAAM,GAAG;GAChC,KAAK,IAAI,QAAQ,GAAG,SAAS,MAAM,QAAQ,SAAS;IAClD,MAAM,SAAS,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,KAAK,GAAG;IAC7C,IAAI,UAAU,SAAS,IAAI,MAAM,GAC/B,MAAM,IAAI,MAAM,+CAA+C,UAAU;IAC3E,MAAM,YAAY,KAAK,KAAK,gBAAgB,MAAM;IAClD,IAAI;KACF,MAAM,OAAO,MAAM,MAAM,SAAS;KAClC,IAAI,KAAK,eAAe,KAAK,UAAU,MAAM,UAAU,CAAC,KAAK,YAAY,GACvE,MAAM,IAAI,MAAM,mDAAmD,UAAU;IACjF,SAAS,OAAO;KACd,IAAI,EAAE,iBAAiB,SAAS,UAAU,SAAS,MAAM,SAAS,WAAW,MAAM;IACrF;GACF;GACA,MAAM,cAAc,KAAK,KAAK,gBAAgB,QAAQ;GACtD,MAAM,MAAM,KAAK,QAAQ,WAAW,GAAG,EAAE,WAAW,KAAK,CAAC;GAC1D,MAAM,UAAU,aAAa,KAAK;EACpC;EAEA,IAAI,aAAa,OAAO,GAAG;GACzB,MAAM,mBAAmB,KAAK,KAAK,gBAAgB,aAAa,WAAW;GAC3E,MAAM,MAAM,kBAAkB,EAAE,WAAW,KAAK,CAAC;GACjD,KAAK,MAAM,CAAC,UAAU,WAAW,cAC/B,MAAM,UAAU,KAAK,KAAK,kBAAkB,QAAQ,GAAG,MAAM;EAEjE;EACA,IAAI,MAAM,gBAAgB,OAAO;GAC/B,MAAM,0BAA0B,KAAK,KAAK,gBAAgB,WAAW;GACrE,MAAM,MAAM,yBAAyB,EAAE,WAAW,KAAK,CAAC;GACxD,MAAM,iBAAiB,CAAC,GAAG,cAAc,OAAO,CAAC;GACjD,MAAM,SAAmB,CAAC;GAC1B,KAAK,IAAI,SAAS,GAAG,SAAS,eAAe,QAAQ,UAAU,KAAK;IAClE,MAAM,WAAW,UAAU,OAAO,OAAO,MAAM,CAAC,CAAC,SAAS,GAAG,GAAG,EAAE;IAClE,OAAO,KAAK,QAAQ;IACpB,MAAM,UACJ,KAAK,KAAK,yBAAyB,QAAQ,GAC3C,KAAK,UAAU,EAAE,SAAS,eAAe,MAAM,QAAQ,SAAS,GAAG,EAAE,CAAC,CACxE;GACF;GACA,MAAM,UACJ,KAAK,KAAK,yBAAyB,mBAAmB,GACtD,KAAK,UAAU;IAAE,OAAO,eAAe;IAAQ;IAAQ,SAAS;GAAE,CAAC,CACrE;EACF;EAEA,MAAM,MAAM,KAAK,KAAK,gBAAgB,WAAW,GAAG,EAAE,WAAW,KAAK,CAAC;EACvE,MAAM,UAAU,KAAK,KAAK,gBAAgB,YAAY,GAAG,oBAAoB;EAC7E,MAAM,cAAc,gBAAgB,WAAW,WAAW,QAAQ,YAAY;EAC9E,OAAO;CACT,UAAU;EACR,IAAI,oBAAoB,MAAM,MAAM,GAAG,iBAAiB;GAAE,WAAW;GAAM,OAAO;EAAK,CAAC;CAC1F;AACF;AAEA,eAAsB,kBAAkB,UAAmC;CACzE,OAAO,SAAS,UAAU,MAAM;AAClC;;;;AC3zBA,SAAgB,WAAW,OAAuB;CAChD,OAAO,MACJ,WAAW,KAAK,OAAO,CAAC,CACxB,WAAW,KAAK,MAAM,CAAC,CACvB,WAAW,KAAK,MAAM,CAAC,CACvB,WAAW,MAAK,QAAQ;AAC7B;;;;ACNA,SAAgB,oBACd,QACA,YACA,WAAW,QAAQ,UACV;CACT,IAAI,OAAO,UAAU,WAAW,KAAK,OAAO;CAC5C,IAAI,OAAO,WAAW,WAAW,KAAK,OAAO;CAC7C,OAAO,aAAa,WAAW,OAAO,WAAW,KAAK,OAAO,UAAU;AACzE;;;ACgCA,IAAa,uBAAb,cAA0C,MAAM;CAC9C,OAAyB;AAC3B;AACA,SAAS,kBAAkB,OAAqB;CAC9C,IACE,MAAM,WAAW,GAAG,KACpB,MAAM,SAAS,IAAI,KACnB,MAAM,SAAS,IAAI,KACnB,MACG,MAAM,GAAG,CAAC,CACV,MAAM,YAAY,YAAY,OAAO,YAAY,QAAS,YAAY,MAAM,UAAU,EAAG,KAC5F,WAAW,KAAK,KAAK,KACrB,UAAU,eACV,MAAM,WAAW,YAAY,GAE7B,MAAM,IAAI,qBAAqB,kBAAkB;AACrD;;AAEA,eAAsB,uBACpB,SAC2B;CAC3B,yBAAyB,OAAO;CAChC,MAAM,OAAO,MAAM,SAAS,QAAQ,IAAI;CACxC,MAAM,cAAc,mBAAmB,QAAQ,WAAW;CAE1D,MAAM,oBAAoB,WAAW;CACrC,sBAAsB,QAAQ,SAAS,SAAS;CAChD,sBAAsB,QAAQ,SAAS,SAAS;CAChD,MAAM,WAAW,YAAY;EAC3B,MAAM,UAAU,MAAM,oBAAoB,WAAW;EACrD,IACE,sBAAsB,QAAQ,SAAS,SAAS,CAAC,CAAC,WAAW,KAC7D,sBAAsB,QAAQ,SAAS,SAAS,CAAC,CAAC,WAAW,KAC7D,CAAC,QAAQ,UAET,OAAO;EACT,QACE,MAAM,kBACJ,MACA,KAAK,KAAK,MAAM,WAAW,GAC3B,QAAQ,SACR,QAAQ,SACR,CAAC,GACD,aACA,OACF,EAAA,CACA;CACJ;CACA,eAAe,SACb,aACA,WAC0B;EAC1B,kBAAkB,WAAW;EAC7B,IAAI,cAAc,QAAQ,CAAC,UAAU,IAAI,WAAW,GAClD,MAAM,IAAI,qBAAqB,kBAAkB;EACnD,MAAM,eAAe,KAAK,KAAK,MAAM,WAAW;EAChD,MAAM,SAAS,gBAAgB,KAAK,OAAO,MAAM,SAAS,KAAK,QAAQ,YAAY,CAAC;EACpF,IAAI,CAAC,aAAa,MAAM,MAAM,GAAG,MAAM,IAAI,qBAAqB,kBAAkB;EAClF,MAAM,4BAAY,IAAI,IAAY,CAAC,IAAI,CAAC;EACxC,IAAI,gBAAgB,IAAI;GACtB,MAAM,WAAW,YAAY,MAAM,GAAG,CAAC,CAAC,MAAM,GAAG,EAAE;GACnD,KAAK,IAAI,SAAS,GAAG,UAAU,SAAS,QAAQ,UAC9C,UAAU,IAAI,MAAM,SAAS,KAAK,KAAK,MAAM,GAAG,SAAS,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC;EAC/E;EACA,OAAO,cAAc;GACnB;GACA,cAAc;GACd,cAAc;GACd;GACA,gBAAgB;GAChB,eAAe;EACjB,CAAC;CACH;CACA,OAAO;EACL,MAAM,YAAY,aAAa;GAC7B,MAAM,YAAY,MAAM,SAAS;GACjC,MAAM,QAAQ,MAAM,SAAS,aAAa,SAAS;GACnD,IACE,MAAM,YACL,MAAM,QAAQ,YAAY,MAAM,QAAQ,iBAAiB,MAAM,QAAQ,mBAExE,MAAM,IAAI,qBAAqB,oBAAoB;GACrD,MAAM,WAAW,MAAM,SAAS,MAAM,YAAY;GAClD,IAAI,CAAC,aAAa,MAAM,QAAQ,GAAG,MAAM,IAAI,qBAAqB,oBAAoB;GACtF,MAAM,YAAY,KAAK,SAAS,MAAM,QAAQ,CAAC,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,GAAG;GACxE,IAAI,cAAc,QAAQ,CAAC,UAAU,IAAI,SAAS,GAChD,MAAM,IAAI,qBAAqB,oBAAoB;GACrD,OAAO;EACT;EACA,MAAM,SAAS,aAAa;GAC1B,kBAAkB,WAAW;GAC7B,MAAM,YAAY,MAAM,SAAS;GACjC,IAAI,QAAQ,MAAM,SAAS,aAAa,SAAS;GACjD,IAAI,MAAM,YAAY,MAAM;IAC1B,IACE,MAAM,QAAQ,YACd,MAAM,QAAQ,iBACd,MAAM,QAAQ,oBACd,MAAM,QAAQ,uBAAuB,MAErC,MAAM,IAAI,qBAAqB,kBAAkB;IACnD,QAAQ,MAAM,SAAS,MAAM,QAAQ,oBAAoB,SAAS;GACpE;GACA,IAAI,MAAM,SAAS,QAAQ,MAAM,IAAI,qBAAqB,kBAAkB;GAC5E,MAAM,SAAS,MAAM,KACnB,MAAM,cACN,UAAU,YAAY,UAAU,cAAc,EAChD;GACA,IAAI;IACF,MAAM,QAAQ,MAAM,OAAO,KAAK;IAChC,MAAM,WAAW,MAAM,SAAS,MAAM,YAAY;IAClD,IACE,CAAC,MAAM,OAAO,KACd,CAAC,aAAa,MAAM,QAAQ,KAC5B,CAAC,oBAAoB,MAAM,UAAU,KAAK,GAE1C,MAAM,IAAI,qBAAqB,6BAA6B;IAC9D,OAAO;KACL;KACA;KACA,eAAe,KAAK,SAAS,MAAM,QAAQ,CAAC,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,GAAG;IACvE;GACF,SAAS,OAAO;IACd,MAAM,OAAO,MAAM;IACnB,MAAM;GACR;EACF;EACA,MAAM,SAAS,aAAa;GAC1B,kBAAkB,WAAW;GAC7B,OAAO,SAAS,aAAa,MAAM,SAAS,CAAC;EAC/C;EACA,MAAM,cAAc,aAAa;GAC/B,kBAAkB,WAAW;GAC7B,MAAM,YAAY,MAAM,SAAS;GACjC,MAAM,QAAQ,MAAM,SAAS,aAAa,SAAS;GACnD,IAAI,MAAM,SAAS,aAAa,MAAM,IAAI,qBAAqB,uBAAuB;GACtF,MAAM,WAAW,MAAM,SAAS,MAAM,YAAY;GAClD,IAAI,CAAC,aAAa,MAAM,QAAQ,GAAG,MAAM,IAAI,qBAAqB,uBAAuB;GACzF,MAAM,SAAS,MAAM,QAAQ,QAAQ,EAAA,CAAG,QACrC,SAAS,gBAAgB,MAAM,SAAS,WAC3C;GA0BA,MAAM,WAAU,MAzBQ,QAAQ,IAC9B,MACG,QAAQ,SAAS,cAAc,QAAQ,UAAU,IAAI,KAAK,MAAM,KAAK,aAAa,IAAI,CAAC,CAAC,CAAC,CACzF,IAAI,OAAO,SAAS;IACnB,IAAI;IACJ,IAAI;KACF,QAAQ,MAAM,SAAS,KAAK,MAAM,KAAK,aAAa,IAAI,GAAG,SAAS;IACtE,SAAS,OAAO;KAEd,IAAI,iBAAiB,SAAS,UAAU,SAAS,MAAM,SAAS,UAAU,OAAO;KACjF,MAAM;IACR;IACA,OAAO,QAAQ,cAAc,KAAA,IACzB,QACA;KACE,GAAG;KACH,YAAY,eACV,MAAM,QAAQ,UAAU,eAAe,KAAK,GAAG;MAC7C,WAAW;MACX,eAAe;KACjB,CAAC,CACH;IACF;GACN,CAAC,CACL,EAAA,CAC0B,QAAQ,UAAoC,UAAU,IAAI;GACpF,QAAQ,MACL,MAAM,UACL,eAAe,MAAM,OAAO,QAAQ,IAAI,MACvC,KAAK,eAAe,MAAM,eACvB,KACA,KAAK,eAAe,MAAM,eACxB,IACA,EACV;GACA,OAAO;IACL,MAAM,MAAM,SAAS,IAAI,SAAS;IAClC,SAAS;KAAE,GAAG;KAAO,MAAM;IAAY;IACvC,QACE,gBAAgB,KACZ,OACA,MAAM,SACJ,KAAK,MAAM,QAAQ,WAAW,MAAM,MAAM,KAAK,KAAK,MAAM,QAAQ,WAAW,GAC7E,SACF;IACN,OAAO,gBAAgB,KAAK,IAAI,YAAY,MAAM,GAAG,CAAC,CAAC;IACvD;GACF;EACF;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/filters.ts","../src/routing.ts","../src/validation.ts","../src/filesystem.ts","../src/listing.ts","../src/publication.ts","../src/generator.ts","../src/html.ts","../src/file-identity.ts","../src/service.ts"],"sourcesContent":["import { opendir, readlink, readFile, realpath } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport picomatch from \"picomatch\";\nimport ignore from \"ignore\";\nimport type { IgnoreFile } from \"./model.ts\";\n\n/** Resolve configuration paths once, before a host changes working directories. */\nexport function resolveIgnoreFiles(\n value: unknown = [],\n cwd = process.cwd(),\n): readonly Exclude<IgnoreFile, string>[] {\n if (!Array.isArray(value)) throw new TypeError(\"ignoreFiles must be an array\");\n return value.map((input) => {\n const file = typeof input === \"string\" ? { path: input } : input;\n if (typeof file !== \"object\" || file === null || Array.isArray(file))\n throw new TypeError(\"ignoreFiles entries must be paths or objects\");\n for (const key of Object.keys(file))\n if (![\"path\", \"baseDir\", \"optional\"].includes(key))\n throw new TypeError(`Unsupported ignoreFiles key \"${key}\"`);\n for (const key of [\"path\", \"baseDir\"] as const)\n if (\n (key === \"path\" || file[key] !== undefined) &&\n (typeof file[key] !== \"string\" || file[key].length === 0)\n )\n throw new TypeError(`ignoreFiles.${key} must be a non-empty string`);\n if (file.optional !== undefined && typeof file.optional !== \"boolean\")\n throw new TypeError(\"ignoreFiles.optional must be a boolean\");\n const filename = path.resolve(cwd, file.path);\n return {\n path: filename,\n baseDir: path.resolve(cwd, file.baseDir ?? path.dirname(filename)),\n optional: file.optional ?? false,\n };\n });\n}\n\nexport async function createIgnoreMatcher(files: readonly IgnoreFile[]) {\n const rules: { base: string; matcher: ReturnType<typeof ignore> }[] = [];\n const scopes = new Map<string, ReturnType<typeof ignore>>();\n let hasRules = false;\n for (const file of resolveIgnoreFiles(files)) {\n let contents: string;\n try {\n contents = await readFile(file.path, \"utf8\");\n } catch (error) {\n if (file.optional && error instanceof Error && \"code\" in error && error.code === \"ENOENT\")\n continue;\n throw error;\n }\n const base = await realpath(file.baseDir!);\n if (contents.split(/\\r?\\n/).some((line) => line.trim() !== \"\" && !line.startsWith(\"#\")))\n hasRules = true;\n let matcher = scopes.get(base);\n if (matcher === undefined) {\n matcher = ignore();\n scopes.set(base, matcher);\n rules.push({ base, matcher });\n }\n matcher.add(contents);\n }\n // Preserve Git's parent-directory negation behavior across files in one scope.\n // Explicit deeper scopes override applicable parent-scope rules.\n rules.sort((left, right) => left.base.split(path.sep).length - right.base.split(path.sep).length);\n const matches = (filename: string, directory: boolean): boolean => {\n let ignored = false;\n for (const rule of rules) {\n if (!isWithin(rule.base, filename)) continue;\n const relative = path.relative(rule.base, filename).split(path.sep).join(\"/\");\n if (relative === \"\") continue;\n const result = rule.matcher.test(relative + (directory ? \"/\" : \"\"));\n if (result.ignored) ignored = true;\n else if (result.unignored) ignored = false;\n }\n return ignored;\n };\n return Object.assign(matches, { hasRules });\n}\n\nexport function normalizePathPatterns(value: unknown, name: string): readonly string[] {\n if (value === undefined) return [];\n const patterns = typeof value === \"string\" ? [value] : value;\n if (\n !Array.isArray(patterns) ||\n patterns.some(\n (pattern) =>\n typeof pattern !== \"string\" ||\n pattern.length === 0 ||\n pattern.startsWith(\"/\") ||\n /^[A-Za-z]:/.test(pattern) ||\n pattern.startsWith(\"!\") ||\n pattern.includes(\"\\\\\") ||\n pattern.split(\"/\").some((segment) => segment === \".\" || segment === \"..\"),\n )\n ) {\n throw new TypeError(`${name} must contain root-relative glob patterns using forward slashes`);\n }\n return patterns as readonly string[];\n}\n\nfunction isWithin(parent: string, candidate: string): boolean {\n const relative = path.relative(parent, candidate);\n return (\n relative === \"\" ||\n (relative !== \"..\" && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative))\n );\n}\n\nexport interface SourceSelection {\n readonly selected: ReadonlySet<string>;\n readonly symlinks: ReadonlySet<string>;\n readonly mirroredSymlinks: ReadonlySet<string>;\n}\nexport interface SelectionBudget {\n readonly maxEntries: number;\n readonly maxDirectories: number;\n readonly signal: AbortSignal;\n readonly allowPath: (logicalPath: string) => Promise<boolean>;\n}\nexport class SelectionBudgetError extends Error {\n override readonly name = \"SelectionBudgetError\";\n}\n\n/** Select physical source paths; parent directories remain when a descendant matches. */\nexport async function selectSourcePaths(\n sourceDir: string,\n outputDir: string,\n include: unknown,\n exclude: unknown,\n ignoredPaths: readonly string[] = [],\n ignoreFiles: readonly IgnoreFile[] = [],\n preparedIgnore?: Awaited<ReturnType<typeof createIgnoreMatcher>>,\n budget?: SelectionBudget,\n): Promise<SourceSelection> {\n const isIgnored = preparedIgnore ?? (await createIgnoreMatcher(ignoreFiles));\n const canonicalRoot = await realpath(sourceDir);\n const ignored = ignoredPaths.map((value) => path.resolve(value));\n const included = normalizePathPatterns(include, \"include\").map((pattern) =>\n picomatch(pattern, { dot: true, nonegate: true }),\n );\n const excluded = normalizePathPatterns(exclude, \"exclude\").map((pattern) =>\n picomatch(pattern, { dot: true, nonegate: true }),\n );\n const selected = new Set<string>([\"\"]);\n const symlinks = new Map<string, string>();\n let entries = 0;\n let directories = 0;\n\n async function visit(\n directory: string,\n relativeDir: string,\n inheritedInclude: boolean,\n ): Promise<boolean> {\n budget?.signal.throwIfAborted();\n if (budget) {\n const physical = await realpath(directory);\n if (!isWithin(canonicalRoot, physical)) return false;\n if (path.relative(canonicalRoot, physical).split(path.sep).join(\"/\") !== relativeDir)\n return false;\n }\n if (budget && !(await budget.allowPath(relativeDir))) return false;\n if (budget && ++directories > budget.maxDirectories)\n throw new SelectionBudgetError(\"Directory scan budget exceeded\");\n let hasSelected = false;\n for await (const entry of await opendir(directory)) {\n budget?.signal.throwIfAborted();\n if (budget && ++entries > budget.maxEntries)\n throw new SelectionBudgetError(\"Entry scan budget exceeded\");\n const absolutePath = path.join(directory, entry.name);\n if (\n isWithin(outputDir, absolutePath) ||\n ignored.some((value) => isWithin(value, absolutePath))\n )\n continue;\n const relativePath = path.posix.join(relativeDir, entry.name);\n if (budget && !(await budget.allowPath(relativePath))) continue;\n const directoryLike = entry.isDirectory();\n const matches = (matcher: (value: string) => boolean) =>\n matcher(relativePath) || (directoryLike && matcher(`${relativePath}/`));\n if (\n excluded.some(matches) ||\n isIgnored(path.join(canonicalRoot, relativePath), directoryLike)\n )\n continue;\n const includedHere = inheritedInclude || included.some(matches);\n if (directoryLike) {\n const hasSelectedChild = await visit(absolutePath, relativePath, includedHere);\n if (included.length === 0 || includedHere || hasSelectedChild) {\n selected.add(relativePath);\n hasSelected = true;\n }\n } else if (included.length === 0 || includedHere) {\n selected.add(relativePath);\n hasSelected = true;\n if (entry.isSymbolicLink()) symlinks.set(relativePath, await readlink(absolutePath));\n }\n }\n return hasSelected;\n }\n\n if (!isIgnored(canonicalRoot, true)) await visit(sourceDir, \"\", false);\n // Absolute, escaping, and filtered targets cannot remain valid inside the copied tree.\n const mirroredSymlinks = new Set<string>();\n for (const [relativePath, target] of symlinks) {\n if (path.isAbsolute(target)) continue;\n const targetPath = path.resolve(sourceDir, path.dirname(relativePath), target);\n if (!isWithin(sourceDir, targetPath)) continue;\n const targetRelativePath = path.relative(sourceDir, targetPath).split(path.sep).join(\"/\");\n if (selected.has(targetRelativePath)) mirroredSymlinks.add(relativePath);\n }\n return { selected, symlinks: new Set(symlinks.keys()), mirroredSymlinks };\n}\n","import path from \"node:path\";\n\nexport type DirectoryUrls = \"explicit\" | \"slash\" | \"clean\";\nexport interface RoutingOptions {\n readonly directoryUrls?: DirectoryUrls;\n}\n\n/** Validate the shared policy before publication or server startup. */\nexport function resolveDirectoryUrls(value: unknown, name = \"routing\"): DirectoryUrls {\n if (value === undefined) return \"explicit\";\n if (value === null || typeof value !== \"object\" || Array.isArray(value))\n throw new TypeError(`${name} must be an object`);\n for (const key of Object.keys(value))\n if (key !== \"directoryUrls\") throw new TypeError(`Unsupported option \"${name}.${key}\"`);\n const style = \"directoryUrls\" in value ? value.directoryUrls : undefined;\n if (style === undefined) return \"explicit\";\n if (style !== \"explicit\" && style !== \"slash\" && style !== \"clean\")\n throw new TypeError(`${name}.directoryUrls must be explicit, slash or clean`);\n return style;\n}\n\nexport function encodeLogicalPath(value: string): string {\n return value.split(\"/\").map(encodeURIComponent).join(\"/\");\n}\n\nexport interface DirectoryRoute {\n readonly outputPath: string;\n /** Unencoded archive-relative public pathname; the root is an empty string. */\n readonly publicPath: string;\n}\n\n/** Output placement and public URL are planned together, before theme rendering. */\nexport function directoryRoute(\n directory: string,\n filename: string,\n style: DirectoryUrls,\n page = 1,\n): DirectoryRoute {\n if (style === \"clean\" && (filename === \"index.html\" || page > 1)) {\n const stem =\n page === 1\n ? directory\n : directory\n ? `${directory}.${filename.slice(0, -5)}`\n : filename.slice(0, -5);\n return { outputPath: stem ? `${stem}.html` : \"index.html\", publicPath: stem };\n }\n const outputPath = path.posix.join(directory, filename);\n return {\n outputPath,\n publicPath:\n style === \"slash\" && filename === \"index.html\"\n ? directory\n ? `${directory}/`\n : \"\"\n : outputPath,\n };\n}\n\n/** Resolve references from the public document, never from its logical directory. */\nexport function createHrefResolver(\n documentPath: string,\n strategy: \"relative\" | \"base\" | \"html-base\",\n base: string,\n): (target: string) => string {\n const documentDirectory = documentPath.endsWith(\"/\")\n ? documentPath\n : path.posix.dirname(documentPath || \".\");\n return (target) => {\n const directory = target === \"\" || target.endsWith(\"/\");\n if (strategy !== \"relative\") {\n const suffix = encodeLogicalPath(target);\n return strategy === \"base\" ? `${base}${suffix}` : suffix || \"./\";\n }\n const relative =\n path.posix.relative(documentDirectory || \".\", target || \".\") ||\n (directory ? \"\" : `../${path.posix.basename(target)}`);\n const encoded = relative\n .split(\"/\")\n .map((segment) =>\n segment === \"..\" || segment === \".\" ? segment : encodeURIComponent(segment),\n )\n .join(\"/\");\n return `${encoded || \".\"}${directory ? \"/\" : \"\"}`;\n };\n}\n","import type { GenerateOptions, ExplorerTheme } from \"./model.ts\";\nimport type { DirectoryServiceOptions } from \"./service.ts\";\nimport type { ListingPlan } from \"./listing.ts\";\nimport { resolveIgnoreFiles } from \"./filters.ts\";\nimport { resolveDirectoryUrls } from \"./routing.ts\";\n\ntype Options = Record<string, unknown>;\nexport function optionObject(value: unknown, name: string): Options {\n if (typeof value !== \"object\" || value === null || Array.isArray(value))\n throw new TypeError(`${name} must be an object`);\n return value as Options;\n}\nfunction distance(left: string, right: string): number {\n let previous = Array.from({ length: right.length + 1 }, (_, index) => index);\n for (let i = 1; i <= left.length; i++) {\n const current = [i];\n for (let j = 1; j <= right.length; j++)\n current[j] = Math.min(\n current[j - 1]! + 1,\n previous[j]! + 1,\n previous[j - 1]! + (left[i - 1] === right[j - 1] ? 0 : 1),\n );\n previous = current;\n }\n return previous[right.length]!;\n}\nexport function optionKeys(value: Options, allowed: readonly string[], name: string): void {\n for (const key of Object.keys(value)) {\n if (allowed.includes(key)) continue;\n const suggestion = allowed\n .map((candidate) => ({ candidate, distance: distance(key, candidate) }))\n .sort((left, right) => left.distance - right.distance)\n .find((item) => item.distance <= 2)?.candidate;\n throw new TypeError(\n `Unsupported option \"${name}.${key}\".${suggestion ? ` Did you mean \"${name}.${suggestion}\"?` : ` Supported keys: ${allowed.join(\", \")}.`}`,\n );\n }\n}\nfunction scalar(value: unknown, type: \"boolean\" | \"string\" | \"function\", name: string): void {\n if (value !== undefined && typeof value !== type)\n throw new TypeError(`${name} must be a ${type}`);\n}\nfunction choice(value: unknown, allowed: readonly unknown[], name: string): void {\n if (value !== undefined && !allowed.includes(value))\n throw new TypeError(`${name} must be one of ${allowed.join(\", \")}`);\n}\nfunction positiveInteger(value: unknown, name: string): void {\n if (typeof value !== \"number\" || !Number.isSafeInteger(value) || value < 1)\n throw new TypeError(`${name} must be a positive safe integer`);\n}\nexport function validateListingPlan(value: unknown): asserts value is ListingPlan {\n const plan = optionObject(value, \"listing\");\n optionKeys(plan, [\"html\", \"data\"], \"listing\");\n for (const [key, strategy, size] of [\n [\"html\", \"paged\", \"pageSize\"],\n [\"data\", \"blocks\", \"blockSize\"],\n ] as const) {\n const section = optionObject(plan[key], `listing.${key}`);\n optionKeys(\n section,\n section.strategy === strategy ? [\"strategy\", size] : [\"strategy\"],\n `listing.${key}`,\n );\n if (section.strategy !== \"all\" && section.strategy !== strategy)\n throw new TypeError(`listing.${key}.strategy must be all or ${strategy}`);\n if (section.strategy === strategy) positiveInteger(section[size], `listing.${key}.${size}`);\n }\n}\nfunction selection(value: Options, name: string): void {\n for (const key of [\"include\", \"exclude\"])\n if (\n value[key] !== undefined &&\n typeof value[key] !== \"string\" &&\n (!Array.isArray(value[key]) || value[key].some((item: unknown) => typeof item !== \"string\"))\n )\n throw new TypeError(`${name}.${key} must be a string or array of strings`);\n resolveIgnoreFiles(value.ignoreFiles);\n scalar(value.entryData, \"function\", `${name}.entryData`);\n if (value.sort !== undefined) {\n const sort = optionObject(value.sort, `${name}.sort`);\n optionKeys(sort, [\"field\", \"direction\", \"directoriesFirst\", \"nameMode\"], `${name}.sort`);\n scalar(sort.directoriesFirst, \"boolean\", `${name}.sort.directoriesFirst`);\n choice(sort.field, [\"name\", \"size\", \"modified\"], `${name}.sort.field`);\n choice(sort.direction, [\"asc\", \"desc\"], `${name}.sort.direction`);\n choice(sort.nameMode, [\"natural\", \"locale\", \"unicode\"], `${name}.sort.nameMode`);\n }\n}\nfunction requiredString(value: unknown, name: string): void {\n if (typeof value !== \"string\" || !value.length)\n throw new TypeError(`${name} must be a non-empty string`);\n}\nexport function validateTheme(\n value: unknown,\n name = \"options.theme\",\n): asserts value is ExplorerTheme {\n if (value === undefined) throw new TypeError(\"Core requires an explicit theme\");\n const theme = optionObject(value, name);\n requiredString(theme.name, `${name}.name`);\n if (typeof theme.render !== \"function\") throw new TypeError(`${name}.render must be a function`);\n scalar(theme.entryData, \"function\", `${name}.entryData`);\n scalar(theme.searchIndex, \"boolean\", `${name}.searchIndex`);\n if (theme.listing !== undefined) {\n const listing = optionObject(theme.listing, `${name}.listing`);\n optionKeys(listing, [\"pagination\"], `${name}.listing`);\n if (typeof listing.pagination !== \"boolean\")\n throw new TypeError(`${name}.listing.pagination must be a boolean`);\n }\n}\nconst generateKeys = {\n routing: true,\n sourceDir: true,\n outputDir: true,\n theme: true,\n include: true,\n exclude: true,\n ignoreFiles: true,\n sort: true,\n entryData: true,\n fileHref: true,\n dryRun: true,\n outputPolicy: true,\n listing: true,\n metadata: true,\n base: true,\n mode: true,\n mirror: true,\n outputName: true,\n urlStrategy: true,\n symlinks: true,\n} satisfies Record<keyof GenerateOptions, true>;\nexport function validateGenerateOptions(value: unknown): asserts value is GenerateOptions {\n const options = optionObject(value, \"options\");\n optionKeys(options, Object.keys(generateKeys), \"options\");\n resolveDirectoryUrls(options.routing, \"options.routing\");\n requiredString(options.sourceDir, \"options.sourceDir\");\n requiredString(options.outputDir, \"options.outputDir\");\n validateTheme(options.theme);\n selection(options, \"options\");\n for (const key of [\"fileHref\", \"metadata\"]) scalar(options[key], \"function\", `options.${key}`);\n for (const key of [\"dryRun\", \"mirror\"]) scalar(options[key], \"boolean\", `options.${key}`);\n scalar(options.base, \"string\", \"options.base\");\n choice(options.mode, [\"ssg\", \"mpa\"], \"options.mode\");\n choice(\n options.urlStrategy,\n Object.keys({\n base: true,\n \"html-base\": true,\n relative: true,\n } satisfies Record<NonNullable<GenerateOptions[\"urlStrategy\"]>, true>),\n \"options.urlStrategy\",\n );\n choice(options.outputPolicy, [\"owned\", \"replace\"], \"options.outputPolicy\");\n if (\n options.outputName !== undefined &&\n typeof options.outputName !== \"string\" &&\n typeof options.outputName !== \"function\"\n )\n throw new TypeError(\"options.outputName must be a string or function\");\n if (options.listing !== undefined && typeof options.listing !== \"function\")\n validateListingPlan(options.listing);\n if (options.symlinks !== undefined) {\n const links = optionObject(options.symlinks, \"options.symlinks\");\n optionKeys(links, [\"follow\", \"boundary\", \"onCycle\"], \"options.symlinks\");\n scalar(links.follow, \"boolean\", \"options.symlinks.follow\");\n choice(links.boundary, [\"root\", \"anywhere\"], \"options.symlinks.boundary\");\n choice(links.onCycle, [\"error\", \"skip\"], \"options.symlinks.onCycle\");\n }\n}\nconst serviceKeys = {\n root: true,\n include: true,\n exclude: true,\n ignoreFiles: true,\n sort: true,\n entryData: true,\n} satisfies Record<keyof DirectoryServiceOptions, true>;\nexport function validateDirectoryOptions(value: unknown): asserts value is DirectoryServiceOptions {\n const options = optionObject(value, \"options\");\n optionKeys(options, Object.keys(serviceKeys), \"options\");\n requiredString(options.root, \"options.root\");\n selection(options, \"options\");\n}\n","import { lstat, readlink, realpath, stat } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { Stats } from \"node:fs\";\nimport type { EntryKind, FileMetadata, FileSystemEntry, SymlinkMetadata } from \"./model.ts\";\nfunction entryKind(stats: Stats): EntryKind {\n if (stats.isDirectory()) return \"directory\";\n if (stats.isFile()) return \"file\";\n if (stats.isSymbolicLink()) return \"symlink\";\n return \"other\";\n}\n\nfunction metadata(stats: Stats): FileMetadata {\n return {\n device: stats.dev,\n groupId: stats.gid,\n hardLinkCount: stats.nlink,\n inode: stats.ino,\n mode: stats.mode,\n ownerId: stats.uid,\n size: stats.size,\n times: {\n accessedAt: stats.atime.toISOString(),\n changedAt: stats.ctime.toISOString(),\n createdAt: stats.birthtime.toISOString(),\n modifiedAt: stats.mtime.toISOString(),\n },\n };\n}\n\nexport function isInsideRoot(rootPath: string, candidatePath: string): boolean {\n const relative = path.relative(rootPath, candidatePath);\n return (\n relative === \"\" ||\n (relative !== \"..\" && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative))\n );\n}\n\nexport async function describeEntry({\n absolutePath,\n relativePath,\n rootRealPath,\n ancestors,\n followSymlinks,\n selectedPaths,\n}: {\n absolutePath: string;\n relativePath: string;\n rootRealPath: string;\n ancestors: ReadonlySet<string>;\n followSymlinks: boolean;\n selectedPaths: ReadonlySet<string> | null;\n}): Promise<FileSystemEntry> {\n const linkStats = await lstat(absolutePath);\n const kind = entryKind(linkStats);\n let symlink: SymlinkMetadata | null = null;\n\n if (kind === \"symlink\") {\n const target = await readlink(absolutePath);\n let resolvedPath: string | null = null;\n let targetKind: Exclude<EntryKind, \"symlink\"> | null = null;\n let targetSize: number | null = null;\n try {\n resolvedPath = await realpath(absolutePath);\n const targetStats = await stat(absolutePath);\n targetKind = entryKind(targetStats) as Exclude<EntryKind, \"symlink\">;\n targetSize = targetStats.size;\n } catch {\n // Broken and inaccessible links are represented as data.\n }\n const isOutsideRoot = resolvedPath === null || !isInsideRoot(rootRealPath, resolvedPath);\n const isCycle = resolvedPath !== null && ancestors.has(resolvedPath);\n const targetRelativePath =\n resolvedPath !== null && !isOutsideRoot\n ? path.relative(rootRealPath, resolvedPath).split(path.sep).join(\"/\")\n : null;\n const isTargetExcluded =\n targetRelativePath !== null &&\n selectedPaths !== null &&\n !selectedPaths.has(targetRelativePath);\n symlink = {\n target,\n resolvedPath,\n targetRelativePath,\n targetKind,\n targetSize:\n isOutsideRoot || isTargetExcluded || targetKind === \"directory\" ? null : targetSize,\n isBroken: resolvedPath === null,\n isCycle,\n isOutsideRoot,\n isTargetExcluded,\n wasFollowed:\n followSymlinks &&\n targetKind === \"directory\" &&\n !isCycle &&\n !isOutsideRoot &&\n !isTargetExcluded,\n };\n }\n\n return {\n name: path.basename(absolutePath),\n absolutePath,\n relativePath,\n kind,\n metadata: metadata(linkStats),\n symlink,\n };\n}\n","import type { JsonObject } from \"./json.ts\";\nimport { createHash } from \"node:crypto\";\nimport type { EntryKind, FileSystemEntry, ThemeContext, SortOptions } from \"./model.ts\";\nimport { copyJsonObject } from \"./json.ts\";\n\n/** Public transport fields only. Internal filesystem metadata must never be spread here. */\nexport interface ListingEntry {\n readonly data?: JsonObject;\n readonly presentation?: JsonObject;\n readonly id: string;\n readonly name: string;\n readonly kind: EntryKind;\n readonly href: string | null;\n readonly exitsExplorer: boolean;\n readonly size: number;\n readonly modifiedAt: string;\n readonly link: null | {\n readonly target: string;\n readonly targetKind: Exclude<EntryKind, \"symlink\"> | null;\n readonly targetSize: number | null;\n readonly unavailable: boolean;\n readonly cycle: boolean;\n };\n}\nexport interface ListingPlan {\n readonly html:\n | { readonly strategy: \"all\" }\n | { readonly strategy: \"paged\"; readonly pageSize: number };\n readonly data:\n | { readonly strategy: \"all\" }\n | { readonly strategy: \"blocks\"; readonly blockSize: number };\n}\nexport interface ListingPolicyContext {\n readonly operation?: \"build\" | \"server\";\n readonly directoryPath: string;\n readonly visibleCount: number;\n readonly dataBytes: number;\n readonly mode: \"ssg\" | \"mpa\";\n readonly supportsPagination: boolean;\n}\nexport type ListingPolicy = ListingPlan | ((context: ListingPolicyContext) => ListingPlan);\nexport interface ListingPage {\n readonly number: number;\n readonly count: number;\n readonly offset: number;\n readonly total: number;\n readonly href: string;\n readonly previousHref: string | null;\n readonly nextHref: string | null;\n}\nexport interface ListingContext {\n readonly revision: string;\n readonly manifestHref: string;\n readonly page: ListingPage;\n}\nexport interface ListingBlock {\n readonly version: 1;\n readonly revision: string;\n readonly offset: number;\n readonly total: number;\n readonly entries: readonly ListingEntry[];\n}\nexport interface ListingManifest {\n readonly version: 1;\n readonly revision: string;\n readonly directory: string;\n /** Resolve entry hrefs against this URL, resolved relative to the manifest URL. */\n readonly hrefBase: string;\n readonly total: number;\n readonly sort: Required<SortOptions>;\n /** Block hrefs are relative to the manifest URL. */\n readonly blocks: readonly {\n readonly href: string;\n readonly offset: number;\n readonly count: number;\n }[];\n}\n\nexport function projectListingEntry(\n entry: FileSystemEntry,\n context: Pick<ThemeContext, \"hrefFor\" | \"exitsExplorerFor\">,\n): ListingEntry {\n const link = entry.symlink;\n return {\n ...(entry.publicData === undefined ? {} : { data: copyJsonObject(entry.publicData) }),\n id: entry.relativePath,\n name: entry.name,\n kind: entry.kind,\n href: context.hrefFor(entry),\n exitsExplorer: context.exitsExplorerFor(entry),\n size: entry.metadata.size,\n modifiedAt: entry.metadata.times.modifiedAt,\n link:\n link === null\n ? null\n : {\n target: link.target,\n targetKind: link.targetKind,\n targetSize: link.targetSize ?? null,\n unavailable: link.isBroken || link.isOutsideRoot || Boolean(link.isTargetExcluded),\n cycle: link.isCycle,\n },\n };\n}\n\n/** Annotation providers receive public facts, not transport URLs or raw stat objects. */\nexport function entryDataFacts(\n entry: FileSystemEntry,\n): Omit<ListingEntry, \"href\" | \"exitsExplorer\" | \"presentation\" | \"data\"> {\n const {\n href: _href,\n exitsExplorer: _exits,\n data: _data,\n ...facts\n } = projectListingEntry(entry, { hrefFor: () => null, exitsExplorerFor: () => false });\n return facts;\n}\nexport { validateListingPlan } from \"./validation.ts\";\nexport function listingRevision(\n entries: readonly ListingEntry[],\n sort: Required<SortOptions>,\n hrefBase: string,\n blockSize: number,\n): string {\n return createHash(\"sha256\")\n .update(JSON.stringify({ version: 1, entries, sort, hrefBase, blockSize }))\n .digest(\"hex\");\n}\n","import { lstat, readdir, readFile, realpath, rename, rm, mkdir, open } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { randomUUID } from \"node:crypto\";\nimport { isInsideRoot } from \"./filesystem.ts\";\n\nexport const outputMarker = \"__dirwell/ownership.json\";\nexport const outputMarkerContents = '{\"type\":\"dirwell-output\",\"version\":1}\\n';\n\nexport async function lockOutput(output: string) {\n const canonical = await canonicalPath(output);\n const lockPath = path.join(path.dirname(canonical), `.${path.basename(canonical)}-dirwell.lock`);\n await mkdir(path.dirname(lockPath), { recursive: true });\n let handle;\n try {\n handle = await open(lockPath, \"wx\");\n } catch (error) {\n if (error instanceof Error && \"code\" in error && error.code === \"EEXIST\")\n throw new Error(\n `Output is locked by another build: ${lockPath}. Wait for it to finish. Remove a stale lock only after confirming its owner has stopped; --force does not bypass locks.`,\n );\n throw error;\n }\n try {\n await handle.writeFile(JSON.stringify({ pid: process.pid }));\n } catch (error) {\n await handle.close();\n await rm(lockPath);\n throw error;\n }\n return {\n lockPath,\n async release() {\n await handle.close();\n await rm(lockPath);\n },\n };\n}\n\nexport async function canonicalPath(filename: string): Promise<string> {\n try {\n return await realpath(filename);\n } catch (error) {\n if (!(error instanceof Error && \"code\" in error && error.code === \"ENOENT\")) throw error;\n const parent = path.dirname(filename);\n if (parent === filename) throw error;\n return path.join(await canonicalPath(parent), path.basename(filename));\n }\n}\n\n/** Force permits output ownership replacement, never source containment violations. */\nexport async function checkOutput(\n source: string,\n output: string,\n policy: \"owned\" | \"replace\" = \"owned\",\n): Promise<boolean> {\n if (policy !== \"owned\" && policy !== \"replace\")\n throw new TypeError(\"outputPolicy must be owned or replace\");\n if (isInsideRoot(await canonicalPath(output), await realpath(source)))\n throw new Error(\"outputDir must not contain the sourceDir\");\n let stat;\n try {\n stat = await lstat(output);\n } catch (error) {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") return false;\n throw error;\n }\n if (stat.isSymbolicLink() || !stat.isDirectory())\n throw new Error(\"outputDir must be a directory, not a file or symlink\");\n if ((await readdir(output)).length === 0 || policy === \"replace\") return true;\n try {\n const marker = path.join(output, outputMarker);\n if (\n !(await lstat(path.dirname(marker))).isSymbolicLink() &&\n !(await lstat(marker)).isSymbolicLink() &&\n (await readFile(marker, \"utf8\")) === outputMarkerContents\n )\n return true;\n } catch (error) {\n if (!(error instanceof Error && \"code\" in error && error.code === \"ENOENT\")) throw error;\n }\n throw new Error(\n \"Output is nonempty and not owned by Dirwell. Choose another directory or explicitly replace it with --force (-f) / outputPolicy: 'replace'.\",\n );\n}\n\n/** Keep a recoverable previous tree until the new tree is installed. */\nexport async function publishOutput(\n staged: string,\n output: string,\n source: string,\n policy: \"owned\" | \"replace\" = \"owned\",\n): Promise<void> {\n const exists = await checkOutput(source, output, policy);\n await mkdir(path.dirname(output), { recursive: true });\n const backup = path.join(\n path.dirname(output),\n `.${path.basename(output)}-previous-${randomUUID()}`,\n );\n if (exists) await rename(output, backup);\n try {\n await rename(staged, output);\n } catch (error) {\n if (exists) {\n try {\n await rename(backup, output);\n } catch (restoreError) {\n throw new AggregateError(\n [error, restoreError],\n `Publication failed; previous output is retained at ${backup}`,\n );\n }\n }\n throw error;\n }\n if (exists) await rm(backup, { recursive: true, force: true });\n}\n","import { validateGenerateOptions } from \"./validation.ts\";\nimport {\n resolveDirectoryUrls,\n directoryRoute,\n createHrefResolver,\n encodeLogicalPath,\n} from \"./routing.ts\";\nimport { copyJsonObject } from \"./json.ts\";\nimport { describeEntry, isInsideRoot } from \"./filesystem.ts\";\nimport {\n listingRevision,\n projectListingEntry,\n entryDataFacts,\n validateListingPlan,\n type ListingManifest,\n} from \"./listing.ts\";\nimport {\n cp,\n lstat,\n mkdtemp,\n mkdir,\n readFile,\n readdir,\n realpath,\n rm,\n writeFile,\n} from \"node:fs/promises\";\nimport { createHash } from \"node:crypto\";\nimport path from \"node:path\";\nimport type {\n DirectoryData,\n EntryKind,\n ExplorerTheme,\n FileSystemEntry,\n GenerateOptions,\n NameSortMode,\n OutputNameResolver,\n SortOptions,\n ThemeContext,\n} from \"./model.ts\";\nimport { selectSourcePaths } from \"./filters.ts\";\nimport {\n checkOutput,\n publishOutput,\n outputMarker,\n outputMarkerContents,\n lockOutput,\n canonicalPath,\n} from \"./publication.ts\";\nimport type { BuildReport } from \"./model.ts\";\n\nconst indexPattern = /^index\\.html?$/i;\n\nconst defaultSort = {\n direction: \"asc\",\n directoriesFirst: true,\n field: \"name\",\n nameMode: \"natural\",\n} as const satisfies Required<SortOptions>;\n\nexport const defaultOutputName: OutputNameResolver = ({ entries }) =>\n entries.some((entry) => indexPattern.test(entry.name))\n ? entries.some((entry) => entry.name.toLowerCase() === \"_dirwell.html\")\n ? null\n : \"_dirwell.html\"\n : \"index.html\";\n\nfunction assertSafeOutputName(name: string): void {\n if (\n name.length === 0 ||\n name.includes(\"\\\\\") ||\n Array.from(name).some((character) => character.charCodeAt(0) < 32) ||\n path.basename(name) !== name ||\n name === \".\" ||\n name === \"..\"\n ) {\n throw new Error(`Output name must be a single safe filename: ${name}`);\n }\n}\n\nfunction normalizeBase(value: string): string {\n if (value.includes(\"?\") || value.includes(\"#\")) {\n throw new TypeError(\"base must not contain a query string or fragment\");\n }\n if (/^https?:\\/\\//i.test(value)) {\n const url = new URL(value);\n url.pathname = `${url.pathname.replace(/\\/+$/, \"\")}/`;\n return url.href;\n }\n if (!value.startsWith(\"/\")) {\n throw new TypeError('base must start with \"/\" or be an absolute HTTP(S) URL');\n }\n return `${value.replace(/\\/+$/, \"\")}/`;\n}\n\nfunction isDirectoryLike(entry: FileSystemEntry): boolean {\n return entry.kind === \"directory\" || entry.symlink?.targetKind === \"directory\";\n}\n\nfunction isUnavailableSymlink(entry: FileSystemEntry): boolean {\n return Boolean(\n entry.symlink?.isBroken || entry.symlink?.isOutsideRoot || entry.symlink?.isTargetExcluded,\n );\n}\n\nfunction compareUnicode(left: string, right: string): number {\n const leftPoints = [...left];\n const rightPoints = [...right];\n const length = Math.min(leftPoints.length, rightPoints.length);\n for (let index = 0; index < length; index += 1) {\n const difference =\n (leftPoints[index]?.codePointAt(0) ?? 0) - (rightPoints[index]?.codePointAt(0) ?? 0);\n if (difference !== 0) return difference;\n }\n return leftPoints.length - rightPoints.length;\n}\n\nfunction compareNames(left: string, right: string, mode: NameSortMode): number {\n if (mode === \"unicode\") return compareUnicode(left, right);\n return left.localeCompare(right, undefined, {\n numeric: mode === \"natural\",\n sensitivity: \"base\",\n });\n}\n\nexport function compareEntries(\n left: FileSystemEntry,\n right: FileSystemEntry,\n options: SortOptions = {},\n): number {\n const resolved = { ...defaultSort, ...options };\n if (resolved.directoriesFirst && isDirectoryLike(left) !== isDirectoryLike(right)) {\n return isDirectoryLike(left) ? -1 : 1;\n }\n let result: number;\n if (resolved.field === \"modified\") {\n result = left.metadata.times.modifiedAt.localeCompare(right.metadata.times.modifiedAt);\n } else if (resolved.field === \"size\") {\n result =\n (left.kind === \"directory\" ? 0 : left.metadata.size) -\n (right.kind === \"directory\" ? 0 : right.metadata.size);\n } else {\n result = compareNames(left.name, right.name, resolved.nameMode);\n }\n if (result === 0) result = compareNames(left.name, right.name, resolved.nameMode);\n return resolved.direction === \"asc\" ? result : -result;\n}\n\nfunction rawLinkFilename(entry: FileSystemEntry): string {\n return `${createHash(\"sha256\")\n .update(entry.relativePath)\n .update(\"\\0\")\n .update(entry.symlink?.target ?? \"\")\n .digest(\"hex\")\n .slice(0, 16)}.txt`;\n}\n\nexport async function generateExplorer(options: GenerateOptions): Promise<BuildReport> {\n return generateExplorerSkippingPaths(options, []);\n}\n\n/** Internal entry point for hosts that stage previews outside their published output. */\nexport async function generateExplorerSkippingPaths(\n options: GenerateOptions,\n ignoredPaths: readonly string[],\n): Promise<BuildReport> {\n validateGenerateOptions(options);\n const source = path.resolve(options.sourceDir),\n output = path.resolve(options.outputDir);\n await checkOutput(source, output, options.outputPolicy);\n if (options.dryRun) return generateExplorerUnlocked(options, ignoredPaths);\n const lock = await lockOutput(output);\n const lexicalLock = path.join(path.dirname(output), path.basename(lock.lockPath));\n try {\n return await generateExplorerUnlocked(options, [...ignoredPaths, lock.lockPath, lexicalLock]);\n } finally {\n await lock.release();\n }\n}\n\nasync function generateExplorerUnlocked(\n options: GenerateOptions,\n ignoredPaths: readonly string[],\n): Promise<BuildReport> {\n const sourceDir = await realpath(path.resolve(options.sourceDir));\n const outputDir = await canonicalPath(path.resolve(options.outputDir));\n const ignoredSourcePaths = ignoredPaths.map((value) => path.resolve(value));\n const rootRealPath = await realpath(sourceDir);\n const replacesOutput = await checkOutput(sourceDir, outputDir, options.outputPolicy);\n const outputName = options.outputName ?? defaultOutputName;\n const resolveOutputName: OutputNameResolver =\n typeof outputName === \"string\" ? () => outputName : outputName;\n const theme: ExplorerTheme = options.theme;\n if (theme === undefined) throw new TypeError(\"Core requires an explicit theme\");\n const directoryUrls = resolveDirectoryUrls(options.routing);\n const mode = options.mode ?? \"ssg\";\n const urlStrategy = options.urlStrategy ?? \"relative\";\n const base = normalizeBase(options.base ?? \"/\");\n const followSymlinks = options.symlinks?.follow ?? false;\n const boundary = options.symlinks?.boundary ?? \"root\";\n const onCycle = options.symlinks?.onCycle ?? \"skip\";\n const filtersActive =\n options.include !== undefined ||\n options.exclude !== undefined ||\n (options.ignoreFiles?.length ?? 0) > 0 ||\n ignoredSourcePaths.length > 0;\n const plans: Array<{\n readonly directory: DirectoryData;\n readonly logicalDir: string;\n readonly outputName: string | null;\n }> = [];\n if (isInsideRoot(outputDir, sourceDir)) {\n throw new Error(\"outputDir must not contain the sourceDir\");\n }\n if (\n (await readdir(sourceDir)).some((name) => name.normalize(\"NFC\").toLowerCase() === \"__dirwell\")\n )\n throw new Error('Dirwell reserves the source-root path \"__dirwell\"');\n const outputIsWithinSource = isInsideRoot(sourceDir, outputDir);\n const selection = await selectSourcePaths(\n sourceDir,\n outputDir,\n options.include,\n options.exclude,\n ignoredSourcePaths,\n options.ignoreFiles,\n );\n const stageParent = outputIsWithinSource ? path.dirname(sourceDir) : path.dirname(outputDir);\n if (!options.dryRun) await mkdir(stageParent, { recursive: true });\n const stagedOutputDir = options.dryRun\n ? null\n : await mkdtemp(path.join(stageParent, `.${path.basename(outputDir)}-dirwell-`));\n const buildOutputDir = stagedOutputDir!;\n\n try {\n if (!options.dryRun && (options.mirror ?? true)) {\n await rm(buildOutputDir, { recursive: true, force: true });\n await mkdir(buildOutputDir, { recursive: true });\n await cp(sourceDir, buildOutputDir, {\n recursive: true,\n dereference: false,\n preserveTimestamps: true,\n filter: (sourcePath) => {\n const relativePath = path.relative(sourceDir, sourcePath).split(path.sep).join(\"/\");\n return (\n selection.selected.has(relativePath) &&\n (!selection.symlinks.has(relativePath) || selection.mirroredSymlinks.has(relativePath))\n );\n },\n });\n }\n } catch (error) {\n if (stagedOutputDir !== null) await rm(stagedOutputDir, { recursive: true, force: true });\n throw error;\n }\n\n async function visit(\n logicalDir: string,\n physicalDir: string,\n ancestors: ReadonlySet<string>,\n ): Promise<void> {\n const physicalRealPath = await realpath(physicalDir);\n if (ancestors.has(physicalRealPath)) {\n if (onCycle === \"error\") throw new Error(`Symlink cycle at ${logicalDir}`);\n return;\n }\n\n const nextAncestors = new Set(ancestors).add(physicalRealPath);\n const names = (await readdir(physicalDir)).filter((name) => {\n if (outputIsWithinSource && isInsideRoot(outputDir, path.join(physicalDir, name)))\n return false;\n if (ignoredSourcePaths.some((value) => isInsideRoot(value, path.join(physicalDir, name))))\n return false;\n if (!filtersActive) return true;\n const relativePath = path.relative(rootRealPath, path.join(physicalRealPath, name));\n return selection.selected.has(relativePath.split(path.sep).join(\"/\"));\n });\n const entries = await Promise.all(\n names.map(async (name) => {\n const entry = await describeEntry({\n absolutePath: path.join(physicalDir, name),\n relativePath: path.posix.join(logicalDir, name),\n rootRealPath,\n ancestors: nextAncestors,\n followSymlinks,\n selectedPaths: filtersActive ? selection.selected : null,\n });\n return options.entryData === undefined\n ? entry\n : {\n ...entry,\n publicData: copyJsonObject(\n await options.entryData(entryDataFacts(entry), {\n operation: \"build\",\n directoryPath: logicalDir,\n }),\n ),\n };\n }),\n );\n entries.sort(\n (left, right) =>\n compareEntries(left, right, options.sort) ||\n (options.listing === undefined ? 0 : compareUnicode(left.relativePath, right.relativePath)),\n );\n\n const describedCurrent = await describeEntry({\n absolutePath: physicalDir,\n relativePath: logicalDir,\n rootRealPath,\n ancestors,\n followSymlinks,\n selectedPaths: filtersActive ? selection.selected : null,\n });\n if (describedCurrent.kind !== \"directory\") {\n throw new Error(`${physicalDir} is not a directory`);\n }\n const current: DirectoryData[\"current\"] = {\n ...describedCurrent,\n kind: \"directory\",\n };\n\n const root = await describeEntry({\n absolutePath: sourceDir,\n relativePath: \"\",\n rootRealPath,\n ancestors: new Set(),\n followSymlinks,\n selectedPaths: filtersActive ? selection.selected : null,\n });\n const parent =\n logicalDir === \"\"\n ? null\n : await describeEntry({\n absolutePath: path.dirname(physicalDir),\n relativePath:\n path.posix.dirname(logicalDir) === \".\" ? \"\" : path.posix.dirname(logicalDir),\n rootRealPath,\n ancestors,\n followSymlinks,\n selectedPaths: filtersActive ? selection.selected : null,\n });\n const directory: DirectoryData = {\n root,\n current,\n parent,\n depth: logicalDir === \"\" ? 0 : logicalDir.split(\"/\").length,\n entries,\n };\n\n const name = await resolveOutputName(directory);\n if (name !== null) {\n assertSafeOutputName(name);\n if (options.outputName !== undefined && directoryUrls !== \"explicit\" && name !== \"index.html\")\n throw new TypeError(\"Custom outputName requires routing.directoryUrls: explicit\");\n }\n plans.push({ directory, logicalDir, outputName: name });\n\n for (const entry of entries) {\n if (entry.kind === \"directory\") {\n await visit(path.posix.join(logicalDir, entry.name), entry.absolutePath, nextAncestors);\n continue;\n }\n if (!followSymlinks || entry.symlink?.targetKind !== \"directory\") continue;\n const physicalLinkPath = path\n .relative(rootRealPath, path.join(physicalRealPath, entry.name))\n .split(path.sep)\n .join(\"/\");\n if ((options.mirror ?? true) && !selection.mirroredSymlinks.has(physicalLinkPath)) continue;\n if (entry.symlink.isCycle) {\n if (onCycle === \"error\") throw new Error(`Symlink cycle at ${entry.relativePath}`);\n continue;\n }\n if (entry.symlink.resolvedPath === null) continue;\n if (boundary === \"root\" && entry.symlink.isOutsideRoot) continue;\n if (\n filtersActive &&\n (entry.symlink.targetRelativePath === null ||\n !selection.selected.has(entry.symlink.targetRelativePath))\n )\n continue;\n // Mirrored internal aliases already resolve to the target's generated page.\n // Writing an alias page through the copied symlink would overwrite that page.\n if (\n (options.mirror ?? true) &&\n entry.symlink.targetRelativePath !== null &&\n !entry.symlink.isOutsideRoot\n )\n continue;\n await visit(\n path.posix.join(logicalDir, entry.name),\n entry.symlink.resolvedPath,\n nextAncestors,\n );\n }\n }\n\n try {\n await visit(\"\", sourceDir, new Set());\n\n const generatedPages = new Map(\n plans\n .filter((plan) => plan.outputName !== null)\n .map((plan) => [\n plan.logicalDir,\n directoryRoute(plan.logicalDir, plan.outputName!, directoryUrls),\n ]),\n );\n const sourcePaths = new Set(\n [...selection.selected].map((value) => value.normalize(\"NFC\").toLowerCase()),\n );\n const claimedPages = new Set<string>();\n const claimedAssets = new Set<string>();\n const claimPage = (outputPath: string) => {\n const key = outputPath.normalize(\"NFC\").toLowerCase();\n if (\n key === \"__dirwell\" ||\n key.startsWith(\"__dirwell/\") ||\n sourcePaths.has(key) ||\n claimedPages.has(key) ||\n claimedAssets.has(key)\n )\n throw new Error(`Explorer page conflicts with source or generated output: ${outputPath}`);\n claimedPages.add(key);\n };\n for (const route of generatedPages.values()) claimPage(route.outputPath);\n const ownedDirectories = new Set(\n plans\n .filter(\n (plan) =>\n plan.outputName === null &&\n plan.directory.entries.some(\n (entry) => entry.kind === \"file\" && indexPattern.test(entry.name),\n ),\n )\n .map((plan) => plan.logicalDir),\n );\n const generatedDirectories = new Set(generatedPages.keys());\n const pagePathForDirectory = (logicalPath: string): string | null =>\n generatedPages.get(logicalPath)?.publicPath ??\n (ownedDirectories.has(logicalPath) ? (logicalPath ? `${logicalPath}/` : \"\") : null);\n const report: BuildReport = {\n sourceDir,\n outputDir,\n theme: theme.name,\n dryRun: options.dryRun ?? false,\n mirror: options.mirror ?? true,\n replacesOutput,\n selectedPaths: [...selection.selected].filter(Boolean).sort(),\n pages: plans.map((plan) => ({\n directory: plan.logicalDir,\n outputName: plan.outputName,\n outputPath: generatedPages.get(plan.logicalDir)?.outputPath ?? null,\n publicPath: generatedPages.get(plan.logicalDir)?.publicPath ?? null,\n })),\n };\n if (options.dryRun) return report;\n const sourceKinds = new Map<string, string>();\n for (const { directory } of plans)\n for (const entry of directory.entries) {\n const sourcePath = path.relative(sourceDir, entry.absolutePath).split(path.sep).join(\"/\");\n if (selection.selected.has(sourcePath)) sourceKinds.set(sourcePath, entry.kind);\n }\n const metadataResolver = await options.metadata?.({ sourceDir, sourceKinds });\n const sharedAssets = new Map<string, string | Uint8Array>();\n const rawLinkFiles = new Map<string, string>();\n const searchEntries = new Map<\n string,\n {\n href: string | null;\n exitsExplorer: boolean;\n isLink: boolean;\n kind: EntryKind;\n modifiedAt: string;\n name: string;\n path: string;\n size: number;\n target: string | null;\n targetKind: Exclude<EntryKind, \"symlink\"> | null;\n targetSize: number | null;\n isTargetUnavailable: boolean;\n isCycle: boolean;\n }\n >();\n\n const fileHrefs = new Map<string, string | null>();\n const mappedFileHref = (logicalPath: string): string | null => {\n if (!fileHrefs.has(logicalPath)) {\n const href = options.fileHref!(logicalPath);\n if (\n href !== null &&\n (typeof href !== \"string\" ||\n href.length === 0 ||\n href.includes(\"\\\\\") ||\n Array.from(href).some((character) => character.charCodeAt(0) < 32) ||\n ![\"http:\", \"https:\"].includes(new URL(href, \"https://dirwell.invalid/\").protocol))\n )\n throw new TypeError(\n \"fileHref must return a synchronous HTTP(S), root-relative or relative URL, or null\",\n );\n fileHrefs.set(logicalPath, href);\n }\n return fileHrefs.get(logicalPath)!;\n };\n\n for (const { directory, logicalDir } of theme.searchIndex === false ? [] : plans) {\n for (const entry of directory.entries) {\n if (searchEntries.has(entry.relativePath)) continue;\n const targetLogicalPath = isUnavailableSymlink(entry)\n ? null\n : entry.symlink?.targetRelativePath !== null && entry.symlink !== null\n ? entry.symlink.targetRelativePath\n : path.posix.join(logicalDir, entry.name);\n const directoryLike = isDirectoryLike(entry);\n const linkedPath =\n directoryLike && targetLogicalPath !== null\n ? pagePathForDirectory(targetLogicalPath)\n : targetLogicalPath;\n let href: string | null = null;\n if (entry.symlink !== null && isUnavailableSymlink(entry)) {\n const filename = rawLinkFilename(entry);\n rawLinkFiles.set(filename, entry.symlink.target);\n href = `raw-links/${filename}`;\n } else if (linkedPath !== null) {\n href = createHrefResolver(\"__dirwell/search-index.json\", \"relative\", base)(linkedPath);\n if (!directoryLike && options.fileHref !== undefined) href = mappedFileHref(linkedPath);\n }\n searchEntries.set(entry.relativePath, {\n exitsExplorer:\n isUnavailableSymlink(entry) ||\n (targetLogicalPath !== null &&\n (!directoryLike || !generatedDirectories.has(targetLogicalPath))),\n href,\n isLink: entry.kind === \"symlink\",\n kind: entry.kind,\n modifiedAt: entry.metadata.times.modifiedAt,\n name: entry.name,\n path: entry.relativePath,\n size: entry.metadata.size,\n target: entry.symlink?.target ?? null,\n targetKind: entry.symlink?.targetKind ?? null,\n targetSize: entry.symlink?.targetSize ?? null,\n isTargetUnavailable: isUnavailableSymlink(entry),\n isCycle: entry.symlink?.isCycle ?? false,\n });\n }\n }\n\n for (const plan of plans) {\n const { directory, logicalDir, outputName: name } = plan;\n if (name === null) continue;\n\n const targetLogicalPathFor = (entry: FileSystemEntry): string | null => {\n if (isUnavailableSymlink(entry)) return null;\n if (entry.symlink?.targetRelativePath !== null && entry.symlink !== null) {\n return entry.symlink.targetRelativePath;\n }\n return path.posix.join(logicalDir, entry.name);\n };\n const currentRoute = generatedPages.get(logicalDir)!;\n const hrefForPath = createHrefResolver(currentRoute.publicPath, urlStrategy, base);\n const hrefForDirectory = (targetPath: string) => {\n const target = pagePathForDirectory(targetPath);\n return target === null ? null : hrefForPath(target);\n };\n const assetHrefForDirectory = (targetPath: string, assetName: string) => {\n assertSafeOutputName(assetName);\n return hrefForPath(path.posix.join(mode === \"ssg\" ? targetPath : \"__dirwell\", assetName));\n };\n const directoryTargets = new Map(\n directory.entries\n .filter((entry) => isDirectoryLike(entry) && !isUnavailableSymlink(entry))\n .flatMap((entry) => {\n const target = targetLogicalPathFor(entry);\n return target !== null && generatedDirectories.has(target)\n ? [[entry.relativePath, target] as const]\n : [];\n }),\n );\n const directoryPathForEntry = (entryId: string) => directoryTargets.get(entryId) ?? null;\n const hrefFor = (entry: FileSystemEntry): string | null => {\n if (entry.symlink !== null && isUnavailableSymlink(entry)) {\n const filename = rawLinkFilename(entry);\n rawLinkFiles.set(filename, entry.symlink.target);\n return hrefForPath(path.posix.join(\"__dirwell\", \"raw-links\", filename));\n }\n const targetLogicalPath = targetLogicalPathFor(entry);\n if (targetLogicalPath === null) return null;\n const directoryLike =\n entry.kind === \"directory\" || entry.symlink?.targetKind === \"directory\";\n if (!directoryLike && options.fileHref !== undefined)\n return mappedFileHref(targetLogicalPath);\n return directoryLike ? hrefForDirectory(targetLogicalPath) : hrefForPath(targetLogicalPath);\n };\n const baseContext: ThemeContext = {\n operation: \"build\",\n directory,\n documentBaseHref: urlStrategy === \"html-base\" ? base : null,\n outputName: name,\n mode,\n searchIndexHref: hrefForPath(\"__dirwell/search-index.json\"),\n sort: { ...defaultSort, ...options.sort },\n assetHref: (assetName) => assetHrefForDirectory(logicalDir, assetName),\n hrefFor,\n hrefForDirectory,\n assetHrefForDirectory,\n directoryPathForEntry,\n hasPageForDirectory: (relativePath) => generatedDirectories.has(relativePath),\n exitsExplorerFor: (entry) => {\n if (isUnavailableSymlink(entry)) return true;\n const targetLogicalPath = targetLogicalPathFor(entry);\n const directoryLike =\n entry.kind === \"directory\" || entry.symlink?.targetKind === \"directory\";\n return (\n targetLogicalPath !== null &&\n (!directoryLike || !generatedDirectories.has(targetLogicalPath))\n );\n },\n };\n const targetDir = path.join(buildOutputDir, logicalDir);\n await mkdir(targetDir, { recursive: true });\n const sort = baseContext.sort;\n let revision = \"\";\n let manifestHref = \"\";\n let pageSize = Math.max(1, directory.entries.length);\n if (options.listing !== undefined) {\n const records = directory.entries.map((entry) => {\n const projected = projectListingEntry(entry, baseContext);\n return theme.entryData === undefined\n ? projected\n : {\n ...projected,\n presentation: copyJsonObject(\n theme.entryData(projected, {\n mode: baseContext.mode,\n outputName: baseContext.outputName,\n assetHref: baseContext.assetHref,\n hrefForDirectory: baseContext.hrefForDirectory,\n assetHrefForDirectory,\n directoryPathForEntry,\n documentBaseHref: baseContext.documentBaseHref,\n directoryPath: logicalDir,\n }),\n ),\n };\n });\n const candidate =\n typeof options.listing === \"function\"\n ? options.listing({\n directoryPath: logicalDir,\n visibleCount: records.length,\n dataBytes: Buffer.byteLength(JSON.stringify(records)),\n mode,\n operation: \"build\",\n supportsPagination: theme.listing?.pagination === true,\n })\n : options.listing;\n validateListingPlan(candidate);\n if (candidate.html.strategy === \"paged\") {\n if (theme.listing?.pagination !== true)\n throw new Error(`Theme ${theme.name} does not support listing pagination`);\n pageSize = candidate.html.pageSize;\n }\n const directoryId = createHash(\"sha256\").update(logicalDir).digest(\"hex\");\n const manifestDirectory = path.posix.join(\"__dirwell\", \"listing\", directoryId);\n // Relative records belong to the document directory, not to the JSON asset directory.\n const documentDirectory = currentRoute.publicPath.endsWith(\"/\")\n ? currentRoute.publicPath\n : path.posix.dirname(currentRoute.publicPath || \".\");\n const hrefBase =\n urlStrategy === \"relative\"\n ? `../../../${documentDirectory === \".\" ? \"\" : `${encodeLogicalPath(documentDirectory.replace(/\\/$/, \"\"))}/`}`\n : base;\n const manifestPath = path.posix.join(manifestDirectory, \"manifest.json\");\n manifestHref = hrefForPath(manifestPath);\n const blocks: Array<ListingManifest[\"blocks\"][number]> = [];\n const blockSize =\n candidate.data.strategy === \"blocks\"\n ? candidate.data.blockSize\n : Math.max(1, records.length);\n revision = listingRevision(records, sort, hrefBase, blockSize);\n const outputListingDir = path.join(buildOutputDir, manifestDirectory);\n await mkdir(outputListingDir, { recursive: true });\n for (let offset = 0; offset < records.length || offset === 0; offset += blockSize) {\n const entries = records.slice(offset, offset + blockSize);\n const filename = `${revision}-${blocks.length}.json`;\n blocks.push({ href: filename, offset, count: entries.length });\n await writeFile(\n path.join(outputListingDir, filename),\n JSON.stringify({ version: 1, revision, offset, total: records.length, entries }),\n );\n }\n const manifest: ListingManifest = {\n version: 1,\n revision,\n directory: logicalDir,\n hrefBase,\n total: records.length,\n sort,\n blocks,\n };\n await writeFile(path.join(outputListingDir, \"manifest.json\"), JSON.stringify(manifest));\n }\n const pageCount = Math.max(1, Math.ceil(directory.entries.length / pageSize));\n const pagePrefix = `__dirwell-page-${createHash(\"sha256\").update(name).digest(\"hex\").slice(0, 16)}`;\n const pageNames = Array.from({ length: pageCount }, (_, index) =>\n index === 0 ? name : `${pagePrefix}-${index + 1}.html`,\n );\n const pageRoutes = pageNames.map((pageName, index) =>\n directoryRoute(\n logicalDir,\n pageName,\n name === \"index.html\" ? directoryUrls : \"explicit\",\n index + 1,\n ),\n );\n for (const route of pageRoutes.slice(1)) claimPage(route.outputPath);\n const pagedAssets = new Map<string, Buffer>();\n for (const [index, pageName] of pageNames.entries()) {\n const pageRoute = pageRoutes[index]!;\n const metadata = await metadataResolver?.resolve(\n directory,\n (outputPath) => hrefForPath(outputPath),\n pageRoute.publicPath,\n );\n const pageHref = (number: number): string =>\n hrefForPath(pageRoutes[number]?.publicPath ?? currentRoute.publicPath);\n const page = await theme.render({\n ...baseContext,\n ...(metadata === undefined ? {} : { metadata }),\n outputName: pageName,\n directory:\n options.listing === undefined\n ? directory\n : {\n ...directory,\n entries: directory.entries.slice(index * pageSize, (index + 1) * pageSize),\n },\n ...(options.listing === undefined\n ? {}\n : {\n listing: {\n revision,\n manifestHref,\n page: {\n number: index + 1,\n count: pageCount,\n offset: index * pageSize,\n total: directory.entries.length,\n href: pageHref(index),\n previousHref: index === 0 ? null : pageHref(index - 1),\n nextHref: index + 1 === pageCount ? null : pageHref(index + 1),\n },\n },\n }),\n });\n const pageDestination = path.join(buildOutputDir, pageRoute.outputPath);\n await mkdir(path.dirname(pageDestination), { recursive: true });\n await writeFile(pageDestination, page.html);\n for (const [assetName, contents] of Object.entries(page.assets ?? {})) {\n assertSafeOutputName(assetName);\n const assetPathKey = path.posix\n .join(mode === \"ssg\" ? logicalDir : \"__dirwell\", assetName)\n .normalize(\"NFC\")\n .toLowerCase();\n if (sourcePaths.has(assetPathKey))\n throw new Error(`Theme asset conflicts with source entry: ${assetName}`);\n if (claimedPages.has(assetPathKey))\n throw new Error(`Theme asset conflicts with listing page: ${assetName}`);\n claimedAssets.add(assetPathKey);\n if (options.listing !== undefined && pageCount > 1) {\n const bytes = Buffer.from(contents);\n const existing = pagedAssets.get(assetName);\n if (existing !== undefined && !existing.equals(bytes))\n throw new Error(`Theme emitted conflicting paginated asset: ${assetName}`);\n pagedAssets.set(assetName, bytes);\n }\n if (mode === \"ssg\") {\n await writeFile(path.join(targetDir, assetName), contents);\n } else {\n const bytes = Buffer.from(contents);\n const existing = sharedAssets.get(assetName);\n if (existing !== undefined && !Buffer.from(existing).equals(bytes)) {\n throw new Error(`Theme emitted conflicting shared asset: ${assetName}`);\n }\n sharedAssets.set(assetName, bytes);\n }\n }\n }\n }\n\n if (mode === \"mpa\" && sharedAssets.size > 0) {\n const assetDirectory = path.join(buildOutputDir, \"__dirwell\");\n await mkdir(assetDirectory, { recursive: true });\n for (const [assetName, contents] of sharedAssets) {\n await writeFile(path.join(assetDirectory, assetName), contents);\n }\n }\n\n for (const [filename, bytes] of metadataResolver?.assets ?? []) {\n const parts = filename.split(\"/\");\n for (let index = 1; index <= parts.length; index++) {\n const prefix = parts.slice(0, index).join(\"/\");\n if (selection.selected.has(prefix))\n throw new Error(`Metadata image conflicts with source entry: ${filename}`);\n const candidate = path.join(buildOutputDir, prefix);\n try {\n const info = await lstat(candidate);\n if (info.isSymbolicLink() || index === parts.length || !info.isDirectory())\n throw new Error(`Metadata image conflicts with generated output: ${filename}`);\n } catch (error) {\n if (!(error instanceof Error && \"code\" in error && error.code === \"ENOENT\")) throw error;\n }\n }\n const destination = path.join(buildOutputDir, filename);\n await mkdir(path.dirname(destination), { recursive: true });\n await writeFile(destination, bytes);\n }\n\n if (rawLinkFiles.size > 0) {\n const rawLinkDirectory = path.join(buildOutputDir, \"__dirwell\", \"raw-links\");\n await mkdir(rawLinkDirectory, { recursive: true });\n for (const [filename, target] of rawLinkFiles) {\n await writeFile(path.join(rawLinkDirectory, filename), target);\n }\n }\n if (theme.searchIndex !== false) {\n const generatedAssetDirectory = path.join(buildOutputDir, \"__dirwell\");\n await mkdir(generatedAssetDirectory, { recursive: true });\n const indexedEntries = [...searchEntries.values()];\n const shards: string[] = [];\n for (let offset = 0; offset < indexedEntries.length; offset += 512) {\n const filename = `search-${String(shards.length).padStart(5, \"0\")}.json`;\n shards.push(filename);\n await writeFile(\n path.join(generatedAssetDirectory, filename),\n JSON.stringify({ entries: indexedEntries.slice(offset, offset + 512) }),\n );\n }\n await writeFile(\n path.join(generatedAssetDirectory, \"search-index.json\"),\n JSON.stringify({ count: indexedEntries.length, shards, version: 3 }),\n );\n }\n\n await mkdir(path.join(buildOutputDir, \"__dirwell\"), { recursive: true });\n await writeFile(path.join(buildOutputDir, outputMarker), outputMarkerContents);\n await publishOutput(buildOutputDir, outputDir, sourceDir, options.outputPolicy);\n return report;\n } finally {\n if (stagedOutputDir !== null) await rm(stagedOutputDir, { recursive: true, force: true });\n }\n}\n\nexport async function readGeneratedPage(filePath: string): Promise<string> {\n return readFile(filePath, \"utf8\");\n}\n","/** Escape untrusted text for HTML/XML text nodes and double-quoted attributes. */\nexport function escapeHtml(value: string): string {\n return value\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replaceAll('\"', \""\");\n}\n","/** Some Windows Node/libuv versions omit the path-stat device ID but retain the file ID. */\nexport function fileIdentityMatches(\n before: { readonly device: number; readonly inode: number },\n descriptor: { readonly dev: number; readonly ino: number },\n platform = process.platform,\n): boolean {\n if (before.inode !== descriptor.ino) return false;\n if (before.device === descriptor.dev) return true;\n return platform === \"win32\" && before.device === 0 && before.inode !== 0;\n}\n","import { validateDirectoryOptions } from \"./validation.ts\";\nimport { readdir, realpath, open, type FileHandle } from \"node:fs/promises\";\nimport { constants } from \"node:fs\";\nimport { fileIdentityMatches } from \"./file-identity.ts\";\nimport path from \"node:path\";\nimport { describeEntry, isInsideRoot } from \"./filesystem.ts\";\nimport {\n selectSourcePaths,\n resolveIgnoreFiles,\n createIgnoreMatcher,\n normalizePathPatterns,\n type SelectionBudget,\n} from \"./filters.ts\";\nimport { compareEntries } from \"./generator.ts\";\nimport { entryDataFacts } from \"./listing.ts\";\nimport { copyJsonObject } from \"./json.ts\";\nimport type {\n DirectoryData,\n FileSystemEntry,\n SortOptions,\n IgnoreFile,\n EntryDataProvider,\n} from \"./model.ts\";\n\nexport interface DirectoryServiceOptions {\n readonly entryData?: EntryDataProvider;\n readonly root: string;\n readonly include?: string | readonly string[];\n readonly exclude?: string | readonly string[];\n readonly ignoreFiles?: readonly IgnoreFile[];\n readonly sort?: SortOptions;\n}\nexport interface DirectoryService {\n /** Bounded fresh enumeration; authorization runs before directory traversal. */\n readonly scan: (budget: SelectionBudget) => Promise<readonly FileSystemEntry[]>;\n readonly resolvePath: (logicalPath: string) => Promise<string>;\n readonly openFile: (logicalPath: string) => Promise<{\n readonly entry: FileSystemEntry;\n readonly handle: FileHandle;\n readonly canonicalPath: string;\n }>;\n readonly getEntry: (logicalPath: string) => Promise<FileSystemEntry>;\n readonly listDirectory: (logicalPath: string) => Promise<DirectoryData>;\n}\nexport class DirectoryAccessError extends Error {\n override readonly name = \"DirectoryAccessError\";\n}\nfunction assertLogicalPath(value: string): void {\n if (\n value.startsWith(\"/\") ||\n value.includes(\"\\\\\") ||\n value.includes(\"\\0\") ||\n value\n .split(\"/\")\n .some((segment) => segment === \".\" || segment === \"..\" || (segment === \"\" && value !== \"\")) ||\n /^[a-z]:/i.test(value) ||\n value === \"__dirwell\" ||\n value.startsWith(\"__dirwell/\")\n )\n throw new DirectoryAccessError(\"Unavailable path\");\n}\n/** Live reads; no watcher, recursive stat snapshot, output tree or implicit cache. */\nexport async function createDirectoryService(\n options: DirectoryServiceOptions,\n): Promise<DirectoryService> {\n validateDirectoryOptions(options);\n const root = await realpath(options.root);\n const ignoreFiles = resolveIgnoreFiles(options.ignoreFiles);\n // Validate explicit sources before an HTTP host starts accepting requests.\n await createIgnoreMatcher(ignoreFiles);\n normalizePathPatterns(options.include, \"include\");\n normalizePathPatterns(options.exclude, \"exclude\");\n const selected = async () => {\n const matcher = await createIgnoreMatcher(ignoreFiles);\n if (\n normalizePathPatterns(options.include, \"include\").length === 0 &&\n normalizePathPatterns(options.exclude, \"exclude\").length === 0 &&\n !matcher.hasRules\n )\n return null;\n return (\n await selectSourcePaths(\n root,\n path.join(root, \"__dirwell\"),\n options.include,\n options.exclude,\n [],\n ignoreFiles,\n matcher,\n )\n ).selected;\n };\n async function describe(\n logicalPath: string,\n selection: ReadonlySet<string> | null,\n ): Promise<FileSystemEntry> {\n assertLogicalPath(logicalPath);\n if (selection !== null && !selection.has(logicalPath))\n throw new DirectoryAccessError(\"Unavailable path\");\n const absolutePath = path.join(root, logicalPath);\n const parent = logicalPath === \"\" ? root : await realpath(path.dirname(absolutePath));\n if (!isInsideRoot(root, parent)) throw new DirectoryAccessError(\"Unavailable path\");\n const ancestors = new Set<string>([root]);\n if (logicalPath !== \"\") {\n const segments = logicalPath.split(\"/\").slice(0, -1);\n for (let length = 1; length <= segments.length; length++)\n ancestors.add(await realpath(path.join(root, ...segments.slice(0, length))));\n }\n return describeEntry({\n absolutePath,\n relativePath: logicalPath,\n rootRealPath: root,\n ancestors,\n followSymlinks: false,\n selectedPaths: selection,\n });\n }\n return {\n async scan(budget) {\n if (\n !budget ||\n !Number.isSafeInteger(budget.maxEntries) ||\n budget.maxEntries < 1 ||\n !Number.isSafeInteger(budget.maxDirectories) ||\n budget.maxDirectories < 1 ||\n !(budget.signal instanceof AbortSignal) ||\n typeof budget.allowPath !== \"function\"\n )\n throw new TypeError(\n \"scan requires positive entry/directory limits, an AbortSignal and allowPath\",\n );\n const selection = (\n await selectSourcePaths(\n root,\n path.join(root, \"__dirwell\"),\n options.include,\n options.exclude,\n [],\n ignoreFiles,\n undefined,\n budget,\n )\n ).selected;\n const entries: FileSystemEntry[] = [];\n for (const logical of [...selection].sort()) {\n budget.signal.throwIfAborted();\n if (!logical) continue;\n try {\n const entry = await describe(logical, selection);\n // Canonical physical entries cover safe aliases once, without following cycles.\n if (entry.kind === \"file\" || entry.kind === \"directory\") entries.push(entry);\n } catch (error) {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") continue;\n throw error;\n }\n }\n return entries;\n },\n async resolvePath(logicalPath) {\n const selection = await selected();\n const entry = await describe(logicalPath, selection);\n if (\n entry.symlink &&\n (entry.symlink.isBroken || entry.symlink.isOutsideRoot || entry.symlink.isTargetExcluded)\n )\n throw new DirectoryAccessError(\"Unavailable target\");\n const resolved = await realpath(entry.absolutePath);\n if (!isInsideRoot(root, resolved)) throw new DirectoryAccessError(\"Unavailable target\");\n const canonical = path.relative(root, resolved).split(path.sep).join(\"/\");\n if (selection !== null && !selection.has(canonical))\n throw new DirectoryAccessError(\"Unavailable target\");\n return canonical;\n },\n async openFile(logicalPath) {\n assertLogicalPath(logicalPath);\n const selection = await selected();\n let entry = await describe(logicalPath, selection);\n if (entry.symlink !== null) {\n if (\n entry.symlink.isBroken ||\n entry.symlink.isOutsideRoot ||\n entry.symlink.isTargetExcluded ||\n entry.symlink.targetRelativePath === null\n )\n throw new DirectoryAccessError(\"Unavailable file\");\n entry = await describe(entry.symlink.targetRelativePath, selection);\n }\n if (entry.kind !== \"file\") throw new DirectoryAccessError(\"Unavailable file\");\n const handle = await open(\n entry.absolutePath,\n constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0),\n );\n try {\n const stats = await handle.stat();\n const resolved = await realpath(entry.absolutePath);\n if (\n !stats.isFile() ||\n !isInsideRoot(root, resolved) ||\n !fileIdentityMatches(entry.metadata, stats)\n )\n throw new DirectoryAccessError(\"File changed before opening\");\n return {\n entry,\n handle,\n canonicalPath: path.relative(root, resolved).split(path.sep).join(\"/\"),\n };\n } catch (error) {\n await handle.close();\n throw error;\n }\n },\n async getEntry(logicalPath) {\n assertLogicalPath(logicalPath);\n return describe(logicalPath, await selected());\n },\n async listDirectory(logicalPath) {\n assertLogicalPath(logicalPath);\n const selection = await selected();\n const entry = await describe(logicalPath, selection);\n if (entry.kind !== \"directory\") throw new DirectoryAccessError(\"Unavailable directory\");\n const physical = await realpath(entry.absolutePath);\n if (!isInsideRoot(root, physical)) throw new DirectoryAccessError(\"Unavailable directory\");\n const names = (await readdir(physical)).filter(\n (name) => logicalPath !== \"\" || name !== \"__dirwell\",\n );\n const described = await Promise.all(\n names\n .filter((name) => selection === null || selection.has(path.posix.join(logicalPath, name)))\n .map(async (name) => {\n let entry: FileSystemEntry;\n try {\n entry = await describe(path.posix.join(logicalPath, name), selection);\n } catch (error) {\n // An enumerated child may disappear before stat; the directory is still available.\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") return null;\n throw error;\n }\n return options.entryData === undefined\n ? entry\n : {\n ...entry,\n publicData: copyJsonObject(\n await options.entryData(entryDataFacts(entry), {\n operation: \"server\",\n directoryPath: logicalPath,\n }),\n ),\n };\n }),\n );\n const entries = described.filter((entry): entry is FileSystemEntry => entry !== null);\n entries.sort(\n (left, right) =>\n compareEntries(left, right, options.sort) ||\n (left.relativePath < right.relativePath\n ? -1\n : left.relativePath > right.relativePath\n ? 1\n : 0),\n );\n return {\n root: await describe(\"\", selection),\n current: { ...entry, kind: \"directory\" },\n parent:\n logicalPath === \"\"\n ? null\n : await describe(\n path.posix.dirname(logicalPath) === \".\" ? \"\" : path.posix.dirname(logicalPath),\n selection,\n ),\n depth: logicalPath === \"\" ? 0 : logicalPath.split(\"/\").length,\n entries,\n };\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -65,6 +65,22 @@ interface PageMetadata {
|
|
|
65
65
|
readonly head: string;
|
|
66
66
|
}
|
|
67
67
|
//#endregion
|
|
68
|
+
//#region src/routing.d.ts
|
|
69
|
+
type DirectoryUrls = "explicit" | "slash" | "clean";
|
|
70
|
+
interface RoutingOptions {
|
|
71
|
+
readonly directoryUrls?: DirectoryUrls;
|
|
72
|
+
}
|
|
73
|
+
/** Validate the shared policy before publication or server startup. */
|
|
74
|
+
declare function resolveDirectoryUrls(value: unknown, name?: string): DirectoryUrls;
|
|
75
|
+
declare function encodeLogicalPath(value: string): string;
|
|
76
|
+
interface DirectoryRoute {
|
|
77
|
+
readonly outputPath: string;
|
|
78
|
+
/** Unencoded archive-relative public pathname; the root is an empty string. */
|
|
79
|
+
readonly publicPath: string;
|
|
80
|
+
}
|
|
81
|
+
/** Output placement and public URL are planned together, before theme rendering. */
|
|
82
|
+
declare function directoryRoute(directory: string, filename: string, style: DirectoryUrls, page?: number): DirectoryRoute;
|
|
83
|
+
//#endregion
|
|
68
84
|
//#region src/model.d.ts
|
|
69
85
|
type EntryKind = "directory" | "file" | "symlink" | "other";
|
|
70
86
|
interface FileTimes {
|
|
@@ -144,6 +160,8 @@ interface SortOptions {
|
|
|
144
160
|
interface ThemeContext {
|
|
145
161
|
readonly operation?: "build" | "server";
|
|
146
162
|
readonly updatesHref?: string;
|
|
163
|
+
readonly searchQueryHref?: string;
|
|
164
|
+
readonly searchRefreshIntervalMs?: number;
|
|
147
165
|
/** Optional host route for live framework page snapshots. */
|
|
148
166
|
readonly pageDataHref?: (theme: string, directoryPath: string, page: number) => string;
|
|
149
167
|
/** Present only when core listing publication is enabled. Entries are the current HTML slice. */
|
|
@@ -156,14 +174,18 @@ interface ThemeContext {
|
|
|
156
174
|
readonly searchIndexHref: string;
|
|
157
175
|
readonly sort: Required<SortOptions>;
|
|
158
176
|
readonly assetHref: (assetName: string) => string;
|
|
159
|
-
readonly hrefForDirectory: (relativePath: string) => string;
|
|
177
|
+
readonly hrefForDirectory: (relativePath: string) => string | null;
|
|
178
|
+
/** Resolve a theme-owned asset without inferring a directory from a page URL. */
|
|
179
|
+
readonly assetHrefForDirectory: (relativePath: string, assetName: string) => string;
|
|
180
|
+
/** Resolve a public entry id to its permitted generated directory identity. */
|
|
181
|
+
readonly directoryPathForEntry: (entryId: string) => string | null;
|
|
160
182
|
readonly hasPageForDirectory?: (relativePath: string) => boolean;
|
|
161
183
|
readonly hrefFor: (entry: FileSystemEntry) => string | null;
|
|
162
184
|
readonly exitsExplorerFor: (entry: FileSystemEntry) => boolean;
|
|
163
185
|
}
|
|
164
186
|
interface ExplorerTheme {
|
|
165
187
|
/** Node-side derivation receives public facts only, never raw stat/source paths. */
|
|
166
|
-
readonly entryData?: (entry: ListingEntry, context: Pick<ThemeContext, "mode" | "outputName" | "assetHref" | "hrefForDirectory" | "documentBaseHref" | "pageDataHref"> & {
|
|
188
|
+
readonly entryData?: (entry: ListingEntry, context: Pick<ThemeContext, "mode" | "outputName" | "assetHref" | "hrefForDirectory" | "documentBaseHref" | "pageDataHref" | "assetHrefForDirectory" | "directoryPathForEntry"> & {
|
|
167
189
|
readonly directoryPath: string;
|
|
168
190
|
}) => JsonObject;
|
|
169
191
|
/** Opt in only when the renderer handles listing.page and emits native pagination anchors. */
|
|
@@ -178,6 +200,7 @@ interface ExplorerTheme {
|
|
|
178
200
|
readonly render: (context: ThemeContext) => RenderedPage | Promise<RenderedPage>;
|
|
179
201
|
}
|
|
180
202
|
interface GenerateOptions {
|
|
203
|
+
readonly routing?: RoutingOptions;
|
|
181
204
|
/** Synchronous public file URL mapping, independent of explorer/data/asset URLs. */
|
|
182
205
|
readonly fileHref?: (logicalPath: string) => string | null;
|
|
183
206
|
readonly entryData?: EntryDataProvider;
|
|
@@ -221,6 +244,8 @@ interface BuildReport {
|
|
|
221
244
|
readonly pages: readonly {
|
|
222
245
|
readonly directory: string;
|
|
223
246
|
readonly outputName: string | null;
|
|
247
|
+
readonly outputPath: string | null;
|
|
248
|
+
readonly publicPath: string | null;
|
|
224
249
|
}[];
|
|
225
250
|
}
|
|
226
251
|
interface MetadataProvider {
|
|
@@ -316,5 +341,5 @@ declare function projectListingEntry(entry: FileSystemEntry, context: Pick<Theme
|
|
|
316
341
|
declare function entryDataFacts(entry: FileSystemEntry): Omit<ListingEntry, "href" | "exitsExplorer" | "presentation" | "data">;
|
|
317
342
|
declare function listingRevision(entries: readonly ListingEntry[], sort: Required<SortOptions>, hrefBase: string, blockSize: number): string;
|
|
318
343
|
//#endregion
|
|
319
|
-
export { SymlinkMetadata as A,
|
|
320
|
-
//# sourceMappingURL=listing-
|
|
344
|
+
export { SymlinkMetadata as A, MetadataImage as B, MetadataProviderFactory as C, SortDirection as D, RenderedPage as E, encodeLogicalPath as F, ResolvedMetadataContext as G, MetadataOptions as H, resolveDirectoryUrls as I, JsonValue as J, ThemeMetadataDefaults as K, ContentCounts as L, DirectoryUrls as M, RoutingOptions as N, SortField as O, directoryRoute as P, ImageSource as R, MetadataProvider as S, OutputNameResolver as T, MetadataValue as U, MetadataImageValue as V, PageMetadata as W, copyJsonObject as Y, FileMetadata as _, ListingPage as a, GenerateOptions as b, ListingPolicyContext as c, projectListingEntry as d, BuildReport as f, ExplorerTheme as g, EntryKind as h, ListingManifest as i, ThemeContext as j, SortOptions as k, entryDataFacts as l, EntryDataProvider as m, ListingContext as n, ListingPlan as o, DirectoryData as p, JsonObject as q, ListingEntry as r, ListingPolicy as s, ListingBlock as t, listingRevision as u, FileSystemEntry as v, NameSortMode as w, IgnoreFile as x, FileTimes as y, MetadataContext as z };
|
|
345
|
+
//# sourceMappingURL=listing-BrPLLSiX.d.mts.map
|