Files
vtt_work/help_scripts/pack_nedb_to_leveldb.mjs
gitea a67c18d691 fix(foundry): poprawki zgodności v13/v14 + skrypt NeDB→LevelDB
Poprawki punktowe (niskiego ryzyka, z fallbackiem na v11/v12):
- greyknights/auto-loadout: pochodzenie z paczki przez _stats.compendiumSource
  (flags.core.sourceId deprecated od v12)
- greyknights/gk-glow-toggle: canvas.perception.update -> płaska flaga
  refreshLighting (stary format {lighting:{refresh}} nie działa od v11)
- greyknights/helpers: TextEditor -> foundry.applications.ux.TextEditor;
  game.ui -> ui (martwy no-op)
- greyknights/ui-gkpanel: scene control tool v13 onClick -> onChange
- voidships-builder/voidships: loadTemplates -> foundry.applications.handlebars
- voidships-builder/voidship-actor: Actors -> foundry.documents.collections.Actors
- usunięto śmieciowe packs/*.db.bak

Skrypt help_scripts/pack_nedb_to_leveldb.mjs: przepakowuje kompendia NeDB(.db)
-> LevelDB (wymagane w v11+/v13/v14), czyta paczki z module.json, domyślnie
dry-run. Wykrywa też rozbieżność: gk-greyknights i wg-voidships-journal mają
w manifeście ścieżkę bez .db, a na dysku plik NeDB.

Nie ruszano vendored investigation-board (do aktualizacji z upstreamu).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-29 19:31:37 +02:00

151 lines
5.4 KiB
JavaScript
Executable File

#!/usr/bin/env node
// pack_nedb_to_leveldb.mjs
// Przepakowuje kompendia z legacy NeDB (.db, jeden JSON/linia) do formatu LevelDB
// (katalog), którego wymaga Foundry VTT v11+ (i v13/v14). Czyta listę paczek
// wprost z module.json danego modułu, więc nie trzeba niczego wpisywać ręcznie.
//
// Wymaga jednorazowo: npm i -D @foundryvtt/foundryvtt-cli (lub -g)
//
// Użycie:
// node help_scripts/pack_nedb_to_leveldb.mjs [--write] [--replace] [ścieżki/do/modułów...]
//
// (bez argumentów ścieżek) -> domyślnie 3 własne moduły w tym repo
// --write faktycznie buduje katalogi LevelDB (bez tego: tylko dry-run/plan)
// --replace po udanym zbudowaniu usuwa stary plik .db ORAZ podmienia ścieżkę
// w module.json (packs[].path: "packs/foo.db" -> "packs/foo")
//
// Bezpiecznie: domyślnie nic nie nadpisuje. LevelDB trafia obok, do packs/<name>.
// Stare .db zostają, dopóki nie podasz --replace.
import { readFile, writeFile, rm, mkdtemp, mkdir } from "node:fs/promises";
import { existsSync, statSync } from "node:fs";
import { tmpdir } from "node:os";
import { fileURLToPath } from "node:url";
import path from "node:path";
const HERE = path.dirname(fileURLToPath(import.meta.url));
const REPO = path.resolve(HERE, "..");
const args = process.argv.slice(2);
const WRITE = args.includes("--write");
const REPLACE = args.includes("--replace");
const moduleDirs = args.filter((a) => !a.startsWith("--"));
const DEFAULT_MODULES = [
"wg-greyknights",
"wg-voidships-builder",
"wg-voidships-journal",
].map((m) => path.join(REPO, m));
const targets = (moduleDirs.length ? moduleDirs : DEFAULT_MODULES).map((p) =>
path.resolve(p)
);
// Typ paczki z module.json -> documentType wymagany przez CLI dla źródła NeDB.
const TYPE_OK = new Set([
"Actor", "Item", "JournalEntry", "RollTable", "Macro",
"Scene", "Playlist", "Cards", "Adventure",
]);
async function loadCli() {
try {
return await import("@foundryvtt/foundryvtt-cli");
} catch {
console.error(
"\n[!] Brak @foundryvtt/foundryvtt-cli. Zainstaluj jednorazowo:\n" +
" npm i -D @foundryvtt/foundryvtt-cli\n"
);
process.exit(2);
}
}
async function processModule(cli, modDir) {
const manifestPath = path.join(modDir, "module.json");
if (!existsSync(manifestPath)) {
console.warn(`- pomijam ${modDir} (brak module.json)`);
return;
}
const manifest = JSON.parse(await readFile(manifestPath, "utf8"));
const packs = manifest.packs ?? [];
if (!packs.length) {
console.log(`- ${manifest.id ?? path.basename(modDir)}: brak paczek`);
return;
}
let manifestDirty = false;
console.log(`\n=== ${manifest.id ?? path.basename(modDir)} ===`);
for (const pack of packs) {
const rel = pack.path; // np. "packs/wg-voidship-hulls.db" LUB "packs/gk-items"
const newRel = rel.replace(/\.db$/, ""); // docelowa ścieżka LevelDB (bez .db)
const outDir = path.join(modDir, newRel);
// Plik NeDB może być tam, gdzie wskazuje manifest (…​.db) albo obok, gdy
// manifest podaje ścieżkę bez rozszerzenia, a na dysku i tak leży <path>.db.
const nedbAbs = rel.endsWith(".db")
? path.join(modDir, rel)
: path.join(modDir, rel + ".db");
if (existsSync(outDir) && statSync(outDir).isDirectory() && !existsSync(nedbAbs)) {
console.log(` • ${pack.name}: już LevelDB (${newRel}/) — pomijam`);
continue;
}
if (!existsSync(nedbAbs) || !statSync(nedbAbs).isFile()) {
console.warn(` • ${pack.name}: nie znaleziono źródła NeDB (${path.relative(modDir, nedbAbs)}) — pomijam`);
continue;
}
if (rel === newRel) {
// manifest już wskazuje bez .db, ale plik to NeDB -> rozbieżność do naprawy
console.log(` (uwaga: module.json wskazuje "${rel}", a na dysku jest NeDB "${path.basename(nedbAbs)}")`);
}
const type = pack.type;
if (!TYPE_OK.has(type)) {
console.warn(` • ${pack.name}: nieznany type="${type}" — pomijam`);
continue;
}
const abs = nedbAbs;
console.log(
` • ${pack.name} [${type}] ${rel} -> ${newRel}/ ${WRITE ? "" : "(dry-run)"}`
);
if (!WRITE) continue;
// 1) NeDB -> katalog źródeł (.json z poprawnymi _key)
const srcTmp = await mkdtemp(path.join(tmpdir(), "fvtt-src-"));
await cli.extractPack(abs, srcTmp, {
nedb: true,
documentType: type,
yaml: false,
log: false,
});
// 2) źródła -> LevelDB
await mkdir(outDir, { recursive: true });
await cli.compilePack(srcTmp, outDir, { yaml: false, log: false });
await rm(srcTmp, { recursive: true, force: true });
console.log(` ✓ zbudowano LevelDB: ${newRel}/`);
if (REPLACE) {
await rm(abs, { force: true });
if (pack.path !== newRel) {
pack.path = newRel;
manifestDirty = true;
}
console.log(
` ✓ usunięto ${path.basename(abs)}` +
(manifestDirty ? ", zaktualizowano ścieżkę w module.json" : "")
);
}
}
if (manifestDirty && WRITE && REPLACE) {
await writeFile(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
}
}
const cli = WRITE ? await loadCli() : null; // CLI potrzebny tylko do budowania
console.log(
WRITE
? `Przepakowuję (${REPLACE ? "z podmianą .db i module.json" : "obok, .db zostają"})`
: "DRY-RUN — plan (dodaj --write aby zbudować, --replace aby podmienić):"
);
for (const t of targets) await processModule(cli, t);
console.log("\nGotowe.");