v0.3.0: rename module id hax-hooks-lib -> foundry-hooks-lib
User callout: 'Hax' is Kaysser's nickname. The module id should not use it. Rename the Foundry module id from 'hax-hooks-lib' to 'foundry-hooks-lib'. Gitea repo name stays as 'hooks-lib' (kept for the user-facing URL); the Gitea manifest URL is unchanged. **Scope of rename:** - module.json: id, title, version (0.2.0 -> 0.3.0), download URL - package.json: name - README.md, HOOK_CONTRACT.md, LICENSE: branding text - All 6 production JS files: MODULE_ID constant + comments - 4 active test files: console.log strings + test descriptions - Rename of release zips in git: hooks-lib-X.Y.Z.zip -> foundry-hooks-lib-X.Y.Z.zip (preserves the v0.1.0 and v0.2.0 zips as historical artifacts; the v0.3.0 zip is the new release artifact) - .gitignore: glob + un-ignore lines updated to match **Out of scope (deliberate):** - Gitea repo name 'kaykayyali/hooks-lib' stays. Per the user's direction, only the module id is renamed; the Gitea URL path is preserved for the existing 'url', 'manifest', 'download' fields. - scripts/_archive/v0.1.0/*: historical v0.1.0 code is left as-is. Those files tested 'hax-hooks-lib v0.1.0'; rewriting the history would be misleading. - tests/_archive_v0.1.0_*.mjs: same reason, left untouched. - .hermes/plans/* session-historian plans that reference 'Hax's Tools split': session artifact, not a release asset. **Verification:** 554/554 smoke assertions pass, 6/6 perf assertions pass, median 0.0004ms/fire (well under 0.1ms budget). No logic change; rename is string-only. **Consumer action required:** battle-focus and its-achievable both declare 'relationships.requires' pointing to 'hax-hooks-lib'. The next commits on those repos will update their relationships to 'foundry-hooks-lib' + bump their versions. Foundry instances with v0.2.0 of the old id installed will need to be reinstalled as v0.3.0 of the new id.
This commit is contained in:
10
.gitignore
vendored
10
.gitignore
vendored
@@ -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
|
||||
|
||||
3
LICENSE
3
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.
|
||||
|
||||
26
README.md
26
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.
|
||||
|
||||
|
||||
@@ -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(...)`.
|
||||
|
||||
---
|
||||
|
||||
|
||||
BIN
foundry-hooks-lib-0.3.0.zip
Normal file
BIN
foundry-hooks-lib-0.3.0.zip
Normal file
Binary file not shown.
@@ -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",
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 = [
|
||||
|
||||
@@ -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, ...]
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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.
|
||||
v0.3.0 implementation starts.
|
||||
@@ -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();
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user