@dirwell/cli 0.0.2 → 0.0.4

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 CHANGED
@@ -11,8 +11,8 @@ The CLI supports short theme names and automatic `dirwell.config.mjs` discovery.
11
11
  ## Open or publish a folder
12
12
 
13
13
  ```sh
14
- npx @dirwell/cli@0.0.2 server ./files
15
- npx @dirwell/cli@0.0.2 build ./files --out ./generated
14
+ npx @dirwell/cli@0.0.3 server ./files
15
+ npx @dirwell/cli@0.0.3 build ./files --out ./generated
16
16
  ```
17
17
 
18
18
  No project, configuration file or global installation is required. Open the URL
@@ -42,7 +42,7 @@ Builds replace missing, empty or Dirwell-marked output directories. A nonempty u
42
42
  For repeated project use, install the CLI and the theme you want:
43
43
 
44
44
  ```sh
45
- npm install --save-dev @dirwell/cli@0.0.2 @dirwell/theme-ledger@0.0.2
45
+ npm install --save-dev @dirwell/cli@0.0.3 @dirwell/theme-ledger@0.0.3
46
46
  npx dirwell server ./files --theme ledger
47
47
  ```
48
48
 
@@ -89,3 +89,13 @@ Mode-specific flags fail when used with the other mode. Legacy dev/daemon
89
89
  commands are unsupported; the CLI does not silently translate them.
90
90
 
91
91
  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.
92
+
93
+ ## Directory URLs
94
+
95
+ Version 0.0.3 uses Core `routing: { directoryUrls: "explicit" }` by default
96
+ for every theme. Choose `slash` for directory-index hosts or `clean` for a
97
+ qualified extensionless host. Themes do not rewrite links. Existing source
98
+ homepages and download names are preserved; output conflicts fail. CLI config
99
+ puts routing at the top level, and `--directory-urls` overrides it.
100
+ See [directory URL guidance](https://vp-tw.github.io/dirwell/urls/) for output
101
+ placement, host requirements, native Plain file navigation and migration.
package/dist/bin.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as runCli } from "./src-BadHyOPx.mjs";
2
+ import { t as runCli } from "./src-CFuBl9jc.mjs";
3
3
  //#region src/bin.ts
4
4
  try {
5
5
  const server = await runCli(process.argv.slice(2));
package/dist/index.d.mts CHANGED
@@ -6,11 +6,12 @@ export declare function defineConfig<const T extends CliConfig>(config: T): T;
6
6
  //#endregion
7
7
  //#region src/index.d.ts
8
8
  export interface CliConfig {
9
+ readonly routing?: GenerateOptions["routing"];
9
10
  readonly ignoreFiles?: GenerateOptions["ignoreFiles"];
10
- readonly build?: Omit<GenerateOptions, "sourceDir" | "outputDir" | "theme" | "listing">;
11
+ readonly build?: Omit<GenerateOptions, "sourceDir" | "outputDir" | "theme" | "listing" | "routing">;
11
12
  readonly theme?: ExplorerTheme;
12
13
  readonly listing?: ListingPolicy;
13
- readonly server?: Omit<Parameters<typeof startExplorerServer>[0], "root" | "theme" | "listing">;
14
+ readonly server?: Omit<Parameters<typeof startExplorerServer>[0], "root" | "theme" | "listing" | "routing">;
14
15
  }
15
16
  /** Parse explicit commands; return server ownership to the caller for graceful shutdown. */
16
17
  export declare function runCli(args: readonly string[], cwd?: string, log?: (value: string) => void): Promise<{
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { n as defineConfig, t as runCli } from "./src-BadHyOPx.mjs";
1
+ import { n as defineConfig, t as runCli } from "./src-CFuBl9jc.mjs";
2
2
  export { defineConfig, runCli };
@@ -3,7 +3,7 @@ import { access, stat } from "node:fs/promises";
3
3
  import { pathToFileURL } from "node:url";
4
4
  import { resolve } from "import-meta-resolve";
5
5
  import { parseArgs } from "node:util";
6
- import { generateExplorer, resolveIgnoreFiles } from "@dirwell/core";
6
+ import { directoryRoute, encodeLogicalPath, generateExplorer, resolveDirectoryUrls, resolveIgnoreFiles } from "@dirwell/core";
7
7
  import { createPlainTheme } from "@dirwell/theme-plain";
8
8
  import { startExplorerServer } from "@dirwell/server/node";
9
9
  //#region src/config.ts
@@ -123,8 +123,10 @@ function validateCliConfig(value) {
123
123
  "listing",
124
124
  "build",
125
125
  "server",
126
- "ignoreFiles"
127
- ].includes(key)) throw new TypeError(`Unsupported config key "${key}". Use { theme, listing, build, server }; selection rules belong in build/server.`);
126
+ "ignoreFiles",
127
+ "routing"
128
+ ].includes(key)) throw new TypeError(`Unsupported config key "${key}". Use { theme, routing, listing, build, server }; selection rules belong in build/server.`);
129
+ resolveDirectoryUrls(config.routing);
128
130
  listing(config.listing, "listing");
129
131
  resolveIgnoreFiles(config.ignoreFiles);
130
132
  for (const mode of ["build", "server"]) {
@@ -223,7 +225,7 @@ Build options: --mode ssg|mpa, --urls relative|base|html-base, --base <path>
223
225
  Build safety: --dry-run, --force (-f) replaces an unowned nonempty output.
224
226
  Selection: --ignore-file <path> (repeatable), --no-ignore disables ignore files.
225
227
  Server options: --host <host>, --port <port>, --mount <path>, --live
226
- Both modes: --page-size <n>, --block-size <n>
228
+ Both modes: --directory-urls explicit|slash|clean, --page-size <n>, --block-size <n>
227
229
  Live server reads on request. It does not build an output tree or require watch mode.
228
230
  `;
229
231
  const positive = (value, name) => {
@@ -288,6 +290,7 @@ async function runCli(args, cwd = process.cwd(), log = console.log) {
288
290
  out: { type: "string" },
289
291
  mode: { type: "string" },
290
292
  urls: { type: "string" },
293
+ "directory-urls": { type: "string" },
291
294
  base: { type: "string" },
292
295
  host: { type: "string" },
293
296
  port: { type: "string" },
@@ -342,19 +345,14 @@ async function runCli(args, cwd = process.cwd(), log = console.log) {
342
345
  if (hasConfig) {
343
346
  const module = await import(pathToFileURL(configPath).href);
344
347
  if (typeof module.default !== "object" || module.default === null || Array.isArray(module.default)) throw new TypeError("Config must export an object");
345
- for (const key of Object.keys(module.default)) if (![
346
- "theme",
347
- "listing",
348
- "build",
349
- "server",
350
- "ignoreFiles"
351
- ].includes(key)) throw new TypeError(`Unsupported config key "${key}". Use { theme, listing, build, server }; selection rules belong in build/server.`);
352
348
  if (typeof module.default.then === "function") throw new TypeError("Config must export a resolved object; use top-level await for async setup");
353
349
  validateCliConfig(module.default);
354
350
  config = module.default;
355
351
  }
356
352
  const theme = parsed.values.theme !== void 0 ? await loadTheme(parsed.values.theme, cwd) : config.theme === void 0 ? createPlainTheme() : config.theme;
357
353
  assertTheme(theme);
354
+ const directoryUrls = resolveDirectoryUrls(parsed.values["directory-urls"] === void 0 ? config.routing : { directoryUrls: parsed.values["directory-urls"] });
355
+ const routing = { directoryUrls };
358
356
  const pageSize = positive(parsed.values["page-size"], "page-size"), blockSize = positive(parsed.values["block-size"], "block-size");
359
357
  const listing = pageSize !== void 0 || blockSize !== void 0 ? {
360
358
  html: pageSize === void 0 ? { strategy: "all" } : {
@@ -385,6 +383,7 @@ async function runCli(args, cwd = process.cwd(), log = console.log) {
385
383
  ].includes(urlStrategy)) throw new TypeError("Unknown URL strategy");
386
384
  const report = await generateExplorer({
387
385
  ...config.build,
386
+ routing,
388
387
  sourceDir: root,
389
388
  outputDir: path.resolve(cwd, parsed.values.out),
390
389
  theme,
@@ -400,7 +399,7 @@ async function runCli(args, cwd = process.cwd(), log = console.log) {
400
399
  else {
401
400
  log(`Built ${parsed.values.out}`);
402
401
  log(`${report.selectedPaths.length} selected paths; ${report.pages.filter((page) => page.outputName !== null).length} directory pages; ${report.pages.filter((page) => page.outputName === null).length} skipped pages.`);
403
- for (const page of report.pages) if (page.directory === "" || page.outputName === null) log(page.outputName === null ? `Skipped explorer page: ${page.directory || "/"}` : `Explorer entry: ${path.join(report.outputDir, page.outputName)}`);
402
+ for (const page of report.pages) if (page.directory === "" || page.outputName === null) log(page.outputName === null ? `Skipped explorer page: ${page.directory || "/"}` : `Explorer entry: ${path.join(report.outputDir, page.outputPath)} (public: ${page.publicPath || "/"})`);
404
403
  }
405
404
  return null;
406
405
  }
@@ -408,6 +407,7 @@ async function runCli(args, cwd = process.cwd(), log = console.log) {
408
407
  if (!Number.isSafeInteger(port) || port < 0 || port > 65535) throw new TypeError("port must be between 0 and 65535");
409
408
  const server = await startExplorerServer({
410
409
  ...config.server,
410
+ routing,
411
411
  ...parsed.values.live ? { live: config.server?.live || {} } : {},
412
412
  root,
413
413
  theme,
@@ -419,10 +419,10 @@ async function runCli(args, cwd = process.cwd(), log = console.log) {
419
419
  ...parsed.values.host === void 0 ? {} : { host: parsed.values.host },
420
420
  ...parsed.values.mount === void 0 ? {} : { mountPath: parsed.values.mount }
421
421
  });
422
- log(`Dirwell server: ${server.url}${parsed.values.mount ?? config.server?.mountPath ?? ""}/browse/`);
422
+ log(`Dirwell server: ${server.url}${parsed.values.mount ?? config.server?.mountPath ?? ""}/browse${directoryUrls === "clean" ? "" : "/"}${encodeLogicalPath(directoryRoute("", "index.html", directoryUrls).publicPath)}`);
423
423
  return server;
424
424
  }
425
425
  //#endregion
426
426
  export { defineConfig as n, runCli as t };
427
427
 
428
- //# sourceMappingURL=src-BadHyOPx.mjs.map
428
+ //# sourceMappingURL=src-CFuBl9jc.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"src-CFuBl9jc.mjs","names":["resolveModule"],"sources":["../src/config.ts","../src/index.ts"],"sourcesContent":["import type { CliConfig } from \"./index.ts\";\nimport { resolveIgnoreFiles, resolveDirectoryUrls } from \"@dirwell/core\";\n\ntype RecordValue = Record<string, unknown>;\nfunction object(value: unknown, name: string): RecordValue {\n if (typeof value !== \"object\" || value === null || Array.isArray(value))\n throw new TypeError(`${name} must be an object`);\n return value as RecordValue;\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}\nfunction keys(value: RecordValue, 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 config key \"${name}.${key}\".${suggestion ? ` Did you mean \"${name}.${suggestion}\"?` : ` Supported keys: ${allowed.join(\", \")}.`}`,\n );\n }\n}\nfunction scalar(value: unknown, type: \"string\" | \"boolean\" | \"number\" | \"function\", name: string) {\n if (\n value !== undefined &&\n (typeof value !== type || (type === \"number\" && !Number.isFinite(value)))\n )\n throw new TypeError(`${name} must be a ${type}`);\n}\nfunction patterns(value: unknown, name: string): void {\n if (\n value !== undefined &&\n typeof value !== \"string\" &&\n (!Array.isArray(value) || value.some((item) => typeof item !== \"string\"))\n )\n throw new TypeError(`${name} must be a string or an array of strings`);\n}\nfunction sort(value: unknown, name: string): void {\n if (value === undefined) return;\n const options = object(value, name);\n keys(options, [\"direction\", \"directoriesFirst\", \"field\", \"nameMode\"], name);\n scalar(options.directoriesFirst, \"boolean\", `${name}.directoriesFirst`);\n for (const [key, allowed] of Object.entries({\n direction: [\"asc\", \"desc\"],\n field: [\"name\", \"size\", \"modified\"],\n nameMode: [\"natural\", \"unicode\", \"locale\"],\n }))\n if (options[key] !== undefined && !allowed.includes(String(options[key])))\n throw new TypeError(`${name}.${key} must be one of ${allowed.join(\", \")}`);\n}\nfunction listing(value: unknown, name: string): void {\n if (value === undefined || typeof value === \"function\") return;\n const plan = object(value, name);\n keys(plan, [\"html\", \"data\"], name);\n for (const [key, sizedStrategy, sizeKey] of [\n [\"html\", \"paged\", \"pageSize\"],\n [\"data\", \"blocks\", \"blockSize\"],\n ] as const) {\n const strategy = object(plan[key], `${name}.${key}`);\n keys(\n strategy,\n strategy.strategy === sizedStrategy ? [\"strategy\", sizeKey] : [\"strategy\"],\n `${name}.${key}`,\n );\n if (strategy.strategy !== \"all\" && strategy.strategy !== sizedStrategy)\n throw new TypeError(`${name}.${key}.strategy must be all or ${sizedStrategy}`);\n if (\n strategy.strategy === sizedStrategy &&\n (!Number.isSafeInteger(strategy[sizeKey]) || Number(strategy[sizeKey]) < 1)\n )\n throw new TypeError(`${name}.${key}.${sizeKey} must be a positive integer`);\n }\n}\nconst buildKeys = {\n fileHref: true,\n entryData: true,\n dryRun: true,\n outputPolicy: true,\n ignoreFiles: true,\n metadata: true,\n base: true,\n include: true,\n exclude: true,\n mode: true,\n mirror: true,\n outputName: true,\n urlStrategy: true,\n symlinks: true,\n sort: true,\n} satisfies Record<keyof NonNullable<CliConfig[\"build\"]>, true>;\nconst serverKeys = {\n entryData: true,\n ignoreFiles: true,\n live: true,\n search: true,\n blockSize: true,\n mountPath: true,\n files: true,\n pageSize: true,\n maxPageSize: true,\n fileHref: true,\n headers: true,\n authorize: true,\n include: true,\n exclude: true,\n sort: true,\n host: true,\n port: true,\n} satisfies Record<keyof NonNullable<CliConfig[\"server\"]>, true>;\n\n/** Validate both modes before filesystem publication or server startup. */\nexport function validateCliConfig(value: unknown): asserts value is CliConfig {\n const config = object(value, \"Config\");\n // Preserve the top-level diagnostic used by CLI consumers.\n for (const key of Object.keys(config))\n if (![\"theme\", \"listing\", \"build\", \"server\", \"ignoreFiles\", \"routing\"].includes(key))\n throw new TypeError(\n `Unsupported config key \"${key}\". Use { theme, routing, listing, build, server }; selection rules belong in build/server.`,\n );\n resolveDirectoryUrls(config.routing);\n listing(config.listing, \"listing\");\n resolveIgnoreFiles(config.ignoreFiles as CliConfig[\"ignoreFiles\"]);\n for (const mode of [\"build\", \"server\"] as const) {\n if (config[mode] === undefined) continue;\n const options = object(config[mode], mode);\n keys(options, Object.keys(mode === \"build\" ? buildKeys : serverKeys), mode);\n patterns(options.include, `${mode}.include`);\n patterns(options.exclude, `${mode}.exclude`);\n resolveIgnoreFiles(options.ignoreFiles as CliConfig[\"ignoreFiles\"]);\n sort(options.sort, `${mode}.sort`);\n scalar(options.entryData, \"function\", `${mode}.entryData`);\n if (mode === \"build\") {\n scalar(options.fileHref, \"function\", \"build.fileHref\");\n for (const key of [\"base\", \"mode\", \"urlStrategy\"])\n scalar(options[key], \"string\", `${mode}.${key}`);\n scalar(options.mirror, \"boolean\", \"build.mirror\");\n scalar(options.dryRun, \"boolean\", \"build.dryRun\");\n if (\n options.outputPolicy !== undefined &&\n options.outputPolicy !== \"owned\" &&\n options.outputPolicy !== \"replace\"\n )\n throw new TypeError(\"build.outputPolicy must be owned or replace\");\n scalar(options.metadata, \"function\", \"build.metadata\");\n if (\n options.outputName !== undefined &&\n typeof options.outputName !== \"string\" &&\n typeof options.outputName !== \"function\"\n )\n throw new TypeError(\"build.outputName must be a string or function\");\n if (options.symlinks !== undefined) {\n const links = object(options.symlinks, \"build.symlinks\");\n keys(links, [\"follow\", \"boundary\", \"onCycle\"], \"build.symlinks\");\n scalar(links.follow, \"boolean\", \"build.symlinks.follow\");\n for (const [key, allowed] of Object.entries({\n boundary: [\"root\", \"anywhere\"],\n onCycle: [\"error\", \"skip\"],\n }))\n if (links[key] !== undefined && !allowed.includes(String(links[key])))\n throw new TypeError(`build.symlinks.${key} must be one of ${allowed.join(\", \")}`);\n }\n } else {\n for (const key of [\"host\", \"mountPath\"]) scalar(options[key], \"string\", `server.${key}`);\n for (const key of [\"port\", \"pageSize\", \"maxPageSize\", \"blockSize\"])\n scalar(options[key], \"number\", `server.${key}`);\n for (const key of [\"fileHref\", \"headers\", \"authorize\"])\n scalar(options[key], \"function\", `server.${key}`);\n if (\n options.files !== undefined &&\n options.files !== false &&\n ![\"media\", \"attachment\", \"inline\"].includes(String(options.files))\n )\n throw new TypeError(\"server.files must be false, media, attachment or inline\");\n if (options.live !== undefined && options.live !== false) {\n const live = object(options.live, \"server.live\");\n keys(live, [\"intervalMs\", \"maxClients\"], \"server.live\");\n for (const key of [\"intervalMs\", \"maxClients\"])\n scalar(live[key], \"number\", `server.live.${key}`);\n }\n if (options.search !== undefined && options.search !== false) {\n const search = object(options.search, \"server.search\");\n keys(\n search,\n [\n \"maxEntries\",\n \"maxDirectories\",\n \"maxConcurrent\",\n \"maxResults\",\n \"timeoutMs\",\n \"refreshIntervalMs\",\n ],\n \"server.search\",\n );\n if (typeof search.maxResults === \"number\" && search.maxResults > 10000)\n throw new TypeError(\"server.search.maxResults must not exceed 10000\");\n for (const key of Object.keys(search)) {\n scalar(search[key], \"number\", `server.search.${key}`);\n if (\n !Number.isSafeInteger(search[key]) ||\n Number(search[key]) < (key === \"refreshIntervalMs\" ? 1000 : 1)\n )\n throw new TypeError(`server.search.${key} must be a positive safe integer`);\n }\n }\n }\n }\n}\n\n/** Preserve literal types and validate JavaScript configuration at definition time. */\nexport function defineConfig<const T extends CliConfig>(config: T): T {\n validateCliConfig(config);\n return config;\n}\n","import path from \"node:path\";\nimport { access, stat } from \"node:fs/promises\";\nimport { pathToFileURL } from \"node:url\";\nimport { resolve as resolveModule } from \"import-meta-resolve\";\nimport { parseArgs } from \"node:util\";\nimport {\n generateExplorer,\n resolveIgnoreFiles,\n resolveDirectoryUrls,\n directoryRoute,\n encodeLogicalPath,\n} from \"@dirwell/core\";\nimport type { ExplorerTheme, ListingPolicy, GenerateOptions } from \"@dirwell/core\";\nimport { createPlainTheme } from \"@dirwell/theme-plain\";\nimport { startExplorerServer } from \"@dirwell/server/node\";\nimport { validateCliConfig } from \"./config.ts\";\nexport { defineConfig } from \"./config.ts\";\n\nexport interface CliConfig {\n readonly routing?: GenerateOptions[\"routing\"];\n readonly ignoreFiles?: GenerateOptions[\"ignoreFiles\"];\n readonly build?: Omit<\n GenerateOptions,\n \"sourceDir\" | \"outputDir\" | \"theme\" | \"listing\" | \"routing\"\n >;\n readonly theme?: ExplorerTheme;\n readonly listing?: ListingPolicy;\n readonly server?: Omit<\n Parameters<typeof startExplorerServer>[0],\n \"root\" | \"theme\" | \"listing\" | \"routing\"\n >;\n}\nconst help = `Usage:\n dirwell build <source> --out <output>\n dirwell server <source> [--port 3000]\n\nPlain is included and selected by default. Install other themes separately.\nOfficial themes: --theme plain|ledger|crosswave\nCustom themes: --theme <module>#<factory>, or a module's default export.\nReads dirwell.config.mjs in the working folder; --config <file.mjs> selects another file.\nConfig exports theme, listing, build options, and server hooks.\nBuild options: --mode ssg|mpa, --urls relative|base|html-base, --base <path>\nBuild safety: --dry-run, --force (-f) replaces an unowned nonempty output.\nSelection: --ignore-file <path> (repeatable), --no-ignore disables ignore files.\nServer options: --host <host>, --port <port>, --mount <path>, --live\nBoth modes: --directory-urls explicit|slash|clean, --page-size <n>, --block-size <n>\nLive server reads on request. It does not build an output tree or require watch mode.\n`;\nconst positive = (value: string | undefined, name: string): number | undefined => {\n if (value === undefined) return undefined;\n const number = Number(value);\n if (!Number.isSafeInteger(number) || number < 1)\n throw new TypeError(`${name} must be a positive integer`);\n return number;\n};\nfunction assertTheme(candidate: unknown): asserts candidate is ExplorerTheme {\n if (\n typeof candidate !== \"object\" ||\n candidate === null ||\n !(\"name\" in candidate) ||\n typeof candidate.name !== \"string\" ||\n !(\"render\" in candidate) ||\n typeof candidate.render !== \"function\"\n )\n throw new TypeError(\"Theme must be an object with name and render(context)\");\n}\nasync function loadTheme(specifier: string, cwd: string): Promise<ExplorerTheme> {\n const official: Record<string, string> = {\n plain: \"@dirwell/theme-plain#createPlainTheme\",\n ledger: \"@dirwell/theme-ledger#createLedgerTheme\",\n crosswave: \"@dirwell/theme-crosswave#createCrosswaveTheme\",\n };\n if (Object.hasOwn(official, specifier)) specifier = official[specifier]!;\n const delimiter = specifier.lastIndexOf(\"#\"),\n name = delimiter < 0 ? specifier : specifier.slice(0, delimiter),\n exported = delimiter < 0 ? \"default\" : specifier.slice(delimiter + 1);\n if (!name || !exported) throw new TypeError(\"Theme needs a module and export name\");\n let moduleUrl: string;\n if (name.startsWith(\".\") || path.isAbsolute(name)) {\n moduleUrl = pathToFileURL(path.resolve(cwd, name)).href;\n } else {\n try {\n moduleUrl = resolveModule(name, pathToFileURL(path.join(cwd, \"package.json\")).href);\n } catch (error) {\n if (!(error instanceof Error && \"code\" in error && error.code === \"ERR_MODULE_NOT_FOUND\"))\n throw error;\n // npm exec installs explicit --package entries beside the CLI, outside the user's folder.\n try {\n moduleUrl = resolveModule(name, import.meta.url);\n } catch (fallbackError) {\n if (\n fallbackError instanceof Error &&\n \"code\" in fallbackError &&\n fallbackError.code === \"ERR_MODULE_NOT_FOUND\"\n )\n throw new TypeError(\n `Theme package \"${name}\" was not found. Install it in your project or include it with npm exec --package=${name}.`,\n );\n throw fallbackError;\n }\n }\n }\n const module: Record<string, unknown> = await import(moduleUrl);\n const candidate =\n typeof module[exported] === \"function\" ? await module[exported]() : module[exported];\n assertTheme(candidate);\n return candidate;\n}\n/** Parse explicit commands; return server ownership to the caller for graceful shutdown. */\nexport async function runCli(\n args: readonly string[],\n cwd = process.cwd(),\n log: (value: string) => void = console.log,\n) {\n const parsed = parseArgs({\n args: [...args],\n allowPositionals: true,\n options: {\n help: { type: \"boolean\", short: \"h\" },\n force: { type: \"boolean\", short: \"f\" },\n \"dry-run\": { type: \"boolean\" },\n \"ignore-file\": { type: \"string\", multiple: true },\n \"no-ignore\": { type: \"boolean\" },\n live: { type: \"boolean\" },\n theme: { type: \"string\" },\n config: { type: \"string\" },\n out: { type: \"string\" },\n mode: { type: \"string\" },\n urls: { type: \"string\" },\n \"directory-urls\": { type: \"string\" },\n base: { type: \"string\" },\n host: { type: \"string\" },\n port: { type: \"string\" },\n mount: { type: \"string\" },\n \"page-size\": { type: \"string\" },\n \"block-size\": { type: \"string\" },\n },\n });\n if (parsed.values.help) {\n log(help);\n return null;\n }\n const [command, source] = parsed.positionals;\n if (\n ![\"build\", \"server\"].includes(command ?? \"\") ||\n source === undefined ||\n source === \"\" ||\n parsed.positionals.length !== 2\n )\n throw new TypeError(help);\n const otherModeOptions =\n command === \"build\"\n ? [\"host\", \"port\", \"mount\", \"live\"]\n : [\"out\", \"mode\", \"urls\", \"base\", \"force\", \"dry-run\"];\n for (const option of otherModeOptions)\n if (Object.hasOwn(parsed.values, option))\n throw new TypeError(`--${option} is not supported in ${command} mode`);\n let config: CliConfig = {};\n const configPath = path.resolve(cwd, parsed.values.config ?? \"dirwell.config.mjs\");\n let hasConfig = parsed.values.config !== undefined;\n if (!hasConfig) {\n try {\n await access(configPath);\n hasConfig = true;\n } catch (error) {\n if (!(error instanceof Error && \"code\" in error && error.code === \"ENOENT\")) throw error;\n }\n }\n if (!hasConfig) {\n for (const filename of [\n \"dirwell.config.ts\",\n \"dirwell.config.js\",\n \"dirwell.config.mts\",\n \"dirwell.config.cjs\",\n ]) {\n try {\n await access(path.resolve(cwd, filename));\n } catch (error) {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") continue;\n throw error;\n }\n throw new TypeError(\n `Found ${filename}. This CLI discovers dirwell.config.mjs only. Migrate the config or select a native JavaScript module with --config; existing rules will not be silently ignored.`,\n );\n }\n }\n if (hasConfig) {\n const module = await import(pathToFileURL(configPath).href);\n if (\n typeof module.default !== \"object\" ||\n module.default === null ||\n Array.isArray(module.default)\n )\n throw new TypeError(\"Config must export an object\");\n if (typeof module.default.then === \"function\")\n throw new TypeError(\n \"Config must export a resolved object; use top-level await for async setup\",\n );\n validateCliConfig(module.default);\n config = module.default;\n }\n const theme =\n parsed.values.theme !== undefined\n ? await loadTheme(parsed.values.theme, cwd)\n : config.theme === undefined\n ? createPlainTheme()\n : config.theme;\n assertTheme(theme);\n const directoryUrls = resolveDirectoryUrls(\n parsed.values[\"directory-urls\"] === undefined\n ? config.routing\n : { directoryUrls: parsed.values[\"directory-urls\"] },\n );\n const routing = { directoryUrls };\n const pageSize = positive(parsed.values[\"page-size\"], \"page-size\"),\n blockSize = positive(parsed.values[\"block-size\"], \"block-size\");\n const listing: ListingPolicy | undefined =\n pageSize !== undefined || blockSize !== undefined\n ? {\n html: pageSize === undefined ? { strategy: \"all\" } : { strategy: \"paged\", pageSize },\n data: blockSize === undefined ? { strategy: \"all\" } : { strategy: \"blocks\", blockSize },\n }\n : config.listing;\n const root = path.resolve(cwd, source);\n if (!(await stat(root)).isDirectory())\n throw new TypeError(\"Source must be an existing directory\");\n if (parsed.values[\"no-ignore\"] && parsed.values[\"ignore-file\"])\n throw new TypeError(\"--no-ignore cannot be combined with --ignore-file\");\n const modeOptions = command === \"build\" ? config.build : config.server;\n const ignoreFiles = resolveIgnoreFiles(\n parsed.values[\"no-ignore\"]\n ? []\n : (parsed.values[\"ignore-file\"] ??\n modeOptions?.ignoreFiles ??\n config.ignoreFiles ?? [{ path: \".gitignore\", optional: true }]),\n cwd,\n );\n if (command === \"build\") {\n if (parsed.values.out === undefined)\n throw new TypeError(\"Build requires --out pointing to a dedicated generated output tree\");\n const mode = parsed.values.mode ?? config.build?.mode ?? \"ssg\",\n urlStrategy = parsed.values.urls ?? config.build?.urlStrategy ?? \"relative\";\n if (mode !== \"ssg\" && mode !== \"mpa\") throw new TypeError(\"mode must be ssg or mpa\");\n if (![\"relative\", \"base\", \"html-base\"].includes(urlStrategy))\n throw new TypeError(\"Unknown URL strategy\");\n const report = await generateExplorer({\n ...config.build,\n routing,\n sourceDir: root,\n outputDir: path.resolve(cwd, parsed.values.out),\n theme,\n ignoreFiles,\n ...(parsed.values.force ? { outputPolicy: \"replace\" } : {}),\n ...(parsed.values[\"dry-run\"] ? { dryRun: true } : {}),\n mode,\n urlStrategy: urlStrategy as \"relative\" | \"base\" | \"html-base\",\n ...(parsed.values.base === undefined ? {} : { base: parsed.values.base }),\n ...(listing === undefined ? {} : { listing }),\n });\n if (report.dryRun) log(JSON.stringify(report, null, 2));\n else {\n log(`Built ${parsed.values.out}`);\n log(\n `${report.selectedPaths.length} selected paths; ${report.pages.filter((page) => page.outputName !== null).length} directory pages; ${report.pages.filter((page) => page.outputName === null).length} skipped pages.`,\n );\n for (const page of report.pages)\n if (page.directory === \"\" || page.outputName === null)\n log(\n page.outputName === null\n ? `Skipped explorer page: ${page.directory || \"/\"}`\n : `Explorer entry: ${path.join(report.outputDir, page.outputPath!)} (public: ${page.publicPath || \"/\"})`,\n );\n }\n return null;\n }\n const port = Number(parsed.values.port ?? process.env.PORT ?? config.server?.port ?? 0);\n if (!Number.isSafeInteger(port) || port < 0 || port > 65535)\n throw new TypeError(\"port must be between 0 and 65535\");\n const server = await startExplorerServer({\n ...config.server,\n routing,\n ...(parsed.values.live ? { live: config.server?.live || {} } : {}),\n root,\n theme,\n ignoreFiles,\n port,\n ...(listing === undefined ? {} : { listing }),\n ...(pageSize === undefined ? {} : { pageSize }),\n ...(blockSize === undefined ? {} : { blockSize }),\n ...(parsed.values.host === undefined ? {} : { host: parsed.values.host }),\n ...(parsed.values.mount === undefined ? {} : { mountPath: parsed.values.mount }),\n });\n log(\n `Dirwell server: ${server.url}${parsed.values.mount ?? config.server?.mountPath ?? \"\"}/browse${directoryUrls === \"clean\" ? \"\" : \"/\"}${encodeLogicalPath(directoryRoute(\"\", \"index.html\", directoryUrls).publicPath)}`,\n );\n return server;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dirwell/cli",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "Minimal file explorer build and live-server commands with Plain included.",
5
5
  "homepage": "https://vp-tw.github.io/dirwell/getting-started/",
6
6
  "bugs": {
@@ -33,9 +33,9 @@
33
33
  "tag": "latest"
34
34
  },
35
35
  "dependencies": {
36
- "@dirwell/core": "0.0.2",
37
- "@dirwell/server": "0.0.2",
38
- "@dirwell/theme-plain": "0.0.2",
36
+ "@dirwell/core": "0.0.4",
37
+ "@dirwell/server": "0.0.4",
38
+ "@dirwell/theme-plain": "0.0.4",
39
39
  "import-meta-resolve": "4.2.0"
40
40
  },
41
41
  "engines": {
@@ -1 +0,0 @@
1
- {"version":3,"file":"src-BadHyOPx.mjs","names":["resolveModule"],"sources":["../src/config.ts","../src/index.ts"],"sourcesContent":["import type { CliConfig } from \"./index.ts\";\nimport { resolveIgnoreFiles } from \"@dirwell/core\";\n\ntype RecordValue = Record<string, unknown>;\nfunction object(value: unknown, name: string): RecordValue {\n if (typeof value !== \"object\" || value === null || Array.isArray(value))\n throw new TypeError(`${name} must be an object`);\n return value as RecordValue;\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}\nfunction keys(value: RecordValue, 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 config key \"${name}.${key}\".${suggestion ? ` Did you mean \"${name}.${suggestion}\"?` : ` Supported keys: ${allowed.join(\", \")}.`}`,\n );\n }\n}\nfunction scalar(value: unknown, type: \"string\" | \"boolean\" | \"number\" | \"function\", name: string) {\n if (\n value !== undefined &&\n (typeof value !== type || (type === \"number\" && !Number.isFinite(value)))\n )\n throw new TypeError(`${name} must be a ${type}`);\n}\nfunction patterns(value: unknown, name: string): void {\n if (\n value !== undefined &&\n typeof value !== \"string\" &&\n (!Array.isArray(value) || value.some((item) => typeof item !== \"string\"))\n )\n throw new TypeError(`${name} must be a string or an array of strings`);\n}\nfunction sort(value: unknown, name: string): void {\n if (value === undefined) return;\n const options = object(value, name);\n keys(options, [\"direction\", \"directoriesFirst\", \"field\", \"nameMode\"], name);\n scalar(options.directoriesFirst, \"boolean\", `${name}.directoriesFirst`);\n for (const [key, allowed] of Object.entries({\n direction: [\"asc\", \"desc\"],\n field: [\"name\", \"size\", \"modified\"],\n nameMode: [\"natural\", \"unicode\", \"locale\"],\n }))\n if (options[key] !== undefined && !allowed.includes(String(options[key])))\n throw new TypeError(`${name}.${key} must be one of ${allowed.join(\", \")}`);\n}\nfunction listing(value: unknown, name: string): void {\n if (value === undefined || typeof value === \"function\") return;\n const plan = object(value, name);\n keys(plan, [\"html\", \"data\"], name);\n for (const [key, sizedStrategy, sizeKey] of [\n [\"html\", \"paged\", \"pageSize\"],\n [\"data\", \"blocks\", \"blockSize\"],\n ] as const) {\n const strategy = object(plan[key], `${name}.${key}`);\n keys(\n strategy,\n strategy.strategy === sizedStrategy ? [\"strategy\", sizeKey] : [\"strategy\"],\n `${name}.${key}`,\n );\n if (strategy.strategy !== \"all\" && strategy.strategy !== sizedStrategy)\n throw new TypeError(`${name}.${key}.strategy must be all or ${sizedStrategy}`);\n if (\n strategy.strategy === sizedStrategy &&\n (!Number.isSafeInteger(strategy[sizeKey]) || Number(strategy[sizeKey]) < 1)\n )\n throw new TypeError(`${name}.${key}.${sizeKey} must be a positive integer`);\n }\n}\nconst buildKeys = {\n fileHref: true,\n entryData: true,\n dryRun: true,\n outputPolicy: true,\n ignoreFiles: true,\n metadata: true,\n base: true,\n include: true,\n exclude: true,\n mode: true,\n mirror: true,\n outputName: true,\n urlStrategy: true,\n symlinks: true,\n sort: true,\n} satisfies Record<keyof NonNullable<CliConfig[\"build\"]>, true>;\nconst serverKeys = {\n entryData: true,\n ignoreFiles: true,\n live: true,\n search: true,\n blockSize: true,\n mountPath: true,\n files: true,\n pageSize: true,\n maxPageSize: true,\n fileHref: true,\n headers: true,\n authorize: true,\n include: true,\n exclude: true,\n sort: true,\n host: true,\n port: true,\n} satisfies Record<keyof NonNullable<CliConfig[\"server\"]>, true>;\n\n/** Validate both modes before filesystem publication or server startup. */\nexport function validateCliConfig(value: unknown): asserts value is CliConfig {\n const config = object(value, \"Config\");\n // Preserve the top-level diagnostic used by CLI consumers.\n for (const key of Object.keys(config))\n if (![\"theme\", \"listing\", \"build\", \"server\", \"ignoreFiles\"].includes(key))\n throw new TypeError(\n `Unsupported config key \"${key}\". Use { theme, listing, build, server }; selection rules belong in build/server.`,\n );\n listing(config.listing, \"listing\");\n resolveIgnoreFiles(config.ignoreFiles as CliConfig[\"ignoreFiles\"]);\n for (const mode of [\"build\", \"server\"] as const) {\n if (config[mode] === undefined) continue;\n const options = object(config[mode], mode);\n keys(options, Object.keys(mode === \"build\" ? buildKeys : serverKeys), mode);\n patterns(options.include, `${mode}.include`);\n patterns(options.exclude, `${mode}.exclude`);\n resolveIgnoreFiles(options.ignoreFiles as CliConfig[\"ignoreFiles\"]);\n sort(options.sort, `${mode}.sort`);\n scalar(options.entryData, \"function\", `${mode}.entryData`);\n if (mode === \"build\") {\n scalar(options.fileHref, \"function\", \"build.fileHref\");\n for (const key of [\"base\", \"mode\", \"urlStrategy\"])\n scalar(options[key], \"string\", `${mode}.${key}`);\n scalar(options.mirror, \"boolean\", \"build.mirror\");\n scalar(options.dryRun, \"boolean\", \"build.dryRun\");\n if (\n options.outputPolicy !== undefined &&\n options.outputPolicy !== \"owned\" &&\n options.outputPolicy !== \"replace\"\n )\n throw new TypeError(\"build.outputPolicy must be owned or replace\");\n scalar(options.metadata, \"function\", \"build.metadata\");\n if (\n options.outputName !== undefined &&\n typeof options.outputName !== \"string\" &&\n typeof options.outputName !== \"function\"\n )\n throw new TypeError(\"build.outputName must be a string or function\");\n if (options.symlinks !== undefined) {\n const links = object(options.symlinks, \"build.symlinks\");\n keys(links, [\"follow\", \"boundary\", \"onCycle\"], \"build.symlinks\");\n scalar(links.follow, \"boolean\", \"build.symlinks.follow\");\n for (const [key, allowed] of Object.entries({\n boundary: [\"root\", \"anywhere\"],\n onCycle: [\"error\", \"skip\"],\n }))\n if (links[key] !== undefined && !allowed.includes(String(links[key])))\n throw new TypeError(`build.symlinks.${key} must be one of ${allowed.join(\", \")}`);\n }\n } else {\n for (const key of [\"host\", \"mountPath\"]) scalar(options[key], \"string\", `server.${key}`);\n for (const key of [\"port\", \"pageSize\", \"maxPageSize\", \"blockSize\"])\n scalar(options[key], \"number\", `server.${key}`);\n for (const key of [\"fileHref\", \"headers\", \"authorize\"])\n scalar(options[key], \"function\", `server.${key}`);\n if (\n options.files !== undefined &&\n options.files !== false &&\n ![\"media\", \"attachment\", \"inline\"].includes(String(options.files))\n )\n throw new TypeError(\"server.files must be false, media, attachment or inline\");\n if (options.live !== undefined && options.live !== false) {\n const live = object(options.live, \"server.live\");\n keys(live, [\"intervalMs\", \"maxClients\"], \"server.live\");\n for (const key of [\"intervalMs\", \"maxClients\"])\n scalar(live[key], \"number\", `server.live.${key}`);\n }\n if (options.search !== undefined && options.search !== false) {\n const search = object(options.search, \"server.search\");\n keys(\n search,\n [\n \"maxEntries\",\n \"maxDirectories\",\n \"maxConcurrent\",\n \"maxResults\",\n \"timeoutMs\",\n \"refreshIntervalMs\",\n ],\n \"server.search\",\n );\n if (typeof search.maxResults === \"number\" && search.maxResults > 10000)\n throw new TypeError(\"server.search.maxResults must not exceed 10000\");\n for (const key of Object.keys(search)) {\n scalar(search[key], \"number\", `server.search.${key}`);\n if (\n !Number.isSafeInteger(search[key]) ||\n Number(search[key]) < (key === \"refreshIntervalMs\" ? 1000 : 1)\n )\n throw new TypeError(`server.search.${key} must be a positive safe integer`);\n }\n }\n }\n }\n}\n\n/** Preserve literal types and validate JavaScript configuration at definition time. */\nexport function defineConfig<const T extends CliConfig>(config: T): T {\n validateCliConfig(config);\n return config;\n}\n","import path from \"node:path\";\nimport { access, stat } from \"node:fs/promises\";\nimport { pathToFileURL } from \"node:url\";\nimport { resolve as resolveModule } from \"import-meta-resolve\";\nimport { parseArgs } from \"node:util\";\nimport { generateExplorer, resolveIgnoreFiles } from \"@dirwell/core\";\nimport type { ExplorerTheme, ListingPolicy, GenerateOptions } from \"@dirwell/core\";\nimport { createPlainTheme } from \"@dirwell/theme-plain\";\nimport { startExplorerServer } from \"@dirwell/server/node\";\nimport { validateCliConfig } from \"./config.ts\";\nexport { defineConfig } from \"./config.ts\";\n\nexport interface CliConfig {\n readonly ignoreFiles?: GenerateOptions[\"ignoreFiles\"];\n readonly build?: Omit<GenerateOptions, \"sourceDir\" | \"outputDir\" | \"theme\" | \"listing\">;\n readonly theme?: ExplorerTheme;\n readonly listing?: ListingPolicy;\n readonly server?: Omit<Parameters<typeof startExplorerServer>[0], \"root\" | \"theme\" | \"listing\">;\n}\nconst help = `Usage:\n dirwell build <source> --out <output>\n dirwell server <source> [--port 3000]\n\nPlain is included and selected by default. Install other themes separately.\nOfficial themes: --theme plain|ledger|crosswave\nCustom themes: --theme <module>#<factory>, or a module's default export.\nReads dirwell.config.mjs in the working folder; --config <file.mjs> selects another file.\nConfig exports theme, listing, build options, and server hooks.\nBuild options: --mode ssg|mpa, --urls relative|base|html-base, --base <path>\nBuild safety: --dry-run, --force (-f) replaces an unowned nonempty output.\nSelection: --ignore-file <path> (repeatable), --no-ignore disables ignore files.\nServer options: --host <host>, --port <port>, --mount <path>, --live\nBoth modes: --page-size <n>, --block-size <n>\nLive server reads on request. It does not build an output tree or require watch mode.\n`;\nconst positive = (value: string | undefined, name: string): number | undefined => {\n if (value === undefined) return undefined;\n const number = Number(value);\n if (!Number.isSafeInteger(number) || number < 1)\n throw new TypeError(`${name} must be a positive integer`);\n return number;\n};\nfunction assertTheme(candidate: unknown): asserts candidate is ExplorerTheme {\n if (\n typeof candidate !== \"object\" ||\n candidate === null ||\n !(\"name\" in candidate) ||\n typeof candidate.name !== \"string\" ||\n !(\"render\" in candidate) ||\n typeof candidate.render !== \"function\"\n )\n throw new TypeError(\"Theme must be an object with name and render(context)\");\n}\nasync function loadTheme(specifier: string, cwd: string): Promise<ExplorerTheme> {\n const official: Record<string, string> = {\n plain: \"@dirwell/theme-plain#createPlainTheme\",\n ledger: \"@dirwell/theme-ledger#createLedgerTheme\",\n crosswave: \"@dirwell/theme-crosswave#createCrosswaveTheme\",\n };\n if (Object.hasOwn(official, specifier)) specifier = official[specifier]!;\n const delimiter = specifier.lastIndexOf(\"#\"),\n name = delimiter < 0 ? specifier : specifier.slice(0, delimiter),\n exported = delimiter < 0 ? \"default\" : specifier.slice(delimiter + 1);\n if (!name || !exported) throw new TypeError(\"Theme needs a module and export name\");\n let moduleUrl: string;\n if (name.startsWith(\".\") || path.isAbsolute(name)) {\n moduleUrl = pathToFileURL(path.resolve(cwd, name)).href;\n } else {\n try {\n moduleUrl = resolveModule(name, pathToFileURL(path.join(cwd, \"package.json\")).href);\n } catch (error) {\n if (!(error instanceof Error && \"code\" in error && error.code === \"ERR_MODULE_NOT_FOUND\"))\n throw error;\n // npm exec installs explicit --package entries beside the CLI, outside the user's folder.\n try {\n moduleUrl = resolveModule(name, import.meta.url);\n } catch (fallbackError) {\n if (\n fallbackError instanceof Error &&\n \"code\" in fallbackError &&\n fallbackError.code === \"ERR_MODULE_NOT_FOUND\"\n )\n throw new TypeError(\n `Theme package \"${name}\" was not found. Install it in your project or include it with npm exec --package=${name}.`,\n );\n throw fallbackError;\n }\n }\n }\n const module: Record<string, unknown> = await import(moduleUrl);\n const candidate =\n typeof module[exported] === \"function\" ? await module[exported]() : module[exported];\n assertTheme(candidate);\n return candidate;\n}\n/** Parse explicit commands; return server ownership to the caller for graceful shutdown. */\nexport async function runCli(\n args: readonly string[],\n cwd = process.cwd(),\n log: (value: string) => void = console.log,\n) {\n const parsed = parseArgs({\n args: [...args],\n allowPositionals: true,\n options: {\n help: { type: \"boolean\", short: \"h\" },\n force: { type: \"boolean\", short: \"f\" },\n \"dry-run\": { type: \"boolean\" },\n \"ignore-file\": { type: \"string\", multiple: true },\n \"no-ignore\": { type: \"boolean\" },\n live: { type: \"boolean\" },\n theme: { type: \"string\" },\n config: { type: \"string\" },\n out: { type: \"string\" },\n mode: { type: \"string\" },\n urls: { type: \"string\" },\n base: { type: \"string\" },\n host: { type: \"string\" },\n port: { type: \"string\" },\n mount: { type: \"string\" },\n \"page-size\": { type: \"string\" },\n \"block-size\": { type: \"string\" },\n },\n });\n if (parsed.values.help) {\n log(help);\n return null;\n }\n const [command, source] = parsed.positionals;\n if (\n ![\"build\", \"server\"].includes(command ?? \"\") ||\n source === undefined ||\n source === \"\" ||\n parsed.positionals.length !== 2\n )\n throw new TypeError(help);\n const otherModeOptions =\n command === \"build\"\n ? [\"host\", \"port\", \"mount\", \"live\"]\n : [\"out\", \"mode\", \"urls\", \"base\", \"force\", \"dry-run\"];\n for (const option of otherModeOptions)\n if (Object.hasOwn(parsed.values, option))\n throw new TypeError(`--${option} is not supported in ${command} mode`);\n let config: CliConfig = {};\n const configPath = path.resolve(cwd, parsed.values.config ?? \"dirwell.config.mjs\");\n let hasConfig = parsed.values.config !== undefined;\n if (!hasConfig) {\n try {\n await access(configPath);\n hasConfig = true;\n } catch (error) {\n if (!(error instanceof Error && \"code\" in error && error.code === \"ENOENT\")) throw error;\n }\n }\n if (!hasConfig) {\n for (const filename of [\n \"dirwell.config.ts\",\n \"dirwell.config.js\",\n \"dirwell.config.mts\",\n \"dirwell.config.cjs\",\n ]) {\n try {\n await access(path.resolve(cwd, filename));\n } catch (error) {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") continue;\n throw error;\n }\n throw new TypeError(\n `Found ${filename}. This CLI discovers dirwell.config.mjs only. Migrate the config or select a native JavaScript module with --config; existing rules will not be silently ignored.`,\n );\n }\n }\n if (hasConfig) {\n const module = await import(pathToFileURL(configPath).href);\n if (\n typeof module.default !== \"object\" ||\n module.default === null ||\n Array.isArray(module.default)\n )\n throw new TypeError(\"Config must export an object\");\n for (const key of Object.keys(module.default))\n if (![\"theme\", \"listing\", \"build\", \"server\", \"ignoreFiles\"].includes(key))\n throw new TypeError(\n `Unsupported config key \"${key}\". Use { theme, listing, build, server }; selection rules belong in build/server.`,\n );\n if (typeof module.default.then === \"function\")\n throw new TypeError(\n \"Config must export a resolved object; use top-level await for async setup\",\n );\n validateCliConfig(module.default);\n config = module.default;\n }\n const theme =\n parsed.values.theme !== undefined\n ? await loadTheme(parsed.values.theme, cwd)\n : config.theme === undefined\n ? createPlainTheme()\n : config.theme;\n assertTheme(theme);\n const pageSize = positive(parsed.values[\"page-size\"], \"page-size\"),\n blockSize = positive(parsed.values[\"block-size\"], \"block-size\");\n const listing: ListingPolicy | undefined =\n pageSize !== undefined || blockSize !== undefined\n ? {\n html: pageSize === undefined ? { strategy: \"all\" } : { strategy: \"paged\", pageSize },\n data: blockSize === undefined ? { strategy: \"all\" } : { strategy: \"blocks\", blockSize },\n }\n : config.listing;\n const root = path.resolve(cwd, source);\n if (!(await stat(root)).isDirectory())\n throw new TypeError(\"Source must be an existing directory\");\n if (parsed.values[\"no-ignore\"] && parsed.values[\"ignore-file\"])\n throw new TypeError(\"--no-ignore cannot be combined with --ignore-file\");\n const modeOptions = command === \"build\" ? config.build : config.server;\n const ignoreFiles = resolveIgnoreFiles(\n parsed.values[\"no-ignore\"]\n ? []\n : (parsed.values[\"ignore-file\"] ??\n modeOptions?.ignoreFiles ??\n config.ignoreFiles ?? [{ path: \".gitignore\", optional: true }]),\n cwd,\n );\n if (command === \"build\") {\n if (parsed.values.out === undefined)\n throw new TypeError(\"Build requires --out pointing to a dedicated generated output tree\");\n const mode = parsed.values.mode ?? config.build?.mode ?? \"ssg\",\n urlStrategy = parsed.values.urls ?? config.build?.urlStrategy ?? \"relative\";\n if (mode !== \"ssg\" && mode !== \"mpa\") throw new TypeError(\"mode must be ssg or mpa\");\n if (![\"relative\", \"base\", \"html-base\"].includes(urlStrategy))\n throw new TypeError(\"Unknown URL strategy\");\n const report = await generateExplorer({\n ...config.build,\n sourceDir: root,\n outputDir: path.resolve(cwd, parsed.values.out),\n theme,\n ignoreFiles,\n ...(parsed.values.force ? { outputPolicy: \"replace\" } : {}),\n ...(parsed.values[\"dry-run\"] ? { dryRun: true } : {}),\n mode,\n urlStrategy: urlStrategy as \"relative\" | \"base\" | \"html-base\",\n ...(parsed.values.base === undefined ? {} : { base: parsed.values.base }),\n ...(listing === undefined ? {} : { listing }),\n });\n if (report.dryRun) log(JSON.stringify(report, null, 2));\n else {\n log(`Built ${parsed.values.out}`);\n log(\n `${report.selectedPaths.length} selected paths; ${report.pages.filter((page) => page.outputName !== null).length} directory pages; ${report.pages.filter((page) => page.outputName === null).length} skipped pages.`,\n );\n for (const page of report.pages)\n if (page.directory === \"\" || page.outputName === null)\n log(\n page.outputName === null\n ? `Skipped explorer page: ${page.directory || \"/\"}`\n : `Explorer entry: ${path.join(report.outputDir, page.outputName)}`,\n );\n }\n return null;\n }\n const port = Number(parsed.values.port ?? process.env.PORT ?? config.server?.port ?? 0);\n if (!Number.isSafeInteger(port) || port < 0 || port > 65535)\n throw new TypeError(\"port must be between 0 and 65535\");\n const server = await startExplorerServer({\n ...config.server,\n ...(parsed.values.live ? { live: config.server?.live || {} } : {}),\n root,\n theme,\n ignoreFiles,\n port,\n ...(listing === undefined ? {} : { listing }),\n ...(pageSize === undefined ? {} : { pageSize }),\n ...(blockSize === undefined ? {} : { blockSize }),\n ...(parsed.values.host === undefined ? {} : { host: parsed.values.host }),\n ...(parsed.values.mount === undefined ? {} : { mountPath: parsed.values.mount }),\n });\n log(\n `Dirwell server: ${server.url}${parsed.values.mount ?? config.server?.mountPath ?? \"\"}/browse/`,\n );\n return server;\n}\n"],"mappings":";;;;;;;;;AAIA,SAAS,OAAO,OAAgB,MAA2B;CACzD,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,SAAS,KAAK,OAAoB,SAA4B,MAAoB;CAChF,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,2BAA2B,KAAK,GAAG,IAAI,IAAI,aAAa,kBAAkB,KAAK,GAAG,WAAW,MAAM,oBAAoB,QAAQ,KAAK,IAAI,EAAE,IAC5I;CACF;AACF;AACA,SAAS,OAAO,OAAgB,MAAoD,MAAc;CAChG,IACE,UAAU,KAAA,MACT,OAAO,UAAU,QAAS,SAAS,YAAY,CAAC,OAAO,SAAS,KAAK,IAEtE,MAAM,IAAI,UAAU,GAAG,KAAK,aAAa,MAAM;AACnD;AACA,SAAS,SAAS,OAAgB,MAAoB;CACpD,IACE,UAAU,KAAA,KACV,OAAO,UAAU,aAChB,CAAC,MAAM,QAAQ,KAAK,KAAK,MAAM,MAAM,SAAS,OAAO,SAAS,QAAQ,IAEvE,MAAM,IAAI,UAAU,GAAG,KAAK,yCAAyC;AACzE;AACA,SAAS,KAAK,OAAgB,MAAoB;CAChD,IAAI,UAAU,KAAA,GAAW;CACzB,MAAM,UAAU,OAAO,OAAO,IAAI;CAClC,KAAK,SAAS;EAAC;EAAa;EAAoB;EAAS;CAAU,GAAG,IAAI;CAC1E,OAAO,QAAQ,kBAAkB,WAAW,GAAG,KAAK,kBAAkB;CACtE,KAAK,MAAM,CAAC,KAAK,YAAY,OAAO,QAAQ;EAC1C,WAAW,CAAC,OAAO,MAAM;EACzB,OAAO;GAAC;GAAQ;GAAQ;EAAU;EAClC,UAAU;GAAC;GAAW;GAAW;EAAQ;CAC3C,CAAC,GACC,IAAI,QAAQ,SAAS,KAAA,KAAa,CAAC,QAAQ,SAAS,OAAO,QAAQ,IAAI,CAAC,GACtE,MAAM,IAAI,UAAU,GAAG,KAAK,GAAG,IAAI,kBAAkB,QAAQ,KAAK,IAAI,GAAG;AAC/E;AACA,SAAS,QAAQ,OAAgB,MAAoB;CACnD,IAAI,UAAU,KAAA,KAAa,OAAO,UAAU,YAAY;CACxD,MAAM,OAAO,OAAO,OAAO,IAAI;CAC/B,KAAK,MAAM,CAAC,QAAQ,MAAM,GAAG,IAAI;CACjC,KAAK,MAAM,CAAC,KAAK,eAAe,YAAY,CAC1C;EAAC;EAAQ;EAAS;CAAU,GAC5B;EAAC;EAAQ;EAAU;CAAW,CAChC,GAAY;EACV,MAAM,WAAW,OAAO,KAAK,MAAM,GAAG,KAAK,GAAG,KAAK;EACnD,KACE,UACA,SAAS,aAAa,gBAAgB,CAAC,YAAY,OAAO,IAAI,CAAC,UAAU,GACzE,GAAG,KAAK,GAAG,KACb;EACA,IAAI,SAAS,aAAa,SAAS,SAAS,aAAa,eACvD,MAAM,IAAI,UAAU,GAAG,KAAK,GAAG,IAAI,2BAA2B,eAAe;EAC/E,IACE,SAAS,aAAa,kBACrB,CAAC,OAAO,cAAc,SAAS,QAAQ,KAAK,OAAO,SAAS,QAAQ,IAAI,IAEzE,MAAM,IAAI,UAAU,GAAG,KAAK,GAAG,IAAI,GAAG,QAAQ,4BAA4B;CAC9E;AACF;AACA,MAAM,YAAY;CAChB,UAAU;CACV,WAAW;CACX,QAAQ;CACR,cAAc;CACd,aAAa;CACb,UAAU;CACV,MAAM;CACN,SAAS;CACT,SAAS;CACT,MAAM;CACN,QAAQ;CACR,YAAY;CACZ,aAAa;CACb,UAAU;CACV,MAAM;AACR;AACA,MAAM,aAAa;CACjB,WAAW;CACX,aAAa;CACb,MAAM;CACN,QAAQ;CACR,WAAW;CACX,WAAW;CACX,OAAO;CACP,UAAU;CACV,aAAa;CACb,UAAU;CACV,SAAS;CACT,WAAW;CACX,SAAS;CACT,SAAS;CACT,MAAM;CACN,MAAM;CACN,MAAM;AACR;;AAGA,SAAgB,kBAAkB,OAA4C;CAC5E,MAAM,SAAS,OAAO,OAAO,QAAQ;CAErC,KAAK,MAAM,OAAO,OAAO,KAAK,MAAM,GAClC,IAAI,CAAC;EAAC;EAAS;EAAW;EAAS;EAAU;CAAa,CAAC,CAAC,SAAS,GAAG,GACtE,MAAM,IAAI,UACR,2BAA2B,IAAI,kFACjC;CACJ,QAAQ,OAAO,SAAS,SAAS;CACjC,mBAAmB,OAAO,WAAuC;CACjE,KAAK,MAAM,QAAQ,CAAC,SAAS,QAAQ,GAAY;EAC/C,IAAI,OAAO,UAAU,KAAA,GAAW;EAChC,MAAM,UAAU,OAAO,OAAO,OAAO,IAAI;EACzC,KAAK,SAAS,OAAO,KAAK,SAAS,UAAU,YAAY,UAAU,GAAG,IAAI;EAC1E,SAAS,QAAQ,SAAS,GAAG,KAAK,SAAS;EAC3C,SAAS,QAAQ,SAAS,GAAG,KAAK,SAAS;EAC3C,mBAAmB,QAAQ,WAAuC;EAClE,KAAK,QAAQ,MAAM,GAAG,KAAK,MAAM;EACjC,OAAO,QAAQ,WAAW,YAAY,GAAG,KAAK,WAAW;EACzD,IAAI,SAAS,SAAS;GACpB,OAAO,QAAQ,UAAU,YAAY,gBAAgB;GACrD,KAAK,MAAM,OAAO;IAAC;IAAQ;IAAQ;GAAa,GAC9C,OAAO,QAAQ,MAAM,UAAU,GAAG,KAAK,GAAG,KAAK;GACjD,OAAO,QAAQ,QAAQ,WAAW,cAAc;GAChD,OAAO,QAAQ,QAAQ,WAAW,cAAc;GAChD,IACE,QAAQ,iBAAiB,KAAA,KACzB,QAAQ,iBAAiB,WACzB,QAAQ,iBAAiB,WAEzB,MAAM,IAAI,UAAU,6CAA6C;GACnE,OAAO,QAAQ,UAAU,YAAY,gBAAgB;GACrD,IACE,QAAQ,eAAe,KAAA,KACvB,OAAO,QAAQ,eAAe,YAC9B,OAAO,QAAQ,eAAe,YAE9B,MAAM,IAAI,UAAU,+CAA+C;GACrE,IAAI,QAAQ,aAAa,KAAA,GAAW;IAClC,MAAM,QAAQ,OAAO,QAAQ,UAAU,gBAAgB;IACvD,KAAK,OAAO;KAAC;KAAU;KAAY;IAAS,GAAG,gBAAgB;IAC/D,OAAO,MAAM,QAAQ,WAAW,uBAAuB;IACvD,KAAK,MAAM,CAAC,KAAK,YAAY,OAAO,QAAQ;KAC1C,UAAU,CAAC,QAAQ,UAAU;KAC7B,SAAS,CAAC,SAAS,MAAM;IAC3B,CAAC,GACC,IAAI,MAAM,SAAS,KAAA,KAAa,CAAC,QAAQ,SAAS,OAAO,MAAM,IAAI,CAAC,GAClE,MAAM,IAAI,UAAU,kBAAkB,IAAI,kBAAkB,QAAQ,KAAK,IAAI,GAAG;GACtF;EACF,OAAO;GACL,KAAK,MAAM,OAAO,CAAC,QAAQ,WAAW,GAAG,OAAO,QAAQ,MAAM,UAAU,UAAU,KAAK;GACvF,KAAK,MAAM,OAAO;IAAC;IAAQ;IAAY;IAAe;GAAW,GAC/D,OAAO,QAAQ,MAAM,UAAU,UAAU,KAAK;GAChD,KAAK,MAAM,OAAO;IAAC;IAAY;IAAW;GAAW,GACnD,OAAO,QAAQ,MAAM,YAAY,UAAU,KAAK;GAClD,IACE,QAAQ,UAAU,KAAA,KAClB,QAAQ,UAAU,SAClB,CAAC;IAAC;IAAS;IAAc;GAAQ,CAAC,CAAC,SAAS,OAAO,QAAQ,KAAK,CAAC,GAEjE,MAAM,IAAI,UAAU,yDAAyD;GAC/E,IAAI,QAAQ,SAAS,KAAA,KAAa,QAAQ,SAAS,OAAO;IACxD,MAAM,OAAO,OAAO,QAAQ,MAAM,aAAa;IAC/C,KAAK,MAAM,CAAC,cAAc,YAAY,GAAG,aAAa;IACtD,KAAK,MAAM,OAAO,CAAC,cAAc,YAAY,GAC3C,OAAO,KAAK,MAAM,UAAU,eAAe,KAAK;GACpD;GACA,IAAI,QAAQ,WAAW,KAAA,KAAa,QAAQ,WAAW,OAAO;IAC5D,MAAM,SAAS,OAAO,QAAQ,QAAQ,eAAe;IACrD,KACE,QACA;KACE;KACA;KACA;KACA;KACA;KACA;IACF,GACA,eACF;IACA,IAAI,OAAO,OAAO,eAAe,YAAY,OAAO,aAAa,KAC/D,MAAM,IAAI,UAAU,gDAAgD;IACtE,KAAK,MAAM,OAAO,OAAO,KAAK,MAAM,GAAG;KACrC,OAAO,OAAO,MAAM,UAAU,iBAAiB,KAAK;KACpD,IACE,CAAC,OAAO,cAAc,OAAO,IAAI,KACjC,OAAO,OAAO,IAAI,KAAK,QAAQ,sBAAsB,MAAO,IAE5D,MAAM,IAAI,UAAU,iBAAiB,IAAI,iCAAiC;IAC9E;GACF;EACF;CACF;AACF;;AAGA,SAAgB,aAAwC,QAAc;CACpE,kBAAkB,MAAM;CACxB,OAAO;AACT;;;AC7MA,MAAM,OAAO;;;;;;;;;;;;;;;;AAgBb,MAAM,YAAY,OAA2B,SAAqC;CAChF,IAAI,UAAU,KAAA,GAAW,OAAO,KAAA;CAChC,MAAM,SAAS,OAAO,KAAK;CAC3B,IAAI,CAAC,OAAO,cAAc,MAAM,KAAK,SAAS,GAC5C,MAAM,IAAI,UAAU,GAAG,KAAK,4BAA4B;CAC1D,OAAO;AACT;AACA,SAAS,YAAY,WAAwD;CAC3E,IACE,OAAO,cAAc,YACrB,cAAc,QACd,EAAE,UAAU,cACZ,OAAO,UAAU,SAAS,YAC1B,EAAE,YAAY,cACd,OAAO,UAAU,WAAW,YAE5B,MAAM,IAAI,UAAU,uDAAuD;AAC/E;AACA,eAAe,UAAU,WAAmB,KAAqC;CAC/E,MAAM,WAAmC;EACvC,OAAO;EACP,QAAQ;EACR,WAAW;CACb;CACA,IAAI,OAAO,OAAO,UAAU,SAAS,GAAG,YAAY,SAAS;CAC7D,MAAM,YAAY,UAAU,YAAY,GAAG,GACzC,OAAO,YAAY,IAAI,YAAY,UAAU,MAAM,GAAG,SAAS,GAC/D,WAAW,YAAY,IAAI,YAAY,UAAU,MAAM,YAAY,CAAC;CACtE,IAAI,CAAC,QAAQ,CAAC,UAAU,MAAM,IAAI,UAAU,sCAAsC;CAClF,IAAI;CACJ,IAAI,KAAK,WAAW,GAAG,KAAK,KAAK,WAAW,IAAI,GAC9C,YAAY,cAAc,KAAK,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC;MAEnD,IAAI;EACF,YAAYA,QAAc,MAAM,cAAc,KAAK,KAAK,KAAK,cAAc,CAAC,CAAC,CAAC,IAAI;CACpF,SAAS,OAAO;EACd,IAAI,EAAE,iBAAiB,SAAS,UAAU,SAAS,MAAM,SAAS,yBAChE,MAAM;EAER,IAAI;GACF,YAAYA,QAAc,MAAM,YAAY,GAAG;EACjD,SAAS,eAAe;GACtB,IACE,yBAAyB,SACzB,UAAU,iBACV,cAAc,SAAS,wBAEvB,MAAM,IAAI,UACR,kBAAkB,KAAK,oFAAoF,KAAK,EAClH;GACF,MAAM;EACR;CACF;CAEF,MAAM,SAAkC,MAAM,OAAO;CACrD,MAAM,YACJ,OAAO,OAAO,cAAc,aAAa,MAAM,OAAO,SAAS,CAAC,IAAI,OAAO;CAC7E,YAAY,SAAS;CACrB,OAAO;AACT;;AAEA,eAAsB,OACpB,MACA,MAAM,QAAQ,IAAI,GAClB,MAA+B,QAAQ,KACvC;CACA,MAAM,SAAS,UAAU;EACvB,MAAM,CAAC,GAAG,IAAI;EACd,kBAAkB;EAClB,SAAS;GACP,MAAM;IAAE,MAAM;IAAW,OAAO;GAAI;GACpC,OAAO;IAAE,MAAM;IAAW,OAAO;GAAI;GACrC,WAAW,EAAE,MAAM,UAAU;GAC7B,eAAe;IAAE,MAAM;IAAU,UAAU;GAAK;GAChD,aAAa,EAAE,MAAM,UAAU;GAC/B,MAAM,EAAE,MAAM,UAAU;GACxB,OAAO,EAAE,MAAM,SAAS;GACxB,QAAQ,EAAE,MAAM,SAAS;GACzB,KAAK,EAAE,MAAM,SAAS;GACtB,MAAM,EAAE,MAAM,SAAS;GACvB,MAAM,EAAE,MAAM,SAAS;GACvB,MAAM,EAAE,MAAM,SAAS;GACvB,MAAM,EAAE,MAAM,SAAS;GACvB,MAAM,EAAE,MAAM,SAAS;GACvB,OAAO,EAAE,MAAM,SAAS;GACxB,aAAa,EAAE,MAAM,SAAS;GAC9B,cAAc,EAAE,MAAM,SAAS;EACjC;CACF,CAAC;CACD,IAAI,OAAO,OAAO,MAAM;EACtB,IAAI,IAAI;EACR,OAAO;CACT;CACA,MAAM,CAAC,SAAS,UAAU,OAAO;CACjC,IACE,CAAC,CAAC,SAAS,QAAQ,CAAC,CAAC,SAAS,WAAW,EAAE,KAC3C,WAAW,KAAA,KACX,WAAW,MACX,OAAO,YAAY,WAAW,GAE9B,MAAM,IAAI,UAAU,IAAI;CAC1B,MAAM,mBACJ,YAAY,UACR;EAAC;EAAQ;EAAQ;EAAS;CAAM,IAChC;EAAC;EAAO;EAAQ;EAAQ;EAAQ;EAAS;CAAS;CACxD,KAAK,MAAM,UAAU,kBACnB,IAAI,OAAO,OAAO,OAAO,QAAQ,MAAM,GACrC,MAAM,IAAI,UAAU,KAAK,OAAO,uBAAuB,QAAQ,MAAM;CACzE,IAAI,SAAoB,CAAC;CACzB,MAAM,aAAa,KAAK,QAAQ,KAAK,OAAO,OAAO,UAAU,oBAAoB;CACjF,IAAI,YAAY,OAAO,OAAO,WAAW,KAAA;CACzC,IAAI,CAAC,WACH,IAAI;EACF,MAAM,OAAO,UAAU;EACvB,YAAY;CACd,SAAS,OAAO;EACd,IAAI,EAAE,iBAAiB,SAAS,UAAU,SAAS,MAAM,SAAS,WAAW,MAAM;CACrF;CAEF,IAAI,CAAC,WACH,KAAK,MAAM,YAAY;EACrB;EACA;EACA;EACA;CACF,GAAG;EACD,IAAI;GACF,MAAM,OAAO,KAAK,QAAQ,KAAK,QAAQ,CAAC;EAC1C,SAAS,OAAO;GACd,IAAI,iBAAiB,SAAS,UAAU,SAAS,MAAM,SAAS,UAAU;GAC1E,MAAM;EACR;EACA,MAAM,IAAI,UACR,SAAS,SAAS,kKACpB;CACF;CAEF,IAAI,WAAW;EACb,MAAM,SAAS,MAAM,OAAO,cAAc,UAAU,CAAC,CAAC;EACtD,IACE,OAAO,OAAO,YAAY,YAC1B,OAAO,YAAY,QACnB,MAAM,QAAQ,OAAO,OAAO,GAE5B,MAAM,IAAI,UAAU,8BAA8B;EACpD,KAAK,MAAM,OAAO,OAAO,KAAK,OAAO,OAAO,GAC1C,IAAI,CAAC;GAAC;GAAS;GAAW;GAAS;GAAU;EAAa,CAAC,CAAC,SAAS,GAAG,GACtE,MAAM,IAAI,UACR,2BAA2B,IAAI,kFACjC;EACJ,IAAI,OAAO,OAAO,QAAQ,SAAS,YACjC,MAAM,IAAI,UACR,2EACF;EACF,kBAAkB,OAAO,OAAO;EAChC,SAAS,OAAO;CAClB;CACA,MAAM,QACJ,OAAO,OAAO,UAAU,KAAA,IACpB,MAAM,UAAU,OAAO,OAAO,OAAO,GAAG,IACxC,OAAO,UAAU,KAAA,IACf,iBAAiB,IACjB,OAAO;CACf,YAAY,KAAK;CACjB,MAAM,WAAW,SAAS,OAAO,OAAO,cAAc,WAAW,GAC/D,YAAY,SAAS,OAAO,OAAO,eAAe,YAAY;CAChE,MAAM,UACJ,aAAa,KAAA,KAAa,cAAc,KAAA,IACpC;EACE,MAAM,aAAa,KAAA,IAAY,EAAE,UAAU,MAAM,IAAI;GAAE,UAAU;GAAS;EAAS;EACnF,MAAM,cAAc,KAAA,IAAY,EAAE,UAAU,MAAM,IAAI;GAAE,UAAU;GAAU;EAAU;CACxF,IACA,OAAO;CACb,MAAM,OAAO,KAAK,QAAQ,KAAK,MAAM;CACrC,IAAI,EAAE,MAAM,KAAK,IAAI,EAAA,CAAG,YAAY,GAClC,MAAM,IAAI,UAAU,sCAAsC;CAC5D,IAAI,OAAO,OAAO,gBAAgB,OAAO,OAAO,gBAC9C,MAAM,IAAI,UAAU,mDAAmD;CACzE,MAAM,cAAc,YAAY,UAAU,OAAO,QAAQ,OAAO;CAChE,MAAM,cAAc,mBAClB,OAAO,OAAO,eACV,CAAC,IACA,OAAO,OAAO,kBACb,aAAa,eACb,OAAO,eAAe,CAAC;EAAE,MAAM;EAAc,UAAU;CAAK,CAAC,GACnE,GACF;CACA,IAAI,YAAY,SAAS;EACvB,IAAI,OAAO,OAAO,QAAQ,KAAA,GACxB,MAAM,IAAI,UAAU,oEAAoE;EAC1F,MAAM,OAAO,OAAO,OAAO,QAAQ,OAAO,OAAO,QAAQ,OACvD,cAAc,OAAO,OAAO,QAAQ,OAAO,OAAO,eAAe;EACnE,IAAI,SAAS,SAAS,SAAS,OAAO,MAAM,IAAI,UAAU,yBAAyB;EACnF,IAAI,CAAC;GAAC;GAAY;GAAQ;EAAW,CAAC,CAAC,SAAS,WAAW,GACzD,MAAM,IAAI,UAAU,sBAAsB;EAC5C,MAAM,SAAS,MAAM,iBAAiB;GACpC,GAAG,OAAO;GACV,WAAW;GACX,WAAW,KAAK,QAAQ,KAAK,OAAO,OAAO,GAAG;GAC9C;GACA;GACA,GAAI,OAAO,OAAO,QAAQ,EAAE,cAAc,UAAU,IAAI,CAAC;GACzD,GAAI,OAAO,OAAO,aAAa,EAAE,QAAQ,KAAK,IAAI,CAAC;GACnD;GACa;GACb,GAAI,OAAO,OAAO,SAAS,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM,OAAO,OAAO,KAAK;GACvE,GAAI,YAAY,KAAA,IAAY,CAAC,IAAI,EAAE,QAAQ;EAC7C,CAAC;EACD,IAAI,OAAO,QAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;OACjD;GACH,IAAI,SAAS,OAAO,OAAO,KAAK;GAChC,IACE,GAAG,OAAO,cAAc,OAAO,mBAAmB,OAAO,MAAM,QAAQ,SAAS,KAAK,eAAe,IAAI,CAAC,CAAC,OAAO,oBAAoB,OAAO,MAAM,QAAQ,SAAS,KAAK,eAAe,IAAI,CAAC,CAAC,OAAO,gBACtM;GACA,KAAK,MAAM,QAAQ,OAAO,OACxB,IAAI,KAAK,cAAc,MAAM,KAAK,eAAe,MAC/C,IACE,KAAK,eAAe,OAChB,0BAA0B,KAAK,aAAa,QAC5C,mBAAmB,KAAK,KAAK,OAAO,WAAW,KAAK,UAAU,GACpE;EACN;EACA,OAAO;CACT;CACA,MAAM,OAAO,OAAO,OAAO,OAAO,QAAQ,QAAQ,IAAI,QAAQ,OAAO,QAAQ,QAAQ,CAAC;CACtF,IAAI,CAAC,OAAO,cAAc,IAAI,KAAK,OAAO,KAAK,OAAO,OACpD,MAAM,IAAI,UAAU,kCAAkC;CACxD,MAAM,SAAS,MAAM,oBAAoB;EACvC,GAAG,OAAO;EACV,GAAI,OAAO,OAAO,OAAO,EAAE,MAAM,OAAO,QAAQ,QAAQ,CAAC,EAAE,IAAI,CAAC;EAChE;EACA;EACA;EACA;EACA,GAAI,YAAY,KAAA,IAAY,CAAC,IAAI,EAAE,QAAQ;EAC3C,GAAI,aAAa,KAAA,IAAY,CAAC,IAAI,EAAE,SAAS;EAC7C,GAAI,cAAc,KAAA,IAAY,CAAC,IAAI,EAAE,UAAU;EAC/C,GAAI,OAAO,OAAO,SAAS,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM,OAAO,OAAO,KAAK;EACvE,GAAI,OAAO,OAAO,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,WAAW,OAAO,OAAO,MAAM;CAChF,CAAC;CACD,IACE,mBAAmB,OAAO,MAAM,OAAO,OAAO,SAAS,OAAO,QAAQ,aAAa,GAAG,SACxF;CACA,OAAO;AACT"}