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.
93 lines
2.8 KiB
JavaScript
93 lines
2.8 KiB
JavaScript
// 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:
|
|
// - Hooks.on(name, fn) — register
|
|
// - Hooks.once(name, fn) — register (fires once on next callAll)
|
|
// - Hooks.off(name, fn) — remove
|
|
// - Hooks.callAll(name, ...args) — synchronous fan-out
|
|
//
|
|
// Also stubs globalThis.game and globalThis.ui so the library's init
|
|
// and ready hooks can run in pure Node.
|
|
|
|
import { performance } from "node:perf_hooks";
|
|
|
|
const _listeners = new Map(); // hookName -> [fn, ...]
|
|
const _once = new WeakMap(); // fn -> { hookName } for once-tracking
|
|
const _callLog = []; // every Hooks.callAll(name, ...args) recorded
|
|
|
|
export function installStubs() {
|
|
resetStubs();
|
|
globalThis.Hooks = {
|
|
on(name, fn) {
|
|
_listeners.set(name, [...(_listeners.get(name) ?? []), fn]);
|
|
},
|
|
once(name, fn) {
|
|
_listeners.set(name, [...(_listeners.get(name) ?? []), fn]);
|
|
_once.set(fn, { hookName: name });
|
|
},
|
|
off(name, fn) {
|
|
const list = _listeners.get(name);
|
|
if (!list) return;
|
|
const next = list.filter((f) => f !== fn);
|
|
if (next.length === 0) _listeners.delete(name);
|
|
else _listeners.set(name, next);
|
|
_once.delete(fn);
|
|
},
|
|
callAll(name, ...args) {
|
|
_callLog.push({ name, args, ts: performance.now() });
|
|
const list = _listeners.get(name);
|
|
if (!list) return;
|
|
// Snapshot the list because handlers may add/remove listeners.
|
|
const snapshot = [...list];
|
|
for (const fn of snapshot) {
|
|
if (_once.has(fn)) {
|
|
// Once handler — remove before firing so re-entrant callAll doesn't re-fire.
|
|
this.off(name, fn);
|
|
}
|
|
try {
|
|
fn(...args);
|
|
} catch (e) {
|
|
// Mirror Foundry: errors in handlers are caught (the library
|
|
// adds its own error containment on top).
|
|
console.error(`[stubs] Hooks.callAll(${name}) handler threw:`, e);
|
|
}
|
|
}
|
|
},
|
|
};
|
|
globalThis.game = {
|
|
version: "13.351.0",
|
|
system: { id: "test-system", version: "0.0.0" },
|
|
modules: new Map(),
|
|
user: null,
|
|
ready: true,
|
|
};
|
|
globalThis.ui = { notifications: { info: () => {}, warn: () => {}, error: () => {} } };
|
|
}
|
|
|
|
export function resetStubs() {
|
|
_listeners.clear();
|
|
_callLog.length = 0;
|
|
}
|
|
|
|
export function getListeners(hookName) {
|
|
return _listeners.get(hookName) ?? [];
|
|
}
|
|
|
|
export function getAllCallLog() {
|
|
return [..._callLog];
|
|
}
|
|
|
|
export function clearCallLog() {
|
|
_callLog.length = 0;
|
|
}
|
|
|
|
export function setGameVersion(version) {
|
|
if (typeof globalThis.game === "undefined") globalThis.game = {};
|
|
globalThis.game.version = version;
|
|
}
|
|
|
|
export function setGameSystem(system) {
|
|
if (typeof globalThis.game === "undefined") globalThis.game = {};
|
|
globalThis.game.system = system;
|
|
} |