@vttforge/core 0.6.0 → 0.8.0

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 CHANGED
@@ -1,5 +1,50 @@
1
1
  # @vttforge/core
2
2
 
3
+ ## 0.8.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 2227957: `BaseApplication` — a plain `ApplicationV2` window without the two traps.
8
+
9
+ The document sheets already had a baseline. Everything else a package puts on screen — a config dialog, a picker, a reader — is a bare `ApplicationV2`, and writing one by hand means meeting both of these:
10
+
11
+ - **`_replaceHTML` is easy to forget.** ApplicationV2 splits rendering in two, and implementing only `_renderHTML` leaves the class silently unrenderable. Foundry reports it at the moment something tries to open the window, as an error about abstract methods. Nearly every implementation of the second half is the same line, so this ships it.
12
+ - **A missing `_renderHTML` fails late.** This checks at construction and names the class, so it fails where the class is used rather than deep inside a render.
13
+
14
+ Both were met while porting a real module onto the SDK.
15
+ - 6483344: The base factories now report what they add.
16
+
17
+ Every `Base*` factory returned `any`, which gave up on two things at once: a subclass could not write `override` on a member it really was overriding, and a call to a method that does not exist passed silently. Both happened while porting a real module onto the SDK — the second one shipped a broken call into a release.
18
+
19
+ They now return the members they contribute, with the rest of the Foundry surface reachable through an index signature. A property the SDK knows about carries its real type; anything else behaves as before.
20
+
21
+ This will surface `override` errors in subclasses that were previously allowed to omit the keyword. That is the point: TypeScript can see the member now.
22
+
23
+ The index signature is what `@vttforge/types` replaces when it lands.
24
+
25
+ ### Patch Changes
26
+
27
+ - 257614b: Error code pages are generated for the docs site as well as the repo.
28
+
29
+ `codegen-errors.mjs` wrote one Markdown stub per code into `docs/errors/`. It now writes the same stubs into `apps/docs/errors/` too — one source, two destinations, so the page a reader lands on from GitHub and the page the site publishes cannot drift.
30
+ - d015aee: Stop requiring Node 26 to install a browser package.
31
+
32
+ Every package declared `engines.node: ">=26.0.0"`. Four of them — `core`, `styles`, `types` and `dev-module` — compile to ES2022 and run in the browser inside Foundry. They never touch Node, and the floor did nothing except stop anyone on Node 22 LTS from installing the SDK at all.
33
+
34
+ Those four declare no engine now. `@vttforge/testing` drops to `>=22` — its Quench half runs in the browser too. `@vttforge/cli` and `@vttforge/vite-plugin` keep `>=26`, which is what they actually build against.
35
+
36
+ ## 0.7.0
37
+
38
+ ### Minor Changes
39
+
40
+ - dcd07d5: Type the three embedded fields, and turn `checkJs` back on for the example.
41
+
42
+ - `EmbeddedDataField` is the model instance, not a plain object — the field builds a schema from the model's own `defineSchema()`, but initializing constructs the model, so derived data and methods come with it.
43
+ - `EmbeddedDocumentField` is the same for a Document class, and nullable out of the box.
44
+ - `TypedSchemaField` is a discriminated union. The field supplies a `type` string validated to equal each entry's key when the entry does not declare one, which is what makes narrowing on `type` work.
45
+
46
+ The example system now compiles with `checkJs: true`, which is what proves any of this against real JavaScript rather than only against type tests.
47
+
3
48
  ## 0.6.0
4
49
 
5
50
  ### Minor Changes
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 1,
3
3
  "package": "@vttforge/core",
4
- "packageVersion": "0.6.0",
4
+ "packageVersion": "0.8.0",
5
5
  "entries": [
6
6
  {
7
7
  "code": "VTTF-0001",
package/dist/index.d.mts CHANGED
@@ -1,34 +1,42 @@
1
- //#region src/base-actor-sheet.d.ts
1
+ //#region src/foundry-base.d.ts
2
2
  /**
3
- * BaseActorSheet `ActorSheetV2 + HandlebarsApplicationMixin` baseline with the
4
- * boilerplate every shipping system copy-pastes hoisted into the SDK.
3
+ * How a base factory reports what it returns.
5
4
  *
6
- * What this adds beyond stock Foundry v13:
5
+ * These factories mix VTTForge behaviour into a Foundry class resolved at
6
+ * runtime. Two halves, and they are not equally knowable: what we add is
7
+ * ours and can be typed exactly; what Foundry brings lives in a type package
8
+ * that is not wired up yet.
7
9
  *
8
- * - **`static DRAG_DROP`** declare drag sources / drop targets as data, get
9
- * `foundry.applications.ux.DragDrop` instances wired in `_onRender` with
10
- * `isEditable`-gated permissions and a sensible default `_onDragStart` that
11
- * serialises `data-item-id` elements as `{ type: "Item", uuid }`.
12
- * - **`_prepareContext` auto-fills `context.tabs[group]`** for every group
13
- * declared in ApplicationV2's `static TABS`, so subclass `_prepareContext`
14
- * implementations stop having to call `_prepareTabs(group)` by hand.
15
- * - **Typed drop dispatch** — override `onDropItem(item, event)` /
16
- * `onDropActor(actor, event)` / `onDropFolder(folder, event)` /
17
- * `onDropActiveEffect(effect, event)` and skip the `fromUuid()` ceremony.
18
- * Returning `undefined` falls through to Foundry's default `_onDropX`
19
- * behaviour; return any other value to take ownership.
10
+ * Returning `any` for the whole thing which is what every factory did —
11
+ * gives up on both. It costs more than it looks: a subclass cannot write
12
+ * `override` on a member it really is overriding, and a call to a method
13
+ * that does not exist passes silently. Both happened while porting a real
14
+ * module onto the SDK, the second one shipping a broken call into a release.
20
15
  *
21
- * Intentional non-additions:
16
+ * So: type our half, and let Foundry's half through an index signature. A
17
+ * property we know about carries its real type; anything else is reachable
18
+ * and `any`, the same as before. When `@vttforge/types` lands, the index
19
+ * signature is what it replaces.
20
+ */
21
+ /**
22
+ * The part of an instance this SDK does not describe yet.
22
23
  *
23
- * - `editImage` action already shipped by `DocumentSheetV2` (inherited by
24
- * `ActorSheetV2`). Templates wire `<img data-edit="img">` and Foundry's
25
- * built-in action handles the `FilePicker` flow.
26
- * - `_getTabs()` — ApplicationV2 already owns the tab state machine; we only
27
- * eliminate the `_prepareTabs` call in `_prepareContext`.
24
+ * Deliberately permissive. Narrowing it before the Foundry types exist would
25
+ * only mean rejecting code that works.
26
+ */
27
+ interface UntypedFoundryMembers {
28
+ [member: string]: any;
29
+ }
30
+ /**
31
+ * A class this SDK built on top of a Foundry one.
28
32
  *
29
- * Resolved lazily so subclasses can be declared at module load without
30
- * Foundry globals existing yet (test boot, ESM hoist).
33
+ * `Added` is what the factory contributes. Everything else stays reachable.
31
34
  */
35
+ type VttforgeClass<Added, Statics = unknown> = Statics & {
36
+ new (...args: any[]): Added & UntypedFoundryMembers;
37
+ };
38
+ //#endregion
39
+ //#region src/base-actor-sheet.d.ts
32
40
  /**
33
41
  * Declarative DragDrop entry consumed by `_onRender`. Mirrors the
34
42
  * `foundry.applications.ux.DragDrop` constructor config. Permissions and
@@ -63,8 +71,30 @@ interface SheetBaseStatics {
63
71
  * What the factory hands back: something you can `extend`, whose statics the
64
72
  * compiler can see.
65
73
  */
74
+ /**
75
+ * What the sheet factories add on top of Foundry's own sheet.
76
+ *
77
+ * Only the members a subclass actually reaches for. The rest of the Foundry
78
+ * surface stays reachable and untyped until `@vttforge/types` describes it —
79
+ * see `UntypedFoundryMembers`.
80
+ */
81
+ interface SheetBaseMembers {
82
+ /** Fills in `context.tabs` for every group in `static TABS`. */
83
+ _prepareContext(options: unknown): Promise<Record<string, unknown>>;
84
+ /** Binds the `static DRAG_DROP` entries. */
85
+ _onRender(context: unknown, options: unknown): void;
86
+ _onDragStart(event: DragEvent): void;
87
+ /**
88
+ * The typed drop hooks. Override the one you want; returning `undefined`
89
+ * hands the drop back to Foundry's own handling.
90
+ */
91
+ onDropItem(item: unknown, event: DragEvent): Promise<unknown>;
92
+ onDropActor(actor: unknown, event: DragEvent): Promise<unknown>;
93
+ onDropFolder(folder: unknown, event: DragEvent): Promise<unknown>;
94
+ onDropActiveEffect(effect: unknown, event: DragEvent): Promise<unknown>;
95
+ }
66
96
  interface SheetBaseCtor extends SheetBaseStatics {
67
- new (...args: any[]): any;
97
+ new (...args: any[]): SheetBaseMembers & UntypedFoundryMembers;
68
98
  }
69
99
  /**
70
100
  * Marker class that consumer CSS uses for scoping. Always present on every
@@ -102,6 +132,35 @@ declare const VTTFORGE_SHEET_CLASS = "vttforge";
102
132
  */
103
133
  declare function BaseActorSheet(): SheetBaseCtor;
104
134
  //#endregion
135
+ //#region src/base-application.d.ts
136
+ /**
137
+ * Build an `ApplicationV2` base with the rendering contract filled in.
138
+ *
139
+ * ```ts
140
+ * class PdfConfig extends BaseApplication() {
141
+ * async _renderHTML() {
142
+ * const form = document.createElement('form');
143
+ * // …
144
+ * return form;
145
+ * }
146
+ * }
147
+ * ```
148
+ *
149
+ * `_replaceHTML` is provided. `_renderHTML` is yours, and omitting it throws
150
+ * when the class is constructed rather than when someone opens the window.
151
+ */
152
+ /** What `BaseApplication` adds on top of Foundry's `ApplicationV2`. */
153
+ interface BaseApplicationMembers {
154
+ /**
155
+ * Put the rendered content in the window.
156
+ *
157
+ * Provided because it is the half people forget. Override it for a window
158
+ * that updates in place rather than swapping its whole content.
159
+ */
160
+ _replaceHTML(result: HTMLElement, content: HTMLElement): void;
161
+ }
162
+ declare function BaseApplication(): VttforgeClass<BaseApplicationMembers>;
163
+ //#endregion
105
164
  //#region src/base-item-sheet.d.ts
106
165
  /**
107
166
  * Build the `BaseItemSheet` for the current Foundry runtime.
@@ -198,6 +257,12 @@ interface ForeignDocumentFieldOptions extends DataFieldOptions {
198
257
  */
199
258
  readonly idOnly?: boolean;
200
259
  }
260
+ /** `EmbeddedDataField` builds a SchemaField from the model's own schema. */
261
+ type EmbeddedDataFieldOptions = SchemaFieldOptions;
262
+ /** `EmbeddedDocumentField` is the same, but nullable out of the box. */
263
+ type EmbeddedDocumentFieldOptions = SchemaFieldOptions;
264
+ /** `TypedSchemaField` is required by default and takes no options of its own. */
265
+ type TypedSchemaFieldOptions = DataFieldOptions;
201
266
  //#endregion
202
267
  //#region src/data/fields.d.ts
203
268
  declare const BRAND: unique symbol;
@@ -267,6 +332,42 @@ interface ForeignDocumentFieldInstance<Doc extends DocumentClass = DocumentClass
267
332
  readonly model: Doc;
268
333
  readonly options: O;
269
334
  }
335
+ /**
336
+ * A nested data model.
337
+ *
338
+ * It is a `SchemaField` built from the model class's own `defineSchema()`, so
339
+ * the value is an instance of that model — not a plain object. Reading it
340
+ * gives you the model's derived data and methods too.
341
+ */
342
+ interface EmbeddedDataFieldInstance<Model extends DataModelClass = DataModelClass, O extends EmbeddedDataFieldOptions = EmbeddedDataFieldOptions> extends FieldInstance {
343
+ readonly [BRAND]: 'embeddedData';
344
+ readonly model: Model;
345
+ readonly options: O;
346
+ }
347
+ /**
348
+ * A single embedded document, stored inline.
349
+ *
350
+ * Like `EmbeddedDataField` but for a Document class, and nullable by default:
351
+ * the field's own defaults turn `nullable` on, so an absent one reads `null`.
352
+ */
353
+ interface EmbeddedDocumentFieldInstance<Doc extends DataModelClass = DataModelClass, O extends EmbeddedDocumentFieldOptions = EmbeddedDocumentFieldOptions> extends FieldInstance {
354
+ readonly [BRAND]: 'embeddedDocument';
355
+ readonly model: Doc;
356
+ readonly options: O;
357
+ }
358
+ /**
359
+ * One of several shapes, told apart by a `type` property.
360
+ *
361
+ * Each entry becomes its own SchemaField. When an entry does not declare a
362
+ * `type` field, the field adds one — a required string whose value must equal
363
+ * that entry's key — which is what makes the result a discriminated union you
364
+ * can narrow on.
365
+ */
366
+ interface TypedSchemaFieldInstance<T extends Record<string, Record<string, FieldInstance>> = Record<string, Record<string, FieldInstance>>, O extends TypedSchemaFieldOptions = TypedSchemaFieldOptions> extends FieldInstance {
367
+ readonly [BRAND]: 'typedSchema';
368
+ readonly types: T;
369
+ readonly options: O;
370
+ }
270
371
  interface SchemaFieldInstance<S extends Record<string, FieldInstance> = Record<string, FieldInstance>, O extends SchemaFieldOptions = SchemaFieldOptions> extends FieldInstance {
271
372
  readonly [BRAND]: 'schema';
272
373
  readonly fields: S;
@@ -305,6 +406,17 @@ type DocumentClass = abstract new (...args: never[]) => object;
305
406
  interface ForeignDocumentFieldCtor {
306
407
  new <Doc extends DocumentClass, O extends ForeignDocumentFieldOptions = ForeignDocumentFieldOptions>(model: Doc, options?: O): ForeignDocumentFieldInstance<Doc, O>;
307
408
  }
409
+ /** Any DataModel subclass — what the embedded fields take as their type. */
410
+ type DataModelClass = abstract new (...args: never[]) => object;
411
+ interface EmbeddedDataFieldCtor {
412
+ new <Model extends DataModelClass, O extends EmbeddedDataFieldOptions = EmbeddedDataFieldOptions>(model: Model, options?: O): EmbeddedDataFieldInstance<Model, O>;
413
+ }
414
+ interface EmbeddedDocumentFieldCtor {
415
+ new <Doc extends DataModelClass, O extends EmbeddedDocumentFieldOptions = EmbeddedDocumentFieldOptions>(model: Doc, options?: O): EmbeddedDocumentFieldInstance<Doc, O>;
416
+ }
417
+ interface TypedSchemaFieldCtor {
418
+ new <T extends Record<string, Record<string, FieldInstance>>, O extends TypedSchemaFieldOptions = TypedSchemaFieldOptions>(types: T, options?: O): TypedSchemaFieldInstance<T, O>;
419
+ }
308
420
  interface SchemaFieldCtor {
309
421
  new <S extends Record<string, FieldInstance>, O extends SchemaFieldOptions = SchemaFieldOptions>(fields: S, options?: O): SchemaFieldInstance<S, O>;
310
422
  }
@@ -324,6 +436,9 @@ interface FieldsApi {
324
436
  readonly SetField: SetFieldCtor;
325
437
  readonly ForeignDocumentField: ForeignDocumentFieldCtor;
326
438
  readonly SchemaField: SchemaFieldCtor;
439
+ readonly EmbeddedDataField: EmbeddedDataFieldCtor;
440
+ readonly EmbeddedDocumentField: EmbeddedDocumentFieldCtor;
441
+ readonly TypedSchemaField: TypedSchemaFieldCtor;
327
442
  }
328
443
  /**
329
444
  * Resolve `globalThis.foundry.data.fields` and return it typed as `FieldsApi`.
@@ -500,12 +615,23 @@ type ColorFieldValue<O> = Presence<O, Color, NullStartDefaults>;
500
615
  type ForeignDocumentValue<Doc extends DocumentClass, O> = Presence<O, O extends {
501
616
  idOnly: true;
502
617
  } ? string : InstanceType<Doc>, ReferenceDefaults>;
618
+ /**
619
+ * What a `TypedSchemaField` holds: one shape per entry, each carrying the
620
+ * key it was filed under as its `type`.
621
+ *
622
+ * The field supplies that `type` when an entry does not declare one — a
623
+ * required string validated to equal the key — so narrowing on `type` picks
624
+ * exactly one branch.
625
+ */
626
+ type TypedSchemaValue<T extends Record<string, Record<string, FieldInstance>>> = { [K in keyof T]: Prettify<InferSchema<T[K]> & {
627
+ type: K;
628
+ }>; }[keyof T];
503
629
  /**
504
630
  * Map a single field instance to its runtime TypeScript type. `never` for
505
631
  * shapes we don't recognise — the v1.0 `@vttforge/types` package will widen
506
632
  * this matrix to the remaining Foundry fields.
507
633
  */
508
- type InferField<F> = F extends NumberFieldInstance<infer O> ? Presence<O, number, NumberDefaults> : F extends StringFieldInstance<infer O> ? Presence<O, string, StringDefaults> : F extends BooleanFieldInstance<infer O> ? Presence<O, boolean, BooleanDefaults> : F extends HTMLFieldInstance<infer O> ? Presence<O, string, HTMLDefaults> : F extends ColorFieldInstance<infer O> ? ColorFieldValue<O> : F extends FilePathFieldInstance<infer O> ? Presence<O, string, NullStartDefaults> : F extends ForeignDocumentFieldInstance<infer Doc, infer O> ? ForeignDocumentValue<Doc, O> : F extends ArrayFieldInstance<infer Inner, infer O> ? Presence<O, InferField<Inner>[], ContainerDefaults> : F extends SetFieldInstance<infer Inner, infer O> ? Presence<O, Set<InferField<Inner>>, ContainerDefaults> : F extends SchemaFieldInstance<infer S, infer O> ? Presence<O, InferSchema<S>, ContainerDefaults> : never;
634
+ type InferField<F> = F extends NumberFieldInstance<infer O> ? Presence<O, number, NumberDefaults> : F extends StringFieldInstance<infer O> ? Presence<O, string, StringDefaults> : F extends BooleanFieldInstance<infer O> ? Presence<O, boolean, BooleanDefaults> : F extends HTMLFieldInstance<infer O> ? Presence<O, string, HTMLDefaults> : F extends ColorFieldInstance<infer O> ? ColorFieldValue<O> : F extends FilePathFieldInstance<infer O> ? Presence<O, string, NullStartDefaults> : F extends ForeignDocumentFieldInstance<infer Doc, infer O> ? ForeignDocumentValue<Doc, O> : F extends ArrayFieldInstance<infer Inner, infer O> ? Presence<O, InferField<Inner>[], ContainerDefaults> : F extends SetFieldInstance<infer Inner, infer O> ? Presence<O, Set<InferField<Inner>>, ContainerDefaults> : F extends SchemaFieldInstance<infer S, infer O> ? Presence<O, InferSchema<S>, ContainerDefaults> : F extends EmbeddedDocumentFieldInstance<infer Doc, infer O> ? Presence<O, InstanceType<Doc>, ReferenceDefaults> : F extends EmbeddedDataFieldInstance<infer Model, infer O> ? Presence<O, InstanceType<Model>, ContainerDefaults> : F extends TypedSchemaFieldInstance<infer T, infer O> ? Presence<O, TypedSchemaValue<T>, ContainerDefaults> : never;
509
635
  /**
510
636
  * Map a `defineSchema()` return value to the corresponding `system` shape.
511
637
  *
@@ -527,7 +653,6 @@ type InferField<F> = F extends NumberFieldInstance<infer O> ? Presence<O, number
527
653
  type InferSchema<S extends Record<string, FieldInstance>> = Prettify<{ [K in keyof S]: InferField<S[K]>; }>;
528
654
  //#endregion
529
655
  //#region src/base-type-data-model.d.ts
530
- type AnyConstructor = new (...args: any[]) => any;
531
656
  /**
532
657
  * Resolve the runtime base class, then build a mixin that adds VTTForge defaults.
533
658
  *
@@ -573,10 +698,10 @@ interface TypedTypeDataModelCtor<S extends Record<string, FieldInstance>> {
573
698
  /**
574
699
  * Build a base class with no knowledge of the schema.
575
700
  *
576
- * `this` inside the hooks is untyped. Pass your schema function instead to
577
- * get the fields typed.
701
+ * The hooks are typed; the schema's own fields are not, since nothing said
702
+ * what they are. Pass your schema function instead to get those too.
578
703
  */
579
- declare function BaseTypeDataModel(): AnyConstructor;
704
+ declare function BaseTypeDataModel(): VttforgeClass<TypeDataModelHooks>;
580
705
  /**
581
706
  * Build a base class that knows its schema.
582
707
  *
@@ -978,5 +1103,5 @@ declare class SystemConfig {
978
1103
  */
979
1104
  declare const VTTFORGE_CORE_VERSION = "0.2.0";
980
1105
  //#endregion
981
- export { type ActiveEffectConfig, type ArrayFieldCtor, type ArrayFieldInstance, type ArrayFieldOptions, BaseActorSheet, BaseItemSheet, BaseTypeDataModel, type BooleanFieldCtor, type BooleanFieldInstance, type BooleanFieldOptions, type ColorFieldCtor, type ColorFieldInstance, type ColorFieldOptions, type CombatConfig, type DataFieldOptions, type DocumentClass, type DragDropConfig, ERROR_MANIFEST_VERSION, type ErrorManifest, type FieldInstance, type FieldsApi, type FilePathFieldCtor, type FilePathFieldInstance, type FilePathFieldOptions, type ForeignDocumentFieldCtor, type ForeignDocumentFieldInstance, type ForeignDocumentFieldOptions, type FoundryConfig, type GameApi, type GameSettingsApi, type HTMLFieldCtor, type HTMLFieldInstance, type HTMLFieldOptions, type HookCallback, type HooksApi, type InferField, type InferSchema, type Migration, type MigrationLogger, type MigrationRunner, type MigrationRunnerOptions, type ModuleRegistration, type NumberFieldCtor, type NumberFieldInstance, type NumberFieldOptions, type Prettify, type SchemaFieldCtor, type SchemaFieldInstance, type SchemaFieldOptions, type SetFieldCtor, type SetFieldInstance, type SetFieldOptions, type SettingConfig, type SettingScope, type SheetBaseCtor, type SheetBaseStatics, type StringFieldCtor, type StringFieldInstance, type StringFieldOptions, SystemConfig, type SystemRegistration, type TypeDataModelHooks, type TypedTypeDataModel, type TypedTypeDataModelCtor, VTTFORGE_CORE_VERSION, VTTFORGE_SHEET_CLASS, VttfError, type VttfErrorCode, type VttfErrorEntry, createMigrationRunner, docsUrlFor, fields, getErrorEntry, getErrorManifest, listErrorEntries, moduleSubType, registerModule, registerSystem };
1106
+ export { type ActiveEffectConfig, type ArrayFieldCtor, type ArrayFieldInstance, type ArrayFieldOptions, BaseActorSheet, BaseApplication, type BaseApplicationMembers, BaseItemSheet, BaseTypeDataModel, type BooleanFieldCtor, type BooleanFieldInstance, type BooleanFieldOptions, type ColorFieldCtor, type ColorFieldInstance, type ColorFieldOptions, type CombatConfig, type DataFieldOptions, type DataModelClass, type DocumentClass, type DragDropConfig, ERROR_MANIFEST_VERSION, type EmbeddedDataFieldCtor, type EmbeddedDataFieldInstance, type EmbeddedDataFieldOptions, type EmbeddedDocumentFieldCtor, type EmbeddedDocumentFieldInstance, type EmbeddedDocumentFieldOptions, type ErrorManifest, type FieldInstance, type FieldsApi, type FilePathFieldCtor, type FilePathFieldInstance, type FilePathFieldOptions, type ForeignDocumentFieldCtor, type ForeignDocumentFieldInstance, type ForeignDocumentFieldOptions, type FoundryConfig, type GameApi, type GameSettingsApi, type HTMLFieldCtor, type HTMLFieldInstance, type HTMLFieldOptions, type HookCallback, type HooksApi, type InferField, type InferSchema, type Migration, type MigrationLogger, type MigrationRunner, type MigrationRunnerOptions, type ModuleRegistration, type NumberFieldCtor, type NumberFieldInstance, type NumberFieldOptions, type Prettify, type SchemaFieldCtor, type SchemaFieldInstance, type SchemaFieldOptions, type SetFieldCtor, type SetFieldInstance, type SetFieldOptions, type SettingConfig, type SettingScope, type SheetBaseCtor, type SheetBaseMembers, type SheetBaseStatics, type StringFieldCtor, type StringFieldInstance, type StringFieldOptions, SystemConfig, type SystemRegistration, type TypeDataModelHooks, type TypedSchemaFieldCtor, type TypedSchemaFieldInstance, type TypedSchemaFieldOptions, type TypedTypeDataModel, type TypedTypeDataModelCtor, type UntypedFoundryMembers, VTTFORGE_CORE_VERSION, VTTFORGE_SHEET_CLASS, VttfError, type VttfErrorCode, type VttfErrorEntry, type VttforgeClass, createMigrationRunner, docsUrlFor, fields, getErrorEntry, getErrorManifest, listErrorEntries, moduleSubType, registerModule, registerSystem };
982
1107
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/base-actor-sheet.ts","../src/base-item-sheet.ts","../src/data/field-options.ts","../src/data/fields.ts","../src/data/color.ts","../src/data/infer-schema.ts","../src/base-type-data-model.ts","../src/errors/registry.ts","../src/errors/manifest.ts","../src/foundry-globals.ts","../src/migrations/types.ts","../src/migrations/runner.ts","../src/register-module.ts","../src/register-system.ts","../src/system-config.ts","../src/index.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/foundry-base.ts","../src/base-actor-sheet.ts","../src/base-application.ts","../src/base-item-sheet.ts","../src/data/field-options.ts","../src/data/fields.ts","../src/data/color.ts","../src/data/infer-schema.ts","../src/base-type-data-model.ts","../src/errors/registry.ts","../src/errors/manifest.ts","../src/foundry-globals.ts","../src/migrations/types.ts","../src/migrations/runner.ts","../src/register-module.ts","../src/register-system.ts","../src/system-config.ts","../src/index.ts"],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -310,6 +310,53 @@ function BaseActorSheet() {
310
310
  return VttforgeBaseActorSheet;
311
311
  }
312
312
  //#endregion
313
+ //#region src/base-application.ts
314
+ /**
315
+ * BaseApplication — a plain `ApplicationV2` window, minus the two traps.
316
+ *
317
+ * The document sheets are covered by `BaseActorSheet` and `BaseItemSheet`.
318
+ * Everything else a package puts on screen — a config dialog, a picker, a
319
+ * reader window — is a bare `ApplicationV2`, and writing one by hand means
320
+ * meeting both of these:
321
+ *
322
+ * **`_replaceHTML` is easy to forget.** ApplicationV2 splits rendering in two:
323
+ * `_renderHTML` builds the content and `_replaceHTML` puts it in the window.
324
+ * Implement only the first and the class is silently unrenderable — Foundry
325
+ * says so at the moment something tries to open it, not when it is defined.
326
+ * Nearly every implementation of the second is the same line, so this ships
327
+ * it. Override it when the window updates in place instead of wholesale.
328
+ *
329
+ * **A missing `_renderHTML` fails late.** Foundry's own check fires on first
330
+ * render, which in practice means a user clicks something and gets an error
331
+ * about abstract methods. This checks at construction, so it fails where the
332
+ * class is used rather than deep inside a render.
333
+ */
334
+ function resolveApplicationV2() {
335
+ const cls = globalThis.foundry?.applications?.api?.ApplicationV2;
336
+ if (typeof cls !== "function") throw new VttfError("VTTF-0002", "foundry.applications.api.ApplicationV2 is not available. Define your BaseApplication subclasses inside the Foundry runtime (or stub the global in tests).");
337
+ return cls;
338
+ }
339
+ function BaseApplication() {
340
+ const Base = resolveApplicationV2();
341
+ class VttforgeBaseApplication extends Base {
342
+ constructor(...args) {
343
+ super(...args);
344
+ if (typeof this._renderHTML !== "function") throw new VttfError("VTTF-0002", `${this.constructor.name} extends BaseApplication but does not implement _renderHTML. ApplicationV2 cannot render without it.`);
345
+ }
346
+ /**
347
+ * Put the rendered content in the window.
348
+ *
349
+ * The whole-content swap, which is what almost every window wants.
350
+ * Override to update in place — a viewer that keeps scroll position
351
+ * across a page turn, say.
352
+ */
353
+ _replaceHTML(result, content) {
354
+ content.replaceChildren(result);
355
+ }
356
+ }
357
+ return VttforgeBaseApplication;
358
+ }
359
+ //#endregion
313
360
  //#region src/base-item-sheet.ts
314
361
  function resolveBases() {
315
362
  const foundry = globalThis.foundry;
@@ -906,6 +953,6 @@ var SystemConfig = class {
906
953
  */
907
954
  const VTTFORGE_CORE_VERSION = "0.2.0";
908
955
  //#endregion
909
- export { BaseActorSheet, BaseItemSheet, BaseTypeDataModel, ERROR_MANIFEST_VERSION, SystemConfig, VTTFORGE_CORE_VERSION, VTTFORGE_SHEET_CLASS, VttfError, createMigrationRunner, docsUrlFor, fields, getErrorEntry, getErrorManifest, listErrorEntries, moduleSubType, registerModule, registerSystem };
956
+ export { BaseActorSheet, BaseApplication, BaseItemSheet, BaseTypeDataModel, ERROR_MANIFEST_VERSION, SystemConfig, VTTFORGE_CORE_VERSION, VTTFORGE_SHEET_CLASS, VttfError, createMigrationRunner, docsUrlFor, fields, getErrorEntry, getErrorManifest, listErrorEntries, moduleSubType, registerModule, registerSystem };
910
957
 
911
958
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["resolveBases","resolveDragDrop","registered","vttfError","readHooks","readConfig","applyInit"],"sources":["../src/errors/registry.ts","../src/base-actor-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-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});\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';\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\n // ApplicationV2 options into this; 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 */\nexport interface SheetBaseCtor extends SheetBaseStatics {\n // biome-ignore lint/suspicious/noExplicitAny: mirrors ApplicationV2's own\n // constructor arity, which subclasses pass straight through.\n new (...args: any[]): any;\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 * 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';\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 declaring its own\n // constructor has to pass Foundry's (data, context) pair 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 * `this` inside the hooks is untyped. Pass your schema function instead to\n * get the fields typed.\n */\nexport function BaseTypeDataModel(): AnyConstructor;\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 FilePathFieldOptions,\n ForeignDocumentFieldOptions,\n HTMLFieldOptions,\n NumberFieldOptions,\n SchemaFieldOptions,\n SetFieldOptions,\n StringFieldOptions,\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\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\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}\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 * 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';\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 /** 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\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';\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 * 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\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\nexport const VTTFORGE_CORE_VERSION = '0.2.0';\n\nexport {\n BaseActorSheet,\n type DragDropConfig,\n type SheetBaseCtor,\n type SheetBaseStatics,\n VTTFORGE_SHEET_CLASS,\n} from './base-actor-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 FilePathFieldOptions,\n ForeignDocumentFieldOptions,\n HTMLFieldOptions,\n NumberFieldOptions,\n SchemaFieldOptions,\n SetFieldOptions,\n StringFieldOptions,\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 DocumentClass,\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} 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 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 ModuleRegistration,\n moduleSubType,\n registerModule,\n} from './register-module.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","registered","vttfError","readHooks","readConfig","applyInit"],"sources":["../src/errors/registry.ts","../src/base-actor-sheet.ts","../src/base-application.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-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});\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 { UntypedFoundryMembers } 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 `UntypedFoundryMembers`.\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\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 & UntypedFoundryMembers;\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 { 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<BaseApplicationMembers> {\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<BaseApplicationMembers>;\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 * 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';\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 /** 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\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';\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 * 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\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\nexport const VTTFORGE_CORE_VERSION = '0.2.0';\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 { 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 { UntypedFoundryMembers, VttforgeClass } 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 ModuleRegistration,\n moduleSubType,\n registerModule,\n} from './register-module.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,6 +1,6 @@
1
1
  {
2
2
  "name": "@vttforge/core",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "description": "VTTForge core runtime: BaseTypeDataModel, BaseActorSheet, SystemConfig, registerSystem, error registry.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -43,9 +43,6 @@
43
43
  "unrun": "^0.3.1",
44
44
  "vitest": "^4.1.11"
45
45
  },
46
- "engines": {
47
- "node": ">=26.0.0"
48
- },
49
46
  "publishConfig": {
50
47
  "access": "public",
51
48
  "provenance": false