npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@vauxs-fvtt/foundry-pack-tools

v0.1.0

Published

Document processing utilities for Foundry VTT compendium pack workflows

Readme

@vauxs-fvtt/foundry-pack-tools

Document processing utilities for Foundry VTT compendium pack workflows. Build transform pipelines, clean HTML descriptions, normalize paths, convert UUIDs, and handle cross-system content migration.

Installation

npm install @vauxs-fvtt/foundry-pack-tools

Example Usage

Extract & Compile Compendium Packs

This is just a basic foundryvtt-cli, but might as well.

import { extractPack, compilePack } from "@foundryvtt/foundryvtt-cli";

// Extract a LevelDB compendium pack into individual JSON files
await extractPack("packs/items", "output/extracted", { clean: true });

// Filter entries during extraction — return false to drop a document
await extractPack("packs/items", "output/filtered", {
    clean: true,
    transformEntry: async (entry) => {
        if (entry.type === "feat" && entry.name.includes("Test")) {
            return false;
        }
    },
});

// Compile JSON files back into a LevelDB pack
await compilePack("output/extracted", "packs/items-clean", { recursive: true });

Build a Transform Pipeline with Built-in Plugins

Chain multiple transformations into a single transformEntry callback:

import {
    createTransformEntry,
    createClearFlagsPlugin,
    createCleanDescriptionHTMLPlugin,
    createNormalizeImagePathsPlugin,
    createPruneDefaultsPlugin,
    createValidateLinksPlugin,
} from "@vauxs-fvtt/foundry-pack-tools";
import { extractPack } from "@foundryvtt/foundryvtt-cli";

const transform = createTransformEntry([
    // Strip all document flags
    createClearFlagsPlugin(),

    // Cleanup HTML descriptions
    createCleanDescriptionHTMLPlugin(),

    // Normalize image paths — strip Forge VTT CDN prefix, prepend local system path
    createNormalizeImagePathsPlugin({
        gameSystemPath: "systems/my-system",
    }),

    // Remove default fields (sort, effects, _stats, null folder, default ownership)
    createPruneDefaultsPlugin({ normalizeOwnership: true }),

    // Reject any compendium links to systems other than sf2e
    createValidateLinksPlugin({
        allowedSystems: ["sf2e"],
    }),
]);

await extractPack("packs/items", "output/items-cleaned", {
    clean: true,
    transformEntry: transform,
});

Selective Flag Preservation

Keep only specific flag scopes and optionally strip flags from embedded items:

import { createTransformEntry, createClearFlagsPlugin } from "@vauxs-fvtt/foundry-pack-tools";

const transform = createTransformEntry([
    createClearFlagsPlugin({
        allowScopes: ["core", "my-module"],
        stripEmbedded: true,
    }),
]);

Standalone cleanHTML

Clean Foundry HTML — including Ancestry & Nations copypasta spans — while preserving HTML structure:

import { cleanHTML } from "@vauxs-fvtt/foundry-pack-tools";

const html = `
    <span data-aon-copypasta="true">garbage</span>
    <p>A <strong>bold</strong> ability with <em>italic</em> text.</p>
    <a href="https://example.com">link</a>
`;

const cleaned = cleanHTML(html);
// "<p>A <strong>bold</strong> ability with <em>italic</em> text.</p>\n<a href=\"https://example.com\">link</a>"

UUID Conversion: Names ↔ IDs

Convert @UUID[Compendium.pack.type.ID] references to readable document names and back:

import {
    buildIdNameMap,
    createConvertUUIDsToNamesPlugin,
    createConvertUUIDsToIdsPlugin,
    createTransformEntry,
} from "@vauxs-fvtt/foundry-pack-tools";
import { extractPack } from "@foundryvtt/foundryvtt-cli";
import { readFileSync } from "node:fs";

// Build a name lookup map from the extracted pack contents
const manifest = JSON.parse(readFileSync("system.json", "utf-8"));
const idNameMap = buildIdNameMap("packs", manifest);

// Replace @UUID references with document names
const toNames = createTransformEntry([
    createConvertUUIDsToNamesPlugin(idNameMap),
]);

await extractPack("packs/journal", "output/names", {
    clean: true,
    transformEntry: toNames,
});

// Replace names back to IDs (for re-import)
const toIds = createTransformEntry([
    createConvertUUIDsToIdsPlugin(idNameMap),
]);

Cross-System Resolution

Map content from one game system to another — folders, packs, assets, flags, and UUIDs:

import {
    buildCompendiumRemap,
    createCrossSystemResolver,
} from "@vauxs-fvtt/foundry-pack-tools/cross-system";
import { extractPack } from "@foundryvtt/foundryvtt-cli";
import { readFileSync } from "node:fs";

const sourceManifest = JSON.parse(readFileSync("sf2e/system.json", "utf-8"));
const targetManifest = JSON.parse(readFileSync("pf2e/system.json", "utf-8"));

// Auto-match packs by document type (e.g., sf2e.items → pf2e.items)
const remaps = buildCompendiumRemap(sourceManifest, targetManifest);

const resolver = createCrossSystemResolver({
    remaps,
    sourceFolders,                    // from source pack's folders.db.json
    targetFolders,                    // from target pack's folders.db.json
    assetPathReplacements: [
        { from: "systems/sf2e", to: "systems/pf2e" },
    ],
    flagScopeRename: { from: "sf2e", to: "pf2e" },
    uuidRemap,                        // Map<sourcePack, Map<oldId, newId>>
});

// Apply the resolver as a transform during extraction
await extractPack("sf2e/packs/items", "output/cross-system", {
    clean: true,
    transformEntry: (doc, ctx) => resolver.transform(doc, ctx),
});

Utilities

import {
    prettyPrintPackJSON,
    sluggify,
    readPackJSON,
    getPackJSONPaths,
    PackToolsError,
} from "@vauxs-fvtt/foundry-pack-tools";

// Deterministic JSON — sorted keys, DocID keys at end, 4-space indent
const serialized = prettyPrintPackJSON(document);

// Generate slugs from names
const slug = sluggify("Longsword +1");  // "longsword-1"

// Enumerate all JSON files in an extracted pack
const paths = getPackJSONPaths("packs/items");
// ['packs/items/abc.json', 'packs/items/sub/def.json', ...]

// Read a single document with error handling
try {
    const doc = readPackJSON<PackEntry>(paths[0]);
} catch (err) {
    if (err instanceof PackToolsError) {
        console.error(err.message, err.cause);
    }
}

API Summary

| Category | Exports | | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Error | PackToolsError | | Pipeline | createTransformEntry, createClearFlagsPlugin, createCleanDescriptionHTMLPlugin, cleanHTML, createNormalizeImagePathsPlugin, createPruneDefaultsPlugin, createValidateLinksPlugin, LINK_PATTERNS | | UUID | buildIdNameMap, convertUUIDsToNames, createConvertUUIDsToNamesPlugin, convertUUIDsToIds, createConvertUUIDsToIdsPlugin | | Utils | getPackJSONPaths, getFolderPath, readPackJSON, readFoldersFromPack, prettyPrintPackJSON, assertDocIdsStable, sluggify | | Types | PackEntry, ActorPackEntry, SystemPackEntry, TransformPlugin, TransformContext, PackMetadata, SystemManifest, DBFolder | | Cross-System (subpath) | buildCompendiumRemap, createCrossSystemResolver, CompendiumRemap, CrossSystemResolverOptions |

Cross-System Entrypoint

The cross-system module is a separate entrypoint to avoid pulling in extra dependencies when you only need the core utilities:

import { buildCompendiumRemap, createCrossSystemResolver } from "@vauxs-fvtt/foundry-pack-tools/cross-system";