UNPKG

fvtt-types

Version:
1,248 lines (1,096 loc) 215 kB
import type { RemoveIndexSignatures, SimpleMerge, AnyObject, EmptyObject, NullishProps, InexactPartial, FixedInstanceType, Identity, PrettifyType, InterfaceToObject, AnyArray, GetKey, SplitString, ValueOf, AnyMutableObject, ConcreteKeys, } from "#utils"; import type { DataModel } from "../abstract/data.mts"; import type Document from "../abstract/document.mts"; import type { EmbeddedCollection, EmbeddedCollectionDelta, TypeDataModel } from "../abstract/_module.d.mts"; import type { DOCUMENT_OWNERSHIP_LEVELS } from "../constants.d.mts"; import type { CONST } from "#client/client.d.mts"; import type { DataModelValidationFailure } from "./validation-failure.mts"; import type { _FormInputConfig, FormGroupConfig, FormInputConfig, MultiSelectInputConfig, SelectInputConfig, TextAreaInputConfig, } from "#client/applications/forms/fields.d.mts"; export type DataSchema = Record<string, DataField.Any>; /** * An abstract class that defines the base pattern for a data field within a data schema. * @template Options - the options of the DataField instance * @template AssignmentType - the type of the allowed assignment values of the DataField * @template InitializedType - the type of the initialized values of the DataField * @template PersistedType - the type of the persisted values of the DataField * @remarks * Defaults: * - AssignmentType: `unknown | null | undefined` * - InitializedType: `unknown | undefined` * - PersistedType: `unknown | undefined` * - InitialValue: `undefined` */ declare abstract class DataField< const Options extends DataField.Options.Any = DataField.DefaultOptions, // eslint-disable-next-line @typescript-eslint/no-deprecated const AssignmentType = DataField.AssignmentType<Options>, const InitializedType = DataField.InitializedType<Options>, const PersistedType = InitializedType, > { // Prevent from being bivariant. #assignmentType: AssignmentType; /** * @param options - Options which configure the behavior of the field * @param context - Additional context which describes the field */ constructor(options?: Options, context?: DataField.ConstructionContext); /** @internal */ " __fvtt_types_internal_source_data": PersistedType; /** @internal */ " __fvtt_types_internal_assignment_data": AssignmentType; /** @internal */ " __fvtt_types_internal_initialized_data": InitializedType; /** * The field name of this DataField instance. * This is assigned by {@linkcode SchemaField.initialize | SchemaField#initialize}. * @internal */ name: string | undefined; /** * A reference to the parent schema to which this DataField belongs. * This is assigned by {@linkcode SchemaField.initialize | SchemaField#initialize}. * @internal */ parent: DataField.Any | undefined; /** The initially provided options which configure the data field */ options: Options; /** * Whether this field defines part of a Document/Embedded Document hierarchy. * @defaultValue `false` */ static hierarchical: boolean; /** * Does this field type contain other fields in a recursive structure? * Examples of recursive fields are SchemaField, ArrayField, or TypeDataField * Examples of non-recursive fields are StringField, NumberField, or ObjectField * @defaultValue `false` */ static recursive: boolean; /** * Is this field required to be populated? * @defaultValue `false` */ required: boolean; /** * Can this field have null values? * @defaultValue `false` */ nullable: boolean; /** * Can this field only be modified by a gamemaster or assistant gamemaster? * @defaultValue `false` */ gmOnly: boolean; /** * The initial value of a field, or a function which assigns that initial value. * @defaultValue `undefined` */ initial: DataField.Options.InitialType<InitializedType>; /** * Should the prepared value of the field be read-only, preventing it from being * changed unless a change to the _source data is applied. * @defaultValue `false` */ readonly: boolean; /** * A localizable label displayed on forms which render this field. * @defaultValue `""` */ label: string; /** * Localizable help text displayed on forms which render this field. * @defaultValue `""` */ hint: string; /** * A custom validation error string. When displayed will be prepended with the * document name, field name, and candidate value. This error string is only * used when the return type of the validate function is a boolean. If an Error * is thrown in the validate function, the string message of that Error is used. * @defaultValue `"is not a valid value"` */ validationError: string; /** * Default parameters for this field type * @remarks This is not entirely type-safe, overrides should specify a more concrete return type. */ protected static get _defaults(): DataField.Options.Any; /** * A dot-separated string representation of the field path within the parent schema. * @remarks Returns `""` if both `this.parent?.fieldPath` and `this.name` are falsey */ get fieldPath(): string; /** * Apply a function to this DataField which propagates through recursively to any contained data schema. * @param fn - The function to apply * @param value - The current value of this field * @param options - Additional options passed to the applied function (default `{}`) * @returns The results object */ // TODO: Determine `value` based upon the field metadata in fields-v2 (while allowing subclasses to narrow allowed values) apply<Options, Return>( fn: keyof this | ((this: this, value: unknown, options: Options) => Return), value?: unknown, options?: Options, ): Return; /** * Add types of the source to the data if they are missing. * @param source - The source data * @param changes - The partial data * @param options - Additional options (default: `{}`) * @internal * * @remarks * Called externally by Foundry in `ClientDatabaseBackend##preUpdateDocumentArray`, {@link DataModel.validate | `DataModel#validate`}, * and {@link DataModel.updateSource | `DataModel#updateSource`}. * * The `options` arg is not expected to be passed, it's assembled using the passed `source` and `changes` then used internally for recursive calls. */ protected _addTypes(source?: AnyObject, changes?: AnyObject, options?: DataField.AddTypesOptions): void; /** * Recursively traverse a schema and retrieve a field specification by a given path * @param path - The field path as an array of strings * @returns The corresponding DataField definition for that field, or undefined * @internal */ protected _getField(path: string[]): DataField.Any | undefined; /** * Coerce source data to ensure that it conforms to the correct data type for the field. * Data coercion operations should be simple and synchronous as these are applied whenever a DataModel is constructed. * For one-off cleaning of user-provided input the sanitize method should be used. * @param value - An initial requested value * @param options - Additional options for how the field is cleaned * @returns The cast value */ // TODO (LukeAbby): Because `getInitialValue` trusts function `initial`s too much, this can actually return `| null | undefined` regardless of options, if `value === undefined` clean(value: AssignmentType, options?: DataField.CleanOptions): InitializedType; /** * Apply any cleaning logic specific to this DataField type. * @param value - The appropriately coerced value. * @param options - Additional options for how the field is cleaned. * @returns The cleaned value. * @remarks Simply returns `value` in `DataField`. `options` is unused in `DataField` */ protected _cleanType(value: InitializedType, options?: DataField.CleanOptions): InitializedType; /** * Cast a non-default value to ensure it is the correct type for the field * @param value - The provided non-default value * @returns The standardized value * @remarks No longer so effectively abstract in v13, `DataField`'s implementation now simply returns the provided value, * but since subclasses *should* still implement an `_cast` that matches their `AssignmentType` and `InitializedType`, it * remains `abstract` here */ protected abstract _cast(value: unknown): AssignmentType; /** * Attempt to retrieve a valid initial value for the DataField. * @param data - The source data object for which an initial value is required * @returns A valid initial value * @remarks `data` is unused if the field's `initial` is not a function. */ getInitialValue(data?: unknown): InitializedType; /** * Export the current value of the field into a serializable object. * @param value - The initialized value of the field * @returns An exported representation of the field */ toObject(value: InitializedType): PersistedType; /** * Validate a candidate input for this field, ensuring it meets the field requirements. * A validation failure can be provided as a raised Error (with a string message), by returning false, or by returning * a DataModelValidationFailure instance. * A validator which returns true denotes that the result is certainly valid and further validations are unnecessary. * @param value - The initial value * @param options - Options which affect validation behavior (default: `{}`) * @returns Returns a ModelValidationError if a validation failure occurred */ validate(value: AssignmentType, options?: DataField.ValidateOptions<this>): DataModelValidationFailure | void; /** * Special validation rules which supersede regular field validation. * This validator screens for certain values which are otherwise incompatible with this field like null or undefined. * @param value - The candidate value * @returns A boolean to indicate with certainty whether the value is valid. * Otherwise, return void. * @throws May throw a specific error if the value is not valid */ protected _validateSpecial(value: AssignmentType): boolean | void; /** * A default type-specific validator that can be overridden by child classes * @param value - The candidate value * @param options - Options which affect validation behavior * @returns A boolean to indicate with certainty whether the value is valid, or specific DataModelValidationFailure information, otherwise void. * @throws May throw a specific error if the value is not valid */ protected _validateType( value: InitializedType, options?: DataField.ValidateOptions<this>, ): boolean | DataModelValidationFailure | void; /** * Certain fields may declare joint data validation criteria. * This method will only be called if the field is designated as recursive. * @param data - Candidate data for joint model validation * @param options - Options which modify joint model validation * @throws An error if joint model validation fails * @internal * * @remarks Core never checks the return of this, it should simply either `throw` or not `throw` * * The only place core checks the `options` for any property is in {@link TypeDataField._validateModel | `TypeDataField#_validateModel`}, * where it checks `options.source?.type` * * {@link SchemaField._validateModel | `SchemaField._validateModel`} enforces `source`'s existence for subsidiary calls * * The only place core *calls* this at a top level, it does not pass anything for `options`, relying on SchemaField above * to make TypeDataField work */ protected _validateModel(data: AnyObject, options?: DataField.ValidateModelOptions): void; /** * Initialize the original source data into a mutable copy for the DataModel instance. * @param value - The source value of the field * @param model - The DataModel instance that this field belongs to * @param options - Initialization options * @returns An initialized copy of the source data * @remarks Core fields that return a function: * - {@link ForeignDocumentField | `ForeignDocumentField`} * - `ActorDeltaField` (exported in the BaseToken file but not re-exported by the relevant `_module`, so unlinkable) */ // TODO: investigate narrowing return to just `InitializedType` on inheritance lines that don't possibly return one // TODO: (everything except SchemaField and ObjectField and their descendants) initialize( value: PersistedType, model: DataModel.Any, options?: DataField.InitializeOptions, ): InitializedType | (() => InitializedType | null); /** * Update the source data for a DataModel which includes this DataField. * This method is responsible for modifying the provided source data as well as updating the tracked diff included * in provided metadata. * @param source - Source data of the DataModel which should be updated. This object is always a partial node of source data, relative to which this field belongs. * @param key - The name of this field within the context of the source data. * @param value - The candidate value that should be applied as an update. * @param difference - The accumulated diff that is recursively populated as the model traverses through its schema fields. * @param options - Options which modify how this update workflow is performed. * @throws An error if the requested update cannot be performed. * @internal * @remarks Only `recursive` is checked in `options` by any core fields. Mutates `source`. * * Called externally by Foundry in {@link DataModel.updateSource | `DataModel#updateSource`} and various core field class's overrides (`this.element._updateDiff()`, `field._updateDiff()` etc); * it's been left public for use in user subclasses */ _updateDiff( source: AnyMutableObject, key: string, value: unknown, difference: AnyObject, options?: DataModel.UpdateOptions, ): void; /** * Commit a prepared update to DataModel#_source. * @param source - The parent source object within which the `key` field exists * @param key - The named field in source to commit * @param value - The new value of the field which should be committed to source * @param diff - The reported change to the field * @param options - Options which modify how this update workflow is performed. * @internal * @remarks Mutates `source`. * * Called externally by Foundry in {@link DataModel.updateSource | `DataModel#updateSource`} and various core field class's overrides (`this.element._updateCommit()`, `field._updateCommit()` etc); * it's been left public for use in user subclasses */ _updateCommit( source: AnyMutableObject, key: string, value: unknown, diff: unknown, options?: DataModel.UpdateOptions, ): void; /** * Does this form field class have defined form support? */ static get hasFormSupport(): boolean; /** * Render this DataField as an HTML element. * @param config - Form element configuration parameters * @throws An Error if this DataField subclass does not support input rendering * @returns A rendered HTMLElement for the field */ toInput(config?: DataField.ToInputConfig<InitializedType>): HTMLElement | HTMLCollection; /** * Render this DataField as an HTML element. * Subclasses should implement this method rather than the public toInput method which wraps it. * @param config - Form element configuration parameters * @throws An Error if this DataField subclass does not support input rendering * @returns A rendered HTMLElement for the field * @remarks Would be `abstract` except not all fields are designed to be used in forms */ protected _toInput(config: DataField.ToInputConfig<InitializedType>): HTMLElement | HTMLCollection; /** * Render this DataField as a standardized form-group element. * @param groupConfig - Configuration options passed to the wrapping form-group * @param inputConfig - Input element configuration options passed to DataField#toInput * @returns The rendered form group element */ toFormGroup( groupConfig?: DataField.GroupConfig, inputConfig?: DataField.ToInputConfig<InitializedType>, ): HTMLDivElement; /** * Apply an ActiveEffectChange to this field. * @param value - The field's current value. * @param model - The model instance. * @param change - The change to apply. * @returns The updated value. */ applyChange(value: InitializedType, model: DataModel.Any, change: ActiveEffect.ChangeData): InitializedType; /** * Cast a change delta into an appropriate type to be applied to this field. * @param delta - The change delta. * @internal */ // Note(LukeAbby): Technically since this defers to `_cast` it should take whatever `_cast` can. // But it always must be able to take a `string` because that's how `applyChange` calls it. protected _castChangeDelta(delta: string): InitializedType; /** * Apply an ADD change to this field. * @param value - The field's current value. * @param delta - The change delta. * @param model - The model instance. * @param change - The original change data. * @returns The updated value. * * @remarks Returns `value + delta`. `model` and `change` are unused in `DataField` */ protected _applyChangeAdd( value: InitializedType, delta: InitializedType, model: DataModel.Any, change: ActiveEffect.ChangeData, ): InitializedType; /** * Apply a MULTIPLY change to this field. * @param value - The field's current value. * @param delta - The change delta. * @param model - The model instance. * @param change - The original change data. * @returns The updated value. * * @remarks No-op in `DataField`, returns `undefined` unless overridden */ protected _applyChangeMultiply( value: InitializedType, delta: InitializedType, model: DataModel.Any, change: ActiveEffect.ChangeData, ): InitializedType | undefined; /** * Apply an OVERRIDE change to this field. * @param value - The field's current value. * @param delta - The change delta. * @param model - The model instance. * @param change - The original change data. * @returns The updated value. * * @returns Simply returns `delta`. `value`, `model`, and `change` are unused in `DataField` */ protected _applyChangeOverride( value: InitializedType, delta: InitializedType, model: DataModel.Any, change: ActiveEffect.ChangeData, ): InitializedType; /** * Apply an UPGRADE change to this field. * @param value - The field's current value. * @param delta - The change delta. * @param model - The model instance. * @param change - The original change data. * @returns The updated value. * * @remarks No-op in `DataField`, returns `undefined` unless overridden */ protected _applyChangeUpgrade( value: InitializedType, delta: InitializedType, model: DataModel.Any, change: ActiveEffect.ChangeData, ): InitializedType | undefined; /** * Apply a DOWNGRADE change to this field. * @param value - The field's current value. * @param delta - The change delta. * @param model - The model instance. * @param change - The original change data. * @returns The updated value. * * @remarks No-op in `DataField`, returns `undefined` unless overridden */ protected _applyChangeDowngrade( value: InitializedType, delta: InitializedType, model: DataModel.Any, change: ActiveEffect.ChangeData, ): InitializedType | undefined; /** * Apply a CUSTOM change to this field. * @param value - The field's current value. * @param delta - The change delta. * @param model - The model instance. * @param change - The original change data. * @returns The updated value. * @remarks Only returns a value if the target value of the change actually changed */ protected _applyChangeCustom( value: InitializedType, delta: InitializedType, model: DataModel.Any, change: ActiveEffect.ChangeData, ): InitializedType | undefined; } declare namespace DataField { /** Any DataField. */ interface Any extends AnyDataField {} interface AnyConstructor extends Identity<typeof AnyDataField> {} /** A DataField with unknown inner types. */ type Unknown = DataField<any, unknown, unknown, unknown>; namespace Internal { interface ElementFieldImplementation<Element extends DataField.Any = DataField.Any> { " __fvtt_types_get_field_element": Element; } interface NestedFieldImplementation<Schema extends DataSchema = DataSchema> { " __fvtt_types_get_field_schema": Schema; } } /** * @deprecated AssignmentType is being deprecated. See {@linkcode SchemaField.AssignmentData} * for more details. */ type AssignmentTypeFor<ConcreteDataField extends Any> = ConcreteDataField[" __fvtt_types_internal_assignment_data"]; type InitializedTypeFor<ConcreteDataField extends Any> = ConcreteDataField[" __fvtt_types_internal_initialized_data"]; type PersistedTypeFor<ConcreteDataField extends Any> = ConcreteDataField[" __fvtt_types_internal_source_data"]; /** The type of the default options for the {@linkcode DataField} class. */ interface DefaultOptions { required: false; nullable: false; initial: undefined; readonly: false; gmOnly: false; label: ""; hint: ""; validationError: "is not a valid value"; } /** @internal */ type _Options<BaseAssignmentType> = InexactPartial<{ /** * Is this field required to be populated? * @defaultValue `false` */ required: boolean; /** * Can this field have null values? * @defaultValue `false` */ nullable: boolean; /** * Can this field only be modified by a gamemaster or assistant gamemaster? * @defaultValue `false` */ gmOnly: boolean; /** The initial value of a field, or a function which assigns that initial value. */ initial: DataField.Options.InitialType< // TODO(LukeAbby): Add a `ValidateOptions` type or something of that sort in order to // catch incorrect initial types. DataField.Options.InitialReturnType<BaseAssignmentType, boolean, boolean> >; /** A localizable label displayed on forms which render this field. */ label: string; /** Localizable help text displayed on forms which render this field. */ hint: string; /** A data validation function which accepts one argument with the current value. */ validate: DataField.Validator<DataField.Any, BaseAssignmentType>; /** * A custom validation error string. When displayed will be prepended with the * document name, field name, and candidate value. This error string is only * used when the return type of the validate function is a boolean. If an Error * is thrown in the validate function, the string message of that Error is used. */ validationError: string; }>; interface Options<BaseAssignmentType> extends _Options<BaseAssignmentType> {} namespace Options { /** Any DataField.Options. */ // Note(LukeAbby): Extending `Identity<object>` is intentional. Its purpose is to allow options like `{ integer: true }` to be assigned. // This is an issue because `{ integer: true }` does not extend `{ required?: boolean }` because they have no properties in common. // Even though `{ integer: true, required: undefined }` would extend `{ required?: boolean }` following the regular rules of surplus properties being allowed. // `object` was chosen over `AnyObject` so that people may pass in interfaces. interface Any extends DataField.Options<any>, Identity<object> {} /** * A helper type for the {@linkcode DataField.Options.initial} option. * @template ReturnType - the return type of the option */ type InitialType<ReturnType> = ReturnType | ((initialData: unknown) => ReturnType); /** * The decorated return type for the {@linkcode DataField.Options.initial} option. * @template BaseAssignmentType - the base assignment type for a DataField * @template NullableOption - the value of the nullable option * @template RequiredOption - the value of the required option */ type InitialReturnType<BaseAssignmentType, NullableOption, RequiredOption> = | Exclude<BaseAssignmentType, null | undefined> | (NullableOption extends true ? null : never) | (RequiredOption extends true ? never : undefined); } /** * A helper type for the given options type merged into the default options of the DataField class. * @template Options - the options that override the default options */ type MergedOptions<Options extends DataField.Options.Any> = SimpleMerge<DefaultOptions, Options>; /** * A type to decorate the base assignment type to a DataField, based on the options of the field. * @template BaseAssignmentType - the base assignment type of the DataField, without null or undefined * @template Options - the options of the DataField * * @deprecated This type is being phased out alongside the entirety of the concept of the * `Assignment` type. */ type DerivedAssignmentType<BaseAssignmentType, Options extends DataField.Options.Any> = | Exclude<BaseAssignmentType, null | undefined> // Always include the base type | (Options["nullable"] extends true // determine whether null is in the union ? // when nullable, both `null` and `undefined` can safely be passed null | undefined : never) | (Options["required"] extends true // determine whether undefined is in the union ? never : // when not required, both `null` and `undefined` can safely be passed null | undefined) | ("initial" extends keyof Options ? // TODO(LukeAbby): This should possibly actually be distributive. Options["initial"] extends undefined ? never : null | undefined // when initial is not `undefined` then `null | undefined` are valid. : never); /** @internal */ type _Has<T, U> = U extends unknown ? (U extends T ? true : false) : never; /** * A type to decorate the base initialized type of a DataField, based on the options of the field. * @template BaseInitializedType - the base initialized type of the DataField, without null or undefined * @template Options - the options of the DataField */ type DerivedInitializedType<BaseInitializedType, Options extends DataField.Options.Any> = | Exclude<BaseInitializedType, null | undefined> | _DerivedUndefined<GetKey<Options, "initial", undefined>, GetKey<Options, "required", undefined>> | (Options["nullable"] extends true ? null : never); /** @internal */ type _DerivedUndefined<Initial, Required extends boolean | undefined> = Required extends true ? never // If `required: true` then `undefined` is not possible. : Initial extends undefined ? undefined // If `required: false` and `initial: undefined` then it passes through. : Initial extends (...args: never) => infer Result ? Result extends undefined ? undefined // Passes through `undefined` if the function returns `undefined`. Should this match the case of `() => unknown`? : never : never; /** * A shorthand for the assignment type of a DataField class. * @template Options - the options overriding the defaults * * @deprecated AssignmentData is being phased out. See {@linkcode SchemaField.AssignmentData} * for more details. */ // eslint-disable-next-line @typescript-eslint/no-deprecated type AssignmentType<Options extends DataField.Options.Any> = DerivedAssignmentType<unknown, MergedOptions<Options>>; /** * A shorthand for the initialized type of a DataField class. * @template Options - the options overriding the defaults */ type InitializedType<Options extends DataField.Options.Any> = DerivedInitializedType<unknown, MergedOptions<Options>>; /** @internal */ type _ConstructionContext = InexactPartial<{ /** A field name to assign to the constructed field */ name: string; /** Another data field which is a hierarchical parent of this one */ parent: DataField.Any; }>; interface ConstructionContext extends _ConstructionContext {} /** @internal */ type _AddTypesOptions = InexactPartial<{ /** * The root data model source * @remarks Not expected to be passed externally, the top level `_addTypes` call sets this to the passed `source`, * making it available to subsidiary calls */ source: AnyObject; /** * The root data model changes * @remarks Not expected to be passed externally, the top level `_addTypes` call sets this to the passed `changes`, * making it available to subsidiary calls */ changes: AnyObject; }>; interface AddTypesOptions extends _AddTypesOptions {} /** @internal */ type _ValidationOptions = InexactPartial<{ /** Whether this is a partial schema validation, or a complete one. */ partial: boolean; /** Whether to allow replacing invalid values with valid fallbacks. */ fallback: boolean; /** * If true, invalid embedded documents will emit a warning and be placed in the `invalidDocuments` * collection rather than causing the parent to be considered invalid. */ dropInvalidEmbedded: boolean; /** The full source object being evaluated. */ source: AnyObject; }>; /** * @remarks This is the type for the options for `#validate` and associate methods *without* the * possible inclusion of a `validator` function. * * If you are looking for the type with a generic formerly under this name, see {@link ValidateOptions | `DataField.ValidateOptions`} */ interface ValidationOptions extends _ValidationOptions {} /** @internal */ type _CleanOptions = InexactPartial<{ /** Whether to perform partial cleaning? */ partial: boolean; /** The root data model being cleaned */ source: AnyObject; }>; /** An interface for the options of {@link DataField.clean | `DataField#clean`} and {@link DataField._cleanType | `DataField#_cleanType`}. */ interface CleanOptions extends _CleanOptions {} /** * @remarks The only place core checks the `options` for any property is in {@link TypeDataField._validateModel | `TypeDataField#_validateModel`}, * where it checks `options.source?.type` * * {@link SchemaField._validateModel | `SchemaField._validateModel`} enforces `source`'s existence for subsidiary calls * * The only place core *calls* this at a top level, it does not pass anything for `options`, relying on SchemaField above * to make TypeDataField work */ interface ValidateModelOptions extends Pick<ValidationOptions, "source"> {} /** * A Custom DataField validator function. * * A boolean return value indicates that the value is valid (true) or invalid (false) with certainty. With an explicit * boolean return value no further validation functions will be evaluated. * * An undefined return indicates that the value may be valid but further validation functions should be performed, * if defined. * * An Error may be thrown which provides a custom error message explaining the reason the value is invalid. */ type Validator<CurrentField extends DataField.Any, BaseAssignmentType> = | { validate( this: CurrentField, value: unknown, options: ValidateOptions<CurrentField>, ): value is BaseAssignmentType; }["validate"] | { validate( this: CurrentField, value: unknown, options: ValidateOptions<CurrentField>, ): asserts value is BaseAssignmentType; }["validate"] | { validate( this: CurrentField, value: unknown, options: ValidateOptions<CurrentField>, ): DataModelValidationFailure | boolean | void; }["validate"]; /** * An interface for the options of the {@linkcode DataField} validation functions. * @template CurrentField - the type of the DataField, which is the receiver of the validate function */ interface ValidateOptions<CurrentField extends DataField.Any> extends ValidationOptions { /** * @remarks If {@link DataField.validate | `DataField#validate`} is called with a `validate: someFunc` in its `options`, * it will then pass that `options` object on to that function when it calls it, without alteration. * Nothing in core makes use of the fact that a reference to the function is available, this seems incidental. */ // eslint-disable-next-line @typescript-eslint/no-deprecated validate?: Validator<CurrentField, DataField.AssignmentTypeFor<CurrentField>>; } /** * @remarks The `options` passed to {@link DataField.initialize | `DataField#initialize`} exclusively (in core) come from * {@link DataModel._initialize | `DataModel#_initialize`} or an override (meaning `parent` has been stripped from the * interface), and eventually hits one of: * 1. Document construction, in all cases with `parent` already provided * 2. Gets fed back {@link DataModel._initialize | `DataModel#_initialize`} or an override * 3. {@link Document.get | `Document.get`}, but the one place this happens, `pack` is already provided, and that's the only * option that method cares about. * * This extends the `Document` interface because several core fields use the `pack` property, which isn't available on the * `DataModel` interface */ interface InitializeOptions extends Document.InitializeOptions {} interface ToInputConfig<InitializedType> extends FormInputConfig<InitializedType> {} interface ToInputConfigWithOptions<InitializedType> extends FormInputConfig<InitializedType>, SelectInputConfig {} type AnyChoices = StringField.Choices | NumberField.Choices; type ToInputConfigWithChoices<InitializedType, Choices extends AnyChoices | undefined> = SimpleMerge< Omit<ToInputConfigWithOptions<InitializedType>, "options">, Choices extends undefined ? StringField.PrepareChoiceConfig : Omit<StringField.PrepareChoiceConfig, "choices"> & { choices?: StringField.PrepareChoiceConfig["choices"] | undefined; } >; type SelectableToInputConfig<InitializedType, Choices extends StringField.Choices | undefined> = | ToInputConfig<InitializedType> | ToInputConfigWithOptions<InitializedType> | ToInputConfigWithChoices<InitializedType, Choices>; /** * `label`, `hint`, and `input` are all provided defaults. */ interface GroupConfig extends Omit<FormGroupConfig, "label" | "hint" | "input"> { label?: FormGroupConfig["label"] | undefined; hint?: FormGroupConfig["hint"] | undefined; input?: FormGroupConfig["input"] | undefined; } } declare abstract class AnyDataField extends DataField<any, any, any, any> { constructor(...args: never); } /** * A special class of {@linkcode DataField} which defines a data schema. * @template Fields - the DataSchema fields of the SchemaField * @template Options - the options of the SchemaField instance * @template AssignmentType - the type of the allowed assignment values of the SchemaField * @template InitializedType - the type of the initialized values of the SchemaField * @template PersistedType - the type of the persisted values of the SchemaField * @remarks * Defaults: * - AssignmentType: `SchemaField.AssignmentType<Fields> | null | undefined` * - InitializedType: `SchemaField.InitializedType<Fields>` * - PersistedType: `SchemaField.PersistedType<Fields>` */ declare class SchemaField< const Fields extends DataSchema, const Options extends SchemaField.Options<Fields> = SchemaField.DefaultOptions, // eslint-disable-next-line @typescript-eslint/no-deprecated const AssignmentType = SchemaField.Internal.AssignmentType<Fields, Options>, const InitializedType = SchemaField.Internal.InitializedType<Fields, Options>, const PersistedType extends AnyObject | null | undefined = SchemaField.Internal.PersistedType<Fields, Options>, > extends DataField<Options, AssignmentType, InitializedType, PersistedType> implements DataField.Internal.NestedFieldImplementation { /** * @param fields - The contained field definitions * @param options - Options which configure the behavior of the field * @param context - Additional context which describes the field */ // Saying `fields: Fields` here causes the inference for the fields to be unnecessarily widened. This might effectively be a no-op but it fixes the inference. // options: not null (unchecked `in` operation in super), context: not null (destructured in super) constructor(fields: { [K in keyof Fields]: Fields[K] }, options?: Options, context?: DataField.ConstructionContext); /** @internal */ " __fvtt_types_get_field_schema": Fields; /** @defaultValue `true` */ override required: boolean; /** @defaultValue `false` */ override nullable: boolean; protected static override get _defaults(): SchemaField.Options<DataSchema>; /** @defaultValue `true` */ static override recursive: boolean; /** * The contained field definitions. */ fields: Fields; /** * Initialize and validate the structure of the provided field definitions. * @param fields - The provided field definitions * @returns The validated schema * @remarks * @throws If any field is named `_source` */ protected _initialize(fields: Fields): Fields; /** * Iterate over a SchemaField by iterating over its fields. */ [Symbol.iterator](): Generator<DataField.Unknown, void, undefined>; // TODO: see if its viable to narrow keys, values, entries, has, and get's types via the schema /** * An array of field names which are present in the schema. */ keys(): string[]; /** * An array of DataField instances which are present in the schema. */ values(): DataField.Unknown[]; /** * An array of [name, DataField] tuples which define the schema. */ entries(): [name: string, dataField: DataField.Unknown][]; /** * Test whether a certain field name belongs to this schema definition. * @param fieldName - The field name * @returns Does the named field exist in this schema? */ has(fieldName: string): boolean; /** * Get a DataField instance from the schema by name * @param fieldName - The field name * @returns The DataField instance or undefined */ // TODO(LukeAbby): Enabling this signatures causes a circularity but it would be ideal. // get<FieldName extends string>(fieldName: OverlapsWith<FieldName, keyof Fields>): SchemaField.Get<Fields, FieldName>; get(fieldName: string): DataField.Unknown | void; /** * Traverse the schema, obtaining the DataField definition for a particular field. * @param fieldName - A field path like ["abilities", "strength"] or "abilities.strength" * @returns The corresponding DataField definition for that field, or undefined */ getField(fieldName: string | string[]): DataField.Unknown | undefined; // TODO(LukeAbby): Enabling this signatures causes a circularity but it would be ideal. // getField<FieldName extends SchemaField.FieldName<Fields>>( // fieldName: FieldName, // ): SchemaField.GetField<this, Fields, FieldName>; protected override _getField(path: string[]): DataField.Any | undefined; override getInitialValue(data?: unknown): InitializedType; protected override _cast(value: unknown): AssignmentType; /** * @remarks Ensures `options.source` is set via effectively `||= data`, then forwards to each field's `#clean` * * Deletes any keys from `value` not in the schema, including `-=` and `==` keys */ protected override _cleanType(value: InitializedType, options?: DataField.CleanOptions): InitializedType; override initialize( value: PersistedType, model: DataModel.Any, options?: DataField.InitializeOptions, ): InitializedType | (() => InitializedType | null); override _updateDiff( source: AnyMutableObject, key: string, value: unknown, difference: AnyObject, options?: DataModel.UpdateOptions, ): void; override _updateCommit( source: AnyMutableObject, key: string, value: unknown, diff: unknown, options?: DataModel.UpdateOptions, ): void; protected override _validateType( value: InitializedType, options?: DataField.ValidateOptions<this>, ): boolean | DataModelValidationFailure | void; protected override _validateModel(data: AnyObject, options?: DataField.ValidateModelOptions): void; override toObject(value: InitializedType): PersistedType; override apply<Options, Return>( fn: keyof this | ((this: this, value: AnyObject, options: Options) => Return), value?: AnyObject, options?: Options, ): Return; protected override _addTypes(source?: AnyObject, changes?: AnyObject, options?: DataField.AddTypesOptions): void; /** * Migrate this field's candidate source data. * @param sourceData - Candidate source data of the root model * @param fieldData - The value of this field within the source data */ migrateSource(sourceData: AnyObject, fieldData: unknown): unknown; } declare namespace SchemaField { /** * A shorthand for the options of a SchemaField class. * @template Fields - the DataSchema fields of the SchemaField */ // eslint-disable-next-line @typescript-eslint/no-deprecated type Options<Fields extends DataSchema> = DataField.Options<AssignmentData<Fields>>; /** Any SchemaField. */ interface Any extends SchemaField<any, any, any, any, any> {} /** * Get the constructor type for the given DataSchema. * @template Fields - the DataSchema fields of the SchemaField */ type CreateData<Fields extends DataSchema> = PrettifyType< RemoveIndexSignatures< _OptionalIfNullish<{ [Key in keyof Fields]: Fields[Key][" __fvtt_types_internal_assignment_data"]; }> > >; /** @internal */ type _OptionalIfNullish<T> = { [K in keyof T as true extends _IsNullish<T[K]> ? K : never]?: T[K] | null | undefined; } & { [K in keyof T as true extends _IsNullish<T[K]> ? never : K]: T[K]; }; /** @internal */ type _IsNullish<T> = T extends null | undefined ? true : false; /** * Get the inner assignment type for the given DataSchema. * @template Fields - the DataSchema fields of the SchemaField * * @deprecated This type is a relic of the early days of data models. It was meant to represent * the types that would be valid for the expression `this.schemaProperty = ...`. Modern users will * recognize that the only sane thing to do here is to use `InitializedData` but when data models * were first being introduced there was an attempt to support a sort of strange compromise between * `InitializedData`, `SourceData`, and even `CreateData` for backwards compatibility with existing patterns. * * You should instead use those types as appropriate. */ type AssignmentData<Fields extends DataSchema> = PrettifyType< RemoveIndexSignatures<{ // Note(LukeAbby): Simply stripping off `readonly` may eventually need to be revisited. -readonly [Key in keyof Fields]?: Fields[Key][" __fvtt_types_internal_assignment_data"]; }> >; /** * The required type of data used when updating a document. * @template Fields - the DataSchema fields of the SchemaField */ // Note(LukeAbby): Currently this is too close to `AssignmentData` but the intent is to make it // more accurate in the future. type UpdateData<Fields extends DataSchema> = _AddUpdateKeys< RemoveIndexSignatures<{ -readonly [Key in keyof Fields]?: Fields[Key][" __fvtt_types_internal_assignment_data"]; }> >; /** @internal */ type _AddUpdateKeys<T> = PrettifyType< T & { [K in keyof T as K extends string ? (T[K] extends undefined ? `-=${K}` : never) : never]?: null; } & { // Note(LukeAbby): There's more work to be done here. For example `type` and `==system` must // go together. This will be added once a performant validator type is created. [K in keyof T as K extends string ? `==${K}` : never]?: T[K]; } >; /** * Gets the initialized version of a schema. This means a * @template Fields - the DataSchema fields of the SchemaField */ type InitializedData<Fields extends DataSchema> = PrettifyType< RemoveIndexSignatures<{ -readonly [Key in keyof Fields]: Fields[Key][" __fvtt_types_internal_initialized_data"]; }> >; /** * Get the persisted type for the given DataSchema. This is the type used for source. * @template Fields - the DataSchema fields of the SchemaField */ type SourceData<Fields extends DataSchema> = PrettifyType< RemoveIndexSignatures<{ -readonly [Key in keyof Fields]: Fields[Key][" __fvtt_types_internal_source_data"]; }> >; type UpdateSourceData<Fields extends DataSchema> = PrettifyType< RemoveIndexSignatures<{ -readonly [Key in keyof Fields]: Fields[Key][" __fvtt_types_internal_initialized_data"]; }> >; /** The type of the default options for the {@linkcode SchemaField} class. */ type DefaultOptions = SimpleMerge< DataField.DefaultOptions, { required: true; nullable: false; } >; /** * A helper type for the given options type merged into the default options of the SchemaField class. * @template Fields - the DataSchema fields of the SchemaField * @template Opts - the options that override the default options */ type MergedOptions<Fields extends DataSchema, Opts extends Options<Fields>> = SimpleMerge<DefaultOptions, Opts>; /** * @internal */ // eslint-disable-next-line @typescript-eslint/no-empty-object-type type _EffectiveOptions<AssignmentData, Opts extends Options<any>> = [AssignmentData] extends [{}] ? // eslint-disable-next-line @typescript-eslint/no-empty-object-type SimpleMerge<Opts, { initial: {} }> // If all fields are optional then the initial of `{}` is valid. : Opts; // These exist for calculating the type of schema field with options. // This will be deleted once fields are refactored. // The names are also confusing. Hence these it's put into `Internal. namespace Internal { // FIXME: null or undefined should be permissible, cast as the initialized type /** * A shorthand for the assignment type of a SchemaField class. * @template Fields - the DataSchema fields of the SchemaField * @template Opts - the options that override the default options * * @deprecated This type is a relic of the early days of data models. It was meant to represent * the types that would be valid for the expression `this.schemaProperty = ...`. Modern users will * recognize that the only sane thing to do here is to use `InitializedType` but when data models * were first being introduced there was an attempt to support a sort of strange compromise between * `InitializedType`, `SourceType`, and even `CreateType` to an extent. * * You should instead use those types as appropriate. */ type AssignmentType< Fields extends DataSchema, Opts extends Options<Fields> = DefaultOptions, // eslint-disable-next-line @typescript-eslint/no-deprecated > = DataField.DerivedAssignmentType< // eslint-disable-next-line @typescript-eslint/no-deprecated AssignmentData<Fields>, // eslint-disable-next-line @typescript-eslint/no-deprecated _EffectiveOptions<NonNullable<AssignmentData<Fields>>, MergedOptions<Fields, Opts>> >; /** * A shorthand for the assignment type of a SchemaField class. * @template Fields - the DataSchema fields of the SchemaField * @template Opts - the options that override the default options */ type InitializedType< Fields extends DataSchema, Opts extends Options<Fields> = DefaultOptions, > = DataField.DerivedInitializedType<InitializedData<Fields>, MergedOptions<Fields, Opts>>; /** * A shorthand for the assignment type of a SchemaField class. * @template Fields - the DataSchema fields of the SchemaField * @template Opts - the options that override the default options */ type PersistedType< Fields extends DataSchema, Opts extends Options<Fields> = DefaultOptions, > = DataField.DerivedInitializedType<SourceData<Fields>, MergedOptions<Fields, Opts>>; } /** * @deprecated This type is a relic of the early days of data models. It was meant to represent * the types that would be valid for the expression `this.schemaProperty = ...`. Modern users will * recognize that the only sane thing to do here is to use `InitializedData` but when data models * were first being introduced there was an attempt to support a sort of strange compromise between * `InitializedData`, `SourceData`, and even `CreateData` to an extent. * * You should instead use those types as appropriate. */ // eslint-disable-next-line @typescript-eslint/no-deprecated type InnerAssignmentType<Fields extends DataSchema> = AssignmentData<Fields>; type Get<Schema extends DataSchema, FieldName extends string> = GetKey<Schema, FieldName, undefined>; type FieldName<Schema extends DataSchema> = "" | [] | _FieldName<Schema>; /** * Essentially sets a field depth of 10. * * @internal */