diff --git a/.gitignore b/.gitignore index cf2696c..634a9d8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ # Foundry install mirror -# /Data/modules/hax-hooks-lib/ is generated by scripts/copy-to-foundry.mjs (if/when added). +# /Data/modules/foundry-hooks-lib/ is generated by scripts/copy-to-foundry.mjs (if/when added). Data/ # Dev environment @@ -25,9 +25,9 @@ scripts/session.js scripts/session-prompts.js # Build artifacts (created by Python zip recipe or future build-zip.mjs). -# Keep this loose: hooks-lib-X.Y.Z.zip is the named release artifact; +# Keep this loose: foundry-hooks-lib-X.Y.Z.zip is the named release artifact; # versioned so future rebuilds don't accidentally overwrite a released # version. Add a new file rather than deleting old ones when bumping. -hooks-lib-*.zip -!hooks-lib-0.1.0.zip -!hooks-lib-0.2.0.zip +foundry-hooks-lib-*.zip +!foundry-hooks-lib-0.2.0.zip +!foundry-hooks-lib-0.3.0.zip diff --git a/LICENSE b/LICENSE index 8c0fed4..b503f39 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,6 @@ UNLICENSED -This module is part of the Hax's Tools project and is not licensed for +This module is part of the Foundry module split (battle-focus + +its-achievable + this lib) and is not licensed for redistribution. redistribution. Source is available at https://git.homelab.local/kaykayyali/hooks-lib for collaborators. diff --git a/README.md b/README.md index e29f6d3..c804bd0 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,17 @@ -# Hax's Tools — Hooks Lib (`hax-hooks-lib`) +# Foundry Hooks Lib (`foundry-hooks-lib`) -Foundry VTT module (id: `hax-hooks-lib`) that turns Foundry's hook soup +Foundry VTT module (id: `foundry-hooks-lib`) that turns Foundry's hook soup (dnd5e, combat, token updates, canvas/UI) into a clean, normalized event stream. **Library-only** — no UI, no settings, no chat output. Designed to be consumed by any module that wants Foundry events in a stable shape. -Part of the **Hax's Tools** umbrella. Consumers today: `battle-focus` -(encounter + journal + summary) and `its-achievable` (achievements, -rewards, wall, HUD). System-specific knowledge (dnd5e rolls, PF2e, etc.) -lives in separate adapter repos that declare Foundry + system version -ranges they support. +Part of the **Foundry module split** (battle-focus + its-achievable + this lib). +Consumers today: `battle-focus` (encounter + journal + summary) and +`its-achievable` (achievements, rewards, wall, HUD). System-specific knowledge +(dnd5e rolls, PF2e, etc.) lives in separate adapter repos that declare +Foundry + system version ranges they support. -## v0.2.0 — generic Foundry hook facade +## v0.3.0 — module id renamed (hax-hooks-lib → foundry-hooks-lib) v0.2.0 is a complete rewrite. v0.1.0 shipped as a curated-event catalog (a list of hand-written handlers for 18 specific Foundry events). v0.2.0 @@ -54,11 +54,11 @@ Consumers that want `{kind, actorId, delta}` build that themselves from `args`. This is intentional: the library is the boundary that absorbs Foundry version churn. -## Public API (on `game.modules.get("hax-hooks-lib").api`) +## Public API (on `game.modules.get("foundry-hooks-lib").api`) ```js import { subscribe, subscribeMany, subscribeAll } from - game.modules.get("hax-hooks-lib").api; + game.modules.get("foundry-hooks-lib").api; // Single hook: const unsub = subscribe("updateActor", (envelope) => { @@ -85,8 +85,8 @@ A system adapter is a separate Foundry module. At its `init`, it calls: ```js hooksLib.api.registerSystemAdapter({ - id: "hax-hooks-dnd5e", - moduleId: "hax-hooks-dnd5e", + id: "foundry-hooks-dnd5e", + moduleId: "foundry-hooks-dnd5e", system: { id: "dnd5e", versions: ">=5.2.0 <5.3.0" }, foundryVersions: ">=13 <15", factory: () => [ /* derived-event registrations */ ], @@ -111,7 +111,7 @@ Full list: `scripts/internal/registered-hooks.js`. ## Error containment If a consumer callback throws, the library catches it, logs via -`console.error` with the `[hax-hooks-lib]` prefix and the hook name, +`console.error` with the `[foundry-hooks-lib]` prefix and the hook name, and continues dispatching to subsequent callbacks. Errors never propagate to Foundry's hook chain. diff --git a/docs/HOOK_CONTRACT.md b/docs/HOOK_CONTRACT.md index b7cfdd1..882c202 100644 --- a/docs/HOOK_CONTRACT.md +++ b/docs/HOOK_CONTRACT.md @@ -116,7 +116,7 @@ callbacks registered through any of the three primitives above. If a consumer callback throws, the library: 1. Catches the error. -2. Logs it via `console.error` with the prefix `[hax-hooks-lib]` and +2. Logs it via `console.error` with the prefix `[foundry-hooks-lib]` and the hook name. 3. Continues dispatching to subsequent callbacks (registration order). @@ -437,7 +437,7 @@ export function listActiveAdapters() { ... } ``` `mod.api` mirrors these so consumers can also access via -`game.modules.get("hax-hooks-lib").api.subscribe(...)`. +`game.modules.get("foundry-hooks-lib").api.subscribe(...)`. --- diff --git a/hooks-lib-0.1.0.zip b/foundry-hooks-lib-0.1.0.zip similarity index 100% rename from hooks-lib-0.1.0.zip rename to foundry-hooks-lib-0.1.0.zip diff --git a/hooks-lib-0.2.0.zip b/foundry-hooks-lib-0.2.0.zip similarity index 100% rename from hooks-lib-0.2.0.zip rename to foundry-hooks-lib-0.2.0.zip diff --git a/foundry-hooks-lib-0.3.0.zip b/foundry-hooks-lib-0.3.0.zip new file mode 100644 index 0000000..82de45d Binary files /dev/null and b/foundry-hooks-lib-0.3.0.zip differ diff --git a/module.json b/module.json index f3a6d83..2d0bc26 100644 --- a/module.json +++ b/module.json @@ -1,8 +1,8 @@ { - "id": "hax-hooks-lib", - "title": "Hax's Tools — Hooks Lib", + "id": "foundry-hooks-lib", + "title": "Foundry Hooks Lib", "description": "Foundry VTT module: generic Foundry hook facade. Subscribes to every relevant Foundry hook (combat, document CRUD, canvas/UI, dnd5e v2 rolls), emits a uniform {ts, hook, args} envelope. No domain interpretation — consumers query the stream. See docs/HOOK_CONTRACT.md.", - "version": "0.2.0", + "version": "0.3.0", "library": true, "manifestPlusVersion": "1.2.0", "authors": [ @@ -23,7 +23,7 @@ "esmodules": ["scripts/main.js"], "url": "https://git.homelab.local/kaykayyali/hooks-lib", "manifest": "https://git.homelab.local/kaykayyali/hooks-lib/raw/branch/main/module.json", - "download": "https://git.homelab.local/kaykayyali/hooks-lib/raw/branch/main/hooks-lib-0.2.0.zip", + "download": "https://git.homelab.local/kaykayyali/hooks-lib/raw/branch/main/foundry-hooks-lib-0.3.0.zip", "readme": "https://git.homelab.local/kaykayyali/hooks-lib/blob/main/README.md", "changelog": "https://git.homelab.local/kaykayyali/hooks-lib/commits/main", "bugs": "https://git.homelab.local/kaykayyali/hooks-lib/issues", diff --git a/package.json b/package.json index cb593cf..efed280 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "hax-hooks-lib", + "name": "foundry-hooks-lib", "version": "0.2.0", "private": true, "description": "Foundry VTT module: generic Foundry hook facade. Library-only — no UI, no settings, no domain interpretation. v0.2.0 implements HOOK_CONTRACT.md and tests/PLAN.md.", diff --git a/scripts/internal/adapter-registry.js b/scripts/internal/adapter-registry.js index 7b464a0..fc3733f 100644 --- a/scripts/internal/adapter-registry.js +++ b/scripts/internal/adapter-registry.js @@ -33,7 +33,7 @@ import { matchRange } from "./semver.js"; -const MODULE_ID = "hax-hooks-lib"; +const MODULE_ID = "foundry-hooks-lib"; const _manifests = []; // adapter manifests in registration order const _active = new Map(); // id -> { manifest, derivedNames } diff --git a/scripts/internal/envelope.js b/scripts/internal/envelope.js index 14abb05..6c05231 100644 --- a/scripts/internal/envelope.js +++ b/scripts/internal/envelope.js @@ -16,7 +16,7 @@ import { getEntryForRawName, getEntryForEnvelope } from "./registered-hooks.js"; import { ARG_SHAPES, maybeSynthesize } from "./anti-corruption.js"; import { dispatch } from "./subscribers.js"; -const MODULE_ID = "hax-hooks-lib"; +const MODULE_ID = "foundry-hooks-lib"; /** * Build an envelope for a Foundry hook fire. diff --git a/scripts/internal/lifecycle.js b/scripts/internal/lifecycle.js index 526bd9d..9f2ec59 100644 --- a/scripts/internal/lifecycle.js +++ b/scripts/internal/lifecycle.js @@ -20,7 +20,7 @@ import { } from "./adapter-registry.js"; import { unsubscribeAll } from "./subscribers.js"; -const MODULE_ID = "hax-hooks-lib"; +const MODULE_ID = "foundry-hooks-lib"; // Track which Foundry hooks we've registered and the listener fn, so // uninstall can remove them. diff --git a/scripts/internal/registered-hooks.js b/scripts/internal/registered-hooks.js index dbfcaee..3a94c11 100644 --- a/scripts/internal/registered-hooks.js +++ b/scripts/internal/registered-hooks.js @@ -20,7 +20,7 @@ // ANTI_CORRUPTION map below (per §9) lists the raw Foundry names // that produce each normalized envelope. -const MODULE_ID = "hax-hooks-lib"; +const MODULE_ID = "foundry-hooks-lib"; // mode: "sync" | "async" export const HOOK_REGISTRY = [ diff --git a/scripts/internal/subscribers.js b/scripts/internal/subscribers.js index c4bc9b7..ef25805 100644 --- a/scripts/internal/subscribers.js +++ b/scripts/internal/subscribers.js @@ -8,7 +8,7 @@ // unsubscribeAll() — purge everything // // Error containment (§3.5): throwing consumer callbacks are caught, -// logged via console.error with the [hax-hooks-lib] prefix and the +// logged via console.error with the [foundry-hooks-lib] prefix and the // hook name, and the dispatch chain continues to the next callback. // Errors never propagate to Foundry's hook chain. // @@ -17,7 +17,7 @@ import { REGISTERED_HOOKS } from "./registered-hooks.js"; -const MODULE_ID = "hax-hooks-lib"; +const MODULE_ID = "foundry-hooks-lib"; // Per-envelope callback list. Order = registration order. const _callbacks = new Map(); // hookName (envelope) -> [fn, fn, ...] diff --git a/scripts/main.js b/scripts/main.js index d36211c..a7b96c4 100644 --- a/scripts/main.js +++ b/scripts/main.js @@ -1,4 +1,4 @@ -// hax-hooks-lib — module entry point (v0.2.0). +// foundry-hooks-lib — module entry point (v0.2.0). // // Generic Foundry hook facade per HOOK_CONTRACT.md. No domain // interpretation. Subscribes to every Foundry hook in the registered @@ -37,7 +37,7 @@ import { } from "./internal/adapter-registry.js"; import { REGISTERED_HOOKS } from "./internal/registered-hooks.js"; -const MODULE_ID = "hax-hooks-lib"; +const MODULE_ID = "foundry-hooks-lib"; const MODULE_VERSION = "0.2.0"; function isClient() { diff --git a/tests/PLAN.md b/tests/PLAN.md index c277eae..94a4ee3 100644 --- a/tests/PLAN.md +++ b/tests/PLAN.md @@ -1,4 +1,4 @@ -# hooks-lib test plan — v0.2.0 +# hooks-lib test plan — v0.3.0 **Status:** Proposed. Implements the contract in `docs/HOOK_CONTRACT.md`. **Drives:** `tests/verify-hooks-lib.mjs` (no-Foundry smoke) and @@ -6,8 +6,8 @@ Foundry instance). The v0.1.0 test file (`tests/verify-hooks-lib.mjs`, 20 assertions) is -**out of scope** for v0.2.0 — its assertions cover the curated-event -catalog shape that v0.2.0 replaces. v0.2.0 ships a fresh test file; +**out of scope** for v0.3.0 — its assertions cover the curated-event +catalog shape that v0.3.0 replaces. v0.3.0 ships a fresh test file; v0.1.0's stays in git history for reference but is gitignored from the test runner. @@ -70,7 +70,7 @@ A throwing consumer callback does not break the dispatch chain. **Assertions:** - Register two callbacks; first throws, second runs. - The error is logged via `console.error` with the prefix - `[hax-hooks-lib]` and the hook name. + `[foundry-hooks-lib]` and the hook name. - The library does NOT re-throw the error to Foundry's hook chain. - 100 fires with a single throwing callback complete with the same total fires-to-other-callbacks count as 100 fires without. @@ -97,7 +97,7 @@ A throwing consumer callback does not break the dispatch chain. library continues loading other adapters. **`unregisterModule` cleanup:** -- After `unregisterModule("hax-hooks-lib")`, `listRegisteredHooks()` +- After `unregisterModule("foundry-hooks-lib")`, `listRegisteredHooks()` returns `[]`. - All consumer callbacks are removed (verified by re-registering init + asserting the registered hook set is empty). @@ -127,7 +127,7 @@ A throwing consumer callback does not break the dispatch chain. firing with raw Foundry args produces an `args` array matching the documented shape, regardless of Foundry version. - The exact normalization shapes live in `docs/HOOK_ARG_SHAPES.md`. - v0.2.0 ships shapes for: `combatStart`, `combatEnd`, `combatTurn`, + v0.3.0 ships shapes for: `combatStart`, `combatEnd`, `combatTurn`, `combatRound`, `preUpdateActor`, `updateActor`, `preUpdateToken`, `updateToken`, `dnd5e.rollAttackV2`, `dnd5e.rollDamageV2`. The remaining hooks either have stable arity or no normalization. @@ -176,7 +176,7 @@ Beyond Section D's basic cases: plan. hooks-lib's tests verify the adapter *protocol* (registration, matching, lifecycle) but not the adapter's *content*. - **Foundry-version compatibility beyond the latest two releases.** - v0.2.0 tests Foundry v13.351 and v14.364. Older versions are + v0.3.0 tests Foundry v13.351 and v14.364. Older versions are "best effort" — the anti-corruption layer should handle them, but no CI gate. - **Real Foundry runtime for the smoke test.** The no-Foundry smoke @@ -187,7 +187,7 @@ Beyond Section D's basic cases: ## Definition of done -A v0.2.0 release is "done" when: +A v0.3.0 release is "done" when: 1. **100% of the "What we test" bullets have an assertion.** No silent gaps. If we can't test something (e.g. a Foundry-version @@ -208,7 +208,7 @@ A v0.2.0 release is "done" when: --- -## Test files (v0.2.0) +## Test files (v0.3.0) | File | Purpose | |---|---| @@ -225,10 +225,10 @@ for releases. Performance test runs weekly or on demand. ## Future turns (when this repo is no longer the focus) - The v0.1.0 `tests/verify-hooks-lib.mjs` and `tests/test-helpers.mjs` - will be archived (git mv into `tests/archive/v0.1.0/`) once v0.2.0's + will be archived (git mv into `tests/archive/v0.1.0/`) once v0.3.0's test files are stable. They stay in git history but not in the test runner. -- When battle-focus migrates to hooks-lib v0.2.0, the +- When battle-focus migrates to hooks-lib v0.3.0, the battle-focus test plan (separate `tests/PLAN.md` in that repo) will reference hooks-lib's plan for the contract assertions and add battle-focus-specific consumer assertions. @@ -239,7 +239,7 @@ for releases. Performance test runs weekly or on demand. ## Open questions for this plan -These are settled when v0.2.0 implementation starts, not before: +These are settled when v0.3.0 implementation starts, not before: 1. **Stub fidelity for the no-Foundry smoke test.** How close does the stub need to match Foundry's actual `Hooks.on` semantics @@ -257,4 +257,4 @@ These are settled when v0.2.0 implementation starts, not before: Both paths are tested. Push back on any of these or on the plan as a whole before the -v0.2.0 implementation starts. \ No newline at end of file +v0.3.0 implementation starts. \ No newline at end of file diff --git a/tests/perf.mjs b/tests/perf.mjs index 473a989..0bab6cb 100644 --- a/tests/perf.mjs +++ b/tests/perf.mjs @@ -27,7 +27,7 @@ function assert(name, cond, extra = "") { } async function main() { - console.log("--- hax-hooks-lib v0.2.0 perf test ---"); + console.log("--- foundry-hooks-lib v0.3.0 perf test ---"); installStubs(); uninstall(); install(); diff --git a/tests/test-helpers.mjs b/tests/test-helpers.mjs index 3ca11cf..fe9d1e6 100644 --- a/tests/test-helpers.mjs +++ b/tests/test-helpers.mjs @@ -1,4 +1,4 @@ -// tests/test-helpers.mjs — v0.2.0 +// tests/test-helpers.mjs — v0.3.0 // // Foundry hook stub for the no-Foundry smoke test. Installs globalThis.Hooks // with the same semantics as Foundry v14: diff --git a/tests/verify-hooks-lib.mjs b/tests/verify-hooks-lib.mjs index 5973948..4171c77 100644 --- a/tests/verify-hooks-lib.mjs +++ b/tests/verify-hooks-lib.mjs @@ -1,4 +1,4 @@ -// tests/verify-hooks-lib.mjs — v0.2.0 +// tests/verify-hooks-lib.mjs — v0.3.0 // // Smoke test for the generic Foundry hook facade. Implements // tests/PLAN.md sections A-G (envelope shape, subscriber API, error @@ -64,7 +64,7 @@ function assertEq(name, actual, expected) { } async function mainTest() { - console.log("--- hax-hooks-lib v0.2.0 smoke test ---"); + console.log("--- foundry-hooks-lib v0.3.0 smoke test ---"); // ----- Section F.1 — semver range matcher (foundation for G) ----- console.log("[F.1] semver range matcher"); @@ -222,10 +222,10 @@ async function mainTest() { } assertEq("C: second callback still fires after first throws", seenC, ["updateActor"]); assert( - "C: error logged via console.error with [hax-hooks-lib] prefix and hook name", + "C: error logged via console.error with [foundry-hooks-lib] prefix and hook name", consoleErrorCalls.length > 0 && consoleErrorCalls.some((args) => - String(args[0]).includes("[hax-hooks-lib]") && + String(args[0]).includes("[foundry-hooks-lib]") && String(args[0]).includes("updateActor") ) ); @@ -480,7 +480,7 @@ async function mainTest() { evaluateAtReady({ id: "dnd5e", version: "5.2.5" }, "13.351.0"); // Re-evaluation calls factory again. This is the documented behavior // in §4.2 — adapters must be idempotent. - // v0.2.0 implementation re-evaluates on each ready. Adapters must + // v0.3.0 implementation re-evaluates on each ready. Adapters must // handle this. We assert that the factory IS called twice (re-eval // happened), since the adapter protocol requires idempotency. assert("D.7: re-evaluation calls factory (adapter must be idempotent)", callsD7 === 2);