@vttforge/core 0.11.1 → 0.11.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/README.md +1 -1
- package/dist/errors-manifest.json +2 -2
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @vttforge/core
|
|
2
2
|
|
|
3
|
+
## 0.11.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 7eeeb20: Rewrite the npm package descriptions to say what each package does today. `types` claimed full schema inference it does not have, `vite-plugin` claimed Handlebars HMR that lives in the dev loop, and `cli` did not mention `audit`.
|
|
8
|
+
|
|
3
9
|
## 0.11.1
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ pnpm add @vttforge/core
|
|
|
11
11
|
| Export | What it replaces |
|
|
12
12
|
|---|---|
|
|
13
13
|
| `registerSystem` / `registerModule` | The `Hooks.once("init")` block: data models, document classes, initiative, status effects, sheets, enrichers, and `ready` |
|
|
14
|
-
| `BaseTypeDataModel(defineSchema)` | A `TypeDataModel` whose fields are typed from the schema
|
|
14
|
+
| `BaseTypeDataModel(defineSchema)` | A `TypeDataModel` whose fields are typed from the schema, so `this.level` is a `number` inside `prepareDerivedData` |
|
|
15
15
|
| `fields()` | `foundry.data.fields`, typed, read lazily so the module imports in Node |
|
|
16
16
|
| `InferSchema<T>` / `Model['$inferData']` | The hand-written interface that drifts from the schema |
|
|
17
17
|
| `BaseActorSheet()` / `BaseItemSheet()` | `static TABS`, `static DRAG_DROP`, typed `onDropItem` and friends on `ActorSheetV2` / `ItemSheetV2` with Handlebars |
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 1,
|
|
3
3
|
"package": "@vttforge/core",
|
|
4
|
-
"packageVersion": "0.11.
|
|
4
|
+
"packageVersion": "0.11.2",
|
|
5
5
|
"entries": [
|
|
6
6
|
{
|
|
7
7
|
"code": "VTTF-0001",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
{
|
|
27
27
|
"code": "VTTF-0005",
|
|
28
28
|
"name": "WorldTooOldForMigration",
|
|
29
|
-
"summary": "createMigrationRunner() was called on a world whose stored schemaVersion is older than the configured compatibleVersion floor. Upgrade the world to a supported intermediate version before continuing
|
|
29
|
+
"summary": "createMigrationRunner() was called on a world whose stored schemaVersion is older than the configured compatibleVersion floor. Upgrade the world to a supported intermediate version before continuing. Running migrations across the gap would corrupt data."
|
|
30
30
|
},
|
|
31
31
|
{
|
|
32
32
|
"code": "VTTF-0006",
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region package.json
|
|
2
|
-
var version = "0.11.
|
|
2
|
+
var version = "0.11.2";
|
|
3
3
|
//#endregion
|
|
4
4
|
//#region src/errors/registry.ts
|
|
5
5
|
const DOCS_BASE_URL = "https://vttforge.dev/errors";
|
|
@@ -27,7 +27,7 @@ const REGISTRY = Object.freeze({
|
|
|
27
27
|
"VTTF-0005": Object.freeze({
|
|
28
28
|
code: "VTTF-0005",
|
|
29
29
|
name: "WorldTooOldForMigration",
|
|
30
|
-
summary: "createMigrationRunner() was called on a world whose stored schemaVersion is older than the configured compatibleVersion floor. Upgrade the world to a supported intermediate version before continuing
|
|
30
|
+
summary: "createMigrationRunner() was called on a world whose stored schemaVersion is older than the configured compatibleVersion floor. Upgrade the world to a supported intermediate version before continuing. Running migrations across the gap would corrupt data."
|
|
31
31
|
}),
|
|
32
32
|
"VTTF-0006": Object.freeze({
|
|
33
33
|
code: "VTTF-0006",
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["resolveBases","resolveDragDrop","ID_PATTERN","assertValidId","registered","vttfError","readHooks","readConfig","applyInit"],"sources":["../package.json","../src/errors/registry.ts","../src/base-actor-sheet.ts","../src/base-application.ts","../src/base-document-sheet.ts","../src/base-item-sheet.ts","../src/base-type-data-model.ts","../src/data/fields.ts","../src/errors/manifest.ts","../src/migrations/runner.ts","../src/register-enrichers.ts","../src/register-sheets.ts","../src/register-module.ts","../src/register-system.ts","../src/system-config.ts","../src/index.ts"],"sourcesContent":["","/**\n * VTTF-NNNN error registry — append-only, stable across majors.\n *\n * Every error VTTForge throws has a numeric code (`VTTF-NNNN`) and a PascalCase\n * `name` for stack-trace readability. Codes are URLs — `https://vttforge.dev/errors/VTTF-0001`\n * eventually links to a docs page generated from this registry.\n *\n * Never renumber an entry. To deprecate, mark with `deprecated: true` and add a\n * `replacedBy` pointer. Adding a new code: pick the next unused integer.\n */\n\nexport type VttfErrorCode = `VTTF-${string}`;\n\nexport interface VttfErrorEntry {\n readonly code: VttfErrorCode;\n readonly name: string;\n readonly summary: string;\n readonly deprecated?: boolean;\n readonly replacedBy?: VttfErrorCode;\n}\n\nconst DOCS_BASE_URL = 'https://vttforge.dev/errors';\n\nconst REGISTRY: Readonly<Record<VttfErrorCode, VttfErrorEntry>> = Object.freeze({\n 'VTTF-0001': Object.freeze({\n code: 'VTTF-0001',\n name: 'SystemAlreadyRegistered',\n summary:\n 'registerSystem() was called more than once for the same system id. This is almost always a hot-reload artefact or a duplicate import.',\n }),\n 'VTTF-0002': Object.freeze({\n code: 'VTTF-0002',\n name: 'MissingFoundryGlobals',\n summary:\n 'VTTForge code ran in an environment without Foundry globals (game, Hooks, CONFIG). Initialise inside the Foundry runtime, not in a Node test without mocks.',\n }),\n 'VTTF-0003': Object.freeze({\n code: 'VTTF-0003',\n name: 'UnknownSetting',\n summary:\n 'SystemConfig.get() / set() was called with a key that was never passed to SystemConfig.register(). Register the setting in your init hook before reading it.',\n }),\n 'VTTF-0004': Object.freeze({\n code: 'VTTF-0004',\n name: 'MigrationFailed',\n summary:\n 'A migration function passed to createMigrationRunner() threw. The original error is available on .cause. The schemaVersion setting is not advanced past the failed migration so retrying on the next world load picks up where the failure left off.',\n }),\n 'VTTF-0005': Object.freeze({\n code: 'VTTF-0005',\n name: 'WorldTooOldForMigration',\n summary:\n 'createMigrationRunner() was called on a world whose stored schemaVersion is older than the configured compatibleVersion floor. Upgrade the world to a supported intermediate version before continuing — running migrations across the gap would corrupt data.',\n }),\n 'VTTF-0006': Object.freeze({\n code: 'VTTF-0006',\n name: 'InvalidSheetId',\n summary:\n 'A sheet was registered with an id that is empty, contains a dot, or repeats another sheet in the same package. The id becomes half of the key Foundry persists on every document using the sheet, so it must be a single unambiguous segment.',\n }),\n 'VTTF-0007': Object.freeze({\n code: 'VTTF-0007',\n name: 'InvalidEnricher',\n summary:\n 'A text enricher was registered with an id that is empty, contains a dot, or repeats another enricher in the same package, or with a pattern missing the g flag. Foundry looks enrichers up by id and takes the first match, and matches with matchAll, which throws on a non-global regex.',\n }),\n});\n\n/**\n * Look up a registered entry by code. Throws if the code is unknown — the\n * registry is the source of truth, so missing codes mean a typo.\n */\nexport function getErrorEntry(code: VttfErrorCode): VttfErrorEntry {\n const entry = REGISTRY[code];\n if (entry === undefined) {\n throw new Error(`Unknown VTTForge error code: ${code}. Add it to the registry.`);\n }\n return entry;\n}\n\n/**\n * Return every entry currently in the registry. Used by codegen to emit the\n * runtime constants and the JSON manifest that powers the docs pages.\n */\nexport function listErrorEntries(): readonly VttfErrorEntry[] {\n return Object.values(REGISTRY);\n}\n\nexport function docsUrlFor(code: VttfErrorCode): string {\n return `${DOCS_BASE_URL}/${code}`;\n}\n\n/**\n * VttfError — every error VTTForge throws extends this.\n *\n * - `code` is the registry key (string-narrowed).\n * - `name` is the PascalCase name from the registry — shows up in stack traces.\n * - `docsUrl` points at the docs page.\n * - `cause` uses the native ES2022 mechanism. Multiple causes => pass an\n * `AggregateError` as the cause.\n */\nexport class VttfError extends Error {\n readonly code: VttfErrorCode;\n readonly docsUrl: string;\n\n constructor(code: VttfErrorCode, message?: string, options?: ErrorOptions) {\n const entry = getErrorEntry(code);\n const finalMessage = `[${code}] ${message ?? entry.summary}`;\n super(finalMessage, options);\n this.code = code;\n this.name = entry.name;\n this.docsUrl = docsUrlFor(code);\n }\n}\n","/**\n * BaseActorSheet — `ActorSheetV2 + HandlebarsApplicationMixin` baseline with the\n * boilerplate every shipping system copy-pastes hoisted into the SDK.\n *\n * What this adds beyond stock Foundry v13:\n *\n * - **`static DRAG_DROP`** — declare drag sources / drop targets as data, get\n * `foundry.applications.ux.DragDrop` instances wired in `_onRender` with\n * `isEditable`-gated permissions and a sensible default `_onDragStart` that\n * serialises `data-item-id` elements as `{ type: \"Item\", uuid }`.\n * - **`_prepareContext` auto-fills `context.tabs[group]`** for every group\n * declared in ApplicationV2's `static TABS`, so subclass `_prepareContext`\n * implementations stop having to call `_prepareTabs(group)` by hand.\n * - **Typed drop dispatch** — override `onDropItem(item, event)` /\n * `onDropActor(actor, event)` / `onDropFolder(folder, event)` /\n * `onDropActiveEffect(effect, event)` and skip the `fromUuid()` ceremony.\n * Returning `undefined` falls through to Foundry's default `_onDropX`\n * behaviour; return any other value to take ownership.\n *\n * Intentional non-additions:\n *\n * - `editImage` action — already shipped by `DocumentSheetV2` (inherited by\n * `ActorSheetV2`). Templates wire `<img data-edit=\"img\">` and Foundry's\n * built-in action handles the `FilePicker` flow.\n * - `_getTabs()` — ApplicationV2 already owns the tab state machine; we only\n * eliminate the `_prepareTabs` call in `_prepareContext`.\n *\n * Resolved lazily so subclasses can be declared at module load without\n * Foundry globals existing yet (test boot, ESM hoist).\n */\n\nimport { VttfError } from './errors/registry.js';\nimport type { DocumentSheetV2Members } from './foundry-base.js';\n\n// biome-ignore lint/suspicious/noExplicitAny: Foundry's ActorSheetV2 shape lives in fvtt-types (deferred to @vttforge/types v1.0)\ntype AnyConstructor = new (...args: any[]) => any;\n\n/**\n * Declarative DragDrop entry consumed by `_onRender`. Mirrors the\n * `foundry.applications.ux.DragDrop` constructor config. Permissions and\n * callbacks fall back to sensible defaults that honour `this.isEditable` and\n * the default `_onDragStart` / `_onDrop`.\n */\nexport interface DragDropConfig {\n readonly dragSelector?: string;\n readonly dropSelector?: string;\n readonly permissions?: {\n readonly dragstart?: () => boolean;\n readonly drop?: () => boolean;\n };\n // biome-ignore lint/suspicious/noExplicitAny: DragEvent payload is browser-native; consumers route to their own typed handlers\n readonly callbacks?: Record<string, (...args: any[]) => unknown>;\n}\n\n/**\n * The statics a VTTForge sheet base carries.\n *\n * The factory used to return a bare constructor, so a subclass writing\n * `super.DEFAULT_OPTIONS` — the pattern the docs show and every sheet needs —\n * failed to compile. TypeScript cannot see a static through an untyped\n * constructor. The example system never caught it because it is JavaScript.\n *\n * `DEFAULT_OPTIONS` is deliberately loose: a subclass merges its own shape\n * into it, and pinning ours would reject the merge.\n */\nexport interface SheetBaseStatics {\n // biome-ignore lint/suspicious/noExplicitAny: a subclass merges arbitrary ApplicationV2 options in; a narrower type would reject the merge\n readonly DEFAULT_OPTIONS: Record<string, any>;\n readonly DRAG_DROP: ReadonlyArray<DragDropConfig>;\n}\n\n/**\n * What the factory hands back: something you can `extend`, whose statics the\n * compiler can see.\n */\n/**\n * What the sheet factories add on top of Foundry's own sheet.\n *\n * Only the members a subclass actually reaches for. The rest of the Foundry\n * surface stays reachable and untyped until `@vttforge/types` describes it —\n * see `DocumentSheetV2Members`.\n */\nexport interface SheetBaseMembers {\n /** Fills in `context.tabs` for every group in `static TABS`. */\n _prepareContext(options: unknown): Promise<Record<string, unknown>>;\n /** Binds the `static DRAG_DROP` entries. */\n _onRender(context: unknown, options: unknown): void;\n _onDragStart(event: DragEvent): void;\n\n /**\n * The typed drop hooks. Override the one you want; returning `undefined`\n * hands the drop back to Foundry's own handling.\n */\n onDropItem(item: unknown, event: DragEvent): Promise<unknown>;\n onDropActor(actor: unknown, event: DragEvent): Promise<unknown>;\n onDropFolder(folder: unknown, event: DragEvent): Promise<unknown>;\n onDropActiveEffect(effect: unknown, event: DragEvent): Promise<unknown>;\n\n /**\n * Foundry's own drop entry points, implemented here to resolve the payload\n * and hand it to the `onDropX` hook above.\n *\n * Declared because the base really does define them — an earlier version\n * left them off, and nothing said so while an index signature was making\n * every member name legal. Override `onDropItem` rather than this.\n */\n _onDropItem(event: DragEvent, data: unknown): Promise<unknown>;\n _onDropActor(event: DragEvent, data: unknown): Promise<unknown>;\n _onDropFolder(event: DragEvent, data: unknown): Promise<unknown>;\n _onDropActiveEffect(event: DragEvent, data: unknown): Promise<unknown>;\n}\n\nexport interface SheetBaseCtor extends SheetBaseStatics {\n // biome-ignore lint/suspicious/noExplicitAny: mirrors ApplicationV2's constructor arity, which subclasses pass straight through\n new (...args: any[]): SheetBaseMembers & DocumentSheetV2Members;\n}\n\ninterface DragDropInstance {\n bind(element: HTMLElement): void;\n}\n\ninterface DragDropCtor {\n new (config: Record<string, unknown>): DragDropInstance;\n}\n\ninterface FoundryApplicationsApi {\n HandlebarsApplicationMixin?: (base: AnyConstructor) => AnyConstructor;\n}\n\ninterface FoundryApplicationsSheets {\n ActorSheetV2?: AnyConstructor;\n}\n\ninterface FoundryApplicationsUx {\n DragDrop?: DragDropCtor;\n}\n\ninterface FoundryApplications {\n api?: FoundryApplicationsApi;\n sheets?: FoundryApplicationsSheets;\n ux?: FoundryApplicationsUx;\n}\n\ninterface FoundryGlobal {\n applications?: FoundryApplications;\n}\n\nfunction resolveBases(): { Base: AnyConstructor; mixin: (b: AnyConstructor) => AnyConstructor } {\n const foundry = (globalThis as Record<string, unknown>).foundry as FoundryGlobal | undefined;\n const Base = foundry?.applications?.sheets?.ActorSheetV2;\n const mixin = foundry?.applications?.api?.HandlebarsApplicationMixin;\n if (typeof Base !== 'function' || typeof mixin !== 'function') {\n throw new VttfError(\n 'VTTF-0002',\n 'foundry.applications.sheets.ActorSheetV2 and/or foundry.applications.api.HandlebarsApplicationMixin are not available. Define your BaseActorSheet subclasses inside the Foundry runtime (or stub the global in tests).',\n );\n }\n return { Base, mixin };\n}\n\nfunction resolveDragDrop(): DragDropCtor | undefined {\n const foundry = (globalThis as Record<string, unknown>).foundry as FoundryGlobal | undefined;\n const ctor = foundry?.applications?.ux?.DragDrop;\n return typeof ctor === 'function' ? ctor : undefined;\n}\n\nasync function resolveFromUuid(uuid: string): Promise<unknown> {\n const fn = (globalThis as Record<string, unknown>).fromUuid as\n | ((u: string) => Promise<unknown>)\n | undefined;\n if (typeof fn !== 'function') return null;\n return fn(uuid);\n}\n\ninterface DropPayload {\n readonly type?: string;\n readonly uuid?: string;\n}\n\n/**\n * Marker class that consumer CSS uses for scoping. Always present on every\n * VTTForge-derived sheet so rules like `.vttforge .actor-sheet { ... }` work.\n */\nexport const VTTFORGE_SHEET_CLASS = 'vttforge';\n\n/**\n * Build the `BaseActorSheet` for the current Foundry runtime. See module\n * header for the boilerplate this base eliminates.\n *\n * @example\n * ```ts\n * class CharacterSheet extends BaseActorSheet() {\n * static DEFAULT_OPTIONS = foundry.utils.mergeObject(\n * super.DEFAULT_OPTIONS,\n * { classes: ['my-system'], position: { width: 720 } },\n * );\n * static PARTS = { ... };\n * static TABS = {\n * primary: {\n * tabs: [\n * { id: 'features', group: 'primary', label: 'Features' },\n * { id: 'inventory', group: 'primary', label: 'Inventory' },\n * ],\n * initial: 'features',\n * },\n * };\n * static DRAG_DROP = [{ dragSelector: '.item[draggable=true]', dropSelector: null }];\n * async onDropItem(item, event) {\n * if (item.type !== 'weapon') return false;\n * // …fall through to super by returning undefined.\n * }\n * }\n * ```\n */\nexport function BaseActorSheet(): SheetBaseCtor {\n const { Base, mixin } = resolveBases();\n const Mixed = mixin(Base);\n\n class VttforgeBaseActorSheet extends Mixed {\n static readonly DEFAULT_OPTIONS = {\n classes: [VTTFORGE_SHEET_CLASS],\n window: { resizable: true },\n position: { width: 600, height: 700 },\n tag: 'form',\n form: { submitOnChange: true, closeOnSubmit: false },\n actions: { vttforgeTab: VttforgeBaseActorSheet._onTab },\n } as const;\n\n /**\n * Declarative DragDrop entries. Each becomes a\n * `foundry.applications.ux.DragDrop` instance bound in `_onRender`.\n * Subclasses override by re-declaring `static DRAG_DROP = [...]`.\n */\n static readonly DRAG_DROP: ReadonlyArray<DragDropConfig> = [];\n\n /**\n * Augment ApplicationV2's context with `tabs[group]` for sheets that\n * declare **multiple** `static TABS` groups. ApplicationV2 already\n * auto-populates `context.tabs` (keyed by tab id) for single-group\n * sheets — overriding that flat shape would force every consumer to\n * either unwrap or write `context.tabs.<group>.<tabId>` in templates.\n * Multi-group sheets get nested `context.tabs.<group>.<tabId>` because\n * ApplicationV2 returns `{}` for them by default.\n */\n async _prepareContext(options: unknown): Promise<Record<string, unknown>> {\n const superPrepare = (\n Mixed.prototype as {\n _prepareContext?: (options: unknown) => Promise<Record<string, unknown>>;\n }\n )._prepareContext;\n const context =\n typeof superPrepare === 'function'\n ? ((await superPrepare.call(this, options)) as Record<string, unknown>)\n : {};\n const tabsConfig = (this.constructor as { TABS?: Record<string, unknown> }).TABS;\n if (tabsConfig && typeof tabsConfig === 'object') {\n const groups = Object.keys(tabsConfig);\n if (groups.length > 1) {\n const prepareTabs = (this as { _prepareTabs?: (group: string) => unknown })._prepareTabs;\n const tabs: Record<string, unknown> = {};\n for (const group of groups) {\n tabs[group] = typeof prepareTabs === 'function' ? prepareTabs.call(this, group) : {};\n }\n context.tabs = tabs;\n }\n }\n return context;\n }\n\n /**\n * Default `tab` action handler. ApplicationV2 doesn't ship one, so every\n * sheet that uses `<button data-action=\"tab\" data-tab=… data-group=…>`\n * has to wire its own. We toggle the `.active` class on the matching\n * nav element (`[data-action=\"tab\"][data-tab=…][data-group=…]`) and\n * on `section.tab[data-tab=…][data-group=…]`, then update\n * `sheet.tabGroups[group]` so subsequent re-renders pick the right\n * initial tab.\n *\n * ApplicationV2's action dispatcher binds `this` to the sheet instance\n * at call time even though the handler is declared `static`.\n */\n static _onTab(_event: Event, target: HTMLElement): void {\n // biome-ignore lint/complexity/noThisInStatic: ApplicationV2 binds `this` to the sheet instance at call time — wrap the cast in a single line so biome's auto-fix can't rewrite downstream references to the class name\n const sheet = this as unknown as { tabGroups: Record<string, string>; element?: HTMLElement };\n const group = target.dataset?.group;\n const tab = target.dataset?.tab;\n if (!group || !tab) return;\n sheet.tabGroups[group] = tab;\n const root = sheet.element;\n if (!root) return;\n for (const link of root.querySelectorAll<HTMLElement>(\n `[data-action=\"vttforgeTab\"][data-group=\"${group}\"]`,\n )) {\n link.classList.toggle('active', link.dataset.tab === tab);\n }\n for (const section of root.querySelectorAll<HTMLElement>(\n `section.tab[data-group=\"${group}\"]`,\n )) {\n section.classList.toggle('active', section.dataset.tab === tab);\n }\n }\n\n /**\n * Wire each `static DRAG_DROP` entry into a real `DragDrop` instance.\n * Permissions default to `this.isEditable`; callbacks default to\n * `_onDragStart` / `_onDrop`. Subclasses extending `_onRender` MUST call\n * `super._onRender(context, options)` to keep DragDrop wired.\n */\n _onRender(context: unknown, options: unknown): void {\n const superRender = (\n Mixed.prototype as { _onRender?: (context: unknown, options: unknown) => void }\n )._onRender;\n if (typeof superRender === 'function') {\n superRender.call(this, context, options);\n }\n const configs = (this.constructor as { DRAG_DROP?: ReadonlyArray<DragDropConfig> }).DRAG_DROP;\n if (!configs?.length) return;\n const DragDrop = resolveDragDrop();\n if (!DragDrop) return;\n const element = (this as { element?: HTMLElement }).element;\n if (!element) return;\n const onDragStart = (this as { _onDragStart?: (event: DragEvent) => void })._onDragStart;\n const onDrop = (this as { _onDrop?: (event: DragEvent) => void })._onDrop;\n for (const cfg of configs) {\n new DragDrop({\n dragSelector: cfg.dragSelector,\n dropSelector: cfg.dropSelector,\n permissions: {\n dragstart: cfg.permissions?.dragstart ?? (() => this.#isEditable()),\n drop: cfg.permissions?.drop ?? (() => this.#isEditable()),\n },\n callbacks: {\n ...(typeof onDragStart === 'function' ? { dragstart: onDragStart.bind(this) } : {}),\n ...(typeof onDrop === 'function' ? { drop: onDrop.bind(this) } : {}),\n ...(cfg.callbacks ?? {}),\n },\n }).bind(element);\n }\n }\n\n /**\n * Default drag handler — serialises the item identified by\n * `data-item-id` on the drag source element. Override for richer payloads\n * (Actor drags, custom UUIDs).\n */\n _onDragStart(event: DragEvent): void {\n const target = event.currentTarget as HTMLElement | null;\n const itemId = target?.dataset?.itemId;\n if (!itemId || !event.dataTransfer) return;\n const items = (\n this as { document?: { items?: { get(id: string): { uuid: string } | undefined } } }\n ).document?.items;\n const item = items?.get(itemId);\n if (!item) return;\n event.dataTransfer.setData(\n 'application/json',\n JSON.stringify({ type: 'Item', uuid: item.uuid }),\n );\n }\n\n /**\n * Typed drop sugar. Subclasses override this instead of `_onDropItem`\n * to skip the `fromUuid()` ceremony. Return `undefined` to fall through\n * to Foundry's default `_onDropItem`; return anything else to take\n * ownership of the drop.\n */\n async onDropItem(_item: unknown, _event: DragEvent): Promise<unknown> {\n return undefined;\n }\n async onDropActor(_actor: unknown, _event: DragEvent): Promise<unknown> {\n return undefined;\n }\n async onDropFolder(_folder: unknown, _event: DragEvent): Promise<unknown> {\n return undefined;\n }\n async onDropActiveEffect(_effect: unknown, _event: DragEvent): Promise<unknown> {\n return undefined;\n }\n\n async _onDropItem(event: DragEvent, data: DropPayload): Promise<unknown> {\n return this.#dispatchDrop('_onDropItem', 'onDropItem', event, data);\n }\n async _onDropActor(event: DragEvent, data: DropPayload): Promise<unknown> {\n return this.#dispatchDrop('_onDropActor', 'onDropActor', event, data);\n }\n async _onDropFolder(event: DragEvent, data: DropPayload): Promise<unknown> {\n return this.#dispatchDrop('_onDropFolder', 'onDropFolder', event, data);\n }\n async _onDropActiveEffect(event: DragEvent, data: DropPayload): Promise<unknown> {\n return this.#dispatchDrop('_onDropActiveEffect', 'onDropActiveEffect', event, data);\n }\n\n async #dispatchDrop(\n superKey: '_onDropItem' | '_onDropActor' | '_onDropFolder' | '_onDropActiveEffect',\n sugarKey: 'onDropItem' | 'onDropActor' | 'onDropFolder' | 'onDropActiveEffect',\n event: DragEvent,\n data: DropPayload,\n ): Promise<unknown> {\n const uuid = data?.uuid;\n if (uuid) {\n const doc = await resolveFromUuid(uuid);\n if (doc) {\n const result = await (\n this as unknown as Record<\n typeof sugarKey,\n (doc: unknown, event: DragEvent) => Promise<unknown>\n >\n )[sugarKey](doc, event);\n if (result !== undefined) return result;\n }\n }\n const superFn = (Mixed.prototype as Record<typeof superKey, unknown>)[superKey] as\n | ((event: DragEvent, data: DropPayload) => Promise<unknown>)\n | undefined;\n if (typeof superFn === 'function') return superFn.call(this, event, data);\n return undefined;\n }\n\n #isEditable(): boolean {\n return Boolean((this as { isEditable?: boolean }).isEditable);\n }\n }\n\n return VttforgeBaseActorSheet as unknown as SheetBaseCtor;\n}\n","/**\n * BaseApplication — a plain `ApplicationV2` window, minus the two traps.\n *\n * The document sheets are covered by `BaseActorSheet` and `BaseItemSheet`.\n * Everything else a package puts on screen — a config dialog, a picker, a\n * reader window — is a bare `ApplicationV2`, and writing one by hand means\n * meeting both of these:\n *\n * **`_replaceHTML` is easy to forget.** ApplicationV2 splits rendering in two:\n * `_renderHTML` builds the content and `_replaceHTML` puts it in the window.\n * Implement only the first and the class is silently unrenderable — Foundry\n * says so at the moment something tries to open it, not when it is defined.\n * Nearly every implementation of the second is the same line, so this ships\n * it. Override it when the window updates in place instead of wholesale.\n *\n * **A missing `_renderHTML` fails late.** Foundry's own check fires on first\n * render, which in practice means a user clicks something and gets an error\n * about abstract methods. This checks at construction, so it fails where the\n * class is used rather than deep inside a render.\n */\n\nimport { VttfError } from './errors/registry.js';\nimport type { ApplicationV2Members, VttforgeClass } from './foundry-base.js';\n\n// biome-ignore lint/suspicious/noExplicitAny: Foundry's ApplicationV2 shape lives in fvtt-types (deferred to @vttforge/types v1.0)\ntype AnyConstructor = new (...args: any[]) => any;\n\ninterface FoundryApplicationsApi {\n ApplicationV2?: AnyConstructor;\n}\n\ninterface FoundryRoot {\n readonly applications?: { readonly api?: FoundryApplicationsApi };\n}\n\nfunction resolveApplicationV2(): AnyConstructor {\n const foundry = (globalThis as Record<string, unknown>).foundry as FoundryRoot | undefined;\n const cls = foundry?.applications?.api?.ApplicationV2;\n if (typeof cls !== 'function') {\n throw new VttfError(\n 'VTTF-0002',\n 'foundry.applications.api.ApplicationV2 is not available. Define your BaseApplication subclasses inside the Foundry runtime (or stub the global in tests).',\n );\n }\n return cls;\n}\n\n/**\n * Build an `ApplicationV2` base with the rendering contract filled in.\n *\n * ```ts\n * class PdfConfig extends BaseApplication() {\n * async _renderHTML() {\n * const form = document.createElement('form');\n * // …\n * return form;\n * }\n * }\n * ```\n *\n * `_replaceHTML` is provided. `_renderHTML` is yours, and omitting it throws\n * when the class is constructed rather than when someone opens the window.\n */\n/** What `BaseApplication` adds on top of Foundry's `ApplicationV2`. */\nexport interface BaseApplicationMembers {\n /**\n * Put the rendered content in the window.\n *\n * Provided because it is the half people forget. Override it for a window\n * that updates in place rather than swapping its whole content.\n */\n _replaceHTML(result: HTMLElement, content: HTMLElement): void;\n}\n\nexport function BaseApplication(): VttforgeClass<\n BaseApplicationMembers,\n unknown,\n ApplicationV2Members\n> {\n const Base = resolveApplicationV2();\n\n class VttforgeBaseApplication extends Base {\n // biome-ignore lint/suspicious/noExplicitAny: forwards Foundry's own constructor arity\n constructor(...args: any[]) {\n super(...args);\n if (typeof (this as { _renderHTML?: unknown })._renderHTML !== 'function') {\n throw new VttfError(\n 'VTTF-0002',\n `${this.constructor.name} extends BaseApplication but does not implement _renderHTML. ApplicationV2 cannot render without it.`,\n );\n }\n }\n\n /**\n * Put the rendered content in the window.\n *\n * The whole-content swap, which is what almost every window wants.\n * Override to update in place — a viewer that keeps scroll position\n * across a page turn, say.\n */\n _replaceHTML(result: HTMLElement, content: HTMLElement): void {\n content.replaceChildren(result);\n }\n }\n\n return VttforgeBaseApplication as unknown as VttforgeClass<\n BaseApplicationMembers,\n unknown,\n ApplicationV2Members\n >;\n}\n","/**\n * BaseDocumentSheet — a document sheet that builds its own DOM.\n *\n * `BaseActorSheet` and `BaseItemSheet` are `HandlebarsApplicationMixin`\n * baselines, which is right for the common case: declare `static PARTS`, write\n * templates, let the mixin render them.\n *\n * It is wrong for a sheet whose content is not a template. A canvas, an\n * embedded PDF, a Svelte or Lit mount — those build an element and hand it\n * over. Extending the Handlebars baseline for one of those does not fail\n * loudly; the mixin's `_replaceHTML` expects a map of part id to markup,\n * receives an element instead, and quietly renders nothing. The window opens\n * empty, or does not open at all, and no error names the mismatch.\n *\n * So this is the same document-sheet plumbing without the mixin, with the\n * `_renderHTML` / `_replaceHTML` contract `BaseApplication` provides.\n */\n\nimport { VttfError } from './errors/registry.js';\nimport type { DocumentSheetV2Members, VttforgeClass } from './foundry-base.js';\n\n// biome-ignore lint/suspicious/noExplicitAny: Foundry's sheet shape lives in fvtt-types (deferred to @vttforge/types v1.0)\ntype AnyConstructor = new (...args: any[]) => any;\n\ninterface FoundryApplicationsSheets {\n ActorSheetV2?: AnyConstructor;\n ItemSheetV2?: AnyConstructor;\n}\n\ninterface FoundryRoot {\n readonly applications?: { readonly sheets?: FoundryApplicationsSheets };\n}\n\n/** Which document the sheet is for. */\nexport type DocumentSheetKind = 'Actor' | 'Item';\n\n/** What `BaseDocumentSheet` adds. */\nexport interface BaseDocumentSheetMembers {\n /**\n * Put the rendered content in the window.\n *\n * The whole-content swap. Override for a sheet that updates in place —\n * a viewer that keeps its scroll position across a page turn, say.\n */\n _replaceHTML(result: HTMLElement, content: HTMLElement): void;\n}\n\nfunction resolveSheetBase(kind: DocumentSheetKind): AnyConstructor {\n const foundry = (globalThis as Record<string, unknown>).foundry as FoundryRoot | undefined;\n const cls = foundry?.applications?.sheets?.[`${kind}SheetV2`];\n if (typeof cls !== 'function') {\n throw new VttfError(\n 'VTTF-0002',\n `foundry.applications.sheets.${kind}SheetV2 is not available. Define your BaseDocumentSheet subclasses inside the Foundry runtime (or stub the global in tests).`,\n );\n }\n return cls;\n}\n\n/**\n * Build a document sheet base that renders an element rather than templates.\n *\n * ```ts\n * class PdfActorSheet extends BaseDocumentSheet('Actor') {\n * async _renderHTML() {\n * const canvas = document.createElement('canvas');\n * // …draw the page…\n * return canvas;\n * }\n * }\n * ```\n *\n * Reach for `BaseActorSheet` or `BaseItemSheet` instead when the sheet is\n * templates and `static PARTS`, which is most of them.\n */\nexport function BaseDocumentSheet(\n kind: DocumentSheetKind,\n): VttforgeClass<BaseDocumentSheetMembers, unknown, DocumentSheetV2Members> {\n const Base = resolveSheetBase(kind);\n\n class VttforgeBaseDocumentSheet extends Base {\n // biome-ignore lint/suspicious/noExplicitAny: forwards Foundry's (data, context) pair\n constructor(...args: any[]) {\n super(...args);\n if (typeof (this as { _renderHTML?: unknown })._renderHTML !== 'function') {\n throw new VttfError(\n 'VTTF-0002',\n `${this.constructor.name} extends BaseDocumentSheet but does not implement _renderHTML. A sheet that renders no element renders nothing.`,\n );\n }\n }\n\n _replaceHTML(result: HTMLElement, content: HTMLElement): void {\n content.replaceChildren(result);\n }\n }\n\n return VttforgeBaseDocumentSheet as unknown as VttforgeClass<\n BaseDocumentSheetMembers,\n unknown,\n DocumentSheetV2Members\n >;\n}\n","/**\n * BaseItemSheet — `ItemSheetV2 + HandlebarsApplicationMixin` baseline. Mirror\n * of `BaseActorSheet` minus the typed drop dispatch (items rarely receive\n * drops; the rare case that does can override `_onDrop` directly).\n *\n * Carries the same boilerplate-eliminators:\n *\n * - `static DRAG_DROP` — declarative `foundry.applications.ux.DragDrop` wiring\n * in `_onRender`, with `isEditable`-gated permissions.\n * - `_prepareContext` auto-fills `context.tabs[group]` for every group declared\n * in ApplicationV2's `static TABS`.\n *\n * As with `BaseActorSheet`, `editImage` is intentionally not added — it ships\n * built-in on `DocumentSheetV2` (parent of `ItemSheetV2`).\n */\n\nimport type { DragDropConfig, SheetBaseCtor } from './base-actor-sheet.js';\nimport { VTTFORGE_SHEET_CLASS } from './base-actor-sheet.js';\nimport { VttfError } from './errors/registry.js';\n\n// biome-ignore lint/suspicious/noExplicitAny: Foundry's ItemSheetV2 shape lives in fvtt-types (deferred to @vttforge/types v1.0)\ntype AnyConstructor = new (...args: any[]) => any;\n\ninterface DragDropInstance {\n bind(element: HTMLElement): void;\n}\n\ninterface DragDropCtor {\n new (config: Record<string, unknown>): DragDropInstance;\n}\n\ninterface FoundryApplicationsApi {\n HandlebarsApplicationMixin?: (base: AnyConstructor) => AnyConstructor;\n}\n\ninterface FoundryApplicationsSheets {\n ItemSheetV2?: AnyConstructor;\n}\n\ninterface FoundryApplicationsUx {\n DragDrop?: DragDropCtor;\n}\n\ninterface FoundryApplications {\n api?: FoundryApplicationsApi;\n sheets?: FoundryApplicationsSheets;\n ux?: FoundryApplicationsUx;\n}\n\ninterface FoundryGlobal {\n applications?: FoundryApplications;\n}\n\nfunction resolveBases(): { Base: AnyConstructor; mixin: (b: AnyConstructor) => AnyConstructor } {\n const foundry = (globalThis as Record<string, unknown>).foundry as FoundryGlobal | undefined;\n const Base = foundry?.applications?.sheets?.ItemSheetV2;\n const mixin = foundry?.applications?.api?.HandlebarsApplicationMixin;\n if (typeof Base !== 'function' || typeof mixin !== 'function') {\n throw new VttfError(\n 'VTTF-0002',\n 'foundry.applications.sheets.ItemSheetV2 and/or foundry.applications.api.HandlebarsApplicationMixin are not available. Define your BaseItemSheet subclasses inside the Foundry runtime (or stub the global in tests).',\n );\n }\n return { Base, mixin };\n}\n\nfunction resolveDragDrop(): DragDropCtor | undefined {\n const foundry = (globalThis as Record<string, unknown>).foundry as FoundryGlobal | undefined;\n const ctor = foundry?.applications?.ux?.DragDrop;\n return typeof ctor === 'function' ? ctor : undefined;\n}\n\n/**\n * Build the `BaseItemSheet` for the current Foundry runtime.\n *\n * @example\n * ```ts\n * class WeaponSheet extends BaseItemSheet() {\n * static DEFAULT_OPTIONS = foundry.utils.mergeObject(\n * super.DEFAULT_OPTIONS,\n * { classes: ['my-system'], position: { width: 540 } },\n * );\n * static PARTS = { ... };\n * static TABS = {\n * primary: {\n * tabs: [\n * { id: 'description', group: 'primary', label: 'Description' },\n * { id: 'details', group: 'primary', label: 'Details' },\n * ],\n * initial: 'description',\n * },\n * };\n * }\n * ```\n */\nexport function BaseItemSheet(): SheetBaseCtor {\n const { Base, mixin } = resolveBases();\n const Mixed = mixin(Base);\n\n class VttforgeBaseItemSheet extends Mixed {\n static readonly DEFAULT_OPTIONS = {\n classes: [VTTFORGE_SHEET_CLASS],\n window: { resizable: true },\n position: { width: 520, height: 480 },\n tag: 'form',\n form: { submitOnChange: true, closeOnSubmit: false },\n actions: { vttforgeTab: VttforgeBaseItemSheet._onTab },\n } as const;\n\n static readonly DRAG_DROP: ReadonlyArray<DragDropConfig> = [];\n\n /**\n * Multi-group sheets get nested `context.tabs.<group>.<tabId>` because\n * ApplicationV2 returns `{}` for them by default; single-group sheets\n * use ApplicationV2's flat `context.tabs.<tabId>` shape untouched. See\n * BaseActorSheet for the long version.\n */\n async _prepareContext(options: unknown): Promise<Record<string, unknown>> {\n const superPrepare = (\n Mixed.prototype as {\n _prepareContext?: (options: unknown) => Promise<Record<string, unknown>>;\n }\n )._prepareContext;\n const context =\n typeof superPrepare === 'function'\n ? ((await superPrepare.call(this, options)) as Record<string, unknown>)\n : {};\n const tabsConfig = (this.constructor as { TABS?: Record<string, unknown> }).TABS;\n if (tabsConfig && typeof tabsConfig === 'object') {\n const groups = Object.keys(tabsConfig);\n if (groups.length > 1) {\n const prepareTabs = (this as { _prepareTabs?: (group: string) => unknown })._prepareTabs;\n const tabs: Record<string, unknown> = {};\n for (const group of groups) {\n tabs[group] = typeof prepareTabs === 'function' ? prepareTabs.call(this, group) : {};\n }\n context.tabs = tabs;\n }\n }\n return context;\n }\n\n static _onTab(_event: Event, target: HTMLElement): void {\n // biome-ignore lint/complexity/noThisInStatic: ApplicationV2 binds `this` to the sheet instance at call time\n const sheet = this as unknown as { tabGroups: Record<string, string>; element?: HTMLElement };\n const group = target.dataset?.group;\n const tab = target.dataset?.tab;\n if (!group || !tab) return;\n sheet.tabGroups[group] = tab;\n const root = sheet.element;\n if (!root) return;\n for (const link of root.querySelectorAll<HTMLElement>(\n `[data-action=\"vttforgeTab\"][data-group=\"${group}\"]`,\n )) {\n link.classList.toggle('active', link.dataset.tab === tab);\n }\n for (const section of root.querySelectorAll<HTMLElement>(\n `section.tab[data-group=\"${group}\"]`,\n )) {\n section.classList.toggle('active', section.dataset.tab === tab);\n }\n }\n\n _onRender(context: unknown, options: unknown): void {\n const superRender = (\n Mixed.prototype as { _onRender?: (context: unknown, options: unknown) => void }\n )._onRender;\n if (typeof superRender === 'function') {\n superRender.call(this, context, options);\n }\n const configs = (this.constructor as { DRAG_DROP?: ReadonlyArray<DragDropConfig> }).DRAG_DROP;\n if (!configs?.length) return;\n const DragDrop = resolveDragDrop();\n if (!DragDrop) return;\n const element = (this as { element?: HTMLElement }).element;\n if (!element) return;\n const onDragStart = (this as { _onDragStart?: (event: DragEvent) => void })._onDragStart;\n const onDrop = (this as { _onDrop?: (event: DragEvent) => void })._onDrop;\n for (const cfg of configs) {\n new DragDrop({\n dragSelector: cfg.dragSelector,\n dropSelector: cfg.dropSelector,\n permissions: {\n dragstart: cfg.permissions?.dragstart ?? (() => this.#isEditable()),\n drop: cfg.permissions?.drop ?? (() => this.#isEditable()),\n },\n callbacks: {\n ...(typeof onDragStart === 'function' ? { dragstart: onDragStart.bind(this) } : {}),\n ...(typeof onDrop === 'function' ? { drop: onDrop.bind(this) } : {}),\n ...(cfg.callbacks ?? {}),\n },\n }).bind(element);\n }\n }\n\n #isEditable(): boolean {\n return Boolean((this as { isEditable?: boolean }).isEditable);\n }\n }\n\n return VttforgeBaseItemSheet as unknown as SheetBaseCtor;\n}\n","/**\n * BaseTypeDataModel — minimal extension of `foundry.abstract.TypeDataModel`.\n *\n * Provides safe defaults that systems usually copy-paste anyway:\n *\n * - `migrateData()` calls `super.migrateData(data)` — every TypeDataModel\n * must do this so chained migrations from base classes still run.\n * - `prepareBaseData()` is a no-op stub — override to initialize fields that\n * Active Effects need to mutate (e.g. base max HP before AE bonus). Foundry\n * applies Active Effects between `prepareBaseData()` and `prepareDerivedData()`,\n * so anything you compute here is the input AEs see.\n * - `prepareDerivedData()` is a no-op stub — override for computed values\n * that depend on AE-mutated state (modifiers, percentages, totals).\n *\n * Subclasses still own `defineSchema()` because there is no useful default —\n * we never invent a schema for you.\n *\n * Resolves the base class from `globalThis.foundry.abstract.TypeDataModel` at\n * runtime. In tests, the test harness installs a stub; in Foundry, the global\n * exists by the time this module runs (we are loaded from system esmodules).\n */\n\nimport type { FieldInstance } from './data/fields.js';\nimport type { InferSchema } from './data/infer-schema.js';\nimport { VttfError } from './errors/registry.js';\nimport type { VttforgeClass } from './foundry-base.js';\n\n// biome-ignore lint/suspicious/noExplicitAny: we mix into Foundry's TypeDataModel whose shape lives in fvtt-types (deferred to @vttforge/types v1.0)\ntype AnyConstructor = new (...args: any[]) => any;\n\nfunction resolveTypeDataModelClass(): AnyConstructor {\n const foundry = (globalThis as Record<string, unknown>).foundry as\n | { abstract?: { TypeDataModel?: AnyConstructor } }\n | undefined;\n const cls = foundry?.abstract?.TypeDataModel;\n if (typeof cls !== 'function') {\n throw new VttfError(\n 'VTTF-0002',\n 'foundry.abstract.TypeDataModel is not available. Define your BaseTypeDataModel subclasses inside the Foundry runtime (or stub the global in tests).',\n );\n }\n return cls;\n}\n\n/**\n * Resolve the runtime base class, then build a mixin that adds VTTForge defaults.\n *\n * Why a function: subclasses are declared once at module load, but Foundry\n * globals may not exist yet (test boot, ESM hoist). Calling `BaseTypeDataModel()`\n * lazy-resolves the global at the moment of subclassing.\n */\n/** The two hooks this base fills in, so a subclass can omit either. */\nexport interface TypeDataModelHooks {\n prepareBaseData(): void;\n prepareDerivedData(): void;\n}\n\n/**\n * What an instance looks like when the schema is known.\n *\n * The schema's fields ARE the instance properties — inside\n * `prepareDerivedData()` you read `this.level`, not `this.system.level`, and\n * `actor.system` is this instance.\n *\n * Derived values are not in the schema, so they are not here either. Declare\n * them on the subclass:\n *\n * ```ts\n * declare armorClass: number;\n * ```\n */\nexport type TypedTypeDataModel<S extends Record<string, FieldInstance>> = InferSchema<S> &\n TypeDataModelHooks & {\n /**\n * Phantom property carrying the schema's inferred shape. Never assigned,\n * never present at runtime — it exists so the type has a name:\n *\n * ```ts\n * type CharacterSystem = CharacterData['$inferData'];\n * ```\n */\n readonly $inferData: InferSchema<S>;\n };\n\nexport interface TypedTypeDataModelCtor<S extends Record<string, FieldInstance>> {\n // biome-ignore lint/suspicious/noExplicitAny: a subclass with its own constructor passes Foundry's (data, context) through to super\n new (...args: any[]): TypedTypeDataModel<S>;\n defineSchema(): S;\n migrateData(data: Record<string, unknown>): Record<string, unknown>;\n}\n\n/**\n * Build a base class with no knowledge of the schema.\n *\n * The hooks are typed; the schema's own fields are not, since nothing said\n * what they are. Pass your schema function instead to get those too.\n */\nexport function BaseTypeDataModel(): VttforgeClass<TypeDataModelHooks>;\n/**\n * Build a base class that knows its schema.\n *\n * Hand it the function that returns your fields and it implements\n * `static defineSchema()` for you, so the schema is written once:\n *\n * ```ts\n * const defineCharacterSchema = () => {\n * const f = fields();\n * return { level: new f.NumberField({ required: true, nullable: false, initial: 1 }) };\n * };\n *\n * class CharacterData extends BaseTypeDataModel(defineCharacterSchema) {\n * declare armorClass: number;\n * prepareDerivedData() {\n * this.armorClass = 10 + this.level; // this.level is number\n * }\n * }\n * ```\n *\n * It has to be a function, not an object: `fields()` reads a Foundry global\n * that does not exist when the module is first evaluated.\n *\n * A subclass may still declare its own `static defineSchema()`; that one wins,\n * the same as any other static.\n */\nexport function BaseTypeDataModel<S extends Record<string, FieldInstance>>(\n defineSchema: () => S,\n): TypedTypeDataModelCtor<S>;\nexport function BaseTypeDataModel(\n defineSchema?: () => Record<string, FieldInstance>,\n): AnyConstructor {\n const Base = resolveTypeDataModelClass();\n\n class VttforgeBaseTypeDataModel extends Base {\n /**\n * Default no-op so subclasses can omit it when they have no value-level\n * migrations. Always end with `super.migrateData(data)` if you override.\n */\n static migrateData(data: Record<string, unknown>): Record<string, unknown> {\n const superMigrateData = (\n Base as { migrateData?: (d: Record<string, unknown>) => Record<string, unknown> }\n ).migrateData;\n if (typeof superMigrateData === 'function') {\n return superMigrateData.call(VttforgeBaseTypeDataModel, data);\n }\n return data;\n }\n\n /**\n * No-op stub. Override per type to initialize fields whose values Active\n * Effects need to consume — base max HP, base AC, etc. Foundry calls this\n * BEFORE applying Active Effects, so anything you set here is the input\n * that AE changes (`ADD`, `MULTIPLY`, `OVERRIDE`, …) operate on.\n *\n * Use `prepareDerivedData()` instead for values that depend on the\n * AE-mutated state (modifiers, percentages, totals).\n *\n * Never write to the database here — purely in-memory.\n */\n prepareBaseData(): void {\n // override me\n }\n\n /**\n * No-op stub. Override per type to compute derived values from the\n * AE-mutated state (modifiers, percentages, totals). Runs AFTER Active\n * Effects apply; use `prepareBaseData()` for values that AEs need to read.\n *\n * Never write to the database here — purely in-memory.\n */\n prepareDerivedData(): void {\n // override me\n }\n }\n\n if (defineSchema !== undefined) {\n // Assigned rather than declared in the class body so the no-argument form\n // keeps inheriting Foundry's own defineSchema instead of shadowing it\n // with one that returns nothing.\n Object.defineProperty(VttforgeBaseTypeDataModel, 'defineSchema', {\n value: defineSchema,\n writable: true,\n configurable: true,\n });\n }\n\n return VttforgeBaseTypeDataModel as unknown as AnyConstructor;\n}\n","/**\n * `fields()` — typed bag of Foundry v13 data-field constructors.\n *\n * Foundry idiom inside `defineSchema()` is `const f = foundry.data.fields`. We\n * mirror that, but routed through a factory so the import succeeds in Node\n * (tests, IDE typecheck) where the global is absent. The factory resolves\n * `globalThis.foundry.data.fields` lazily — same pattern as\n * `BaseTypeDataModel()` (see `base-type-data-model.ts:25`) and\n * `BaseActorSheet()` (see `base-actor-sheet.ts:40`).\n *\n * v0.1 covers eight fields (PRD §7): NumberField, StringField, BooleanField,\n * HTMLField, ArrayField, SchemaField, ColorField, FilePathField. The instance\n * interfaces carry a phantom `[BRAND]` tag and an `options` capture so the\n * conditional types in `./infer-schema.ts` can extract the runtime semantics\n * (e.g. `nullable: true`).\n *\n * `EmbeddedDataField`, `EmbeddedDocumentField`, `TypedSchemaField`, and the\n * full required×initial nullability matrix ship with `@vttforge/types` v1.0.\n */\n\nimport { VttfError } from '../errors/registry.js';\nimport type {\n ArrayFieldOptions,\n BooleanFieldOptions,\n ColorFieldOptions,\n EmbeddedDataFieldOptions,\n EmbeddedDocumentFieldOptions,\n FilePathFieldOptions,\n ForeignDocumentFieldOptions,\n HTMLFieldOptions,\n NumberFieldOptions,\n SchemaFieldOptions,\n SetFieldOptions,\n StringFieldOptions,\n TypedSchemaFieldOptions,\n} from './field-options.js';\n\ndeclare const BRAND: unique symbol;\n\n/**\n * Anything that satisfies the `FieldInstance` shape — used as the inner-field\n * constraint on `ArrayField` and as the value type of `SchemaField`'s child\n * map. Keeps the conditional types in `./infer-schema.ts` straightforward.\n */\nexport interface FieldInstance {\n readonly [BRAND]: string;\n readonly options: unknown;\n}\n\nexport interface NumberFieldInstance<O extends NumberFieldOptions = NumberFieldOptions>\n extends FieldInstance {\n readonly [BRAND]: 'number';\n readonly options: O;\n}\n\nexport interface StringFieldInstance<O extends StringFieldOptions = StringFieldOptions>\n extends FieldInstance {\n readonly [BRAND]: 'string';\n readonly options: O;\n}\n\nexport interface BooleanFieldInstance<O extends BooleanFieldOptions = BooleanFieldOptions>\n extends FieldInstance {\n readonly [BRAND]: 'boolean';\n readonly options: O;\n}\n\nexport interface HTMLFieldInstance<O extends HTMLFieldOptions = HTMLFieldOptions>\n extends FieldInstance {\n readonly [BRAND]: 'html';\n readonly options: O;\n}\n\nexport interface ColorFieldInstance<O extends ColorFieldOptions = ColorFieldOptions>\n extends FieldInstance {\n readonly [BRAND]: 'color';\n readonly options: O;\n}\n\nexport interface FilePathFieldInstance<O extends FilePathFieldOptions = FilePathFieldOptions>\n extends FieldInstance {\n readonly [BRAND]: 'filePath';\n readonly options: O;\n}\n\nexport interface ArrayFieldInstance<\n Inner extends FieldInstance = FieldInstance,\n O extends ArrayFieldOptions = ArrayFieldOptions,\n> extends FieldInstance {\n readonly [BRAND]: 'array';\n readonly element: Inner;\n readonly options: O;\n}\n\n/**\n * A `SetField` holds a `Set`, not an array.\n *\n * It extends `ArrayField` and validates the same way, but `initialize`\n * wraps the result in `new Set(...)` — so a schema that declares one and\n * types it as an array gets `.push` and index access from the compiler on a\n * value that has neither.\n */\nexport interface SetFieldInstance<\n Inner extends FieldInstance = FieldInstance,\n O extends SetFieldOptions = SetFieldOptions,\n> extends FieldInstance {\n readonly [BRAND]: 'set';\n readonly element: Inner;\n readonly options: O;\n}\n\n/**\n * A reference to another document, stored as its id.\n *\n * What you read back depends on `idOnly`. With it, the id string. Without\n * it, the document itself: the field resolves to a getter, so reading the\n * property looks the document up in its collection and hands back the\n * instance — or `null` when it is gone or lives in a compendium.\n *\n * The field is nullable by default, so both shapes admit `null`.\n */\nexport interface ForeignDocumentFieldInstance<\n Doc extends DocumentClass = DocumentClass,\n O extends ForeignDocumentFieldOptions = ForeignDocumentFieldOptions,\n> extends FieldInstance {\n readonly [BRAND]: 'foreignDocument';\n readonly model: Doc;\n readonly options: O;\n}\n\n/**\n * A nested data model.\n *\n * It is a `SchemaField` built from the model class's own `defineSchema()`, so\n * the value is an instance of that model — not a plain object. Reading it\n * gives you the model's derived data and methods too.\n */\nexport interface EmbeddedDataFieldInstance<\n Model extends DataModelClass = DataModelClass,\n O extends EmbeddedDataFieldOptions = EmbeddedDataFieldOptions,\n> extends FieldInstance {\n readonly [BRAND]: 'embeddedData';\n readonly model: Model;\n readonly options: O;\n}\n\n/**\n * A single embedded document, stored inline.\n *\n * Like `EmbeddedDataField` but for a Document class, and nullable by default:\n * the field's own defaults turn `nullable` on, so an absent one reads `null`.\n */\nexport interface EmbeddedDocumentFieldInstance<\n Doc extends DataModelClass = DataModelClass,\n O extends EmbeddedDocumentFieldOptions = EmbeddedDocumentFieldOptions,\n> extends FieldInstance {\n readonly [BRAND]: 'embeddedDocument';\n readonly model: Doc;\n readonly options: O;\n}\n\n/**\n * One of several shapes, told apart by a `type` property.\n *\n * Each entry becomes its own SchemaField. When an entry does not declare a\n * `type` field, the field adds one — a required string whose value must equal\n * that entry's key — which is what makes the result a discriminated union you\n * can narrow on.\n */\nexport interface TypedSchemaFieldInstance<\n T extends Record<string, Record<string, FieldInstance>> = Record<\n string,\n Record<string, FieldInstance>\n >,\n O extends TypedSchemaFieldOptions = TypedSchemaFieldOptions,\n> extends FieldInstance {\n readonly [BRAND]: 'typedSchema';\n readonly types: T;\n readonly options: O;\n}\n\nexport interface SchemaFieldInstance<\n S extends Record<string, FieldInstance> = Record<string, FieldInstance>,\n O extends SchemaFieldOptions = SchemaFieldOptions,\n> extends FieldInstance {\n readonly [BRAND]: 'schema';\n readonly fields: S;\n readonly options: O;\n}\n\nexport interface NumberFieldCtor {\n new <O extends NumberFieldOptions = NumberFieldOptions>(options?: O): NumberFieldInstance<O>;\n}\n\nexport interface StringFieldCtor {\n new <O extends StringFieldOptions = StringFieldOptions>(options?: O): StringFieldInstance<O>;\n}\n\nexport interface BooleanFieldCtor {\n new <O extends BooleanFieldOptions = BooleanFieldOptions>(options?: O): BooleanFieldInstance<O>;\n}\n\nexport interface HTMLFieldCtor {\n new <O extends HTMLFieldOptions = HTMLFieldOptions>(options?: O): HTMLFieldInstance<O>;\n}\n\nexport interface ColorFieldCtor {\n new <O extends ColorFieldOptions = ColorFieldOptions>(options?: O): ColorFieldInstance<O>;\n}\n\nexport interface FilePathFieldCtor {\n new <O extends FilePathFieldOptions = FilePathFieldOptions>(\n options?: O,\n ): FilePathFieldInstance<O>;\n}\n\nexport interface ArrayFieldCtor {\n new <Inner extends FieldInstance, O extends ArrayFieldOptions = ArrayFieldOptions>(\n element: Inner,\n options?: O,\n ): ArrayFieldInstance<Inner, O>;\n}\n\nexport interface SetFieldCtor {\n new <Inner extends FieldInstance, O extends SetFieldOptions = SetFieldOptions>(\n element: Inner,\n options?: O,\n ): SetFieldInstance<Inner, O>;\n}\n\n/**\n * Any document class — what `ForeignDocumentField` takes as its first\n * argument. Declared structurally so the inference surface stays free of a\n * dependency on a Foundry type package.\n */\nexport type DocumentClass = abstract new (...args: never[]) => object;\n\nexport interface ForeignDocumentFieldCtor {\n new <\n Doc extends DocumentClass,\n O extends ForeignDocumentFieldOptions = ForeignDocumentFieldOptions,\n >(\n model: Doc,\n options?: O,\n ): ForeignDocumentFieldInstance<Doc, O>;\n}\n\n/** Any DataModel subclass — what the embedded fields take as their type. */\nexport type DataModelClass = abstract new (...args: never[]) => object;\n\nexport interface EmbeddedDataFieldCtor {\n new <Model extends DataModelClass, O extends EmbeddedDataFieldOptions = EmbeddedDataFieldOptions>(\n model: Model,\n options?: O,\n ): EmbeddedDataFieldInstance<Model, O>;\n}\n\nexport interface EmbeddedDocumentFieldCtor {\n new <\n Doc extends DataModelClass,\n O extends EmbeddedDocumentFieldOptions = EmbeddedDocumentFieldOptions,\n >(\n model: Doc,\n options?: O,\n ): EmbeddedDocumentFieldInstance<Doc, O>;\n}\n\nexport interface TypedSchemaFieldCtor {\n new <\n T extends Record<string, Record<string, FieldInstance>>,\n O extends TypedSchemaFieldOptions = TypedSchemaFieldOptions,\n >(\n types: T,\n options?: O,\n ): TypedSchemaFieldInstance<T, O>;\n}\n\nexport interface SchemaFieldCtor {\n new <S extends Record<string, FieldInstance>, O extends SchemaFieldOptions = SchemaFieldOptions>(\n fields: S,\n options?: O,\n ): SchemaFieldInstance<S, O>;\n}\n\n/**\n * Typed bag returned by `fields()`. Each property is the corresponding\n * `foundry.data.fields.*` class — the runtime value is Foundry's own\n * constructor; the type is our overlay.\n */\nexport interface FieldsApi {\n readonly NumberField: NumberFieldCtor;\n readonly StringField: StringFieldCtor;\n readonly BooleanField: BooleanFieldCtor;\n readonly HTMLField: HTMLFieldCtor;\n readonly ColorField: ColorFieldCtor;\n readonly FilePathField: FilePathFieldCtor;\n readonly ArrayField: ArrayFieldCtor;\n readonly SetField: SetFieldCtor;\n readonly ForeignDocumentField: ForeignDocumentFieldCtor;\n readonly SchemaField: SchemaFieldCtor;\n readonly EmbeddedDataField: EmbeddedDataFieldCtor;\n readonly EmbeddedDocumentField: EmbeddedDocumentFieldCtor;\n readonly TypedSchemaField: TypedSchemaFieldCtor;\n}\n\ninterface FoundryDataNamespace {\n readonly fields?: Record<string, unknown>;\n}\n\ninterface FoundryRoot {\n readonly data?: FoundryDataNamespace;\n}\n\n/**\n * Resolve `globalThis.foundry.data.fields` and return it typed as `FieldsApi`.\n *\n * Call this inside `defineSchema()` (or any code that runs after Foundry's\n * `init` hook). Calling at module scope will throw when imported from Node\n * tests — the global only exists inside the Foundry runtime.\n *\n * @throws `VttfError` with code `VTTF-0002` when `foundry.data.fields` is\n * missing.\n */\nexport function fields(): FieldsApi {\n const foundry = (globalThis as Record<string, unknown>).foundry as FoundryRoot | undefined;\n const f = foundry?.data?.fields;\n if (f === undefined || f === null) {\n throw new VttfError(\n 'VTTF-0002',\n 'foundry.data.fields is not available. Call fields() inside the Foundry runtime (or stub the global in tests).',\n );\n }\n return f as unknown as FieldsApi;\n}\n","/**\n * Typed runtime view over the VTTF-NNNN registry.\n *\n * Same data as `listErrorEntries()` — the manifest wraps it in a versioned\n * envelope so external tooling (the v0.3 docs site, IDE extensions, lint\n * rules) has a stable shape to consume. The matching JSON projection is\n * emitted to `dist/errors-manifest.json` at build time by\n * `packages/core/scripts/codegen-errors.mjs`.\n */\n\nimport { listErrorEntries, type VttfErrorEntry } from './registry.js';\n\nexport const ERROR_MANIFEST_VERSION = 1 as const;\n\nexport interface ErrorManifest {\n readonly version: typeof ERROR_MANIFEST_VERSION;\n readonly package: '@vttforge/core';\n readonly entries: ReadonlyArray<VttfErrorEntry>;\n}\n\n/**\n * Snapshot the current registry as a manifest object. Recomputed on every\n * call — cheap (the registry is a frozen literal). For the JSON projection\n * shipped with the package, see `dist/errors-manifest.json`.\n */\nexport function getErrorManifest(): ErrorManifest {\n return {\n version: ERROR_MANIFEST_VERSION,\n package: '@vttforge/core',\n entries: listErrorEntries(),\n };\n}\n","/**\n * `createMigrationRunner` — declarative schema migrations for Foundry systems.\n *\n * Replaces the copy-pasted \"schemaVersion setting + Hooks.once('ready') +\n * isNewerVersion compare + sequential await\" pattern that every system\n * eventually grows on its own. The runner owns no hooks — call\n * `register()` from your `init` hook and `run()` from your `ready` hook\n * (gated by `game.user.isGM`).\n *\n * Versions are semver strings, compared with `foundry.utils.isNewerVersion`.\n * The data lives in a per-system world setting and lines up cleanly with\n * `system.json`'s `flags.<systemId>.needsMigrationVersion` /\n * `compatibleMigrationVersion`.\n *\n * Failures advance `schemaVersion` only past migrations that *completed* — a\n * mid-sequence throw leaves the world at the last successful version so the\n * retry on the next world load picks up exactly where it failed.\n */\n\nimport { VttfError } from '../errors/registry.js';\nimport type { GameSettingsApi } from '../foundry-globals.js';\nimport type {\n Migration,\n MigrationLogger,\n MigrationRunner,\n MigrationRunnerOptions,\n} from './types.js';\n\nconst DEFAULT_SETTING_KEY = 'schemaVersion';\nconst INITIAL_VERSION = '0.0.0';\n\ninterface FoundryUtilsApi {\n isNewerVersion?: (next: string, current: string) => boolean;\n}\n\ninterface FoundryUiNotifications {\n info?: (msg: string) => unknown;\n warn?: (msg: string) => unknown;\n error?: (msg: string) => unknown;\n}\n\nfunction resolveIsNewerVersion(): (next: string, current: string) => boolean {\n const foundry = (globalThis as Record<string, unknown>).foundry as\n | { utils?: FoundryUtilsApi }\n | undefined;\n const fn = foundry?.utils?.isNewerVersion;\n if (typeof fn === 'function') return fn;\n // Last-resort fallback for non-Foundry runtimes — naive numeric semver compare.\n // Real consumers always run inside Foundry where the proper comparator exists.\n return naiveIsNewerVersion;\n}\n\nfunction naiveIsNewerVersion(next: string, current: string): boolean {\n const parse = (v: string): number[] =>\n v.split('.').map((part) => {\n const n = Number.parseInt(part, 10);\n return Number.isNaN(n) ? 0 : n;\n });\n const a = parse(next);\n const b = parse(current);\n const len = Math.max(a.length, b.length);\n for (let i = 0; i < len; i++) {\n const ai = a[i] ?? 0;\n const bi = b[i] ?? 0;\n if (ai > bi) return true;\n if (ai < bi) return false;\n }\n return false;\n}\n\nfunction resolveSettings(): GameSettingsApi {\n const game = (globalThis as Record<string, unknown>).game as\n | { settings?: GameSettingsApi }\n | undefined;\n const settings = game?.settings;\n if (\n settings === undefined ||\n typeof settings.register !== 'function' ||\n typeof settings.get !== 'function' ||\n typeof settings.set !== 'function'\n ) {\n throw new VttfError(\n 'VTTF-0002',\n 'globalThis.game.settings is not available — call createMigrationRunner().register() inside the Foundry runtime (or pass an explicit settings adapter in MigrationRunnerOptions).',\n );\n }\n return settings;\n}\n\nfunction resolveLogger(): MigrationLogger {\n const ui = (globalThis as Record<string, unknown>).ui as\n | { notifications?: FoundryUiNotifications }\n | undefined;\n const notifications = ui?.notifications;\n return {\n info(message) {\n // biome-ignore lint/suspicious/noConsole: console.info is the only Foundry-portable info-level logger\n console.info(message);\n notifications?.info?.(message);\n },\n warn(message) {\n console.warn(message);\n notifications?.warn?.(message);\n },\n error(message) {\n console.error(message);\n notifications?.error?.(message);\n },\n };\n}\n\nfunction lastVersion(migrations: ReadonlyArray<Migration>): string {\n return migrations.at(-1)?.version ?? INITIAL_VERSION;\n}\n\nfunction assertAscending(\n migrations: ReadonlyArray<Migration>,\n isNewer: (next: string, current: string) => boolean,\n): void {\n for (let i = 1; i < migrations.length; i++) {\n const prevMig = migrations[i - 1];\n const nextMig = migrations[i];\n // Loop bounds guarantee both indices are valid; the explicit guard\n // exists to satisfy TypeScript's flow analysis without a non-null\n // assertion.\n if (prevMig === undefined || nextMig === undefined) continue;\n if (!isNewer(nextMig.version, prevMig.version)) {\n throw new VttfError(\n 'VTTF-0004',\n `Migration list out of order: ${nextMig.version} must be newer than ${prevMig.version}.`,\n );\n }\n }\n}\n\n/**\n * Build a migration runner for a system. See module header for the failure\n * semantics; see `Migration` JSDoc for the per-entry shape.\n *\n * @example\n * ```ts\n * const migrations = createMigrationRunner({\n * systemId: 'my-system',\n * migrations: [\n * { version: '1.0.0', description: 'Rename bio → biography', fn: migrateV1 },\n * { version: '2.0.0', description: 'Add hp.temp', fn: migrateV2 },\n * ],\n * compatibleVersion: '0.9.0',\n * });\n *\n * registerSystem({\n * id: 'my-system',\n * onAfterInit: () => migrations.register(),\n * onReady: async () => {\n * if (!game.user.isGM) return;\n * await migrations.run();\n * },\n * });\n * ```\n */\nexport function createMigrationRunner(options: MigrationRunnerOptions): MigrationRunner {\n const settingKey = options.settingKey ?? DEFAULT_SETTING_KEY;\n const target = lastVersion(options.migrations);\n const settingsOverride = options.settings;\n const loggerOverride = options.logger;\n const isNewerOverride = options.isNewerVersion;\n\n return {\n targetVersion: target,\n\n register(): void {\n const settings = settingsOverride ?? resolveSettings();\n settings.register<string>(options.systemId, settingKey, {\n name: 'Schema Version',\n hint: 'Internal schema version for VTTForge data migration tracking. Do not edit by hand.',\n scope: 'world',\n config: false,\n type: String,\n default: INITIAL_VERSION,\n });\n },\n\n async run(): Promise<ReadonlyArray<string>> {\n if (options.migrations.length === 0) return [];\n\n const settings = settingsOverride ?? resolveSettings();\n const logger = loggerOverride ?? resolveLogger();\n const isNewer = isNewerOverride ?? resolveIsNewerVersion();\n\n assertAscending(options.migrations, isNewer);\n\n const stored = settings.get<string>(options.systemId, settingKey);\n const current = stored ?? INITIAL_VERSION;\n\n if (options.compatibleVersion !== undefined) {\n if (isNewer(options.compatibleVersion, current)) {\n throw new VttfError(\n 'VTTF-0005',\n `World schemaVersion ${current} is older than ${options.systemId}'s compatibleVersion ${options.compatibleVersion}. Upgrade through an intermediate release first.`,\n );\n }\n }\n\n const pending = options.migrations.filter((m) => isNewer(m.version, current));\n if (pending.length === 0) return [];\n\n const ran: string[] = [];\n let lastApplied = current;\n logger.warn(\n `${options.systemId} | Running ${pending.length} pending migration(s) from ${current} to ${target}.`,\n );\n\n for (const migration of pending) {\n const label = migration.description\n ? `${migration.version} — ${migration.description}`\n : migration.version;\n logger.info(`${options.systemId} | Migrating to ${label}`);\n try {\n await migration.fn();\n } catch (cause) {\n if (isNewer(lastApplied, current)) {\n await settings.set(options.systemId, settingKey, lastApplied);\n }\n throw new VttfError(\n 'VTTF-0004',\n `Migration to ${label} failed for system \"${options.systemId}\". schemaVersion left at ${lastApplied}.`,\n { cause },\n );\n }\n await settings.set(options.systemId, settingKey, migration.version);\n lastApplied = migration.version;\n ran.push(migration.version);\n }\n\n logger.info(`${options.systemId} | Migration complete. schemaVersion = ${target}.`);\n return ran;\n },\n };\n}\n","/**\n * Text enricher registration.\n *\n * An enricher turns a pattern in any rich text field — chat, journals, item\n * descriptions — into markup. `@PDF[handbook|page=12]{Player's Handbook}`\n * becomes a link that opens the book at that page.\n *\n * `CONFIG.TextEditor.enrichers` is a plain array, so registering by hand is one\n * `push`. The reason this exists is that the array has four ways to accept an\n * entry and then do nothing with it, and Foundry names none of them:\n *\n * - **`onRender` without `id` never fires.** Foundry wraps enriched output in a\n * custom element only when both are present; without the wrapper there is\n * nothing to fire the callback from. The enricher still produces markup, so\n * the text looks right and only the behaviour is missing.\n * - **A duplicate `id` silently loses.** The wrapper stores the id as an\n * attribute and looks the enricher back up with `find` — first match wins.\n * Two packages using `link` means the first one's `onRender` runs against the\n * second one's markup. Only reproducible with both installed.\n * - **A pattern without the `g` flag throws.** Enrichment runs the pattern\n * through `matchAll`, which rejects a non-global regex, and that throw is\n * outside the handler Foundry wraps enrichers in.\n * - **The same id can collide with a system or another module**, because the\n * id lives in one flat namespace shared by everything installed.\n *\n * So ids are namespaced the way `moduleSubType` namespaces document types, and\n * the rest is checked when you register rather than when someone opens a chat\n * message.\n */\n\nimport { VttfError } from './errors/registry.js';\n\n/** The enriched element Foundry hands to `onRender`. */\nexport interface EnricherRegistration {\n /**\n * Name for this enricher, unique within the package.\n *\n * Registered as `<package id>.<id>`. Foundry keeps every enricher in one\n * namespace, so the prefix is what stops a `link` from colliding with the\n * `link` of whatever else the world has installed.\n */\n readonly id: string;\n\n /**\n * The pattern to match. Must carry the `g` flag — enrichment runs it through\n * `matchAll`, which refuses a non-global regex.\n */\n readonly pattern: RegExp;\n\n /**\n * Build the replacement for one match. Return `null` to leave the text\n * alone — the usual answer when the thing referenced does not exist or the\n * reader is not allowed to see it.\n */\n readonly enricher: (\n match: RegExpMatchArray,\n options?: unknown,\n ) => Promise<HTMLElement | null> | HTMLElement | null;\n\n /**\n * Runs when enriched content enters the DOM. The place to bind listeners:\n * it fires on every render, so the markup and its behaviour stay declared\n * together instead of in a separate hook.\n */\n readonly onRender?: (element: HTMLElement) => void;\n\n /** Replace the whole parent element rather than just the matched text. */\n readonly replaceParent?: boolean;\n}\n\ninterface FoundryEnricherEntry {\n id: string;\n pattern: RegExp;\n enricher: EnricherRegistration['enricher'];\n onRender?: EnricherRegistration['onRender'];\n replaceParent?: boolean;\n}\n\ninterface TextEditorConfig {\n enrichers?: FoundryEnricherEntry[];\n}\n\ninterface ConfigWithTextEditor {\n TextEditor?: TextEditorConfig;\n}\n\n/** Ids are one segment: Foundry joins the package id and this with a dot. */\nconst ID_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i;\n\nfunction assertValidId(id: string, packageId: string): void {\n if (!ID_PATTERN.test(id)) {\n throw new VttfError(\n 'VTTF-0007',\n `\"${id}\" is not a usable enricher id for \"${packageId}\". Use letters, digits and hyphens — no dots: the package id and the enricher id are joined with one.`,\n );\n }\n}\n\nfunction assertGlobalPattern(entry: EnricherRegistration, packageId: string): void {\n if (!entry.pattern.global) {\n throw new VttfError(\n 'VTTF-0007',\n `The pattern for enricher \"${packageId}.${entry.id}\" is missing the g flag. Enrichment matches with matchAll, which throws on a non-global regex — and it throws while rendering someone's chat message, not here.`,\n );\n }\n}\n\nfunction readEnrichers(): FoundryEnricherEntry[] {\n const config = (globalThis as Record<string, unknown>).CONFIG as ConfigWithTextEditor | undefined;\n const enrichers = config?.TextEditor?.enrichers;\n if (!Array.isArray(enrichers)) {\n throw new VttfError(\n 'VTTF-0002',\n 'CONFIG.TextEditor.enrichers is not available — register enrichers inside the Foundry runtime, or stub CONFIG in tests.',\n );\n }\n return enrichers;\n}\n\n/**\n * Register a package's text enrichers under namespaced ids.\n *\n * Called for you by `registerSystem` and `registerModule`; exported for a\n * package that registers enrichers outside either.\n */\nexport function registerEnrichers(\n packageId: string,\n enrichers: readonly EnricherRegistration[],\n): void {\n const seen = new Set<string>();\n for (const entry of enrichers) {\n assertValidId(entry.id, packageId);\n assertGlobalPattern(entry, packageId);\n if (seen.has(entry.id)) {\n throw new VttfError(\n 'VTTF-0007',\n `\"${packageId}\" registers two enrichers with the id \"${entry.id}\". Foundry looks an enricher up by id and takes the first match, so the second one's onRender would never run.`,\n );\n }\n seen.add(entry.id);\n }\n\n const registry = readEnrichers();\n for (const entry of enrichers) {\n registry.push({\n id: `${packageId}.${entry.id}`,\n pattern: entry.pattern,\n enricher: entry.enricher,\n onRender: entry.onRender,\n replaceParent: entry.replaceParent,\n });\n }\n}\n","/**\n * Sheet registration with an id that survives a rebuild.\n *\n * Foundry keys a registered sheet by `${scope}.${sheetClass.name}` and writes\n * that key to `flags.core.sheetClass` on every document whose owner picked the\n * sheet. So the key is persisted user data, derived from a JavaScript class\n * name.\n *\n * That is fine for an unbundled package and quietly broken for a bundled one.\n * A minifier renames classes, and it does not promise the same name twice: the\n * same sheet registers as `mo` in one build and `vo` in the next. The saved\n * key then names a sheet that no longer exists, Foundry falls back to the\n * default, and the reader's chosen sheet is gone with nothing in the console.\n * It hits released upgrades, not just a dev loop — pick the sheet in 1.0, ship\n * 1.1, and the choice is lost.\n *\n * So the caller names the sheet and VTTForge fixes the class name to that\n * before registering. The name is written down, not inferred, and a build tool\n * cannot change it.\n *\n * Because the key is persisted, the way it is derived is a compatibility\n * promise: `${package id}.${sheet id}` cannot change later without orphaning\n * every saved selection.\n */\n\nimport { VttfError } from './errors/registry.js';\n\n/** Which document collection the sheet belongs to. */\nexport type SheetDocumentKind = 'Actor' | 'Item';\n\nexport interface SheetRegistration {\n /**\n * Stable name for this sheet, unique within the package.\n *\n * It becomes the second half of the key Foundry persists, so treat it the\n * way you treat a database column name: pick it once and keep it. Renaming\n * it later loses the sheet choice of every document already using it.\n */\n readonly id: string;\n\n /** The document the sheet is for. */\n readonly document: SheetDocumentKind;\n\n /** The sheet class. */\n readonly sheet: unknown;\n\n /**\n * Document sub-types the sheet applies to. Omit to offer it for every type.\n *\n * A module registering a sheet for its own sub-type must pass the prefixed\n * key — `moduleSubType(id, 'pdf')`, not `'pdf'`.\n */\n readonly types?: readonly string[];\n\n /** Localization key for the name shown in the sheet picker. */\n readonly label?: string;\n\n /** Make this the default sheet for the listed types. */\n readonly makeDefault?: boolean;\n\n /** Allow a user to choose this sheet as their default. Defaults to true. */\n readonly canBeDefault?: boolean;\n\n /** Offer this sheet in the sheet configuration dialog. Defaults to true. */\n readonly canConfigure?: boolean;\n}\n\ninterface DocumentSheetConfigApi {\n registerSheet(\n documentClass: unknown,\n scope: string,\n sheetClass: unknown,\n options: Record<string, unknown>,\n ): void;\n}\n\ninterface FoundryRoot {\n readonly applications?: { readonly apps?: { readonly DocumentSheetConfig?: unknown } };\n}\n\n/**\n * Ids are one segment. Foundry joins scope and id with a dot, so a dotted id\n * produces a key that cannot be told apart from a different scope's.\n */\nconst ID_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i;\n\nfunction assertValidId(id: string, packageId: string): void {\n if (!ID_PATTERN.test(id)) {\n throw new VttfError(\n 'VTTF-0006',\n `\"${id}\" is not a usable sheet id for \"${packageId}\". Use letters, digits and hyphens — no dots: Foundry joins the package id and the sheet id with one, so a dotted id produces an ambiguous key.`,\n );\n }\n}\n\nfunction readDocumentSheetConfig(): DocumentSheetConfigApi {\n const foundry = (globalThis as Record<string, unknown>).foundry as FoundryRoot | undefined;\n const config = foundry?.applications?.apps?.DocumentSheetConfig as\n | DocumentSheetConfigApi\n | undefined;\n if (config === undefined || typeof config.registerSheet !== 'function') {\n throw new VttfError(\n 'VTTF-0002',\n 'foundry.applications.apps.DocumentSheetConfig is not available — register sheets inside the Foundry runtime, or stub the global in tests.',\n );\n }\n return config;\n}\n\n/**\n * Give the class the name the caller chose.\n *\n * `Function.prototype.name` is configurable, so this is a plain redefinition\n * rather than a trick. It is what makes the registered key stable, and it also\n * makes the class report its real name in a stack trace from a minified build.\n */\nfunction nameClass(sheet: unknown, id: string): void {\n Object.defineProperty(sheet as object, 'name', { value: id, configurable: true });\n}\n\n/**\n * Register a package's sheets under keys that do not move.\n *\n * Called for you by `registerSystem` and `registerModule`; exported for a\n * package that registers sheets outside either.\n */\nexport function registerSheets(\n packageId: string,\n sheets: readonly SheetRegistration[],\n documentClasses: Readonly<Record<SheetDocumentKind, unknown>>,\n): void {\n const seen = new Set<string>();\n for (const entry of sheets) {\n assertValidId(entry.id, packageId);\n if (seen.has(entry.id)) {\n throw new VttfError(\n 'VTTF-0006',\n `\"${packageId}\" registers two sheets with the id \"${entry.id}\". Ids are the persisted key — the second would overwrite the first.`,\n );\n }\n seen.add(entry.id);\n }\n\n const config = readDocumentSheetConfig();\n for (const entry of sheets) {\n nameClass(entry.sheet, entry.id);\n config.registerSheet(documentClasses[entry.document], packageId, entry.sheet, {\n types: entry.types === undefined ? undefined : [...entry.types],\n label: entry.label,\n makeDefault: entry.makeDefault ?? false,\n canBeDefault: entry.canBeDefault ?? true,\n canConfigure: entry.canConfigure ?? true,\n });\n }\n}\n","/**\n * registerModule — the module counterpart to `registerSystem`.\n *\n * A module is a guest in someone else's world, and Foundry enforces that. The\n * two differences that matter:\n *\n * - **Sub-type keys are namespaced.** A system registers `character`; a module\n * registering the same thing must register `<module-id>.character`, and the\n * manifest must declare it under `documentTypes`. Forget the prefix and the\n * type silently never appears. This function adds it for you.\n * - **A module never owns the globals.** Document classes, the initiative\n * formula and the status-effect array belong to the system. So there is no\n * option here to replace them — `statusEffects` only appends, which is what\n * a module is allowed to do.\n */\n\nimport { VttfError, type VttfErrorCode } from './errors/registry.js';\nimport type { FoundryConfig, HooksApi } from './foundry-globals.js';\nimport { type EnricherRegistration, registerEnrichers } from './register-enrichers.js';\nimport { registerSheets, type SheetRegistration } from './register-sheets.js';\n\nexport interface ModuleRegistration {\n /** Module id — must match the folder name and `module.json` `id`. */\n readonly id: string;\n\n /**\n * Actor sub-types this module contributes, keyed by the bare type name.\n * Registered under `<id>.<type>`, so declare them the same way in\n * `documentTypes.Actor` in your manifest.\n */\n readonly actorDataModels?: Readonly<Record<string, unknown>>;\n\n /** Item sub-types, same rule as `actorDataModels`. */\n readonly itemDataModels?: Readonly<Record<string, unknown>>;\n\n /**\n * Status effects to append to `CONFIG.statusEffects`.\n *\n * Appended, never assigned: the array belongs to the system, and replacing\n * it would delete conditions the world depends on.\n */\n readonly statusEffects?: readonly unknown[];\n\n /**\n * Sheets this module offers, registered under `<id>.<sheet id>`.\n *\n * Register them here rather than calling Foundry's `registerSheet` yourself:\n * Foundry derives the persisted key from the class name, and a bundler\n * renames classes between builds. See `registerSheets`.\n */\n readonly sheets?: readonly SheetRegistration[];\n\n /**\n * Text enrichers this module contributes, registered under `<id>.<enricher\n * id>`.\n *\n * Register them here rather than pushing to `CONFIG.TextEditor.enrichers`\n * yourself: that array has four ways to accept an entry and then do nothing\n * with it. See `registerEnrichers`.\n */\n readonly enrichers?: readonly EnricherRegistration[];\n\n /** Runs before any CONFIG mutation — the usual home for the module API. */\n readonly onBeforeInit?: () => void;\n\n /** Runs after the mutations above, inside the same `init` hook. */\n readonly onAfterInit?: () => void;\n\n /**\n * Runs once on `ready`.\n *\n * **Not GM-gated.** Guard inside your callback when the work is GM-only.\n */\n readonly onReady?: () => void | Promise<void>;\n}\n\nconst registered = new Set<string>();\n\n/** For tests — clears the in-process \"already registered\" guard. */\nexport function _resetRegisteredModulesForTests(): void {\n registered.clear();\n}\n\n/**\n * The key Foundry files a module's document sub-type under.\n *\n * Use it wherever you name the type outside `registerModule` — registering the\n * sheet, checking `actor.type`, writing `documentTypes` in the manifest. The\n * prefix is easy to get wrong by hand and fails silently when you do.\n *\n * @example\n * ```ts\n * moduleSubType('pdf-character-sheet', 'pdf'); // 'pdf-character-sheet.pdf'\n * ```\n */\nexport function moduleSubType(moduleId: string, type: string): string {\n return `${moduleId}.${type}`;\n}\n\nfunction vttfError(code: VttfErrorCode, message?: string): VttfError {\n return new VttfError(code, message);\n}\n\nfunction readHooks(): HooksApi {\n const hooks = (globalThis as Record<string, unknown>).Hooks as HooksApi | undefined;\n if (hooks === undefined || typeof hooks.once !== 'function') {\n throw vttfError(\n 'VTTF-0002',\n 'globalThis.Hooks is not available — call registerModule() inside a Foundry runtime or stub Hooks in tests',\n );\n }\n return hooks;\n}\n\nfunction readConfig(): FoundryConfig {\n const config = (globalThis as Record<string, unknown>).CONFIG as FoundryConfig | undefined;\n if (config === undefined) {\n throw vttfError(\n 'VTTF-0002',\n 'globalThis.CONFIG is not available — call registerModule() inside a Foundry runtime or stub CONFIG in tests',\n );\n }\n return config;\n}\n\nfunction assignSubTypes(\n target: Record<string, unknown>,\n moduleId: string,\n models: Readonly<Record<string, unknown>>,\n): void {\n for (const [type, model] of Object.entries(models)) {\n target[moduleSubType(moduleId, type)] = model;\n }\n}\n\n/**\n * Register a Foundry module with VTTForge.\n *\n * Calling twice with the same `id` throws VTTF-0001 — almost always a\n * hot-reload artefact or a duplicate import. The CONFIG mutations are deferred\n * until Foundry's `init` hook fires.\n */\nexport function registerModule(config: ModuleRegistration): ModuleRegistration {\n if (registered.has(config.id)) {\n throw vttfError('VTTF-0001', `Module \"${config.id}\" was already registered`);\n }\n registered.add(config.id);\n\n const hooks = readHooks();\n hooks.once('init', () => {\n applyInit(config);\n });\n if (config.onReady !== undefined) {\n hooks.once('ready', () => {\n void config.onReady?.();\n });\n }\n\n return config;\n}\n\nfunction applyInit(config: ModuleRegistration): void {\n config.onBeforeInit?.();\n const CONFIG = readConfig();\n\n if (config.actorDataModels !== undefined) {\n assignSubTypes(CONFIG.Actor.dataModels, config.id, config.actorDataModels);\n }\n if (config.itemDataModels !== undefined) {\n assignSubTypes(CONFIG.Item.dataModels, config.id, config.itemDataModels);\n }\n if (config.statusEffects !== undefined && config.statusEffects.length > 0) {\n CONFIG.statusEffects ??= [];\n CONFIG.statusEffects.push(...config.statusEffects);\n }\n if (config.enrichers !== undefined && config.enrichers.length > 0) {\n registerEnrichers(config.id, config.enrichers);\n }\n if (config.sheets !== undefined && config.sheets.length > 0) {\n registerSheets(config.id, config.sheets, {\n Actor: CONFIG.Actor.documentClass,\n Item: CONFIG.Item.documentClass,\n });\n }\n\n config.onAfterInit?.();\n}\n","/**\n * registerSystem — one call that replaces the boilerplate `Hooks.once(\"init\", ...)`\n * block in every Foundry system.\n *\n * Conforms to the canonical Foundry init lifecycle:\n *\n * init → CONFIG mutations (dataModels, documentClass, statusEffects)\n * i18nInit → translate CONFIG labels\n * setup → enrichers, packs\n * ready → migrations (GM-only — consumer guards inside onReady)\n *\n * v0.1 scope: `init` + `ready`. `setup` / `i18nInit` callbacks remain v0.1.1.\n *\n * Per PRD §11 open question #1, we wrap the hook ourselves (\"explicit hook for\n * now\"); callers don't need to write `Hooks.once(\"init\", ...)` themselves.\n */\n\nimport { VttfError, type VttfErrorCode } from './errors/registry.js';\nimport type {\n ActiveEffectConfig,\n CombatConfig,\n FoundryConfig,\n HooksApi,\n} from './foundry-globals.js';\nimport { type EnricherRegistration, registerEnrichers } from './register-enrichers.js';\nimport { registerSheets, type SheetRegistration } from './register-sheets.js';\n\nexport interface SystemRegistration {\n /** System id — must match the folder name and `system.json` `id`. */\n readonly id: string;\n\n /** Map of `documentTypes.Actor` key → TypeDataModel class. */\n readonly actorDataModels?: Readonly<Record<string, unknown>>;\n\n /** Map of `documentTypes.Item` key → TypeDataModel class. */\n readonly itemDataModels?: Readonly<Record<string, unknown>>;\n\n /** Replacement for `CONFIG.Actor.documentClass`. */\n readonly actorDocumentClass?: unknown;\n\n /** Replacement for `CONFIG.Item.documentClass`. */\n readonly itemDocumentClass?: unknown;\n\n /** Global initiative formula — assigned to `CONFIG.Combat.initiative`. */\n readonly combat?: CombatConfig;\n\n /** Disables legacy Active Effect transferral. Defaults to true. */\n readonly activeEffect?: ActiveEffectConfig;\n\n /**\n * Replaces `CONFIG.statusEffects` (systems own this array — modules push).\n * If omitted, the existing array is kept untouched.\n */\n readonly statusEffects?: readonly unknown[];\n\n /**\n * Sheets this system offers, registered under `<id>.<sheet id>`.\n *\n * Register them here rather than calling Foundry's `registerSheet` yourself:\n * Foundry derives the persisted key from the class name, and a bundler\n * renames classes between builds. See `registerSheets`.\n */\n readonly sheets?: readonly SheetRegistration[];\n\n /**\n * Text enrichers this system contributes, registered under `<id>.<enricher\n * id>`.\n *\n * Register them here rather than pushing to `CONFIG.TextEditor.enrichers`\n * yourself: that array has four ways to accept an entry and then do nothing\n * with it. See `registerEnrichers`.\n */\n readonly enrichers?: readonly EnricherRegistration[];\n\n /**\n * Optional pre-init hook for work that has to run before any of the CONFIG\n * mutations (rare — usually used to assign `globalThis.<systemId>` API).\n */\n readonly onBeforeInit?: () => void;\n\n /** Optional post-init hook for work that depends on the mutations above. */\n readonly onAfterInit?: () => void;\n\n /**\n * Optional `ready` hook — fires once after Foundry has finished bootstrap.\n * The natural home for migration runners (`createMigrationRunner().run()`).\n *\n * **Not GM-gated.** Guard inside your callback (`if (!game.user.isGM) return;`)\n * when the work is GM-only — migrations always are.\n */\n readonly onReady?: () => void | Promise<void>;\n}\n\nconst registered = new Set<string>();\n\n/** For tests — clears the in-process \"already registered\" guard. */\nexport function _resetRegisteredSystemsForTests(): void {\n registered.clear();\n}\n\nfunction readHooks(): HooksApi {\n const hooks = (globalThis as Record<string, unknown>).Hooks as HooksApi | undefined;\n if (hooks === undefined || typeof hooks.once !== 'function') {\n throw vttfError(\n 'VTTF-0002',\n 'globalThis.Hooks is not available — call registerSystem() inside a Foundry runtime or stub Hooks in tests',\n );\n }\n return hooks;\n}\n\nfunction readConfig(): FoundryConfig {\n const config = (globalThis as Record<string, unknown>).CONFIG as FoundryConfig | undefined;\n if (config === undefined) {\n throw vttfError(\n 'VTTF-0002',\n 'globalThis.CONFIG is not available — call registerSystem() inside a Foundry runtime or stub CONFIG in tests',\n );\n }\n return config;\n}\n\nfunction vttfError(code: VttfErrorCode, message?: string): VttfError {\n return new VttfError(code, message);\n}\n\n/**\n * Register a Foundry system with VTTForge. Idempotency: the same `id` calling\n * twice throws VTTF-0001 — almost always a hot-reload or duplicate import bug.\n *\n * Returns the registration object so consumers can inspect what was applied\n * (useful in tests). The actual CONFIG mutations are deferred until Foundry's\n * `init` hook fires.\n */\nexport function registerSystem(config: SystemRegistration): SystemRegistration {\n if (registered.has(config.id)) {\n throw vttfError('VTTF-0001', `System \"${config.id}\" was already registered`);\n }\n registered.add(config.id);\n\n const hooks = readHooks();\n hooks.once('init', () => {\n applyInit(config);\n });\n if (config.onReady !== undefined) {\n hooks.once('ready', () => {\n // Foundry awaits ready-hook results, but `Hooks.once` types it as\n // `unknown` so we don't return anything ourselves — Foundry treats\n // Promise rejections as unhandled, which is the right escalation.\n void config.onReady?.();\n });\n }\n\n return config;\n}\n\nfunction applyInit(config: SystemRegistration): void {\n config.onBeforeInit?.();\n const CONFIG = readConfig();\n\n if (config.actorDataModels !== undefined) {\n Object.assign(CONFIG.Actor.dataModels, config.actorDataModels);\n }\n if (config.itemDataModels !== undefined) {\n Object.assign(CONFIG.Item.dataModels, config.itemDataModels);\n }\n if (config.actorDocumentClass !== undefined) {\n CONFIG.Actor.documentClass = config.actorDocumentClass;\n }\n if (config.itemDocumentClass !== undefined) {\n CONFIG.Item.documentClass = config.itemDocumentClass;\n }\n if (config.combat?.initiative !== undefined) {\n CONFIG.Combat.initiative = config.combat.initiative;\n }\n\n // Disable legacy Active Effect transferral by default — every modern v13\n // system wants this off (the modern AE model is opt-in via this flag).\n const legacyTransferral = config.activeEffect?.legacyTransferral ?? false;\n CONFIG.ActiveEffect.legacyTransferral = legacyTransferral;\n\n if (config.statusEffects !== undefined) {\n CONFIG.statusEffects = [...config.statusEffects];\n }\n if (config.enrichers !== undefined && config.enrichers.length > 0) {\n registerEnrichers(config.id, config.enrichers);\n }\n if (config.sheets !== undefined && config.sheets.length > 0) {\n registerSheets(config.id, config.sheets, {\n Actor: CONFIG.Actor.documentClass,\n Item: CONFIG.Item.documentClass,\n });\n }\n\n config.onAfterInit?.();\n}\n","/**\n * SystemConfig — typed wrapper around `game.settings.register/get/set`.\n *\n * Eliminates the boilerplate of repeating the system id in every call:\n *\n * // before\n * game.settings.register(\"ordemparanormal\", \"homebrewRules\", { ... });\n * game.settings.get(\"ordemparanormal\", \"homebrewRules\");\n *\n * // after\n * const cfg = new SystemConfig(\"ordemparanormal\");\n * cfg.register(\"homebrewRules\", { ... });\n * cfg.get<boolean>(\"homebrewRules\");\n *\n * Also keeps a local manifest of registered keys so attempts to read an\n * unregistered key fail with VTTF-0003 instead of returning undefined.\n *\n * Registration must happen during the `init` hook; reads can happen any\n * time after.\n */\n\nimport { VttfError } from './errors/registry.js';\nimport type { GameApi, SettingConfig } from './foundry-globals.js';\n\nfunction readGame(): GameApi {\n const candidate = (globalThis as Record<string, unknown>).game as GameApi | undefined;\n if (candidate === undefined || candidate.settings === undefined) {\n throw new VttfError(\n 'VTTF-0002',\n 'game.settings is not available — call SystemConfig methods inside or after the Foundry \"init\" hook',\n );\n }\n return candidate;\n}\n\nexport class SystemConfig {\n readonly systemId: string;\n readonly #registered = new Set<string>();\n\n constructor(systemId: string) {\n this.systemId = systemId;\n }\n\n register<T>(key: string, config: SettingConfig<T>): void {\n const game = readGame();\n game.settings.register(this.systemId, key, config);\n this.#registered.add(key);\n }\n\n get<T>(key: string): T {\n if (!this.#registered.has(key)) {\n throw new VttfError(\n 'VTTF-0003',\n `SystemConfig.get(\"${key}\") was called before register(\"${key}\")`,\n );\n }\n return readGame().settings.get<T>(this.systemId, key);\n }\n\n async set<T>(key: string, value: T): Promise<T> {\n if (!this.#registered.has(key)) {\n throw new VttfError(\n 'VTTF-0003',\n `SystemConfig.set(\"${key}\") was called before register(\"${key}\")`,\n );\n }\n return readGame().settings.set<T>(this.systemId, key, value);\n }\n\n isRegistered(key: string): boolean {\n return this.#registered.has(key);\n }\n}\n","/**\n * @vttforge/core — runtime utilities for FoundryVTT v13+ systems and modules.\n *\n * v0.1 surface:\n *\n * - registerSystem() — one-call init, replaces Hooks.once(\"init\")\n * - registerModule() — the same for modules, with namespaced sub-types\n * - SystemConfig — typed wrapper around game.settings\n * - BaseTypeDataModel() — TypeDataModel with safe migrateData default\n * - BaseActorSheet() — ActorSheetV2 + HandlebarsApplicationMixin\n * - BaseItemSheet() — ItemSheetV2 + HandlebarsApplicationMixin\n * - fields() — typed bag of foundry.data.fields constructors\n * - InferSchema<T> — derive `system` shape from defineSchema()\n * - createMigrationRunner() — declarative schema migrations (register + run)\n * - VttfError + error registry — VTTF-NNNN codes with docs URLs\n *\n * Foundry classes are resolved from `globalThis.foundry` lazily so the package\n * imports cleanly in Node/tests; concrete Foundry typing arrives with\n * `@vttforge/types` in v1.0.\n */\n\nimport { version } from '../package.json' with { type: 'json' };\n\nexport const VTTFORGE_CORE_VERSION: string = version;\n\nexport {\n BaseActorSheet,\n type DragDropConfig,\n type SheetBaseCtor,\n type SheetBaseMembers,\n type SheetBaseStatics,\n VTTFORGE_SHEET_CLASS,\n} from './base-actor-sheet.js';\nexport {\n BaseApplication,\n type BaseApplicationMembers,\n} from './base-application.js';\nexport {\n BaseDocumentSheet,\n type BaseDocumentSheetMembers,\n type DocumentSheetKind,\n} from './base-document-sheet.js';\nexport { BaseItemSheet } from './base-item-sheet.js';\nexport {\n BaseTypeDataModel,\n type TypeDataModelHooks,\n type TypedTypeDataModel,\n type TypedTypeDataModelCtor,\n} from './base-type-data-model.js';\nexport type {\n ArrayFieldOptions,\n BooleanFieldOptions,\n ColorFieldOptions,\n DataFieldOptions,\n EmbeddedDataFieldOptions,\n EmbeddedDocumentFieldOptions,\n FilePathFieldOptions,\n ForeignDocumentFieldOptions,\n HTMLFieldOptions,\n NumberFieldOptions,\n SchemaFieldOptions,\n SetFieldOptions,\n StringFieldOptions,\n TypedSchemaFieldOptions,\n} from './data/field-options.js';\nexport {\n type ArrayFieldCtor,\n type ArrayFieldInstance,\n type BooleanFieldCtor,\n type BooleanFieldInstance,\n type ColorFieldCtor,\n type ColorFieldInstance,\n type DataModelClass,\n type DocumentClass,\n type EmbeddedDataFieldCtor,\n type EmbeddedDataFieldInstance,\n type EmbeddedDocumentFieldCtor,\n type EmbeddedDocumentFieldInstance,\n type FieldInstance,\n type FieldsApi,\n type FilePathFieldCtor,\n type FilePathFieldInstance,\n type ForeignDocumentFieldCtor,\n type ForeignDocumentFieldInstance,\n fields,\n type HTMLFieldCtor,\n type HTMLFieldInstance,\n type NumberFieldCtor,\n type NumberFieldInstance,\n type SchemaFieldCtor,\n type SchemaFieldInstance,\n type SetFieldCtor,\n type SetFieldInstance,\n type StringFieldCtor,\n type StringFieldInstance,\n type TypedSchemaFieldCtor,\n type TypedSchemaFieldInstance,\n} from './data/fields.js';\nexport type { InferField, InferSchema, Prettify } from './data/infer-schema.js';\nexport {\n ERROR_MANIFEST_VERSION,\n type ErrorManifest,\n getErrorManifest,\n} from './errors/manifest.js';\nexport {\n docsUrlFor,\n getErrorEntry,\n listErrorEntries,\n VttfError,\n type VttfErrorCode,\n type VttfErrorEntry,\n} from './errors/registry.js';\nexport type {\n ApplicationV2Members,\n DocumentSheetV2Members,\n VttforgeClass,\n} from './foundry-base.js';\nexport type {\n ActiveEffectConfig,\n CombatConfig,\n FoundryConfig,\n GameApi,\n GameSettingsApi,\n HookCallback,\n HooksApi,\n SettingConfig,\n SettingScope,\n} from './foundry-globals.js';\nexport { createMigrationRunner } from './migrations/runner.js';\nexport type {\n Migration,\n MigrationLogger,\n MigrationRunner,\n MigrationRunnerOptions,\n} from './migrations/types.js';\nexport {\n type EnricherRegistration,\n registerEnrichers,\n} from './register-enrichers.js';\nexport {\n type ModuleRegistration,\n moduleSubType,\n registerModule,\n} from './register-module.js';\nexport {\n registerSheets,\n type SheetDocumentKind,\n type SheetRegistration,\n} from './register-sheets.js';\nexport { registerSystem, type SystemRegistration } from './register-system.js';\nexport { SystemConfig } from './system-config.js';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["resolveBases","resolveDragDrop","ID_PATTERN","assertValidId","registered","vttfError","readHooks","readConfig","applyInit"],"sources":["../package.json","../src/errors/registry.ts","../src/base-actor-sheet.ts","../src/base-application.ts","../src/base-document-sheet.ts","../src/base-item-sheet.ts","../src/base-type-data-model.ts","../src/data/fields.ts","../src/errors/manifest.ts","../src/migrations/runner.ts","../src/register-enrichers.ts","../src/register-sheets.ts","../src/register-module.ts","../src/register-system.ts","../src/system-config.ts","../src/index.ts"],"sourcesContent":["","/**\n * VTTF-NNNN error registry — append-only, stable across majors.\n *\n * Every error VTTForge throws has a numeric code (`VTTF-NNNN`) and a PascalCase\n * `name` for stack-trace readability. Codes are URLs — `https://vttforge.dev/errors/VTTF-0001`\n * eventually links to a docs page generated from this registry.\n *\n * Never renumber an entry. To deprecate, mark with `deprecated: true` and add a\n * `replacedBy` pointer. Adding a new code: pick the next unused integer.\n */\n\nexport type VttfErrorCode = `VTTF-${string}`;\n\nexport interface VttfErrorEntry {\n readonly code: VttfErrorCode;\n readonly name: string;\n readonly summary: string;\n readonly deprecated?: boolean;\n readonly replacedBy?: VttfErrorCode;\n}\n\nconst DOCS_BASE_URL = 'https://vttforge.dev/errors';\n\nconst REGISTRY: Readonly<Record<VttfErrorCode, VttfErrorEntry>> = Object.freeze({\n 'VTTF-0001': Object.freeze({\n code: 'VTTF-0001',\n name: 'SystemAlreadyRegistered',\n summary:\n 'registerSystem() was called more than once for the same system id. This is almost always a hot-reload artefact or a duplicate import.',\n }),\n 'VTTF-0002': Object.freeze({\n code: 'VTTF-0002',\n name: 'MissingFoundryGlobals',\n summary:\n 'VTTForge code ran in an environment without Foundry globals (game, Hooks, CONFIG). Initialise inside the Foundry runtime, not in a Node test without mocks.',\n }),\n 'VTTF-0003': Object.freeze({\n code: 'VTTF-0003',\n name: 'UnknownSetting',\n summary:\n 'SystemConfig.get() / set() was called with a key that was never passed to SystemConfig.register(). Register the setting in your init hook before reading it.',\n }),\n 'VTTF-0004': Object.freeze({\n code: 'VTTF-0004',\n name: 'MigrationFailed',\n summary:\n 'A migration function passed to createMigrationRunner() threw. The original error is available on .cause. The schemaVersion setting is not advanced past the failed migration so retrying on the next world load picks up where the failure left off.',\n }),\n 'VTTF-0005': Object.freeze({\n code: 'VTTF-0005',\n name: 'WorldTooOldForMigration',\n summary:\n 'createMigrationRunner() was called on a world whose stored schemaVersion is older than the configured compatibleVersion floor. Upgrade the world to a supported intermediate version before continuing. Running migrations across the gap would corrupt data.',\n }),\n 'VTTF-0006': Object.freeze({\n code: 'VTTF-0006',\n name: 'InvalidSheetId',\n summary:\n 'A sheet was registered with an id that is empty, contains a dot, or repeats another sheet in the same package. The id becomes half of the key Foundry persists on every document using the sheet, so it must be a single unambiguous segment.',\n }),\n 'VTTF-0007': Object.freeze({\n code: 'VTTF-0007',\n name: 'InvalidEnricher',\n summary:\n 'A text enricher was registered with an id that is empty, contains a dot, or repeats another enricher in the same package, or with a pattern missing the g flag. Foundry looks enrichers up by id and takes the first match, and matches with matchAll, which throws on a non-global regex.',\n }),\n});\n\n/**\n * Look up a registered entry by code. Throws if the code is unknown — the\n * registry is the source of truth, so missing codes mean a typo.\n */\nexport function getErrorEntry(code: VttfErrorCode): VttfErrorEntry {\n const entry = REGISTRY[code];\n if (entry === undefined) {\n throw new Error(`Unknown VTTForge error code: ${code}. Add it to the registry.`);\n }\n return entry;\n}\n\n/**\n * Return every entry currently in the registry. Used by codegen to emit the\n * runtime constants and the JSON manifest that powers the docs pages.\n */\nexport function listErrorEntries(): readonly VttfErrorEntry[] {\n return Object.values(REGISTRY);\n}\n\nexport function docsUrlFor(code: VttfErrorCode): string {\n return `${DOCS_BASE_URL}/${code}`;\n}\n\n/**\n * VttfError — every error VTTForge throws extends this.\n *\n * - `code` is the registry key (string-narrowed).\n * - `name` is the PascalCase name from the registry — shows up in stack traces.\n * - `docsUrl` points at the docs page.\n * - `cause` uses the native ES2022 mechanism. Multiple causes => pass an\n * `AggregateError` as the cause.\n */\nexport class VttfError extends Error {\n readonly code: VttfErrorCode;\n readonly docsUrl: string;\n\n constructor(code: VttfErrorCode, message?: string, options?: ErrorOptions) {\n const entry = getErrorEntry(code);\n const finalMessage = `[${code}] ${message ?? entry.summary}`;\n super(finalMessage, options);\n this.code = code;\n this.name = entry.name;\n this.docsUrl = docsUrlFor(code);\n }\n}\n","/**\n * BaseActorSheet — `ActorSheetV2 + HandlebarsApplicationMixin` baseline with the\n * boilerplate every shipping system copy-pastes hoisted into the SDK.\n *\n * What this adds beyond stock Foundry v13:\n *\n * - **`static DRAG_DROP`** — declare drag sources / drop targets as data, get\n * `foundry.applications.ux.DragDrop` instances wired in `_onRender` with\n * `isEditable`-gated permissions and a sensible default `_onDragStart` that\n * serialises `data-item-id` elements as `{ type: \"Item\", uuid }`.\n * - **`_prepareContext` auto-fills `context.tabs[group]`** for every group\n * declared in ApplicationV2's `static TABS`, so subclass `_prepareContext`\n * implementations stop having to call `_prepareTabs(group)` by hand.\n * - **Typed drop dispatch** — override `onDropItem(item, event)` /\n * `onDropActor(actor, event)` / `onDropFolder(folder, event)` /\n * `onDropActiveEffect(effect, event)` and skip the `fromUuid()` ceremony.\n * Returning `undefined` falls through to Foundry's default `_onDropX`\n * behaviour; return any other value to take ownership.\n *\n * Intentional non-additions:\n *\n * - `editImage` action — already shipped by `DocumentSheetV2` (inherited by\n * `ActorSheetV2`). Templates wire `<img data-edit=\"img\">` and Foundry's\n * built-in action handles the `FilePicker` flow.\n * - `_getTabs()` — ApplicationV2 already owns the tab state machine; we only\n * eliminate the `_prepareTabs` call in `_prepareContext`.\n *\n * Resolved lazily so subclasses can be declared at module load without\n * Foundry globals existing yet (test boot, ESM hoist).\n */\n\nimport { VttfError } from './errors/registry.js';\nimport type { DocumentSheetV2Members } from './foundry-base.js';\n\n// biome-ignore lint/suspicious/noExplicitAny: Foundry's ActorSheetV2 shape lives in fvtt-types (deferred to @vttforge/types v1.0)\ntype AnyConstructor = new (...args: any[]) => any;\n\n/**\n * Declarative DragDrop entry consumed by `_onRender`. Mirrors the\n * `foundry.applications.ux.DragDrop` constructor config. Permissions and\n * callbacks fall back to sensible defaults that honour `this.isEditable` and\n * the default `_onDragStart` / `_onDrop`.\n */\nexport interface DragDropConfig {\n readonly dragSelector?: string;\n readonly dropSelector?: string;\n readonly permissions?: {\n readonly dragstart?: () => boolean;\n readonly drop?: () => boolean;\n };\n // biome-ignore lint/suspicious/noExplicitAny: DragEvent payload is browser-native; consumers route to their own typed handlers\n readonly callbacks?: Record<string, (...args: any[]) => unknown>;\n}\n\n/**\n * The statics a VTTForge sheet base carries.\n *\n * The factory used to return a bare constructor, so a subclass writing\n * `super.DEFAULT_OPTIONS` — the pattern the docs show and every sheet needs —\n * failed to compile. TypeScript cannot see a static through an untyped\n * constructor. The example system never caught it because it is JavaScript.\n *\n * `DEFAULT_OPTIONS` is deliberately loose: a subclass merges its own shape\n * into it, and pinning ours would reject the merge.\n */\nexport interface SheetBaseStatics {\n // biome-ignore lint/suspicious/noExplicitAny: a subclass merges arbitrary ApplicationV2 options in; a narrower type would reject the merge\n readonly DEFAULT_OPTIONS: Record<string, any>;\n readonly DRAG_DROP: ReadonlyArray<DragDropConfig>;\n}\n\n/**\n * What the factory hands back: something you can `extend`, whose statics the\n * compiler can see.\n */\n/**\n * What the sheet factories add on top of Foundry's own sheet.\n *\n * Only the members a subclass actually reaches for. The rest of the Foundry\n * surface stays reachable and untyped until `@vttforge/types` describes it —\n * see `DocumentSheetV2Members`.\n */\nexport interface SheetBaseMembers {\n /** Fills in `context.tabs` for every group in `static TABS`. */\n _prepareContext(options: unknown): Promise<Record<string, unknown>>;\n /** Binds the `static DRAG_DROP` entries. */\n _onRender(context: unknown, options: unknown): void;\n _onDragStart(event: DragEvent): void;\n\n /**\n * The typed drop hooks. Override the one you want; returning `undefined`\n * hands the drop back to Foundry's own handling.\n */\n onDropItem(item: unknown, event: DragEvent): Promise<unknown>;\n onDropActor(actor: unknown, event: DragEvent): Promise<unknown>;\n onDropFolder(folder: unknown, event: DragEvent): Promise<unknown>;\n onDropActiveEffect(effect: unknown, event: DragEvent): Promise<unknown>;\n\n /**\n * Foundry's own drop entry points, implemented here to resolve the payload\n * and hand it to the `onDropX` hook above.\n *\n * Declared because the base really does define them — an earlier version\n * left them off, and nothing said so while an index signature was making\n * every member name legal. Override `onDropItem` rather than this.\n */\n _onDropItem(event: DragEvent, data: unknown): Promise<unknown>;\n _onDropActor(event: DragEvent, data: unknown): Promise<unknown>;\n _onDropFolder(event: DragEvent, data: unknown): Promise<unknown>;\n _onDropActiveEffect(event: DragEvent, data: unknown): Promise<unknown>;\n}\n\nexport interface SheetBaseCtor extends SheetBaseStatics {\n // biome-ignore lint/suspicious/noExplicitAny: mirrors ApplicationV2's constructor arity, which subclasses pass straight through\n new (...args: any[]): SheetBaseMembers & DocumentSheetV2Members;\n}\n\ninterface DragDropInstance {\n bind(element: HTMLElement): void;\n}\n\ninterface DragDropCtor {\n new (config: Record<string, unknown>): DragDropInstance;\n}\n\ninterface FoundryApplicationsApi {\n HandlebarsApplicationMixin?: (base: AnyConstructor) => AnyConstructor;\n}\n\ninterface FoundryApplicationsSheets {\n ActorSheetV2?: AnyConstructor;\n}\n\ninterface FoundryApplicationsUx {\n DragDrop?: DragDropCtor;\n}\n\ninterface FoundryApplications {\n api?: FoundryApplicationsApi;\n sheets?: FoundryApplicationsSheets;\n ux?: FoundryApplicationsUx;\n}\n\ninterface FoundryGlobal {\n applications?: FoundryApplications;\n}\n\nfunction resolveBases(): { Base: AnyConstructor; mixin: (b: AnyConstructor) => AnyConstructor } {\n const foundry = (globalThis as Record<string, unknown>).foundry as FoundryGlobal | undefined;\n const Base = foundry?.applications?.sheets?.ActorSheetV2;\n const mixin = foundry?.applications?.api?.HandlebarsApplicationMixin;\n if (typeof Base !== 'function' || typeof mixin !== 'function') {\n throw new VttfError(\n 'VTTF-0002',\n 'foundry.applications.sheets.ActorSheetV2 and/or foundry.applications.api.HandlebarsApplicationMixin are not available. Define your BaseActorSheet subclasses inside the Foundry runtime (or stub the global in tests).',\n );\n }\n return { Base, mixin };\n}\n\nfunction resolveDragDrop(): DragDropCtor | undefined {\n const foundry = (globalThis as Record<string, unknown>).foundry as FoundryGlobal | undefined;\n const ctor = foundry?.applications?.ux?.DragDrop;\n return typeof ctor === 'function' ? ctor : undefined;\n}\n\nasync function resolveFromUuid(uuid: string): Promise<unknown> {\n const fn = (globalThis as Record<string, unknown>).fromUuid as\n | ((u: string) => Promise<unknown>)\n | undefined;\n if (typeof fn !== 'function') return null;\n return fn(uuid);\n}\n\ninterface DropPayload {\n readonly type?: string;\n readonly uuid?: string;\n}\n\n/**\n * Marker class that consumer CSS uses for scoping. Always present on every\n * VTTForge-derived sheet so rules like `.vttforge .actor-sheet { ... }` work.\n */\nexport const VTTFORGE_SHEET_CLASS = 'vttforge';\n\n/**\n * Build the `BaseActorSheet` for the current Foundry runtime. See module\n * header for the boilerplate this base eliminates.\n *\n * @example\n * ```ts\n * class CharacterSheet extends BaseActorSheet() {\n * static DEFAULT_OPTIONS = foundry.utils.mergeObject(\n * super.DEFAULT_OPTIONS,\n * { classes: ['my-system'], position: { width: 720 } },\n * );\n * static PARTS = { ... };\n * static TABS = {\n * primary: {\n * tabs: [\n * { id: 'features', group: 'primary', label: 'Features' },\n * { id: 'inventory', group: 'primary', label: 'Inventory' },\n * ],\n * initial: 'features',\n * },\n * };\n * static DRAG_DROP = [{ dragSelector: '.item[draggable=true]', dropSelector: null }];\n * async onDropItem(item, event) {\n * if (item.type !== 'weapon') return false;\n * // …fall through to super by returning undefined.\n * }\n * }\n * ```\n */\nexport function BaseActorSheet(): SheetBaseCtor {\n const { Base, mixin } = resolveBases();\n const Mixed = mixin(Base);\n\n class VttforgeBaseActorSheet extends Mixed {\n static readonly DEFAULT_OPTIONS = {\n classes: [VTTFORGE_SHEET_CLASS],\n window: { resizable: true },\n position: { width: 600, height: 700 },\n tag: 'form',\n form: { submitOnChange: true, closeOnSubmit: false },\n actions: { vttforgeTab: VttforgeBaseActorSheet._onTab },\n } as const;\n\n /**\n * Declarative DragDrop entries. Each becomes a\n * `foundry.applications.ux.DragDrop` instance bound in `_onRender`.\n * Subclasses override by re-declaring `static DRAG_DROP = [...]`.\n */\n static readonly DRAG_DROP: ReadonlyArray<DragDropConfig> = [];\n\n /**\n * Augment ApplicationV2's context with `tabs[group]` for sheets that\n * declare **multiple** `static TABS` groups. ApplicationV2 already\n * auto-populates `context.tabs` (keyed by tab id) for single-group\n * sheets — overriding that flat shape would force every consumer to\n * either unwrap or write `context.tabs.<group>.<tabId>` in templates.\n * Multi-group sheets get nested `context.tabs.<group>.<tabId>` because\n * ApplicationV2 returns `{}` for them by default.\n */\n async _prepareContext(options: unknown): Promise<Record<string, unknown>> {\n const superPrepare = (\n Mixed.prototype as {\n _prepareContext?: (options: unknown) => Promise<Record<string, unknown>>;\n }\n )._prepareContext;\n const context =\n typeof superPrepare === 'function'\n ? ((await superPrepare.call(this, options)) as Record<string, unknown>)\n : {};\n const tabsConfig = (this.constructor as { TABS?: Record<string, unknown> }).TABS;\n if (tabsConfig && typeof tabsConfig === 'object') {\n const groups = Object.keys(tabsConfig);\n if (groups.length > 1) {\n const prepareTabs = (this as { _prepareTabs?: (group: string) => unknown })._prepareTabs;\n const tabs: Record<string, unknown> = {};\n for (const group of groups) {\n tabs[group] = typeof prepareTabs === 'function' ? prepareTabs.call(this, group) : {};\n }\n context.tabs = tabs;\n }\n }\n return context;\n }\n\n /**\n * Default `tab` action handler. ApplicationV2 doesn't ship one, so every\n * sheet that uses `<button data-action=\"tab\" data-tab=… data-group=…>`\n * has to wire its own. We toggle the `.active` class on the matching\n * nav element (`[data-action=\"tab\"][data-tab=…][data-group=…]`) and\n * on `section.tab[data-tab=…][data-group=…]`, then update\n * `sheet.tabGroups[group]` so subsequent re-renders pick the right\n * initial tab.\n *\n * ApplicationV2's action dispatcher binds `this` to the sheet instance\n * at call time even though the handler is declared `static`.\n */\n static _onTab(_event: Event, target: HTMLElement): void {\n // biome-ignore lint/complexity/noThisInStatic: ApplicationV2 binds `this` to the sheet instance at call time — wrap the cast in a single line so biome's auto-fix can't rewrite downstream references to the class name\n const sheet = this as unknown as { tabGroups: Record<string, string>; element?: HTMLElement };\n const group = target.dataset?.group;\n const tab = target.dataset?.tab;\n if (!group || !tab) return;\n sheet.tabGroups[group] = tab;\n const root = sheet.element;\n if (!root) return;\n for (const link of root.querySelectorAll<HTMLElement>(\n `[data-action=\"vttforgeTab\"][data-group=\"${group}\"]`,\n )) {\n link.classList.toggle('active', link.dataset.tab === tab);\n }\n for (const section of root.querySelectorAll<HTMLElement>(\n `section.tab[data-group=\"${group}\"]`,\n )) {\n section.classList.toggle('active', section.dataset.tab === tab);\n }\n }\n\n /**\n * Wire each `static DRAG_DROP` entry into a real `DragDrop` instance.\n * Permissions default to `this.isEditable`; callbacks default to\n * `_onDragStart` / `_onDrop`. Subclasses extending `_onRender` MUST call\n * `super._onRender(context, options)` to keep DragDrop wired.\n */\n _onRender(context: unknown, options: unknown): void {\n const superRender = (\n Mixed.prototype as { _onRender?: (context: unknown, options: unknown) => void }\n )._onRender;\n if (typeof superRender === 'function') {\n superRender.call(this, context, options);\n }\n const configs = (this.constructor as { DRAG_DROP?: ReadonlyArray<DragDropConfig> }).DRAG_DROP;\n if (!configs?.length) return;\n const DragDrop = resolveDragDrop();\n if (!DragDrop) return;\n const element = (this as { element?: HTMLElement }).element;\n if (!element) return;\n const onDragStart = (this as { _onDragStart?: (event: DragEvent) => void })._onDragStart;\n const onDrop = (this as { _onDrop?: (event: DragEvent) => void })._onDrop;\n for (const cfg of configs) {\n new DragDrop({\n dragSelector: cfg.dragSelector,\n dropSelector: cfg.dropSelector,\n permissions: {\n dragstart: cfg.permissions?.dragstart ?? (() => this.#isEditable()),\n drop: cfg.permissions?.drop ?? (() => this.#isEditable()),\n },\n callbacks: {\n ...(typeof onDragStart === 'function' ? { dragstart: onDragStart.bind(this) } : {}),\n ...(typeof onDrop === 'function' ? { drop: onDrop.bind(this) } : {}),\n ...(cfg.callbacks ?? {}),\n },\n }).bind(element);\n }\n }\n\n /**\n * Default drag handler — serialises the item identified by\n * `data-item-id` on the drag source element. Override for richer payloads\n * (Actor drags, custom UUIDs).\n */\n _onDragStart(event: DragEvent): void {\n const target = event.currentTarget as HTMLElement | null;\n const itemId = target?.dataset?.itemId;\n if (!itemId || !event.dataTransfer) return;\n const items = (\n this as { document?: { items?: { get(id: string): { uuid: string } | undefined } } }\n ).document?.items;\n const item = items?.get(itemId);\n if (!item) return;\n event.dataTransfer.setData(\n 'application/json',\n JSON.stringify({ type: 'Item', uuid: item.uuid }),\n );\n }\n\n /**\n * Typed drop sugar. Subclasses override this instead of `_onDropItem`\n * to skip the `fromUuid()` ceremony. Return `undefined` to fall through\n * to Foundry's default `_onDropItem`; return anything else to take\n * ownership of the drop.\n */\n async onDropItem(_item: unknown, _event: DragEvent): Promise<unknown> {\n return undefined;\n }\n async onDropActor(_actor: unknown, _event: DragEvent): Promise<unknown> {\n return undefined;\n }\n async onDropFolder(_folder: unknown, _event: DragEvent): Promise<unknown> {\n return undefined;\n }\n async onDropActiveEffect(_effect: unknown, _event: DragEvent): Promise<unknown> {\n return undefined;\n }\n\n async _onDropItem(event: DragEvent, data: DropPayload): Promise<unknown> {\n return this.#dispatchDrop('_onDropItem', 'onDropItem', event, data);\n }\n async _onDropActor(event: DragEvent, data: DropPayload): Promise<unknown> {\n return this.#dispatchDrop('_onDropActor', 'onDropActor', event, data);\n }\n async _onDropFolder(event: DragEvent, data: DropPayload): Promise<unknown> {\n return this.#dispatchDrop('_onDropFolder', 'onDropFolder', event, data);\n }\n async _onDropActiveEffect(event: DragEvent, data: DropPayload): Promise<unknown> {\n return this.#dispatchDrop('_onDropActiveEffect', 'onDropActiveEffect', event, data);\n }\n\n async #dispatchDrop(\n superKey: '_onDropItem' | '_onDropActor' | '_onDropFolder' | '_onDropActiveEffect',\n sugarKey: 'onDropItem' | 'onDropActor' | 'onDropFolder' | 'onDropActiveEffect',\n event: DragEvent,\n data: DropPayload,\n ): Promise<unknown> {\n const uuid = data?.uuid;\n if (uuid) {\n const doc = await resolveFromUuid(uuid);\n if (doc) {\n const result = await (\n this as unknown as Record<\n typeof sugarKey,\n (doc: unknown, event: DragEvent) => Promise<unknown>\n >\n )[sugarKey](doc, event);\n if (result !== undefined) return result;\n }\n }\n const superFn = (Mixed.prototype as Record<typeof superKey, unknown>)[superKey] as\n | ((event: DragEvent, data: DropPayload) => Promise<unknown>)\n | undefined;\n if (typeof superFn === 'function') return superFn.call(this, event, data);\n return undefined;\n }\n\n #isEditable(): boolean {\n return Boolean((this as { isEditable?: boolean }).isEditable);\n }\n }\n\n return VttforgeBaseActorSheet as unknown as SheetBaseCtor;\n}\n","/**\n * BaseApplication — a plain `ApplicationV2` window, minus the two traps.\n *\n * The document sheets are covered by `BaseActorSheet` and `BaseItemSheet`.\n * Everything else a package puts on screen — a config dialog, a picker, a\n * reader window — is a bare `ApplicationV2`, and writing one by hand means\n * meeting both of these:\n *\n * **`_replaceHTML` is easy to forget.** ApplicationV2 splits rendering in two:\n * `_renderHTML` builds the content and `_replaceHTML` puts it in the window.\n * Implement only the first and the class is silently unrenderable — Foundry\n * says so at the moment something tries to open it, not when it is defined.\n * Nearly every implementation of the second is the same line, so this ships\n * it. Override it when the window updates in place instead of wholesale.\n *\n * **A missing `_renderHTML` fails late.** Foundry's own check fires on first\n * render, which in practice means a user clicks something and gets an error\n * about abstract methods. This checks at construction, so it fails where the\n * class is used rather than deep inside a render.\n */\n\nimport { VttfError } from './errors/registry.js';\nimport type { ApplicationV2Members, VttforgeClass } from './foundry-base.js';\n\n// biome-ignore lint/suspicious/noExplicitAny: Foundry's ApplicationV2 shape lives in fvtt-types (deferred to @vttforge/types v1.0)\ntype AnyConstructor = new (...args: any[]) => any;\n\ninterface FoundryApplicationsApi {\n ApplicationV2?: AnyConstructor;\n}\n\ninterface FoundryRoot {\n readonly applications?: { readonly api?: FoundryApplicationsApi };\n}\n\nfunction resolveApplicationV2(): AnyConstructor {\n const foundry = (globalThis as Record<string, unknown>).foundry as FoundryRoot | undefined;\n const cls = foundry?.applications?.api?.ApplicationV2;\n if (typeof cls !== 'function') {\n throw new VttfError(\n 'VTTF-0002',\n 'foundry.applications.api.ApplicationV2 is not available. Define your BaseApplication subclasses inside the Foundry runtime (or stub the global in tests).',\n );\n }\n return cls;\n}\n\n/**\n * Build an `ApplicationV2` base with the rendering contract filled in.\n *\n * ```ts\n * class PdfConfig extends BaseApplication() {\n * async _renderHTML() {\n * const form = document.createElement('form');\n * // …\n * return form;\n * }\n * }\n * ```\n *\n * `_replaceHTML` is provided. `_renderHTML` is yours, and omitting it throws\n * when the class is constructed rather than when someone opens the window.\n */\n/** What `BaseApplication` adds on top of Foundry's `ApplicationV2`. */\nexport interface BaseApplicationMembers {\n /**\n * Put the rendered content in the window.\n *\n * Provided because it is the half people forget. Override it for a window\n * that updates in place rather than swapping its whole content.\n */\n _replaceHTML(result: HTMLElement, content: HTMLElement): void;\n}\n\nexport function BaseApplication(): VttforgeClass<\n BaseApplicationMembers,\n unknown,\n ApplicationV2Members\n> {\n const Base = resolveApplicationV2();\n\n class VttforgeBaseApplication extends Base {\n // biome-ignore lint/suspicious/noExplicitAny: forwards Foundry's own constructor arity\n constructor(...args: any[]) {\n super(...args);\n if (typeof (this as { _renderHTML?: unknown })._renderHTML !== 'function') {\n throw new VttfError(\n 'VTTF-0002',\n `${this.constructor.name} extends BaseApplication but does not implement _renderHTML. ApplicationV2 cannot render without it.`,\n );\n }\n }\n\n /**\n * Put the rendered content in the window.\n *\n * The whole-content swap, which is what almost every window wants.\n * Override to update in place — a viewer that keeps scroll position\n * across a page turn, say.\n */\n _replaceHTML(result: HTMLElement, content: HTMLElement): void {\n content.replaceChildren(result);\n }\n }\n\n return VttforgeBaseApplication as unknown as VttforgeClass<\n BaseApplicationMembers,\n unknown,\n ApplicationV2Members\n >;\n}\n","/**\n * BaseDocumentSheet — a document sheet that builds its own DOM.\n *\n * `BaseActorSheet` and `BaseItemSheet` are `HandlebarsApplicationMixin`\n * baselines, which is right for the common case: declare `static PARTS`, write\n * templates, let the mixin render them.\n *\n * It is wrong for a sheet whose content is not a template. A canvas, an\n * embedded PDF, a Svelte or Lit mount — those build an element and hand it\n * over. Extending the Handlebars baseline for one of those does not fail\n * loudly; the mixin's `_replaceHTML` expects a map of part id to markup,\n * receives an element instead, and quietly renders nothing. The window opens\n * empty, or does not open at all, and no error names the mismatch.\n *\n * So this is the same document-sheet plumbing without the mixin, with the\n * `_renderHTML` / `_replaceHTML` contract `BaseApplication` provides.\n */\n\nimport { VttfError } from './errors/registry.js';\nimport type { DocumentSheetV2Members, VttforgeClass } from './foundry-base.js';\n\n// biome-ignore lint/suspicious/noExplicitAny: Foundry's sheet shape lives in fvtt-types (deferred to @vttforge/types v1.0)\ntype AnyConstructor = new (...args: any[]) => any;\n\ninterface FoundryApplicationsSheets {\n ActorSheetV2?: AnyConstructor;\n ItemSheetV2?: AnyConstructor;\n}\n\ninterface FoundryRoot {\n readonly applications?: { readonly sheets?: FoundryApplicationsSheets };\n}\n\n/** Which document the sheet is for. */\nexport type DocumentSheetKind = 'Actor' | 'Item';\n\n/** What `BaseDocumentSheet` adds. */\nexport interface BaseDocumentSheetMembers {\n /**\n * Put the rendered content in the window.\n *\n * The whole-content swap. Override for a sheet that updates in place —\n * a viewer that keeps its scroll position across a page turn, say.\n */\n _replaceHTML(result: HTMLElement, content: HTMLElement): void;\n}\n\nfunction resolveSheetBase(kind: DocumentSheetKind): AnyConstructor {\n const foundry = (globalThis as Record<string, unknown>).foundry as FoundryRoot | undefined;\n const cls = foundry?.applications?.sheets?.[`${kind}SheetV2`];\n if (typeof cls !== 'function') {\n throw new VttfError(\n 'VTTF-0002',\n `foundry.applications.sheets.${kind}SheetV2 is not available. Define your BaseDocumentSheet subclasses inside the Foundry runtime (or stub the global in tests).`,\n );\n }\n return cls;\n}\n\n/**\n * Build a document sheet base that renders an element rather than templates.\n *\n * ```ts\n * class PdfActorSheet extends BaseDocumentSheet('Actor') {\n * async _renderHTML() {\n * const canvas = document.createElement('canvas');\n * // …draw the page…\n * return canvas;\n * }\n * }\n * ```\n *\n * Reach for `BaseActorSheet` or `BaseItemSheet` instead when the sheet is\n * templates and `static PARTS`, which is most of them.\n */\nexport function BaseDocumentSheet(\n kind: DocumentSheetKind,\n): VttforgeClass<BaseDocumentSheetMembers, unknown, DocumentSheetV2Members> {\n const Base = resolveSheetBase(kind);\n\n class VttforgeBaseDocumentSheet extends Base {\n // biome-ignore lint/suspicious/noExplicitAny: forwards Foundry's (data, context) pair\n constructor(...args: any[]) {\n super(...args);\n if (typeof (this as { _renderHTML?: unknown })._renderHTML !== 'function') {\n throw new VttfError(\n 'VTTF-0002',\n `${this.constructor.name} extends BaseDocumentSheet but does not implement _renderHTML. A sheet that renders no element renders nothing.`,\n );\n }\n }\n\n _replaceHTML(result: HTMLElement, content: HTMLElement): void {\n content.replaceChildren(result);\n }\n }\n\n return VttforgeBaseDocumentSheet as unknown as VttforgeClass<\n BaseDocumentSheetMembers,\n unknown,\n DocumentSheetV2Members\n >;\n}\n","/**\n * BaseItemSheet — `ItemSheetV2 + HandlebarsApplicationMixin` baseline. Mirror\n * of `BaseActorSheet` minus the typed drop dispatch (items rarely receive\n * drops; the rare case that does can override `_onDrop` directly).\n *\n * Carries the same boilerplate-eliminators:\n *\n * - `static DRAG_DROP` — declarative `foundry.applications.ux.DragDrop` wiring\n * in `_onRender`, with `isEditable`-gated permissions.\n * - `_prepareContext` auto-fills `context.tabs[group]` for every group declared\n * in ApplicationV2's `static TABS`.\n *\n * As with `BaseActorSheet`, `editImage` is intentionally not added — it ships\n * built-in on `DocumentSheetV2` (parent of `ItemSheetV2`).\n */\n\nimport type { DragDropConfig, SheetBaseCtor } from './base-actor-sheet.js';\nimport { VTTFORGE_SHEET_CLASS } from './base-actor-sheet.js';\nimport { VttfError } from './errors/registry.js';\n\n// biome-ignore lint/suspicious/noExplicitAny: Foundry's ItemSheetV2 shape lives in fvtt-types (deferred to @vttforge/types v1.0)\ntype AnyConstructor = new (...args: any[]) => any;\n\ninterface DragDropInstance {\n bind(element: HTMLElement): void;\n}\n\ninterface DragDropCtor {\n new (config: Record<string, unknown>): DragDropInstance;\n}\n\ninterface FoundryApplicationsApi {\n HandlebarsApplicationMixin?: (base: AnyConstructor) => AnyConstructor;\n}\n\ninterface FoundryApplicationsSheets {\n ItemSheetV2?: AnyConstructor;\n}\n\ninterface FoundryApplicationsUx {\n DragDrop?: DragDropCtor;\n}\n\ninterface FoundryApplications {\n api?: FoundryApplicationsApi;\n sheets?: FoundryApplicationsSheets;\n ux?: FoundryApplicationsUx;\n}\n\ninterface FoundryGlobal {\n applications?: FoundryApplications;\n}\n\nfunction resolveBases(): { Base: AnyConstructor; mixin: (b: AnyConstructor) => AnyConstructor } {\n const foundry = (globalThis as Record<string, unknown>).foundry as FoundryGlobal | undefined;\n const Base = foundry?.applications?.sheets?.ItemSheetV2;\n const mixin = foundry?.applications?.api?.HandlebarsApplicationMixin;\n if (typeof Base !== 'function' || typeof mixin !== 'function') {\n throw new VttfError(\n 'VTTF-0002',\n 'foundry.applications.sheets.ItemSheetV2 and/or foundry.applications.api.HandlebarsApplicationMixin are not available. Define your BaseItemSheet subclasses inside the Foundry runtime (or stub the global in tests).',\n );\n }\n return { Base, mixin };\n}\n\nfunction resolveDragDrop(): DragDropCtor | undefined {\n const foundry = (globalThis as Record<string, unknown>).foundry as FoundryGlobal | undefined;\n const ctor = foundry?.applications?.ux?.DragDrop;\n return typeof ctor === 'function' ? ctor : undefined;\n}\n\n/**\n * Build the `BaseItemSheet` for the current Foundry runtime.\n *\n * @example\n * ```ts\n * class WeaponSheet extends BaseItemSheet() {\n * static DEFAULT_OPTIONS = foundry.utils.mergeObject(\n * super.DEFAULT_OPTIONS,\n * { classes: ['my-system'], position: { width: 540 } },\n * );\n * static PARTS = { ... };\n * static TABS = {\n * primary: {\n * tabs: [\n * { id: 'description', group: 'primary', label: 'Description' },\n * { id: 'details', group: 'primary', label: 'Details' },\n * ],\n * initial: 'description',\n * },\n * };\n * }\n * ```\n */\nexport function BaseItemSheet(): SheetBaseCtor {\n const { Base, mixin } = resolveBases();\n const Mixed = mixin(Base);\n\n class VttforgeBaseItemSheet extends Mixed {\n static readonly DEFAULT_OPTIONS = {\n classes: [VTTFORGE_SHEET_CLASS],\n window: { resizable: true },\n position: { width: 520, height: 480 },\n tag: 'form',\n form: { submitOnChange: true, closeOnSubmit: false },\n actions: { vttforgeTab: VttforgeBaseItemSheet._onTab },\n } as const;\n\n static readonly DRAG_DROP: ReadonlyArray<DragDropConfig> = [];\n\n /**\n * Multi-group sheets get nested `context.tabs.<group>.<tabId>` because\n * ApplicationV2 returns `{}` for them by default; single-group sheets\n * use ApplicationV2's flat `context.tabs.<tabId>` shape untouched. See\n * BaseActorSheet for the long version.\n */\n async _prepareContext(options: unknown): Promise<Record<string, unknown>> {\n const superPrepare = (\n Mixed.prototype as {\n _prepareContext?: (options: unknown) => Promise<Record<string, unknown>>;\n }\n )._prepareContext;\n const context =\n typeof superPrepare === 'function'\n ? ((await superPrepare.call(this, options)) as Record<string, unknown>)\n : {};\n const tabsConfig = (this.constructor as { TABS?: Record<string, unknown> }).TABS;\n if (tabsConfig && typeof tabsConfig === 'object') {\n const groups = Object.keys(tabsConfig);\n if (groups.length > 1) {\n const prepareTabs = (this as { _prepareTabs?: (group: string) => unknown })._prepareTabs;\n const tabs: Record<string, unknown> = {};\n for (const group of groups) {\n tabs[group] = typeof prepareTabs === 'function' ? prepareTabs.call(this, group) : {};\n }\n context.tabs = tabs;\n }\n }\n return context;\n }\n\n static _onTab(_event: Event, target: HTMLElement): void {\n // biome-ignore lint/complexity/noThisInStatic: ApplicationV2 binds `this` to the sheet instance at call time\n const sheet = this as unknown as { tabGroups: Record<string, string>; element?: HTMLElement };\n const group = target.dataset?.group;\n const tab = target.dataset?.tab;\n if (!group || !tab) return;\n sheet.tabGroups[group] = tab;\n const root = sheet.element;\n if (!root) return;\n for (const link of root.querySelectorAll<HTMLElement>(\n `[data-action=\"vttforgeTab\"][data-group=\"${group}\"]`,\n )) {\n link.classList.toggle('active', link.dataset.tab === tab);\n }\n for (const section of root.querySelectorAll<HTMLElement>(\n `section.tab[data-group=\"${group}\"]`,\n )) {\n section.classList.toggle('active', section.dataset.tab === tab);\n }\n }\n\n _onRender(context: unknown, options: unknown): void {\n const superRender = (\n Mixed.prototype as { _onRender?: (context: unknown, options: unknown) => void }\n )._onRender;\n if (typeof superRender === 'function') {\n superRender.call(this, context, options);\n }\n const configs = (this.constructor as { DRAG_DROP?: ReadonlyArray<DragDropConfig> }).DRAG_DROP;\n if (!configs?.length) return;\n const DragDrop = resolveDragDrop();\n if (!DragDrop) return;\n const element = (this as { element?: HTMLElement }).element;\n if (!element) return;\n const onDragStart = (this as { _onDragStart?: (event: DragEvent) => void })._onDragStart;\n const onDrop = (this as { _onDrop?: (event: DragEvent) => void })._onDrop;\n for (const cfg of configs) {\n new DragDrop({\n dragSelector: cfg.dragSelector,\n dropSelector: cfg.dropSelector,\n permissions: {\n dragstart: cfg.permissions?.dragstart ?? (() => this.#isEditable()),\n drop: cfg.permissions?.drop ?? (() => this.#isEditable()),\n },\n callbacks: {\n ...(typeof onDragStart === 'function' ? { dragstart: onDragStart.bind(this) } : {}),\n ...(typeof onDrop === 'function' ? { drop: onDrop.bind(this) } : {}),\n ...(cfg.callbacks ?? {}),\n },\n }).bind(element);\n }\n }\n\n #isEditable(): boolean {\n return Boolean((this as { isEditable?: boolean }).isEditable);\n }\n }\n\n return VttforgeBaseItemSheet as unknown as SheetBaseCtor;\n}\n","/**\n * BaseTypeDataModel — minimal extension of `foundry.abstract.TypeDataModel`.\n *\n * Provides safe defaults that systems usually copy-paste anyway:\n *\n * - `migrateData()` calls `super.migrateData(data)` — every TypeDataModel\n * must do this so chained migrations from base classes still run.\n * - `prepareBaseData()` is a no-op stub — override to initialize fields that\n * Active Effects need to mutate (e.g. base max HP before AE bonus). Foundry\n * applies Active Effects between `prepareBaseData()` and `prepareDerivedData()`,\n * so anything you compute here is the input AEs see.\n * - `prepareDerivedData()` is a no-op stub — override for computed values\n * that depend on AE-mutated state (modifiers, percentages, totals).\n *\n * Subclasses still own `defineSchema()` because there is no useful default —\n * we never invent a schema for you.\n *\n * Resolves the base class from `globalThis.foundry.abstract.TypeDataModel` at\n * runtime. In tests, the test harness installs a stub; in Foundry, the global\n * exists by the time this module runs (we are loaded from system esmodules).\n */\n\nimport type { FieldInstance } from './data/fields.js';\nimport type { InferSchema } from './data/infer-schema.js';\nimport { VttfError } from './errors/registry.js';\nimport type { VttforgeClass } from './foundry-base.js';\n\n// biome-ignore lint/suspicious/noExplicitAny: we mix into Foundry's TypeDataModel whose shape lives in fvtt-types (deferred to @vttforge/types v1.0)\ntype AnyConstructor = new (...args: any[]) => any;\n\nfunction resolveTypeDataModelClass(): AnyConstructor {\n const foundry = (globalThis as Record<string, unknown>).foundry as\n | { abstract?: { TypeDataModel?: AnyConstructor } }\n | undefined;\n const cls = foundry?.abstract?.TypeDataModel;\n if (typeof cls !== 'function') {\n throw new VttfError(\n 'VTTF-0002',\n 'foundry.abstract.TypeDataModel is not available. Define your BaseTypeDataModel subclasses inside the Foundry runtime (or stub the global in tests).',\n );\n }\n return cls;\n}\n\n/**\n * Resolve the runtime base class, then build a mixin that adds VTTForge defaults.\n *\n * Why a function: subclasses are declared once at module load, but Foundry\n * globals may not exist yet (test boot, ESM hoist). Calling `BaseTypeDataModel()`\n * lazy-resolves the global at the moment of subclassing.\n */\n/** The two hooks this base fills in, so a subclass can omit either. */\nexport interface TypeDataModelHooks {\n prepareBaseData(): void;\n prepareDerivedData(): void;\n}\n\n/**\n * What an instance looks like when the schema is known.\n *\n * The schema's fields ARE the instance properties — inside\n * `prepareDerivedData()` you read `this.level`, not `this.system.level`, and\n * `actor.system` is this instance.\n *\n * Derived values are not in the schema, so they are not here either. Declare\n * them on the subclass:\n *\n * ```ts\n * declare armorClass: number;\n * ```\n */\nexport type TypedTypeDataModel<S extends Record<string, FieldInstance>> = InferSchema<S> &\n TypeDataModelHooks & {\n /**\n * Phantom property carrying the schema's inferred shape. Never assigned,\n * never present at runtime — it exists so the type has a name:\n *\n * ```ts\n * type CharacterSystem = CharacterData['$inferData'];\n * ```\n */\n readonly $inferData: InferSchema<S>;\n };\n\nexport interface TypedTypeDataModelCtor<S extends Record<string, FieldInstance>> {\n // biome-ignore lint/suspicious/noExplicitAny: a subclass with its own constructor passes Foundry's (data, context) through to super\n new (...args: any[]): TypedTypeDataModel<S>;\n defineSchema(): S;\n migrateData(data: Record<string, unknown>): Record<string, unknown>;\n}\n\n/**\n * Build a base class with no knowledge of the schema.\n *\n * The hooks are typed; the schema's own fields are not, since nothing said\n * what they are. Pass your schema function instead to get those too.\n */\nexport function BaseTypeDataModel(): VttforgeClass<TypeDataModelHooks>;\n/**\n * Build a base class that knows its schema.\n *\n * Hand it the function that returns your fields and it implements\n * `static defineSchema()` for you, so the schema is written once:\n *\n * ```ts\n * const defineCharacterSchema = () => {\n * const f = fields();\n * return { level: new f.NumberField({ required: true, nullable: false, initial: 1 }) };\n * };\n *\n * class CharacterData extends BaseTypeDataModel(defineCharacterSchema) {\n * declare armorClass: number;\n * prepareDerivedData() {\n * this.armorClass = 10 + this.level; // this.level is number\n * }\n * }\n * ```\n *\n * It has to be a function, not an object: `fields()` reads a Foundry global\n * that does not exist when the module is first evaluated.\n *\n * A subclass may still declare its own `static defineSchema()`; that one wins,\n * the same as any other static.\n */\nexport function BaseTypeDataModel<S extends Record<string, FieldInstance>>(\n defineSchema: () => S,\n): TypedTypeDataModelCtor<S>;\nexport function BaseTypeDataModel(\n defineSchema?: () => Record<string, FieldInstance>,\n): AnyConstructor {\n const Base = resolveTypeDataModelClass();\n\n class VttforgeBaseTypeDataModel extends Base {\n /**\n * Default no-op so subclasses can omit it when they have no value-level\n * migrations. Always end with `super.migrateData(data)` if you override.\n */\n static migrateData(data: Record<string, unknown>): Record<string, unknown> {\n const superMigrateData = (\n Base as { migrateData?: (d: Record<string, unknown>) => Record<string, unknown> }\n ).migrateData;\n if (typeof superMigrateData === 'function') {\n return superMigrateData.call(VttforgeBaseTypeDataModel, data);\n }\n return data;\n }\n\n /**\n * No-op stub. Override per type to initialize fields whose values Active\n * Effects need to consume — base max HP, base AC, etc. Foundry calls this\n * BEFORE applying Active Effects, so anything you set here is the input\n * that AE changes (`ADD`, `MULTIPLY`, `OVERRIDE`, …) operate on.\n *\n * Use `prepareDerivedData()` instead for values that depend on the\n * AE-mutated state (modifiers, percentages, totals).\n *\n * Never write to the database here — purely in-memory.\n */\n prepareBaseData(): void {\n // override me\n }\n\n /**\n * No-op stub. Override per type to compute derived values from the\n * AE-mutated state (modifiers, percentages, totals). Runs AFTER Active\n * Effects apply; use `prepareBaseData()` for values that AEs need to read.\n *\n * Never write to the database here — purely in-memory.\n */\n prepareDerivedData(): void {\n // override me\n }\n }\n\n if (defineSchema !== undefined) {\n // Assigned rather than declared in the class body so the no-argument form\n // keeps inheriting Foundry's own defineSchema instead of shadowing it\n // with one that returns nothing.\n Object.defineProperty(VttforgeBaseTypeDataModel, 'defineSchema', {\n value: defineSchema,\n writable: true,\n configurable: true,\n });\n }\n\n return VttforgeBaseTypeDataModel as unknown as AnyConstructor;\n}\n","/**\n * `fields()` — typed bag of Foundry v13 data-field constructors.\n *\n * Foundry idiom inside `defineSchema()` is `const f = foundry.data.fields`. We\n * mirror that, but routed through a factory so the import succeeds in Node\n * (tests, IDE typecheck) where the global is absent. The factory resolves\n * `globalThis.foundry.data.fields` lazily — same pattern as\n * `BaseTypeDataModel()` (see `base-type-data-model.ts:25`) and\n * `BaseActorSheet()` (see `base-actor-sheet.ts:40`).\n *\n * v0.1 covers eight fields (PRD §7): NumberField, StringField, BooleanField,\n * HTMLField, ArrayField, SchemaField, ColorField, FilePathField. The instance\n * interfaces carry a phantom `[BRAND]` tag and an `options` capture so the\n * conditional types in `./infer-schema.ts` can extract the runtime semantics\n * (e.g. `nullable: true`).\n *\n * `EmbeddedDataField`, `EmbeddedDocumentField`, `TypedSchemaField`, and the\n * full required×initial nullability matrix ship with `@vttforge/types` v1.0.\n */\n\nimport { VttfError } from '../errors/registry.js';\nimport type {\n ArrayFieldOptions,\n BooleanFieldOptions,\n ColorFieldOptions,\n EmbeddedDataFieldOptions,\n EmbeddedDocumentFieldOptions,\n FilePathFieldOptions,\n ForeignDocumentFieldOptions,\n HTMLFieldOptions,\n NumberFieldOptions,\n SchemaFieldOptions,\n SetFieldOptions,\n StringFieldOptions,\n TypedSchemaFieldOptions,\n} from './field-options.js';\n\ndeclare const BRAND: unique symbol;\n\n/**\n * Anything that satisfies the `FieldInstance` shape — used as the inner-field\n * constraint on `ArrayField` and as the value type of `SchemaField`'s child\n * map. Keeps the conditional types in `./infer-schema.ts` straightforward.\n */\nexport interface FieldInstance {\n readonly [BRAND]: string;\n readonly options: unknown;\n}\n\nexport interface NumberFieldInstance<O extends NumberFieldOptions = NumberFieldOptions>\n extends FieldInstance {\n readonly [BRAND]: 'number';\n readonly options: O;\n}\n\nexport interface StringFieldInstance<O extends StringFieldOptions = StringFieldOptions>\n extends FieldInstance {\n readonly [BRAND]: 'string';\n readonly options: O;\n}\n\nexport interface BooleanFieldInstance<O extends BooleanFieldOptions = BooleanFieldOptions>\n extends FieldInstance {\n readonly [BRAND]: 'boolean';\n readonly options: O;\n}\n\nexport interface HTMLFieldInstance<O extends HTMLFieldOptions = HTMLFieldOptions>\n extends FieldInstance {\n readonly [BRAND]: 'html';\n readonly options: O;\n}\n\nexport interface ColorFieldInstance<O extends ColorFieldOptions = ColorFieldOptions>\n extends FieldInstance {\n readonly [BRAND]: 'color';\n readonly options: O;\n}\n\nexport interface FilePathFieldInstance<O extends FilePathFieldOptions = FilePathFieldOptions>\n extends FieldInstance {\n readonly [BRAND]: 'filePath';\n readonly options: O;\n}\n\nexport interface ArrayFieldInstance<\n Inner extends FieldInstance = FieldInstance,\n O extends ArrayFieldOptions = ArrayFieldOptions,\n> extends FieldInstance {\n readonly [BRAND]: 'array';\n readonly element: Inner;\n readonly options: O;\n}\n\n/**\n * A `SetField` holds a `Set`, not an array.\n *\n * It extends `ArrayField` and validates the same way, but `initialize`\n * wraps the result in `new Set(...)` — so a schema that declares one and\n * types it as an array gets `.push` and index access from the compiler on a\n * value that has neither.\n */\nexport interface SetFieldInstance<\n Inner extends FieldInstance = FieldInstance,\n O extends SetFieldOptions = SetFieldOptions,\n> extends FieldInstance {\n readonly [BRAND]: 'set';\n readonly element: Inner;\n readonly options: O;\n}\n\n/**\n * A reference to another document, stored as its id.\n *\n * What you read back depends on `idOnly`. With it, the id string. Without\n * it, the document itself: the field resolves to a getter, so reading the\n * property looks the document up in its collection and hands back the\n * instance — or `null` when it is gone or lives in a compendium.\n *\n * The field is nullable by default, so both shapes admit `null`.\n */\nexport interface ForeignDocumentFieldInstance<\n Doc extends DocumentClass = DocumentClass,\n O extends ForeignDocumentFieldOptions = ForeignDocumentFieldOptions,\n> extends FieldInstance {\n readonly [BRAND]: 'foreignDocument';\n readonly model: Doc;\n readonly options: O;\n}\n\n/**\n * A nested data model.\n *\n * It is a `SchemaField` built from the model class's own `defineSchema()`, so\n * the value is an instance of that model — not a plain object. Reading it\n * gives you the model's derived data and methods too.\n */\nexport interface EmbeddedDataFieldInstance<\n Model extends DataModelClass = DataModelClass,\n O extends EmbeddedDataFieldOptions = EmbeddedDataFieldOptions,\n> extends FieldInstance {\n readonly [BRAND]: 'embeddedData';\n readonly model: Model;\n readonly options: O;\n}\n\n/**\n * A single embedded document, stored inline.\n *\n * Like `EmbeddedDataField` but for a Document class, and nullable by default:\n * the field's own defaults turn `nullable` on, so an absent one reads `null`.\n */\nexport interface EmbeddedDocumentFieldInstance<\n Doc extends DataModelClass = DataModelClass,\n O extends EmbeddedDocumentFieldOptions = EmbeddedDocumentFieldOptions,\n> extends FieldInstance {\n readonly [BRAND]: 'embeddedDocument';\n readonly model: Doc;\n readonly options: O;\n}\n\n/**\n * One of several shapes, told apart by a `type` property.\n *\n * Each entry becomes its own SchemaField. When an entry does not declare a\n * `type` field, the field adds one — a required string whose value must equal\n * that entry's key — which is what makes the result a discriminated union you\n * can narrow on.\n */\nexport interface TypedSchemaFieldInstance<\n T extends Record<string, Record<string, FieldInstance>> = Record<\n string,\n Record<string, FieldInstance>\n >,\n O extends TypedSchemaFieldOptions = TypedSchemaFieldOptions,\n> extends FieldInstance {\n readonly [BRAND]: 'typedSchema';\n readonly types: T;\n readonly options: O;\n}\n\nexport interface SchemaFieldInstance<\n S extends Record<string, FieldInstance> = Record<string, FieldInstance>,\n O extends SchemaFieldOptions = SchemaFieldOptions,\n> extends FieldInstance {\n readonly [BRAND]: 'schema';\n readonly fields: S;\n readonly options: O;\n}\n\nexport interface NumberFieldCtor {\n new <O extends NumberFieldOptions = NumberFieldOptions>(options?: O): NumberFieldInstance<O>;\n}\n\nexport interface StringFieldCtor {\n new <O extends StringFieldOptions = StringFieldOptions>(options?: O): StringFieldInstance<O>;\n}\n\nexport interface BooleanFieldCtor {\n new <O extends BooleanFieldOptions = BooleanFieldOptions>(options?: O): BooleanFieldInstance<O>;\n}\n\nexport interface HTMLFieldCtor {\n new <O extends HTMLFieldOptions = HTMLFieldOptions>(options?: O): HTMLFieldInstance<O>;\n}\n\nexport interface ColorFieldCtor {\n new <O extends ColorFieldOptions = ColorFieldOptions>(options?: O): ColorFieldInstance<O>;\n}\n\nexport interface FilePathFieldCtor {\n new <O extends FilePathFieldOptions = FilePathFieldOptions>(\n options?: O,\n ): FilePathFieldInstance<O>;\n}\n\nexport interface ArrayFieldCtor {\n new <Inner extends FieldInstance, O extends ArrayFieldOptions = ArrayFieldOptions>(\n element: Inner,\n options?: O,\n ): ArrayFieldInstance<Inner, O>;\n}\n\nexport interface SetFieldCtor {\n new <Inner extends FieldInstance, O extends SetFieldOptions = SetFieldOptions>(\n element: Inner,\n options?: O,\n ): SetFieldInstance<Inner, O>;\n}\n\n/**\n * Any document class — what `ForeignDocumentField` takes as its first\n * argument. Declared structurally so the inference surface stays free of a\n * dependency on a Foundry type package.\n */\nexport type DocumentClass = abstract new (...args: never[]) => object;\n\nexport interface ForeignDocumentFieldCtor {\n new <\n Doc extends DocumentClass,\n O extends ForeignDocumentFieldOptions = ForeignDocumentFieldOptions,\n >(\n model: Doc,\n options?: O,\n ): ForeignDocumentFieldInstance<Doc, O>;\n}\n\n/** Any DataModel subclass — what the embedded fields take as their type. */\nexport type DataModelClass = abstract new (...args: never[]) => object;\n\nexport interface EmbeddedDataFieldCtor {\n new <Model extends DataModelClass, O extends EmbeddedDataFieldOptions = EmbeddedDataFieldOptions>(\n model: Model,\n options?: O,\n ): EmbeddedDataFieldInstance<Model, O>;\n}\n\nexport interface EmbeddedDocumentFieldCtor {\n new <\n Doc extends DataModelClass,\n O extends EmbeddedDocumentFieldOptions = EmbeddedDocumentFieldOptions,\n >(\n model: Doc,\n options?: O,\n ): EmbeddedDocumentFieldInstance<Doc, O>;\n}\n\nexport interface TypedSchemaFieldCtor {\n new <\n T extends Record<string, Record<string, FieldInstance>>,\n O extends TypedSchemaFieldOptions = TypedSchemaFieldOptions,\n >(\n types: T,\n options?: O,\n ): TypedSchemaFieldInstance<T, O>;\n}\n\nexport interface SchemaFieldCtor {\n new <S extends Record<string, FieldInstance>, O extends SchemaFieldOptions = SchemaFieldOptions>(\n fields: S,\n options?: O,\n ): SchemaFieldInstance<S, O>;\n}\n\n/**\n * Typed bag returned by `fields()`. Each property is the corresponding\n * `foundry.data.fields.*` class — the runtime value is Foundry's own\n * constructor; the type is our overlay.\n */\nexport interface FieldsApi {\n readonly NumberField: NumberFieldCtor;\n readonly StringField: StringFieldCtor;\n readonly BooleanField: BooleanFieldCtor;\n readonly HTMLField: HTMLFieldCtor;\n readonly ColorField: ColorFieldCtor;\n readonly FilePathField: FilePathFieldCtor;\n readonly ArrayField: ArrayFieldCtor;\n readonly SetField: SetFieldCtor;\n readonly ForeignDocumentField: ForeignDocumentFieldCtor;\n readonly SchemaField: SchemaFieldCtor;\n readonly EmbeddedDataField: EmbeddedDataFieldCtor;\n readonly EmbeddedDocumentField: EmbeddedDocumentFieldCtor;\n readonly TypedSchemaField: TypedSchemaFieldCtor;\n}\n\ninterface FoundryDataNamespace {\n readonly fields?: Record<string, unknown>;\n}\n\ninterface FoundryRoot {\n readonly data?: FoundryDataNamespace;\n}\n\n/**\n * Resolve `globalThis.foundry.data.fields` and return it typed as `FieldsApi`.\n *\n * Call this inside `defineSchema()` (or any code that runs after Foundry's\n * `init` hook). Calling at module scope will throw when imported from Node\n * tests — the global only exists inside the Foundry runtime.\n *\n * @throws `VttfError` with code `VTTF-0002` when `foundry.data.fields` is\n * missing.\n */\nexport function fields(): FieldsApi {\n const foundry = (globalThis as Record<string, unknown>).foundry as FoundryRoot | undefined;\n const f = foundry?.data?.fields;\n if (f === undefined || f === null) {\n throw new VttfError(\n 'VTTF-0002',\n 'foundry.data.fields is not available. Call fields() inside the Foundry runtime (or stub the global in tests).',\n );\n }\n return f as unknown as FieldsApi;\n}\n","/**\n * Typed runtime view over the VTTF-NNNN registry.\n *\n * Same data as `listErrorEntries()` — the manifest wraps it in a versioned\n * envelope so external tooling (the v0.3 docs site, IDE extensions, lint\n * rules) has a stable shape to consume. The matching JSON projection is\n * emitted to `dist/errors-manifest.json` at build time by\n * `packages/core/scripts/codegen-errors.mjs`.\n */\n\nimport { listErrorEntries, type VttfErrorEntry } from './registry.js';\n\nexport const ERROR_MANIFEST_VERSION = 1 as const;\n\nexport interface ErrorManifest {\n readonly version: typeof ERROR_MANIFEST_VERSION;\n readonly package: '@vttforge/core';\n readonly entries: ReadonlyArray<VttfErrorEntry>;\n}\n\n/**\n * Snapshot the current registry as a manifest object. Recomputed on every\n * call — cheap (the registry is a frozen literal). For the JSON projection\n * shipped with the package, see `dist/errors-manifest.json`.\n */\nexport function getErrorManifest(): ErrorManifest {\n return {\n version: ERROR_MANIFEST_VERSION,\n package: '@vttforge/core',\n entries: listErrorEntries(),\n };\n}\n","/**\n * `createMigrationRunner` — declarative schema migrations for Foundry systems.\n *\n * Replaces the copy-pasted \"schemaVersion setting + Hooks.once('ready') +\n * isNewerVersion compare + sequential await\" pattern that every system\n * eventually grows on its own. The runner owns no hooks — call\n * `register()` from your `init` hook and `run()` from your `ready` hook\n * (gated by `game.user.isGM`).\n *\n * Versions are semver strings, compared with `foundry.utils.isNewerVersion`.\n * The data lives in a per-system world setting and lines up cleanly with\n * `system.json`'s `flags.<systemId>.needsMigrationVersion` /\n * `compatibleMigrationVersion`.\n *\n * Failures advance `schemaVersion` only past migrations that *completed* — a\n * mid-sequence throw leaves the world at the last successful version so the\n * retry on the next world load picks up exactly where it failed.\n */\n\nimport { VttfError } from '../errors/registry.js';\nimport type { GameSettingsApi } from '../foundry-globals.js';\nimport type {\n Migration,\n MigrationLogger,\n MigrationRunner,\n MigrationRunnerOptions,\n} from './types.js';\n\nconst DEFAULT_SETTING_KEY = 'schemaVersion';\nconst INITIAL_VERSION = '0.0.0';\n\ninterface FoundryUtilsApi {\n isNewerVersion?: (next: string, current: string) => boolean;\n}\n\ninterface FoundryUiNotifications {\n info?: (msg: string) => unknown;\n warn?: (msg: string) => unknown;\n error?: (msg: string) => unknown;\n}\n\nfunction resolveIsNewerVersion(): (next: string, current: string) => boolean {\n const foundry = (globalThis as Record<string, unknown>).foundry as\n | { utils?: FoundryUtilsApi }\n | undefined;\n const fn = foundry?.utils?.isNewerVersion;\n if (typeof fn === 'function') return fn;\n // Last-resort fallback for non-Foundry runtimes — naive numeric semver compare.\n // Real consumers always run inside Foundry where the proper comparator exists.\n return naiveIsNewerVersion;\n}\n\nfunction naiveIsNewerVersion(next: string, current: string): boolean {\n const parse = (v: string): number[] =>\n v.split('.').map((part) => {\n const n = Number.parseInt(part, 10);\n return Number.isNaN(n) ? 0 : n;\n });\n const a = parse(next);\n const b = parse(current);\n const len = Math.max(a.length, b.length);\n for (let i = 0; i < len; i++) {\n const ai = a[i] ?? 0;\n const bi = b[i] ?? 0;\n if (ai > bi) return true;\n if (ai < bi) return false;\n }\n return false;\n}\n\nfunction resolveSettings(): GameSettingsApi {\n const game = (globalThis as Record<string, unknown>).game as\n | { settings?: GameSettingsApi }\n | undefined;\n const settings = game?.settings;\n if (\n settings === undefined ||\n typeof settings.register !== 'function' ||\n typeof settings.get !== 'function' ||\n typeof settings.set !== 'function'\n ) {\n throw new VttfError(\n 'VTTF-0002',\n 'globalThis.game.settings is not available — call createMigrationRunner().register() inside the Foundry runtime (or pass an explicit settings adapter in MigrationRunnerOptions).',\n );\n }\n return settings;\n}\n\nfunction resolveLogger(): MigrationLogger {\n const ui = (globalThis as Record<string, unknown>).ui as\n | { notifications?: FoundryUiNotifications }\n | undefined;\n const notifications = ui?.notifications;\n return {\n info(message) {\n // biome-ignore lint/suspicious/noConsole: console.info is the only Foundry-portable info-level logger\n console.info(message);\n notifications?.info?.(message);\n },\n warn(message) {\n console.warn(message);\n notifications?.warn?.(message);\n },\n error(message) {\n console.error(message);\n notifications?.error?.(message);\n },\n };\n}\n\nfunction lastVersion(migrations: ReadonlyArray<Migration>): string {\n return migrations.at(-1)?.version ?? INITIAL_VERSION;\n}\n\nfunction assertAscending(\n migrations: ReadonlyArray<Migration>,\n isNewer: (next: string, current: string) => boolean,\n): void {\n for (let i = 1; i < migrations.length; i++) {\n const prevMig = migrations[i - 1];\n const nextMig = migrations[i];\n // Loop bounds guarantee both indices are valid; the explicit guard\n // exists to satisfy TypeScript's flow analysis without a non-null\n // assertion.\n if (prevMig === undefined || nextMig === undefined) continue;\n if (!isNewer(nextMig.version, prevMig.version)) {\n throw new VttfError(\n 'VTTF-0004',\n `Migration list out of order: ${nextMig.version} must be newer than ${prevMig.version}.`,\n );\n }\n }\n}\n\n/**\n * Build a migration runner for a system. See module header for the failure\n * semantics; see `Migration` JSDoc for the per-entry shape.\n *\n * @example\n * ```ts\n * const migrations = createMigrationRunner({\n * systemId: 'my-system',\n * migrations: [\n * { version: '1.0.0', description: 'Rename bio → biography', fn: migrateV1 },\n * { version: '2.0.0', description: 'Add hp.temp', fn: migrateV2 },\n * ],\n * compatibleVersion: '0.9.0',\n * });\n *\n * registerSystem({\n * id: 'my-system',\n * onAfterInit: () => migrations.register(),\n * onReady: async () => {\n * if (!game.user.isGM) return;\n * await migrations.run();\n * },\n * });\n * ```\n */\nexport function createMigrationRunner(options: MigrationRunnerOptions): MigrationRunner {\n const settingKey = options.settingKey ?? DEFAULT_SETTING_KEY;\n const target = lastVersion(options.migrations);\n const settingsOverride = options.settings;\n const loggerOverride = options.logger;\n const isNewerOverride = options.isNewerVersion;\n\n return {\n targetVersion: target,\n\n register(): void {\n const settings = settingsOverride ?? resolveSettings();\n settings.register<string>(options.systemId, settingKey, {\n name: 'Schema Version',\n hint: 'Internal schema version for VTTForge data migration tracking. Do not edit by hand.',\n scope: 'world',\n config: false,\n type: String,\n default: INITIAL_VERSION,\n });\n },\n\n async run(): Promise<ReadonlyArray<string>> {\n if (options.migrations.length === 0) return [];\n\n const settings = settingsOverride ?? resolveSettings();\n const logger = loggerOverride ?? resolveLogger();\n const isNewer = isNewerOverride ?? resolveIsNewerVersion();\n\n assertAscending(options.migrations, isNewer);\n\n const stored = settings.get<string>(options.systemId, settingKey);\n const current = stored ?? INITIAL_VERSION;\n\n if (options.compatibleVersion !== undefined) {\n if (isNewer(options.compatibleVersion, current)) {\n throw new VttfError(\n 'VTTF-0005',\n `World schemaVersion ${current} is older than ${options.systemId}'s compatibleVersion ${options.compatibleVersion}. Upgrade through an intermediate release first.`,\n );\n }\n }\n\n const pending = options.migrations.filter((m) => isNewer(m.version, current));\n if (pending.length === 0) return [];\n\n const ran: string[] = [];\n let lastApplied = current;\n logger.warn(\n `${options.systemId} | Running ${pending.length} pending migration(s) from ${current} to ${target}.`,\n );\n\n for (const migration of pending) {\n const label = migration.description\n ? `${migration.version} — ${migration.description}`\n : migration.version;\n logger.info(`${options.systemId} | Migrating to ${label}`);\n try {\n await migration.fn();\n } catch (cause) {\n if (isNewer(lastApplied, current)) {\n await settings.set(options.systemId, settingKey, lastApplied);\n }\n throw new VttfError(\n 'VTTF-0004',\n `Migration to ${label} failed for system \"${options.systemId}\". schemaVersion left at ${lastApplied}.`,\n { cause },\n );\n }\n await settings.set(options.systemId, settingKey, migration.version);\n lastApplied = migration.version;\n ran.push(migration.version);\n }\n\n logger.info(`${options.systemId} | Migration complete. schemaVersion = ${target}.`);\n return ran;\n },\n };\n}\n","/**\n * Text enricher registration.\n *\n * An enricher turns a pattern in any rich text field — chat, journals, item\n * descriptions — into markup. `@PDF[handbook|page=12]{Player's Handbook}`\n * becomes a link that opens the book at that page.\n *\n * `CONFIG.TextEditor.enrichers` is a plain array, so registering by hand is one\n * `push`. The reason this exists is that the array has four ways to accept an\n * entry and then do nothing with it, and Foundry names none of them:\n *\n * - **`onRender` without `id` never fires.** Foundry wraps enriched output in a\n * custom element only when both are present; without the wrapper there is\n * nothing to fire the callback from. The enricher still produces markup, so\n * the text looks right and only the behaviour is missing.\n * - **A duplicate `id` silently loses.** The wrapper stores the id as an\n * attribute and looks the enricher back up with `find` — first match wins.\n * Two packages using `link` means the first one's `onRender` runs against the\n * second one's markup. Only reproducible with both installed.\n * - **A pattern without the `g` flag throws.** Enrichment runs the pattern\n * through `matchAll`, which rejects a non-global regex, and that throw is\n * outside the handler Foundry wraps enrichers in.\n * - **The same id can collide with a system or another module**, because the\n * id lives in one flat namespace shared by everything installed.\n *\n * So ids are namespaced the way `moduleSubType` namespaces document types, and\n * the rest is checked when you register rather than when someone opens a chat\n * message.\n */\n\nimport { VttfError } from './errors/registry.js';\n\n/** The enriched element Foundry hands to `onRender`. */\nexport interface EnricherRegistration {\n /**\n * Name for this enricher, unique within the package.\n *\n * Registered as `<package id>.<id>`. Foundry keeps every enricher in one\n * namespace, so the prefix is what stops a `link` from colliding with the\n * `link` of whatever else the world has installed.\n */\n readonly id: string;\n\n /**\n * The pattern to match. Must carry the `g` flag — enrichment runs it through\n * `matchAll`, which refuses a non-global regex.\n */\n readonly pattern: RegExp;\n\n /**\n * Build the replacement for one match. Return `null` to leave the text\n * alone — the usual answer when the thing referenced does not exist or the\n * reader is not allowed to see it.\n */\n readonly enricher: (\n match: RegExpMatchArray,\n options?: unknown,\n ) => Promise<HTMLElement | null> | HTMLElement | null;\n\n /**\n * Runs when enriched content enters the DOM. The place to bind listeners:\n * it fires on every render, so the markup and its behaviour stay declared\n * together instead of in a separate hook.\n */\n readonly onRender?: (element: HTMLElement) => void;\n\n /** Replace the whole parent element rather than just the matched text. */\n readonly replaceParent?: boolean;\n}\n\ninterface FoundryEnricherEntry {\n id: string;\n pattern: RegExp;\n enricher: EnricherRegistration['enricher'];\n onRender?: EnricherRegistration['onRender'];\n replaceParent?: boolean;\n}\n\ninterface TextEditorConfig {\n enrichers?: FoundryEnricherEntry[];\n}\n\ninterface ConfigWithTextEditor {\n TextEditor?: TextEditorConfig;\n}\n\n/** Ids are one segment: Foundry joins the package id and this with a dot. */\nconst ID_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i;\n\nfunction assertValidId(id: string, packageId: string): void {\n if (!ID_PATTERN.test(id)) {\n throw new VttfError(\n 'VTTF-0007',\n `\"${id}\" is not a usable enricher id for \"${packageId}\". Use letters, digits and hyphens — no dots: the package id and the enricher id are joined with one.`,\n );\n }\n}\n\nfunction assertGlobalPattern(entry: EnricherRegistration, packageId: string): void {\n if (!entry.pattern.global) {\n throw new VttfError(\n 'VTTF-0007',\n `The pattern for enricher \"${packageId}.${entry.id}\" is missing the g flag. Enrichment matches with matchAll, which throws on a non-global regex — and it throws while rendering someone's chat message, not here.`,\n );\n }\n}\n\nfunction readEnrichers(): FoundryEnricherEntry[] {\n const config = (globalThis as Record<string, unknown>).CONFIG as ConfigWithTextEditor | undefined;\n const enrichers = config?.TextEditor?.enrichers;\n if (!Array.isArray(enrichers)) {\n throw new VttfError(\n 'VTTF-0002',\n 'CONFIG.TextEditor.enrichers is not available — register enrichers inside the Foundry runtime, or stub CONFIG in tests.',\n );\n }\n return enrichers;\n}\n\n/**\n * Register a package's text enrichers under namespaced ids.\n *\n * Called for you by `registerSystem` and `registerModule`; exported for a\n * package that registers enrichers outside either.\n */\nexport function registerEnrichers(\n packageId: string,\n enrichers: readonly EnricherRegistration[],\n): void {\n const seen = new Set<string>();\n for (const entry of enrichers) {\n assertValidId(entry.id, packageId);\n assertGlobalPattern(entry, packageId);\n if (seen.has(entry.id)) {\n throw new VttfError(\n 'VTTF-0007',\n `\"${packageId}\" registers two enrichers with the id \"${entry.id}\". Foundry looks an enricher up by id and takes the first match, so the second one's onRender would never run.`,\n );\n }\n seen.add(entry.id);\n }\n\n const registry = readEnrichers();\n for (const entry of enrichers) {\n registry.push({\n id: `${packageId}.${entry.id}`,\n pattern: entry.pattern,\n enricher: entry.enricher,\n onRender: entry.onRender,\n replaceParent: entry.replaceParent,\n });\n }\n}\n","/**\n * Sheet registration with an id that survives a rebuild.\n *\n * Foundry keys a registered sheet by `${scope}.${sheetClass.name}` and writes\n * that key to `flags.core.sheetClass` on every document whose owner picked the\n * sheet. So the key is persisted user data, derived from a JavaScript class\n * name.\n *\n * That is fine for an unbundled package and quietly broken for a bundled one.\n * A minifier renames classes, and it does not promise the same name twice: the\n * same sheet registers as `mo` in one build and `vo` in the next. The saved\n * key then names a sheet that no longer exists, Foundry falls back to the\n * default, and the reader's chosen sheet is gone with nothing in the console.\n * It hits released upgrades, not just a dev loop — pick the sheet in 1.0, ship\n * 1.1, and the choice is lost.\n *\n * So the caller names the sheet and VTTForge fixes the class name to that\n * before registering. The name is written down, not inferred, and a build tool\n * cannot change it.\n *\n * Because the key is persisted, the way it is derived is a compatibility\n * promise: `${package id}.${sheet id}` cannot change later without orphaning\n * every saved selection.\n */\n\nimport { VttfError } from './errors/registry.js';\n\n/** Which document collection the sheet belongs to. */\nexport type SheetDocumentKind = 'Actor' | 'Item';\n\nexport interface SheetRegistration {\n /**\n * Stable name for this sheet, unique within the package.\n *\n * It becomes the second half of the key Foundry persists, so treat it the\n * way you treat a database column name: pick it once and keep it. Renaming\n * it later loses the sheet choice of every document already using it.\n */\n readonly id: string;\n\n /** The document the sheet is for. */\n readonly document: SheetDocumentKind;\n\n /** The sheet class. */\n readonly sheet: unknown;\n\n /**\n * Document sub-types the sheet applies to. Omit to offer it for every type.\n *\n * A module registering a sheet for its own sub-type must pass the prefixed\n * key — `moduleSubType(id, 'pdf')`, not `'pdf'`.\n */\n readonly types?: readonly string[];\n\n /** Localization key for the name shown in the sheet picker. */\n readonly label?: string;\n\n /** Make this the default sheet for the listed types. */\n readonly makeDefault?: boolean;\n\n /** Allow a user to choose this sheet as their default. Defaults to true. */\n readonly canBeDefault?: boolean;\n\n /** Offer this sheet in the sheet configuration dialog. Defaults to true. */\n readonly canConfigure?: boolean;\n}\n\ninterface DocumentSheetConfigApi {\n registerSheet(\n documentClass: unknown,\n scope: string,\n sheetClass: unknown,\n options: Record<string, unknown>,\n ): void;\n}\n\ninterface FoundryRoot {\n readonly applications?: { readonly apps?: { readonly DocumentSheetConfig?: unknown } };\n}\n\n/**\n * Ids are one segment. Foundry joins scope and id with a dot, so a dotted id\n * produces a key that cannot be told apart from a different scope's.\n */\nconst ID_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i;\n\nfunction assertValidId(id: string, packageId: string): void {\n if (!ID_PATTERN.test(id)) {\n throw new VttfError(\n 'VTTF-0006',\n `\"${id}\" is not a usable sheet id for \"${packageId}\". Use letters, digits and hyphens — no dots: Foundry joins the package id and the sheet id with one, so a dotted id produces an ambiguous key.`,\n );\n }\n}\n\nfunction readDocumentSheetConfig(): DocumentSheetConfigApi {\n const foundry = (globalThis as Record<string, unknown>).foundry as FoundryRoot | undefined;\n const config = foundry?.applications?.apps?.DocumentSheetConfig as\n | DocumentSheetConfigApi\n | undefined;\n if (config === undefined || typeof config.registerSheet !== 'function') {\n throw new VttfError(\n 'VTTF-0002',\n 'foundry.applications.apps.DocumentSheetConfig is not available — register sheets inside the Foundry runtime, or stub the global in tests.',\n );\n }\n return config;\n}\n\n/**\n * Give the class the name the caller chose.\n *\n * `Function.prototype.name` is configurable, so this is a plain redefinition\n * rather than a trick. It is what makes the registered key stable, and it also\n * makes the class report its real name in a stack trace from a minified build.\n */\nfunction nameClass(sheet: unknown, id: string): void {\n Object.defineProperty(sheet as object, 'name', { value: id, configurable: true });\n}\n\n/**\n * Register a package's sheets under keys that do not move.\n *\n * Called for you by `registerSystem` and `registerModule`; exported for a\n * package that registers sheets outside either.\n */\nexport function registerSheets(\n packageId: string,\n sheets: readonly SheetRegistration[],\n documentClasses: Readonly<Record<SheetDocumentKind, unknown>>,\n): void {\n const seen = new Set<string>();\n for (const entry of sheets) {\n assertValidId(entry.id, packageId);\n if (seen.has(entry.id)) {\n throw new VttfError(\n 'VTTF-0006',\n `\"${packageId}\" registers two sheets with the id \"${entry.id}\". Ids are the persisted key — the second would overwrite the first.`,\n );\n }\n seen.add(entry.id);\n }\n\n const config = readDocumentSheetConfig();\n for (const entry of sheets) {\n nameClass(entry.sheet, entry.id);\n config.registerSheet(documentClasses[entry.document], packageId, entry.sheet, {\n types: entry.types === undefined ? undefined : [...entry.types],\n label: entry.label,\n makeDefault: entry.makeDefault ?? false,\n canBeDefault: entry.canBeDefault ?? true,\n canConfigure: entry.canConfigure ?? true,\n });\n }\n}\n","/**\n * registerModule — the module counterpart to `registerSystem`.\n *\n * A module is a guest in someone else's world, and Foundry enforces that. The\n * two differences that matter:\n *\n * - **Sub-type keys are namespaced.** A system registers `character`; a module\n * registering the same thing must register `<module-id>.character`, and the\n * manifest must declare it under `documentTypes`. Forget the prefix and the\n * type silently never appears. This function adds it for you.\n * - **A module never owns the globals.** Document classes, the initiative\n * formula and the status-effect array belong to the system. So there is no\n * option here to replace them — `statusEffects` only appends, which is what\n * a module is allowed to do.\n */\n\nimport { VttfError, type VttfErrorCode } from './errors/registry.js';\nimport type { FoundryConfig, HooksApi } from './foundry-globals.js';\nimport { type EnricherRegistration, registerEnrichers } from './register-enrichers.js';\nimport { registerSheets, type SheetRegistration } from './register-sheets.js';\n\nexport interface ModuleRegistration {\n /** Module id — must match the folder name and `module.json` `id`. */\n readonly id: string;\n\n /**\n * Actor sub-types this module contributes, keyed by the bare type name.\n * Registered under `<id>.<type>`, so declare them the same way in\n * `documentTypes.Actor` in your manifest.\n */\n readonly actorDataModels?: Readonly<Record<string, unknown>>;\n\n /** Item sub-types, same rule as `actorDataModels`. */\n readonly itemDataModels?: Readonly<Record<string, unknown>>;\n\n /**\n * Status effects to append to `CONFIG.statusEffects`.\n *\n * Appended, never assigned: the array belongs to the system, and replacing\n * it would delete conditions the world depends on.\n */\n readonly statusEffects?: readonly unknown[];\n\n /**\n * Sheets this module offers, registered under `<id>.<sheet id>`.\n *\n * Register them here rather than calling Foundry's `registerSheet` yourself:\n * Foundry derives the persisted key from the class name, and a bundler\n * renames classes between builds. See `registerSheets`.\n */\n readonly sheets?: readonly SheetRegistration[];\n\n /**\n * Text enrichers this module contributes, registered under `<id>.<enricher\n * id>`.\n *\n * Register them here rather than pushing to `CONFIG.TextEditor.enrichers`\n * yourself: that array has four ways to accept an entry and then do nothing\n * with it. See `registerEnrichers`.\n */\n readonly enrichers?: readonly EnricherRegistration[];\n\n /** Runs before any CONFIG mutation — the usual home for the module API. */\n readonly onBeforeInit?: () => void;\n\n /** Runs after the mutations above, inside the same `init` hook. */\n readonly onAfterInit?: () => void;\n\n /**\n * Runs once on `ready`.\n *\n * **Not GM-gated.** Guard inside your callback when the work is GM-only.\n */\n readonly onReady?: () => void | Promise<void>;\n}\n\nconst registered = new Set<string>();\n\n/** For tests — clears the in-process \"already registered\" guard. */\nexport function _resetRegisteredModulesForTests(): void {\n registered.clear();\n}\n\n/**\n * The key Foundry files a module's document sub-type under.\n *\n * Use it wherever you name the type outside `registerModule` — registering the\n * sheet, checking `actor.type`, writing `documentTypes` in the manifest. The\n * prefix is easy to get wrong by hand and fails silently when you do.\n *\n * @example\n * ```ts\n * moduleSubType('pdf-character-sheet', 'pdf'); // 'pdf-character-sheet.pdf'\n * ```\n */\nexport function moduleSubType(moduleId: string, type: string): string {\n return `${moduleId}.${type}`;\n}\n\nfunction vttfError(code: VttfErrorCode, message?: string): VttfError {\n return new VttfError(code, message);\n}\n\nfunction readHooks(): HooksApi {\n const hooks = (globalThis as Record<string, unknown>).Hooks as HooksApi | undefined;\n if (hooks === undefined || typeof hooks.once !== 'function') {\n throw vttfError(\n 'VTTF-0002',\n 'globalThis.Hooks is not available — call registerModule() inside a Foundry runtime or stub Hooks in tests',\n );\n }\n return hooks;\n}\n\nfunction readConfig(): FoundryConfig {\n const config = (globalThis as Record<string, unknown>).CONFIG as FoundryConfig | undefined;\n if (config === undefined) {\n throw vttfError(\n 'VTTF-0002',\n 'globalThis.CONFIG is not available — call registerModule() inside a Foundry runtime or stub CONFIG in tests',\n );\n }\n return config;\n}\n\nfunction assignSubTypes(\n target: Record<string, unknown>,\n moduleId: string,\n models: Readonly<Record<string, unknown>>,\n): void {\n for (const [type, model] of Object.entries(models)) {\n target[moduleSubType(moduleId, type)] = model;\n }\n}\n\n/**\n * Register a Foundry module with VTTForge.\n *\n * Calling twice with the same `id` throws VTTF-0001 — almost always a\n * hot-reload artefact or a duplicate import. The CONFIG mutations are deferred\n * until Foundry's `init` hook fires.\n */\nexport function registerModule(config: ModuleRegistration): ModuleRegistration {\n if (registered.has(config.id)) {\n throw vttfError('VTTF-0001', `Module \"${config.id}\" was already registered`);\n }\n registered.add(config.id);\n\n const hooks = readHooks();\n hooks.once('init', () => {\n applyInit(config);\n });\n if (config.onReady !== undefined) {\n hooks.once('ready', () => {\n void config.onReady?.();\n });\n }\n\n return config;\n}\n\nfunction applyInit(config: ModuleRegistration): void {\n config.onBeforeInit?.();\n const CONFIG = readConfig();\n\n if (config.actorDataModels !== undefined) {\n assignSubTypes(CONFIG.Actor.dataModels, config.id, config.actorDataModels);\n }\n if (config.itemDataModels !== undefined) {\n assignSubTypes(CONFIG.Item.dataModels, config.id, config.itemDataModels);\n }\n if (config.statusEffects !== undefined && config.statusEffects.length > 0) {\n CONFIG.statusEffects ??= [];\n CONFIG.statusEffects.push(...config.statusEffects);\n }\n if (config.enrichers !== undefined && config.enrichers.length > 0) {\n registerEnrichers(config.id, config.enrichers);\n }\n if (config.sheets !== undefined && config.sheets.length > 0) {\n registerSheets(config.id, config.sheets, {\n Actor: CONFIG.Actor.documentClass,\n Item: CONFIG.Item.documentClass,\n });\n }\n\n config.onAfterInit?.();\n}\n","/**\n * registerSystem — one call that replaces the boilerplate `Hooks.once(\"init\", ...)`\n * block in every Foundry system.\n *\n * Conforms to the canonical Foundry init lifecycle:\n *\n * init → CONFIG mutations (dataModels, documentClass, statusEffects)\n * i18nInit → translate CONFIG labels\n * setup → enrichers, packs\n * ready → migrations (GM-only — consumer guards inside onReady)\n *\n * v0.1 scope: `init` + `ready`. `setup` / `i18nInit` callbacks remain v0.1.1.\n *\n * Per PRD §11 open question #1, we wrap the hook ourselves (\"explicit hook for\n * now\"); callers don't need to write `Hooks.once(\"init\", ...)` themselves.\n */\n\nimport { VttfError, type VttfErrorCode } from './errors/registry.js';\nimport type {\n ActiveEffectConfig,\n CombatConfig,\n FoundryConfig,\n HooksApi,\n} from './foundry-globals.js';\nimport { type EnricherRegistration, registerEnrichers } from './register-enrichers.js';\nimport { registerSheets, type SheetRegistration } from './register-sheets.js';\n\nexport interface SystemRegistration {\n /** System id — must match the folder name and `system.json` `id`. */\n readonly id: string;\n\n /** Map of `documentTypes.Actor` key → TypeDataModel class. */\n readonly actorDataModels?: Readonly<Record<string, unknown>>;\n\n /** Map of `documentTypes.Item` key → TypeDataModel class. */\n readonly itemDataModels?: Readonly<Record<string, unknown>>;\n\n /** Replacement for `CONFIG.Actor.documentClass`. */\n readonly actorDocumentClass?: unknown;\n\n /** Replacement for `CONFIG.Item.documentClass`. */\n readonly itemDocumentClass?: unknown;\n\n /** Global initiative formula — assigned to `CONFIG.Combat.initiative`. */\n readonly combat?: CombatConfig;\n\n /** Disables legacy Active Effect transferral. Defaults to true. */\n readonly activeEffect?: ActiveEffectConfig;\n\n /**\n * Replaces `CONFIG.statusEffects` (systems own this array — modules push).\n * If omitted, the existing array is kept untouched.\n */\n readonly statusEffects?: readonly unknown[];\n\n /**\n * Sheets this system offers, registered under `<id>.<sheet id>`.\n *\n * Register them here rather than calling Foundry's `registerSheet` yourself:\n * Foundry derives the persisted key from the class name, and a bundler\n * renames classes between builds. See `registerSheets`.\n */\n readonly sheets?: readonly SheetRegistration[];\n\n /**\n * Text enrichers this system contributes, registered under `<id>.<enricher\n * id>`.\n *\n * Register them here rather than pushing to `CONFIG.TextEditor.enrichers`\n * yourself: that array has four ways to accept an entry and then do nothing\n * with it. See `registerEnrichers`.\n */\n readonly enrichers?: readonly EnricherRegistration[];\n\n /**\n * Optional pre-init hook for work that has to run before any of the CONFIG\n * mutations (rare — usually used to assign `globalThis.<systemId>` API).\n */\n readonly onBeforeInit?: () => void;\n\n /** Optional post-init hook for work that depends on the mutations above. */\n readonly onAfterInit?: () => void;\n\n /**\n * Optional `ready` hook — fires once after Foundry has finished bootstrap.\n * The natural home for migration runners (`createMigrationRunner().run()`).\n *\n * **Not GM-gated.** Guard inside your callback (`if (!game.user.isGM) return;`)\n * when the work is GM-only — migrations always are.\n */\n readonly onReady?: () => void | Promise<void>;\n}\n\nconst registered = new Set<string>();\n\n/** For tests — clears the in-process \"already registered\" guard. */\nexport function _resetRegisteredSystemsForTests(): void {\n registered.clear();\n}\n\nfunction readHooks(): HooksApi {\n const hooks = (globalThis as Record<string, unknown>).Hooks as HooksApi | undefined;\n if (hooks === undefined || typeof hooks.once !== 'function') {\n throw vttfError(\n 'VTTF-0002',\n 'globalThis.Hooks is not available — call registerSystem() inside a Foundry runtime or stub Hooks in tests',\n );\n }\n return hooks;\n}\n\nfunction readConfig(): FoundryConfig {\n const config = (globalThis as Record<string, unknown>).CONFIG as FoundryConfig | undefined;\n if (config === undefined) {\n throw vttfError(\n 'VTTF-0002',\n 'globalThis.CONFIG is not available — call registerSystem() inside a Foundry runtime or stub CONFIG in tests',\n );\n }\n return config;\n}\n\nfunction vttfError(code: VttfErrorCode, message?: string): VttfError {\n return new VttfError(code, message);\n}\n\n/**\n * Register a Foundry system with VTTForge. Idempotency: the same `id` calling\n * twice throws VTTF-0001 — almost always a hot-reload or duplicate import bug.\n *\n * Returns the registration object so consumers can inspect what was applied\n * (useful in tests). The actual CONFIG mutations are deferred until Foundry's\n * `init` hook fires.\n */\nexport function registerSystem(config: SystemRegistration): SystemRegistration {\n if (registered.has(config.id)) {\n throw vttfError('VTTF-0001', `System \"${config.id}\" was already registered`);\n }\n registered.add(config.id);\n\n const hooks = readHooks();\n hooks.once('init', () => {\n applyInit(config);\n });\n if (config.onReady !== undefined) {\n hooks.once('ready', () => {\n // Foundry awaits ready-hook results, but `Hooks.once` types it as\n // `unknown` so we don't return anything ourselves — Foundry treats\n // Promise rejections as unhandled, which is the right escalation.\n void config.onReady?.();\n });\n }\n\n return config;\n}\n\nfunction applyInit(config: SystemRegistration): void {\n config.onBeforeInit?.();\n const CONFIG = readConfig();\n\n if (config.actorDataModels !== undefined) {\n Object.assign(CONFIG.Actor.dataModels, config.actorDataModels);\n }\n if (config.itemDataModels !== undefined) {\n Object.assign(CONFIG.Item.dataModels, config.itemDataModels);\n }\n if (config.actorDocumentClass !== undefined) {\n CONFIG.Actor.documentClass = config.actorDocumentClass;\n }\n if (config.itemDocumentClass !== undefined) {\n CONFIG.Item.documentClass = config.itemDocumentClass;\n }\n if (config.combat?.initiative !== undefined) {\n CONFIG.Combat.initiative = config.combat.initiative;\n }\n\n // Disable legacy Active Effect transferral by default — every modern v13\n // system wants this off (the modern AE model is opt-in via this flag).\n const legacyTransferral = config.activeEffect?.legacyTransferral ?? false;\n CONFIG.ActiveEffect.legacyTransferral = legacyTransferral;\n\n if (config.statusEffects !== undefined) {\n CONFIG.statusEffects = [...config.statusEffects];\n }\n if (config.enrichers !== undefined && config.enrichers.length > 0) {\n registerEnrichers(config.id, config.enrichers);\n }\n if (config.sheets !== undefined && config.sheets.length > 0) {\n registerSheets(config.id, config.sheets, {\n Actor: CONFIG.Actor.documentClass,\n Item: CONFIG.Item.documentClass,\n });\n }\n\n config.onAfterInit?.();\n}\n","/**\n * SystemConfig — typed wrapper around `game.settings.register/get/set`.\n *\n * Eliminates the boilerplate of repeating the system id in every call:\n *\n * // before\n * game.settings.register(\"ordemparanormal\", \"homebrewRules\", { ... });\n * game.settings.get(\"ordemparanormal\", \"homebrewRules\");\n *\n * // after\n * const cfg = new SystemConfig(\"ordemparanormal\");\n * cfg.register(\"homebrewRules\", { ... });\n * cfg.get<boolean>(\"homebrewRules\");\n *\n * Also keeps a local manifest of registered keys so attempts to read an\n * unregistered key fail with VTTF-0003 instead of returning undefined.\n *\n * Registration must happen during the `init` hook; reads can happen any\n * time after.\n */\n\nimport { VttfError } from './errors/registry.js';\nimport type { GameApi, SettingConfig } from './foundry-globals.js';\n\nfunction readGame(): GameApi {\n const candidate = (globalThis as Record<string, unknown>).game as GameApi | undefined;\n if (candidate === undefined || candidate.settings === undefined) {\n throw new VttfError(\n 'VTTF-0002',\n 'game.settings is not available — call SystemConfig methods inside or after the Foundry \"init\" hook',\n );\n }\n return candidate;\n}\n\nexport class SystemConfig {\n readonly systemId: string;\n readonly #registered = new Set<string>();\n\n constructor(systemId: string) {\n this.systemId = systemId;\n }\n\n register<T>(key: string, config: SettingConfig<T>): void {\n const game = readGame();\n game.settings.register(this.systemId, key, config);\n this.#registered.add(key);\n }\n\n get<T>(key: string): T {\n if (!this.#registered.has(key)) {\n throw new VttfError(\n 'VTTF-0003',\n `SystemConfig.get(\"${key}\") was called before register(\"${key}\")`,\n );\n }\n return readGame().settings.get<T>(this.systemId, key);\n }\n\n async set<T>(key: string, value: T): Promise<T> {\n if (!this.#registered.has(key)) {\n throw new VttfError(\n 'VTTF-0003',\n `SystemConfig.set(\"${key}\") was called before register(\"${key}\")`,\n );\n }\n return readGame().settings.set<T>(this.systemId, key, value);\n }\n\n isRegistered(key: string): boolean {\n return this.#registered.has(key);\n }\n}\n","/**\n * @vttforge/core — runtime utilities for FoundryVTT v13+ systems and modules.\n *\n * v0.1 surface:\n *\n * - registerSystem() — one-call init, replaces Hooks.once(\"init\")\n * - registerModule() — the same for modules, with namespaced sub-types\n * - SystemConfig — typed wrapper around game.settings\n * - BaseTypeDataModel() — TypeDataModel with safe migrateData default\n * - BaseActorSheet() — ActorSheetV2 + HandlebarsApplicationMixin\n * - BaseItemSheet() — ItemSheetV2 + HandlebarsApplicationMixin\n * - fields() — typed bag of foundry.data.fields constructors\n * - InferSchema<T> — derive `system` shape from defineSchema()\n * - createMigrationRunner() — declarative schema migrations (register + run)\n * - VttfError + error registry — VTTF-NNNN codes with docs URLs\n *\n * Foundry classes are resolved from `globalThis.foundry` lazily so the package\n * imports cleanly in Node/tests; concrete Foundry typing arrives with\n * `@vttforge/types` in v1.0.\n */\n\nimport { version } from '../package.json' with { type: 'json' };\n\nexport const VTTFORGE_CORE_VERSION: string = version;\n\nexport {\n BaseActorSheet,\n type DragDropConfig,\n type SheetBaseCtor,\n type SheetBaseMembers,\n type SheetBaseStatics,\n VTTFORGE_SHEET_CLASS,\n} from './base-actor-sheet.js';\nexport {\n BaseApplication,\n type BaseApplicationMembers,\n} from './base-application.js';\nexport {\n BaseDocumentSheet,\n type BaseDocumentSheetMembers,\n type DocumentSheetKind,\n} from './base-document-sheet.js';\nexport { BaseItemSheet } from './base-item-sheet.js';\nexport {\n BaseTypeDataModel,\n type TypeDataModelHooks,\n type TypedTypeDataModel,\n type TypedTypeDataModelCtor,\n} from './base-type-data-model.js';\nexport type {\n ArrayFieldOptions,\n BooleanFieldOptions,\n ColorFieldOptions,\n DataFieldOptions,\n EmbeddedDataFieldOptions,\n EmbeddedDocumentFieldOptions,\n FilePathFieldOptions,\n ForeignDocumentFieldOptions,\n HTMLFieldOptions,\n NumberFieldOptions,\n SchemaFieldOptions,\n SetFieldOptions,\n StringFieldOptions,\n TypedSchemaFieldOptions,\n} from './data/field-options.js';\nexport {\n type ArrayFieldCtor,\n type ArrayFieldInstance,\n type BooleanFieldCtor,\n type BooleanFieldInstance,\n type ColorFieldCtor,\n type ColorFieldInstance,\n type DataModelClass,\n type DocumentClass,\n type EmbeddedDataFieldCtor,\n type EmbeddedDataFieldInstance,\n type EmbeddedDocumentFieldCtor,\n type EmbeddedDocumentFieldInstance,\n type FieldInstance,\n type FieldsApi,\n type FilePathFieldCtor,\n type FilePathFieldInstance,\n type ForeignDocumentFieldCtor,\n type ForeignDocumentFieldInstance,\n fields,\n type HTMLFieldCtor,\n type HTMLFieldInstance,\n type NumberFieldCtor,\n type NumberFieldInstance,\n type SchemaFieldCtor,\n type SchemaFieldInstance,\n type SetFieldCtor,\n type SetFieldInstance,\n type StringFieldCtor,\n type StringFieldInstance,\n type TypedSchemaFieldCtor,\n type TypedSchemaFieldInstance,\n} from './data/fields.js';\nexport type { InferField, InferSchema, Prettify } from './data/infer-schema.js';\nexport {\n ERROR_MANIFEST_VERSION,\n type ErrorManifest,\n getErrorManifest,\n} from './errors/manifest.js';\nexport {\n docsUrlFor,\n getErrorEntry,\n listErrorEntries,\n VttfError,\n type VttfErrorCode,\n type VttfErrorEntry,\n} from './errors/registry.js';\nexport type {\n ApplicationV2Members,\n DocumentSheetV2Members,\n VttforgeClass,\n} from './foundry-base.js';\nexport type {\n ActiveEffectConfig,\n CombatConfig,\n FoundryConfig,\n GameApi,\n GameSettingsApi,\n HookCallback,\n HooksApi,\n SettingConfig,\n SettingScope,\n} from './foundry-globals.js';\nexport { createMigrationRunner } from './migrations/runner.js';\nexport type {\n Migration,\n MigrationLogger,\n MigrationRunner,\n MigrationRunnerOptions,\n} from './migrations/types.js';\nexport {\n type EnricherRegistration,\n registerEnrichers,\n} from './register-enrichers.js';\nexport {\n type ModuleRegistration,\n moduleSubType,\n registerModule,\n} from './register-module.js';\nexport {\n registerSheets,\n type SheetDocumentKind,\n type SheetRegistration,\n} from './register-sheets.js';\nexport { registerSystem, type SystemRegistration } from './register-system.js';\nexport { SystemConfig } from './system-config.js';\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vttforge/core",
|
|
3
|
-
"version": "0.11.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.11.2",
|
|
4
|
+
"description": "Runtime for Foundry VTT v13+ systems and modules: registerSystem, registerModule, typed data models, sheet bases, settings, migrations, error registry.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://github.com/vttforge/vttforge#readme",
|