5 Commits

Author SHA1 Message Date
2fabb5e98f v0.4.1: drop renderChatMessage, register renderChatMessageHTML
Foundry v13 deprecated renderChatMessage in favor of
renderChatMessageHTML (which passes an HTMLElement, not a jQuery
wrapper). Subscribing to the deprecated hook re-emits Foundry's
compatibility warning on every chat render in worlds still running
v13.351 (the foundry-hooks-lib module's tests run against such a
world).

v0.3.0 already narrowed scope to Foundry v14 only (HOOK_CONTRACT.md
section 9), but the registered hook set still included
renderChatMessage as a legacy fallback. There is no Foundry v14
hook by that name, so the entry was dead weight — and worse, any
v13.351 world running the v14-only library would still see the
deprecation warning every chat render.

Changes:
- registered-hooks.js: replace renderChatMessage entry with
  renderChatMessageHTML. Update arg shape (HTML passes HTMLElement,
  not jQuery). Add comment explaining the deprecation.
- README.md / HOOK_CONTRACT.md section 6: list renderChatMessageHTML
  instead of renderChatMessage.
- tests/verify-hooks-lib.mjs: update stub arg shape from
  [{id}, {}, {}] to [{id}, {}] (v14 signature).

Verification:
- node tests/verify-hooks-lib.mjs: 546/546 (unchanged)
- node tests/perf.mjs: 6/6, median 0.0003ms/fire (well under
  the 0.1ms budget in HOOK_CONTRACT.md section 7)
- node --check on all scripts + tests: clean

Push: Gitea only.

Note: battle-focus's own main.js line 144 still has a
Hooks.on('renderChatMessage', ...) listener for its 'Open in
Journal' button wiring. That listener fires the deprecation warning
on the user's console. Fixing it is a battle-focus change, out of
scope for this turn (hooks-lib only).
2026-06-20 22:49:32 -04:00
ba448b94c9 v0.4.0: scope to Foundry v14 only, drop v13 dual-subscription
User directive: 'update the plan. v14 only'. Implementation:

**scope change (Foundry v14 only):**
- registered-hooks.js: renderChatInput entry drops the v13
  renderChatLog name. Single subscription to the v14 name.
- anti-corruption.js: combatRound arg-normalization no longer
  detects the v13 round-num position. v14's updateOptions.round
  is the only path. Removed the v13 comments from the other
  arg shapes (combatEnd, combatTurn).
- module.json: compatibility.minimum is now 14 (was 13).
  verified stays 14.
- package.json: version 0.3.0 -> 0.4.0 (semver-breaking: dropping
  v13 support is a breaking change for v13 consumers).
- package.json description: 'Foundry VTT v14-only module' prefix.

**test plan:**
- tests/PLAN.md: v14-only scope documented at the top of the file
  and in Section E. Status line bumps 554 to 546 assertions
  (v13-only assertions dropped). Test files table re-scoped to
  v0.4.0.
- tests/verify-hooks-lib.mjs: dropped the v13-only assertions
  (E.2 'renderChatLog in installed', E.3's 'both v13 and v14
  produce' check, E.4's v13 round shape). Kept the v14-only
  assertions + added an inverse assertion: 'renderChatLog is NOT
  in installed raw hooks' to lock the v14-only scope.
- tests/verify-hooks-lib.mjs: stub table at line ~520 drops
  renderChatLog (dead in production now).

**doc updates:**
- README.md: new 'v0.4.0 — Foundry v14 only' section explaining
  the change + migration note for v13 consumers.
- docs/HOOK_CONTRACT.md: v0.3.0 header. §9 marks the v13 column
  as historical. §10 example uses the v14 shape only.

**artifact:**
- foundry-hooks-lib-0.4.0.zip built (82KB, 46 entries, inner
  version 0.4.0, inner compatibility.minimum 14).

**verified:**
- npm test: 546/546 assertions passed
- npm run test:foundry: 30/30 assertions passed
- npm run test:perf: 6/6 assertions passed (median 0.0003ms/fire)
- battle-focus E2E (consumer): 125/125 still green
- its-achievable smoke (consumer): 75/75 still green

**consumer follow-up (separate commits in their own repos):**
- battle-focus/module.json: relationships.requires[0].version
  bumped to ^0.4.0
- its-achievable/module.json: relationships.requires[0].minimum
  bumped to 0.4.0
2026-06-20 17:42:30 -04:00
d038eb8c67 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.
2026-06-20 16:53:37 -04:00
7f0d1bbff1 v0.2.0 — generic Foundry hook facade (rip-and-replace of v0.1.0)
v0.1.0 shipped as a curated-event catalog (18 hand-written handlers
+ system adapters + an encounter.js stub). Wrong shape. v0.2.0 is a
RIP-AND-REPLACE per HOOK_CONTRACT.md (docs/HOOK_CONTRACT.md):
generic Foundry hook facade with NO domain interpretation.

## What's new

scripts/internal/ — new modular structure:
- registered-hooks.js: the §6 hook set (~60 raw Foundry hooks,
  envelope name, sync/async mode)
- envelope.js: buildEnvelope + sync/async dispatch (§8)
- subscribers.js: subscribe/subscribeMany/subscribeAll/unsubscribeAll
  primitives (§3) + error containment (§3.5)
- adapter-registry.js: registerSystemAdapter + semver-range matching
  (§5) + ready-time evaluation
- semver.js: inline semver matcher (no external dep)
- anti-corruption.js: hook rename normalization (§9) + arg shape
  fixes (§10) + combatInactive synthesis from updateCombat
- lifecycle.js: init/ready/unregisterModule hooks (§4)

scripts/main.js — Foundry entry point. Registers the public API on
mod.api; init installs Foundry hooks; ready evaluates adapters;
unregisterModule cleans up.

## Tests

- tests/verify-hooks-lib.mjs — 554 assertions in 0.34s (under the
  2s budget). Sections A-G of tests/PLAN.md:
  - A: envelope shape (every registered hook produces exactly
    {ts, hook, args})
  - B: subscriber API (single, batch, all, atomic, error path)
  - C: error containment (throwing consumer doesn't break chain)
  - D: lifecycle (install/uninstall, adapter eval, world change)
  - E: anti-corruption (renderChatLog→renderChatInput,
    combatInactive synthesis, combatRound arg normalization)
  - F: semver matcher
  - G: adapter loading (validation, dedup, factory failure)
- tests/perf.mjs — 6 assertions. Median 0.0003ms/fire
  (333x under the 0.1ms budget). Heap delta 2.8MB across 10k fires.
- tests/test-helpers.mjs — Foundry stub (Hooks, game, ui).

## Archive

scripts/_archive/v0.1.0/ — v0.1.0 catalog moved here for git
history. The 18 handlers, system adapters, and encounter.js stub
all live there but are not part of the v0.2.0 module.

tests/_archive_v0.1.0_*.mjs — v0.1.0 test files renamed with
prefix to avoid colliding with v0.2.0 files.

## Manifest

- module.json: bumped to 0.2.0; download URL points at the new zip.
- package.json: bumped to 0.2.0; added test:perf script.
- README.md: rewritten for v0.2.0.

Push: Gitea only.
2026-06-20 03:01:22 -04:00
d5d0b1655f v0.1.0 — initial extraction from battle-focus v0.5.0-alpha.12
Stage 1 of the Hax's Tools split (plan: .hermes/plans/2026-06-20_040000-hax-tools-split.md).

What's in this repo:
- module.json, package.json, README.md, LICENSE, .gitignore
- scripts/main.js — module entry, re-exports loadSystems / registerAllEvents / $ctx on mod.api
- scripts/events/registry.js — single-chokepoint hook wrapper (MODULE_ID retagged)
- scripts/events/core/ — 16 system-agnostic events
- scripts/events/dnd5e/ — 2 dnd5e events (roll-attack, roll-damage)
- scripts/systems/loader.js + core-system.js + dnd5e-system.js — system-adapter layer
- scripts/encounter.js — STAGE-1-STUB: returns null; real encounter wiring lands in Stage 2
- tests/verify-hooks-lib.mjs — 20-assertion no-Foundry smoke test (all green)
- hooks-lib-0.1.0.zip — release artifact for Foundry's manifest download URL

Behavior changes vs. battle-focus source:
- Module-id log strings retagged: [battle-focus] -> [hax-hooks-lib] in loader.js + registry.js
- Token/item stash flag namespace: battle-focus -> hax-hooks-lib (the stash is
  internal to the pre/update pair; consumer-agnostic so we own it)
- Hook calls in hooks-lib's copy are gated on a null encounter (the stub),
  so hooks-lib's events don't fire end-to-end. battle-focus's LOCAL copy
  of the events is untouched and still works. Stage 2 will wire the real
  encounter via registerAllEvents({getActiveEncounter}).

Verified:
- node --check passes on all 26 JS files
- npm test (verify-hooks-lib.mjs) — 20/20 assertions
- python validate-module-json.py module.json — 0 errors, 1 warning (no icon, Stage 2)

Push: Gitea only (git.homelab.local/kaykayyali/hooks-lib). No GitHub mirror.

Refs: battle-focus 8b9db20 (v0.5.0-alpha.12, 220/222 tests)
2026-06-20 00:45:08 -04:00