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

@forgeax/engine-vite-plugin-pack

v0.1.36

Published

Vite plugin: dev HMR, build-time Pack output, and explicit project/build DDC lifecycle roots for the forgeax engine asset package system.

Readme

// @forgeax/engine-vite-plugin-pack // Vite plugin for the forgeax engine asset package system. // Dev mode: one scoped Catalog/asset transport plus an explicit host refresh policy. // Build mode: one inventory/producer/publication pass emits Pack v2, artifacts, // and the final pack-index.json through Rollup.

Explicit DDC lifecycle contract

The host owns project identity and injects both DDC roots. The plugin does not derive a root from the working directory, an asset root, a game basename, or a URL:

pluginPack({
  roots: ['assets/scene.pack.json', 'assets/player.pack.ts', 'assets/fire.vfx.wgsl'],
  ddc: {
    buildCacheRoot: '/workspace/.forgeax/ddc/build-cache',
    projectDdcRoot: '/game/.forgeax/ddc/v2',
  },
});

buildCacheRoot is the optional build producer cache. Build-only consumers may omit it; publication remains fail-open and still emits the authoritative Pack and Meta outputs. projectDdcRoot is required for dev publication, runtime scope binding, fixed-root rebinding, and recovery. A missing project root is a closed structured error, never a fallback to cwd or roots[0].

The project root is the only home for v2 project lifecycle state. The Engine DDC lifecycle owns leases, candidate generations, current, lastKnownGood, discard, close, and crash recovery. The plugin remains the single Pack owner: Catalog rows, Pack v2 bodies, Meta, receipts, and artifact closure are published as one validated producer result. current is the active verified publication; lastKnownGood is read-only recovery evidence and is never silently promoted or shipped. runtimeBinding carries the active scope and generation to the browser consumer. Closing or rebinding a host releases the old lease and cannot publish a discarded candidate.

External Meta imports use the same publication boundary in dev mode. The Pack finalizer returns the complete artifact closure, including each producer MIME type; the live artifact transport map is updated only after the DDC publication is accepted. A staged production candidate owns disposable Pack and artifact maps until its outer generation commits. Failed, aborted, or discarded candidates therefore leave the accepted Catalog, Pack body, artifact bytes, and imported GUID set available for the next same-source retry.

After a process restart, dev transport may rebuild its in-memory Pack and artifact maps from the matching current DDC entry when the complete closure is present. This is a read-through cache warm-up only: an incomplete or non-current entry falls back to the declared producer, while lastKnownGood and stale entries remain unavailable for serving.

Consumers should pass the same explicit ddc object to every plugin entry point. Do not add legacy, v1, compatibility, basename, or URL fallback paths.

Native authored Pack cookers

Public ScriptablePack consumer contract

SCRIPTABLE_PACK_CAPABILITY_MANIFEST publishes the 17 durable kinds and the three Host capability boundaries. Dev and production use the same Pack v2, Catalog, payload, refs, artifacts, receipts, and structured recovery fields; only the transport locator changes. Do not add a sidecar fallback for a valid ScriptablePack GUID.

The ordered durable matrix is mesh, material, scene, texture, equirect, sampler, font, render-pipeline, tileset, video, skeleton, skin, animation-clip, animation-graph, audio, and particle-effect, and ies-profile; its SSOT is SCRIPTABLE_PACK_ASSET_KINDS in @forgeax/engine-pack.

ScriptablePack publication

Custom Material outputs use the host's registered material NativeCooker in both dev and build. Its transitive WGSL paths participate in the same watcher closure and its fingerprint participates in the same publication identity. No Pack source edit is required to notice a WGSL-only change.

After a failed rebuild, the scoped Catalog reports degraded authority and its diagnostics while preserving accepted entries and bytes. New lazy imports are fenced; explicit x-forgeax-import-mode: rebuild requests and already-published asset URLs remain usable for recovery. Unknown asset URLs cannot start new production through this exception. Initial startup failure and expired runtime generations remain closed. An external Meta importer can instead publish one failed row while unrelated rows remain authoritative. Its row diagnostics are the rejection signal, and the watcher retains the current page so consumers can keep their last accepted payload and recover in place.

Repeated scoped Catalog reads reuse one serialized body per accepted projection and runtime binding. Lazy publications, changed diagnostics and scope changes invalidate that body; the request freshness barrier still runs before reuse.

pluginPack({ roots }) automatically inventories trusted *.pack.ts sources. The production defaults compose the standard output registry and a fixed staged generation; hosts inject only roots, importers, native cookers, runtime binding, refresh, and DDC policy. A bounded authored build timeout is returned as the structured build-phase failure and the generation keeps its previous Catalog/Pack publication until a corrected source succeeds. Scanner/Catalog projection reads only the validated definition; explicit cook calls build, finalizes all outputs into one Pack v2 transaction, publishes one receipt per output GUID, and stores canonical Meta as scriptable-pack.meta.json in that package.

Dev and build share one scanner inventory and one bounded producer session. The inventory contains canonical declaration identity, producer kind, projected outputs/external GUIDs, closure digests, and diagnostics; Catalog projection and the GUID-to-Meta watcher index consume it without reopening the source. Dev registers owners and materializes on demand or before consume; production materializes every owner through that same session. Content cycles fail structurally. Successful rebuilds replace the complete generation, while a failed watch rebuild leaves the previously installed Catalog and Pack bodies current. Relative TypeScript, JavaScript, and JSON helpers enter the source fingerprint through the Pack inventory. A scene-producing definition must declare its sceneComponents; the producer binds that schema to the definition's output only, so missing fields fail closed without a global ECS roster. Runtime consumes only Catalog, Pack v2, and artifacts.

An authored .pack.json may be runtime direct or cooked; the filename does not decide this. Register native producers at the app composition root so the same producer runs in dev and build mode:

pluginPack({
  roots: ['assets/effects.pack.json', 'assets/effects.vfx.wgsl'],
  cookers: [particleEffectCooker],
});

For a cooked asset row, the cooker owns the runtime payload, dependency refs, and artifact bytes. The plugin validates and finalizes that product before it publishes the catalog row as internal-asset + cooked + current. A cooked row without a registered producer is a build/dev error; raw authored payload must never silently reach the runtime loader.

| Authored row | Producer output | Catalog after successful cook | |:--|:--|:--| | execution: 'direct' | authored payload | internal-asset + direct + current | | execution: 'cooked' | runtime payload + refs + artifacts | internal-asset + cooked + current |

Authoring and recovery index

The audit input and category conclusions live in schemas/asset-authority.schema.json; the executable gate is check-asset-authority-audit.mjs. This plugin projects producer facts into Catalog rows. It does not become the author authority, DDC owner, or Editor write gateway.

| Need | Entry | Safe action | |:--|:--|:--| | Inspect | buildCatalogResult() and Catalog JSON | Branch on authority, diagnostics, and lifecycle fields | | Rebuild | Declared importer or native cooker | Re-run the producer and publish only validated output | | Preview LKG | Explicit evidence locator | Keep lastKnownGood read-only and out of shipped output | | Stop publish | Degraded authority or failed lifecycle | Block the release and follow the diagnostic recovery |

Producer fields are catalog facts

The build producer copies packageId, provenance, revision, sourceKey/sourceIndex, typed relations, and structured diagnostics from pack/meta declarations into every emitted CatalogEntry. It supplies a fallback provenance version from the producer schema when a sidecar omits one; consumers never need to guess importer identity from a URL or file suffix.

[!IMPORTANT] The producer result contains entries, authority, and structured diagnostics. It is the only Catalog truth; a failed or degraded inventory is a structured failure and cannot emit a partial build index.

| Result field | Meaning | Safe consumer action | |:--|:--|:--| | entries | Complete neutral catalog rows | Read producer facts and merge by lowercase GUID | | authority: 'authoritative' | Scan and fold completed with verified inputs | Apply the snapshot or delta | | authority: 'degraded' | Some roots or declarations could not be verified | Keep affected subjects isolated and follow diagnostics | | diagnostics | Structured scan/fold evidence | Branch on code, expected, actual, and hint |

packageId is the topology group identity. sourcePath, relativeUrl, and DDC locations are replaceable locators; they are never used as a fallback identity. Revision and producer facts remain attached to every row in a catalog delta, including a row whose locator changed.

AssetEvidence producer boundary

The plugin publishes catalog navigation, not proof. A row's packageUrl and optional cookReceiptUrl are joined with the producer source meta, CookReceipt, and Pack v2 artifact verification by the pack CLI or runtime SDK as AssetEvidence. Dev and build paths should preserve the same locator semantics so diagnostics do not depend on HMR.

When source bytes change, keep the evidence state explicit: notCooked, ready/current, ready/stale, or unknown; package/artifact checks are notChecked, passed, or failed. Recover by fixing the source/cook producer and rerunning lookup/verify --guid --project --catalog --json, not by inventing a replacement catalog row.

Catalog transport and host refresh

[!IMPORTANT] pluginPack publishes a typed Catalog delta through the scoped Vite channel. A CatalogDelta says which rows were added, changed, or removed; it does not decide whether a host reloads. Hosts that need a reload opt in explicitly.

The browser adapter is createCatalogClient(enumerate, import.meta.hot). It provides the browser-side subscription and enumeration pair; the application keeps one Catalog owner and does not import Vite into the runtime. Subscribe before requesting the initial snapshot, then merge added, changed, and removed by stable GUID. Consult the exported CatalogEntry and CatalogDelta types in @forgeax/engine-types for the schema rather than reproducing it here.

import type { CatalogDelta, CatalogEntry } from '@forgeax/engine-types';
import { createCatalogClient } from '@forgeax/engine-vite-plugin-pack/catalog-client';

const rowsByGuid = new Map<string, CatalogEntry>();

function mergeRows(rows: readonly CatalogEntry[]): void {
  for (const row of rows) rowsByGuid.set(row.guid.toLowerCase(), row);
}

function mergeDelta(delta: CatalogDelta): void {
  for (const guid of delta.removed) rowsByGuid.delete(guid.toLowerCase());
  mergeRows(delta.added);
  mergeRows(delta.changed);
}

const client = createCatalogClient(
  () => assets.enumerateCatalog(),
  import.meta.hot,
);

const stop = assets.subscribeCatalog(mergeDelta);

async function reconcileCatalog(): Promise<void> {
  const snapshot = await assets.enumerateCatalog();
  if (!snapshot.ok) {
    console.error(snapshot.error.code, snapshot.error.hint);
    return;
  }
  mergeRows(snapshot.value);
}

await reconcileCatalog();
// Call reconcileCatalog() again after a late subscription or transport interruption.

// Call from the host's teardown / unmount path, not after setup.
function disposeCatalog(): void {
  stop();
}

The Vite client contributes the HMR subscription and a structured failure result. The application keeps only its derived rowsByGuid view: apply every delta as removals followed by complete row replacements, and merge each successful snapshot into that same view. On a late subscription or interrupted transport, call reconcileCatalog() again. Keep the subscription alive until the host's teardown or unmount path calls disposeCatalog().

Engine app composition roots that require a full refresh for watched asset content declare that choice directly:

import { pluginPack, reloadAssetHost } from '@forgeax/engine-vite-plugin-pack';

pluginPack({ roots: ['assets'], refresh: reloadAssetHost() });

This policy also covers source-only byte changes, which deliberately do not become fake catalog-row changes. A static build has no HMR stream: enumeration still works from its catalog, while subscription is a safe no-op. If a client subscribed late or reconnects, re-enumerate and reconcile by GUID rather than inventing a Vite replay guarantee.

Pack-index entry shape (SSOT: PackIndexEntry in @forgeax/engine-types)

Each row in pack-index.json (build) or the scoped dev Catalog carries:

| Field | Type | Notes | |:--|:--|:--| | guid | string (UUIDv5/v7 lowercase) | asset identity | | packageUrl | string | dev: scoped runtime locator from runtimeBinding; build: hashed Rollup artifact | | kind | string (closed disc.) | 'texture' / 'mesh' / 'scene' / 'material' / future arms | | sourcePath | string | on-disk source path (debugging + grep; build retains source JPG path even though packageUrl points to import artefact) | | metadata | ImageMetadata \| undefined | present iff kind === 'texture'; sub-structure: width? / height? / format: GPUTextureFormat / colorSpace: 'srgb' \| 'linear' / mipmap: boolean; width / height may be absent in dev-mode entries pre-decode (build-mode import fills them) | | packageId, provenance, revision | producer PODs | stable facts copied from the declaration; absent means no evidence was published | | sourceKey, sourceIndex | topology PODs | semantic key plus positional evidence; sourceIndex is never identity | | relations, diagnostics | structured facts | copied into the row and consumed by property, not message parsing |

metadata.mipmap is the boolean form; sidecar *.meta.json importSettings.mipmap string tokens 'auto' / 'none' are mapped at the catalog builder (feat-20260517-vite-plugin-image-build-time-cook D-5; runtime is unaware of the string form). 5-field shape is feat-20260517-vite-plugin-image-build-time-cook D-2 (charter P4 consistent abstraction; metadata field names mirror TextureAsset POD byte-for-byte).

Dev and build semantics

producerReadiness: 'before-consume' materializes every required declaration before a consumer can load it. 'on-demand' keeps the inventory discoverable and materializes the owning declaration at the first GUID request. Both modes use the same producer and finalizer; an absent importer, cooker, or source dependency returns a structured failure and never invents a raw runtime row.

The runtimeBinding identifies the active scope and generation for dev transport. A late request from an old generation is rejected, and a failed rebuild serves only the previous accepted publication. lastKnownGood remains read-only evidence; preview-LKG is an explicit inspection action, never a build input. Build mode always materializes all required declarations and emits only the accepted Pack v2 publication. A host that needs live updates uses createCatalogClient with the plugin's scoped Catalog stream; runtime asset loading stays on the one Catalog/Pack reader path.

Static asset evidence

[!IMPORTANT] Static assets publish as Pack v2 and are navigated by packageUrl; runtime-only bytes remain a separate exception.

The derived AssetEvidence view joins catalog navigation, cook receipt freshness, and artifact verification. Discover it with lookup/verify --guid --project --catalog --json; notCooked, stale, and unknown require different recovery actions.

Indexed producer recovery

This is the shortest path from a catalog symptom to a producer repair:

  1. Inspect the catalog row, source Meta, DDC head, receipt, and Pack verification together. Branch on structured code, detail, expected, and hint; do not classify a row from a URL suffix or log string.
  2. Repair the producer boundary named by detail.stage: register the declared importer, repair source or Meta, or remove an invalid derived DDC entry. The source package plus Meta remains the author authority.
  3. Rebuild or cold-cook the source package through the registered importer or native cooker. The producer finalizer is the only writer of the DDC product and Pack body.
  4. Verify the new receipt, package digest, artifacts, and Catalog lifecycle. A lastKnownGood product is read-only evidence, not current output.
  5. Retry the same GUID only after verification passes. A failed producer attempt must not be hidden by a raw-source or custom-mesh fallback.

producerReadiness: 'before-consume' requires every source-package importer or native cooker needed by the scanned Meta declarations before a consumer can load the row. producerReadiness: 'on-demand' still requires the pluginPack producer registration and the host's Catalog client/refresh wiring. Missing either side is a structured producer failure, not permission for runtime compilation.

The plugin owns Catalog projection and dev/build transport projection. It does not own source authoring, DDC persistence policy, runtime payload semantics, or Editor write operations. Those boundaries remain in the source package, producer, assets-runtime, and asset-authoring gateway respectively.

Idle revision probes

Native watcher hints reconcile immediately. The missed-event probe waits after snapshot completion for max(probeMs, min(snapshotMs * 10, 60000)) milliseconds, so expensive filesystem walks do not continuously occupy the dev server. The 60-second bound applies to the wait, not to snapshot or cook execution time. drain() requests a fresh snapshot even when an earlier crawl is in flight, so writes preceding the call are observed without waiting for the next probe deadline. Shutdown cancels the timer and awaits in-flight work.