fvtt-types
Version:
TypeScript type definitions for Foundry VTT
1,188 lines (1,072 loc) • 135 kB
text/typescript
import type {
ConfiguredDocumentClass,
ConfiguredDocumentInstance,
ConfiguredMetadata,
} from "../../../types/documentConfiguration.d.mts";
import type {
GetKey,
InterfaceToObject,
MakeConform,
MustConform,
ToMethod,
AnyObject,
EmptyObject,
InexactPartial,
RemoveIndexSignatures,
FixedInstanceType,
NullishProps,
PickValue,
Identity,
Brand,
AnyMutableObject,
MaybePromise,
SimpleMerge,
PrettifyType,
AllKeysOf,
Override,
ConcreteKeys,
} from "#utils";
import type * as CONST from "../constants.mts";
import type {
DataSchema,
DataField,
DocumentStatsField,
EmbeddedCollectionField,
EmbeddedDocumentField,
SchemaField,
TypeDataField,
} from "../data/fields.d.mts";
import type { LogCompatibilityWarningOptions } from "../utils/logging.mts";
import type {
DatabaseAction,
DatabaseCreateOperation,
DatabaseDeleteOperation,
DatabaseGetOperation,
DatabaseUpdateOperation,
DocumentSocketRequest,
} from "./_types.d.mts";
import type DataModel from "./data.mts";
import type DocumentSocketResponse from "./socket.d.mts";
import type EmbeddedCollection from "./embedded-collection.d.mts";
import type { SystemConfig } from "#configuration";
export default Document;
type InexactPartialExcept<T extends object, RequiredKey> = {
[K in keyof T as Extract<K, RequiredKey>]: T[K];
} & {
[K in keyof T as Exclude<K, RequiredKey>]?: T[K] | undefined;
};
type _ClassMustBeAssignableToInternal = MustConform<typeof Document, Document.Internal.Constructor>;
type _InstanceMustBeAssignableToInternal = MustConform<Document.Any, Document.Internal.Instance.Any>;
// Note(LukeAbby): Properties from `Schema` technically derive from `DataModel`. This means that if
// `name?: string` etc. were to be put in `Document` directly they'd actually override the schema.
// Therefore this workaround is used to force `DataModel` to override the properties.
declare const _InternalDocument: (new (...args: any[]) => {
// TODO: removing undefined breaks everything, but should be valid to do, investigate
name?: string | null | undefined;
// `{}` is used so that `{}` and the actual shape of `system` are merged.
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
system?: {} | undefined;
_stats?: DocumentStatsField.InitializedData | undefined;
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
flags?: {} | undefined;
}) &
typeof DataModel;
/**
* An extension of the base DataModel which defines a Document.
* Documents are special in that they are persisted to the database and referenced by _id.
*/
declare abstract class Document<
DocumentName extends Document.Type,
Schema extends DataSchema,
Parent extends Document.Any | null = null,
> extends _InternalDocument<Schema, Parent, InterfaceToObject<Document.ConstructionContext<Parent>>> {
/**
* @param data - Initial data provided to construct the Document
* @param context - Construction context options
*/
// Note: The constructor has been replaced with `never` in `Document` itself because it never makes
// sense to try to construct `new Document(...)` directly. Not only is it an abstract class but
// also it varies based upon the `Schema`. While this could be supported it also simplifies
// typechecking and helps stymy circularities.
constructor(...args: never);
override parent: Parent;
// options: not null (destructured)
protected override _configure(options?: Document.ConfigureOptions): void;
/**
* An immutable reverse-reference to the name of the collection that this Document exists in on its parent, if any.
*/
readonly parentCollection: Document.MetadataFor<DocumentName>["collection"] | null;
/**
* An immutable reference to a containing Compendium collection to which this Document belongs.
*/
readonly pack: string | null;
/**
* A mapping of embedded Document collections which exist in this model.
*/
readonly collections: Document.CollectionRecord<Schema>;
/**
* Ensure that all Document classes share the same schema of their base declaration.
*/
static get schema(): SchemaField.Any;
protected static override _initializationOrder(): Generator<[string, DataField.Any], void, undefined>;
/**
* Default metadata which applies to each instance of this Document type.
* @defaultValue
* ```typescript
* {
* name: "Document",
* label: "DOCUMENT.Document"
* coreTypes: [BASE_DOCUMENT_TYPE],
* collection: "documents",
* embedded: {},
* hasTypeData: false,
* indexed: false,
* compendiumIndexFields: [],
* permissions: {
* view: "LIMITED" // At least limited permission is required to view the Document
* create: "ASSISTANT", // Assistants or Gamemasters can create Documents
* update: "ASSISTANT", // Document owners can update Documents (this includes GM users)
* delete: "ASSISTANT" // Assistants or Gamemasters can create Documents
* },
* preserveOnImport: ["_id", "sort", "ownership", folder],
* schemaVersion: undefined
* }
* ```
*/
static metadata: Document.Metadata.Any;
/**
* @defaultValue `["DOCUMENT"]`
*/
static override LOCALIZATION_PREFIXES: string[];
/**
* The database backend used to execute operations and handle results
*/
static get database(): CONFIG["DatabaseBackend"];
/**
* Return a reference to the implemented subclass of this base document type.
*/
static get implementation(): Document.Internal.Constructor;
/**
* The base document definition that this document class extends from.
*/
static get baseDocument(): Document.AnyConstructor;
/**
* The named collection to which this Document belongs.
*/
static get collectionName(): string;
/**
* The named collection to which this Document belongs.
*/
get collectionName(): Document.MetadataFor<DocumentName>["collection"];
/**
* The canonical name of this Document type, for example "Actor".
*/
static get documentName(): Document.Type;
/**
* The canonical name of this Document type, for example "Actor".
*/
get documentName(): DocumentName;
/**
* The allowed types which may exist for this Document class
*/
static get TYPES(): string[];
/**
* Does this Document support additional subtypes?
*/
static get hasTypeData(): undefined | true;
/**
* The Embedded Document hierarchy for this Document.
*/
static get hierarchy(): Record<string, EmbeddedCollectionField.Any | EmbeddedDocumentField.Any>;
/**
* Identify the collection in a parent Document that this Document exists belongs to, if any.
* @param parentCollection - An explicitly provided parent collection name.
* @remarks If passed a value for `parentCollection`, simply returns that value
*
* Foundry marked `@internal`
*/
_getParentCollection(parentCollection?: string): string | null;
_id: string | null;
/**
* The canonical identifier for this Document
*/
get id(): string | null;
/**
* Test whether this Document is embedded within a parent Document
*/
get isEmbedded(): boolean;
/**
* Is this document in a compendium?
*/
get inCompendium(): boolean;
/**
* A Universally Unique Identifier (uuid) for this Document instance.
*/
get uuid(): string;
/**
* Test whether a given User has sufficient permissions to create Documents of this type in general. This does not
* guarantee that the User is able to create all Documents of this type, as certain document-specific requirements
* may also be present.
*
* Generally speaking, this method is used to verify whether a User should be presented with the option to create
* Documents of this type in the UI.
* @param user - The User being tested
* @returns Does the User have a sufficient role to create?
*/
static canUserCreate(user: User.Implementation): boolean;
/**
* Get the explicit permission level that a User has over this Document, a value in {@link CONST.DOCUMENT_OWNERSHIP_LEVELS | `CONST.DOCUMENT_OWNERSHIP_LEVELS`}.
* Compendium content ignores the ownership field in favor of User role-based ownership. Otherwise, Documents use
* granular per-User ownership definitions and Embedded Documents defer to their parent ownership.
*
* This method returns the value recorded in Document ownership, regardless of the User's role, for example a
* GAMEMASTER user might still return a result of NONE if they are not explicitly denoted as having a level.
*
* To test whether a user has a certain capability over the document, testUserPermission should be used.
* @param user - The User being tested (default: `game.user`)
* @returns A numeric permission level from CONST.DOCUMENT_OWNERSHIP_LEVELS or null
*
* @privateRemarks Making this just `User.Implementation` causes circularities
*/
getUserLevel(user?: User.Internal.Implementation | null): CONST.DOCUMENT_OWNERSHIP_LEVELS | null;
/**
* Test whether a certain User has a requested permission level (or greater) over the Document
* @param user - The User being tested
* @param permission - The permission level from DOCUMENT_PERMISSION_LEVELS to test
* @param options - Additional options involved in the permission test
* @returns Does the user have this permission level over the Document?
*
* @privateRemarks Making this just `User.Implementation` causes circularities
*/
// options: not null (destructured)
testUserPermission(
user: User.Internal.Implementation,
permission: Document.ActionPermission,
options?: Document.TestUserPermissionOptions,
): boolean;
/**
* Test whether a given User has permission to perform some action on this Document
* @param user - The User attempting modification
* @param action - The attempted action
* @param data - Data involved in the attempted action (default: `{}`)
* @returns Does the User have permission?
*
* @privateRemarks Making this just `User.Implementation` causes circularities
*/
// data: not null (parameter default only)
canUserModify<Action extends "create" | "update" | "delete">(
user: User.Internal.Implementation,
action: Action,
data?: Document.CanUserModifyData<Schema, Action>,
): boolean;
/**
* Clone a document, creating a new document by combining current data with provided overrides.
* The cloned document is ephemeral and not yet saved to the database.
* @param data - Additional data which overrides current document data at the time of creation
* @param context - Additional context options passed to the create method
* @returns The cloned Document instance
*/
// data: not null (property access), context: not null (destructured)
override clone<Save extends boolean | null | undefined = undefined>(
data?: SchemaField.UpdateData<Schema>,
context?: Document.CloneContext<Save>,
): Document.Clone<this, Save>;
/**
* For Documents which include game system data, migrate the system data object to conform to its latest data model.
* The data model is defined by the template.json specification included by the game system.
* @returns The migrated system data object
* @throws If this document type either doesn't have subtypes or it does but the one on this document is a DataModel
*/
migrateSystemData(): object;
/** @remarks `Document#toObject` calls `this.constructor.shimData()` on the data before returning */
override toObject(source?: boolean | null): SchemaField.SourceData<Schema>;
/**
* Create multiple Documents using provided input data.
* Data is provided as an array of objects where each individual object becomes one new Document.
*
* @param data - An array of data objects or existing Documents to persist.
* (default: `[]`)
* @param operation - Parameters of the requested creation operation
* (default: `{}`)
* @returns An array of created Document instances
*
* @example Create a single Document
* ```typescript
* const data = [{name: "New Actor", type: "character", img: "path/to/profile.jpg"}];
* const created = await Actor.createDocuments(data);
* ```
*
* @example Create multiple Documents
* ```typescript
* const data = [{name: "Tim", type: "npc"], [{name: "Tom", type: "npc"}];
* const created = await Actor.createDocuments(data);
* ```
*
* @example Create multiple embedded Documents within a parent
* ```typescript
* const actor = game.actors.getName("Tim");
* const data = [{name: "Sword", type: "weapon"}, {name: "Breastplate", type: "equipment"}];
* const created = await Item.createDocuments(data, {parent: actor});
* ```
*
* @example Create a Document within a Compendium pack
* ```typescript
* const data = [{name: "Compendium Actor", type: "character", img: "path/to/profile.jpg"}];
* const created = await Actor.createDocuments(data, {pack: "mymodule.mypack"});
* ```
*
* @remarks If a document is skipped by a hook or `_preCreate` then that element is skipped in the
* return type. This means that you receive only documents that were actually created.
*/
// Note: This uses `never` because it's unsound to try to do `Document.createDocuments` directly.
static createDocuments(data: never, operation?: never): Promise<Document.Any[]>;
/**
* Update multiple Document instances using provided differential data.
* Data is provided as an array of objects where each individual object updates one existing Document.
*
* @param updates - An array of differential data objects, each used to update a single Document
* (default: `[]`)
* @param operation - Parameters of the database update operation
* (default: `{}`)
* @returns An array of updated Document instances
*
* @example Update a single Document
* ```typescript
* const updates = [{_id: "12ekjf43kj2312ds", name: "Timothy"}];
* const updated = await Actor.updateDocuments(updates);
* ```
*
* @example Update multiple Documents
* ```typescript
* const updates = [{_id: "12ekjf43kj2312ds", name: "Timothy"}, {_id: "kj549dk48k34jk34", name: "Thomas"}]};
* const updated = await Actor.updateDocuments(updates);
* ```
*
* @example Update multiple embedded Documents within a parent
* ```typescript
* const actor = game.actors.getName("Timothy");
* const updates = [{_id: sword.id, name: "Magic Sword"}, {_id: shield.id, name: "Magic Shield"}];
* const updated = await Item.updateDocuments(updates, {parent: actor});
* ```
*
* @example Update Documents within a Compendium pack
* ```typescript
* const actor = await pack.getDocument(documentId);
* const updated = await Actor.updateDocuments([{_id: actor.id, name: "New Name"}], {pack: "mymodule.mypack"});
* ```
*
* @remarks If a document is skipped by a hook or `_preCreate` then that element is skipped in the
* return type. This means that you receive only documents that were actually updated.
*/
// Note: This uses `never` because it's unsound to try to do `Document.updateDocuments`
static updateDocuments(updates: never, operation?: never): Promise<Document.Any[]>;
/**
* Delete one or multiple existing Documents using an array of provided ids.
* Data is provided as an array of string ids for the documents to delete.
*
* @param ids - An array of string ids for the documents to be deleted
* (default: `[]`)
* @param operation - Parameters of the database deletion operation
* (default: `{}`)
* @returns An array of deleted Document instances
*
* @example Delete a single Document
* ```typescript
* const tim = game.actors.getName("Tim");
* const deleted = await Actor.deleteDocuments([tim.id]);
* ```
*
* @example Delete multiple Documents
* ```typescript
* const tim = game.actors.getName("Tim");
* const tom = game.actors.getName("Tom");
* const deleted = await Actor.deleteDocuments([tim.id, tom.id]);
* ```
*
* @example Delete multiple embedded Documents within a parent
* ```typescript
* const tim = game.actors.getName("Tim");
* const sword = tim.items.getName("Sword");
* const shield = tim.items.getName("Shield");
* const deleted = await Item.deleteDocuments([sword.id, shield.id], parent: actor});
* ```
*
* @example Delete Documents within a Compendium pack
* ```typescript
* const actor = await pack.getDocument(documentId);
* const deleted = await Actor.deleteDocuments([actor.id], {pack: "mymodule.mypack"});
* ```
*
* @remarks If a document is skipped by a hook or `_preDelete` then that element is skipped in the
* return type. This means that you receive only documents that were actually deleted.
*/
// Note: This uses `never` because it's unsound to try to pass the operation for `Document.deleteDocument`
static deleteDocuments(ids?: readonly string[], operation?: never): Promise<Document.Any[]>;
/**
* Create a new Document using provided input data, saving it to the database.
* @see {@linkcode Document.createDocuments}
* @param data - Initial data used to create this Document, or a Document instance to persist.
* @param operation - Parameters of the creation operation
* (default: `{}`)
* @returns The created Document instance
*
* @example Create a World-level Item
* ```typescript
* const data = [{name: "Special Sword", type: "weapon"}];
* const created = await Item.create(data);
* ```
*
* @example Create an Actor-owned Item
* ```typescript
* const data = [{name: "Special Sword", type: "weapon"}];
* const actor = game.actors.getName("My Hero");
* const created = await Item.create(data, {parent: actor});
* ```
*
* @example Create an Item in a Compendium pack
* ```typescript
* const data = [{name: "Special Sword", type: "weapon"}];
* const created = await Item.create(data, {pack: "mymodule.mypack"});
* ```
*
* @remarks If the document creation is skipped by a hook or `_preCreate` then `undefined` is
* returned.
*/
// Note: This uses `never` because it's unsound to try to call `Document.create` directly.
// TODO: This can take an array of data and return an array of documents, in addition to its current typing
static create(data: never, operation?: never): Promise<Document.Any | undefined>;
/**
* Update this Document using incremental data, saving it to the database.
* @see {@linkcode Document.updateDocuments}
* @param data - Differential update data which modifies the existing values of this document data
* (default: `{}`)
* @param operation - Parameters of the update operation
* (default: `{}`)
* @returns The updated Document instance
*
* @remarks If the document update is skipped by a hook or `_preUpdate` then `undefined` is
* returned.
*/
// Note: This uses `never` because it's unsound to try to call `Document#update` directly.
update(data: never, operation: never): Promise<this | undefined>;
/**
* Delete this Document, removing it from the database.
* @see {@linkcode Document.deleteDocuments}
* @param operation - Parameters of the deletion operation
* (default: `{}`)
* @returns The deleted Document instance
*
* @remarks If the document deletion is skipped by a hook or `_preUpdate` then `undefined` is
* returned.
*/
// Note: This uses `never` because it's unsound to try to call `Document#delete` directly.
delete(operation: never): Promise<this | undefined>;
/**
* Get a World-level Document of this type by its id.
* @param documentId - The Document ID
* @param operation - Additional options which customize the request
* @returns The retrieved Document, or null
*
* @remarks If the Document is in a compendium (i.e `operation.pack` is provided), returns the index
* entry (or `null`), instead of the Document.
*
* {@link FogExploration.get | `FogExploration.get`} can possibly forward args and return to/from
* {@link FogExploration.load | `FogExploration.load`}, which accounts for the `Promise<>` part
* of the return; All other documents return `SomeDoc.Implementation | null`
*/
// TODO: Type for possible index entry return
static get(documentId: string, operation?: Document.Database.GetOptions): MaybePromise<Document.Any | null>;
/**
* A compatibility method that returns the appropriate name of an embedded collection within this Document.
* @param name - An existing collection name or a document name.
* @returns The provided collection name if it exists, the first available collection for the
* document name provided, or null if no appropriate embedded collection could be found.
* @example Passing an existing collection name.
* ```js
* Actor.getCollectionName("items");
* // returns "items"
* ```
*
* @example Passing a document name.
* ```js
* Actor.getCollectionName("Item");
* // returns "items"
* ```
*/
static getCollectionName(name: never): string | null;
/**
* Obtain a reference to the Array of source data within the data object for a certain embedded Document name
* @param embeddedName - The name of the embedded Document type
* @returns The Collection instance of embedded Documents of the requested type
* @remarks Usually returns some form of DocumentCollection, but not always (e.g. Token["actors"])
*/
// Note: This uses `never` because it's unsound to try to call `Document#getEmbeddedCollection` directly.
getEmbeddedCollection(embeddedName: never): unknown;
/**
* Get an embedded document by its id from a named collection in the parent document.
* @param embeddedName - The name of the embedded Document type
* @param id - The id of the child document to retrieve
* @param options - Additional options which modify how embedded documents are retrieved
* @returns The retrieved embedded Document instance, or undefined
* @throws If the embedded collection does not exist, or if strict is true and the Embedded Document could not be found.
*/
// Note: This uses `never` because it's unsound to try to call `Document#getEmbeddedDocument` directly.
getEmbeddedDocument(
embeddedName: never,
id: string,
options: Document.GetEmbeddedDocumentOptions,
): Document.Any | undefined;
/**
* Create multiple embedded Document instances within this parent Document using provided input data.
* @see {@linkcode Document.createDocuments}
* @param embeddedName - The name of the embedded Document type
* @param data - An array of data objects used to create multiple documents
* (default: `[]`)
* @param operation - Parameters of the database creation workflow
* (default: `{}`)
* @returns An array of created Document instances
*/
// Note: This uses `never` because it's unsound to try to call `Document#createEmbeddedDocuments` directly.
// Note(LukeAbby): Returns `unknown` instead of `Promise<Array<Document.AnyStored> | undefined>` to stymy errors.
createEmbeddedDocuments(
embeddedName: never,
// Note: Not optional because `createEmbeddedDocuments("Actor")` does effectively nothing.
data: never,
operation?: never,
): unknown;
/**
* Update multiple embedded Document instances within a parent Document using provided differential data.
* @see {@linkcode Document.updateDocuments}
* @param embeddedName - The name of the embedded Document type
* @param updates - An array of differential data objects, each used to update a single Document
* (default: `[]`)
* @param operation - Parameters of the database update workflow
* (default: `{}`)
* @returns An array of updated Document instances
*/
// Note: This uses `never` because it's unsound to try to call `Document#updateEmbeddedDocuments` directly.
// Note(LukeAbby): Returns `unknown` instead of `Promise<Array<Document.AnyStored> | undefined>` to stymy errors.
updateEmbeddedDocuments(
embeddedName: never,
// Note: Not optional because `updateEmbeddedDocuments("Actor")` does effectively nothing.
updates: never,
context?: never,
): unknown;
/**
* Delete multiple embedded Document instances within a parent Document using provided string ids.
* @see {@linkcode Document.deleteDocuments}
* @param embeddedName - The name of the embedded Document type
* @param ids - An array of string ids for each Document to be deleted
* @param operation - Parameters of the database deletion workflow
* (default: `{}`)
* @returns An array of deleted Document instances
*/
// Note: This uses `never` because it's unsound to try to call `Document#deleteEmbeddedDocuments` directly.
// Note(LukeAbby): Returns `unknown` instead of `Promise<Array<Document.AnyStored> | undefined>` to stymy errors.
deleteEmbeddedDocuments(embeddedName: never, ids: Array<string>, operation?: never): unknown;
/**
* Iterate over all embedded Documents that are hierarchical children of this Document.
* @param _parentPath - A parent field path already traversed
* @remarks Not called within Foundry's client-side code, likely exists for server documents
*/
traverseEmbeddedDocuments(_parentPath?: string): Generator<[string, Document.AnyChild<this>], void, undefined>;
/**
* Get the value of a "flag" for this document
* See the setFlag method for more details on flags
*
* @param scope - The flag scope which namespaces the key
* @param key - The flag key
* @returns The flag value
*/
getFlag(scope: never, key: never): unknown;
/**
* Assign a "flag" to this document.
* Flags represent key-value type data which can be used to store flexible or arbitrary data required by either
* the core software, game systems, or user-created modules.
*
* Each flag should be set using a scope which provides a namespace for the flag to help prevent collisions.
*
* Flags set by the core software use the "core" scope.
* Flags set by game systems or modules should use the canonical name attribute for the module
* Flags set by an individual world should "world" as the scope.
*
* Flag values can assume almost any data type. Setting a flag value to null will delete that flag.
*
* @param scope - The flag scope which namespaces the key
* @param key - The flag key
* @param value - The flag value
* @returns A Promise resolving to the updated document
*/
setFlag(scope: never, key: never, value: never): Promise<this>;
/**
* Remove a flag assigned to the document
* @param scope - The flag scope which namespaces the key
* @param key - The flag key
* @returns The updated document instance
*/
unsetFlag(scope: never, key: never): Promise<this>;
/**
* Pre-process a creation operation for a single Document instance.
* Pre-operation events only occur for the client which requested the operation.
* Modifications to the pending Document instance must be performed using {@link Document.updateSource | `Document#updateSource`}.
* @param data - The initial data object provided to the document creation request
* @param options - Additional options which modify the creation request
* @param user - The User requesting the document creation
* @returns Return false to exclude this Document from the creation operation
*/
protected _preCreate(data: never, options: never, user: User.Internal.Implementation): Promise<boolean | void>;
/**
* Post-process a creation operation for a single Document instance.
* Post-operation events occur for all connected clients.
* @param data - The initial data object provided to the document creation request
* @param options - Additional options which modify the creation request
* @param userId - The id of the User requesting the document update
*/
protected _onCreate(data: never, options: never, userId: string): MaybePromise<void>;
/**
* Pre-process a creation operation, potentially altering its instructions or input data. Pre-operation events only
* occur for the client which requested the operation.
*
* This batch-wise workflow occurs after individual {@link Document._preCreate | `Document#_preCreate`} workflows and provides a final
* pre-flight check before a database operation occurs.
*
* Modifications to pending documents must mutate the documents array or alter individual document instances using
* {@link Document.updateSource | `Document#updateSource`}.
* @param documents - Pending document instances ot be created
* @param operation - Parameters of the database creation operation
* @param user - The User requesting the creation operation
* @returns Return false to cancel the creation operation entirely
*/
// Note: This uses `never` because it's unsound to try to do `Document._preCreateOperation` directly.
protected static _preCreateOperation(
documents: never[],
operation: never,
user: User.Internal.Implementation,
): Promise<boolean | void>;
/**
* Post-process a creation operation, reacting to database changes which have occurred. Post-operation events occur
* for all connected clients.
*
* This batch-wise workflow occurs after individual {@link Document._onCreate | `Document#_onCreate`} workflows.
*
* @param documents - The Document instances which were created
* @param operation - Parameters of the database creation operation
* @param user - The User who performed the creation operation
*/
// Note: This uses `never` because it's unsound to try to do `Document._onCreateOperation` directly.
protected static _onCreateOperation(
documents: never,
operation: never,
user: User.Internal.Implementation,
): Promise<void>;
/**
* Perform preliminary operations before a Document of this type is updated.
* Pre-update operations only occur for the client which requested the operation.
* @param changed - The differential data that is changed relative to the documents prior values
* @param options - Additional options which modify the update request
* @param user - The User requesting the document update
* @returns A return value of false indicates the update operation should be cancelled
*/
protected _preUpdate(changed: never, options: never, user: User.Internal.Implementation): Promise<boolean | void>;
/**
* Perform follow-up operations after a Document of this type is updated.
* Post-update operations occur for all clients after the update is broadcast.
* @param changed - The differential data that was changed relative to the documents prior values
* @param options - Additional options which modify the update request
* @param userId - The id of the User requesting the document update
*/
protected _onUpdate(changed: never, options: never, userId: string): MaybePromise<void>;
/**
* Pre-process an update operation, potentially altering its instructions or input data. Pre-operation events only
* occur for the client which requested the operation.
*
* This batch-wise workflow occurs after individual {@link Document._preUpdate | `Document#_preUpdate`} workflows and provides a final
* pre-flight check before a database operation occurs.
*
* Modifications to the requested updates are performed by mutating the data array of the operation.
* {@link Document.updateSource | `Document#updateSource`}.
*
* @param documents - Document instances to be updated
* @param operation - Parameters of the database update operation
* @param user - The User requesting the update operation
* @returns Return false to cancel the update operation entirely
*/
// Note: This uses `never` because it's unsound to try to do `Document._preUpdateOperation` directly.
protected static _preUpdateOperation(
documents: never,
operation: never,
user: User.Internal.Implementation,
): Promise<boolean | void>;
/**
* Post-process an update operation, reacting to database changes which have occurred. Post-operation events occur
* for all connected clients.
*
* This batch-wise workflow occurs after individual {@link Document._onUpdate | `Document#_onUpdate`} workflows.
*
* @param documents - The Document instances which were updated
* @param operation - Parameters of the database update operation
* @param user - The User who performed the update operation
*/
// Note: This uses `never` because it's unsound to try to do `Document._onUpdateOperation` directly.
protected static _onUpdateOperation(
documents: never,
operation: never,
user: User.Internal.Implementation,
): Promise<void>;
/**
* Perform preliminary operations before a Document of this type is deleted.
* Pre-delete operations only occur for the client which requested the operation.
* @param options - Additional options which modify the deletion request
* @param user - The User requesting the document deletion
* @returns A return value of false indicates the delete operation should be cancelled
*/
protected _preDelete(options: never, user: User.Internal.Implementation): Promise<boolean | void>;
/**
* Perform follow-up operations after a Document of this type is deleted.
* Post-deletion operations occur for all clients after the deletion is broadcast.
* @param options - Additional options which modify the deletion request
* @param userId - The id of the User requesting the document update
*/
protected _onDelete(options: never, userId: string): MaybePromise<void>;
/**
* Pre-process a deletion operation, potentially altering its instructions or input data. Pre-operation events only
* occur for the client which requested the operation.
*
* This batch-wise workflow occurs after individual {@link Document._preDelete | `Document#_preDelete`} workflows and provides a final
* pre-flight check before a database operation occurs.
*
* Modifications to the requested deletions are performed by mutating the operation object.
* {@link Document.updateSource | `Document#updateSource`}.
*
* @param documents - Document instances to be deleted
* @param operation - Parameters of the database update operation
* @param user - The User requesting the deletion operation
* @returns Return false to cancel the deletion operation entirely
* @internal
*/
// Note: This uses `never` because it's unsound to try to do `Document._preDeleteOperation` directly.
protected static _preDeleteOperation(
documents: never,
operation: never,
user: User.Internal.Implementation,
): Promise<unknown>;
/**
* Post-process a deletion operation, reacting to database changes which have occurred. Post-operation events occur
* for all connected clients.
*
* This batch-wise workflow occurs after individual {@link Document._onDelete | `Document#_onDelete`} workflows.
*
* @param documents - The Document instances which were deleted
* @param operation - Parameters of the database deletion operation
* @param user - The User who performed the deletion operation
*/
// Note: This uses `never` because it's unsound to try to do `Document._onDeleteOperation` directly.
protected static _onDeleteOperation(
documents: never,
operation: never,
user: User.Internal.Implementation,
): Promise<unknown>;
/**
* A reusable helper for adding migration shims.
*/
// options: not null (parameter default only in _addDataFieldShim)
protected static _addDataFieldShims(
data: AnyMutableObject,
shims: Record<string, string>,
options?: Document.DataFieldShimOptions,
): void;
/**
* A reusable helper for adding a migration shim
*/
// options: not null (parameter default only)
protected static _addDataFieldShim(
data: AnyMutableObject,
oldKey: string,
newKey: string,
options?: Document.DataFieldShimOptions,
): void;
/**
* Define a simple migration from one field name to another.
* The value of the data can be transformed during the migration by an optional application function.
* @param data - The data object being migrated
* @param oldKey - The old field name
* @param newKey - The new field name
* @param apply - An application function, otherwise the old value is applied
* @remarks Foundry marked `@internal`
*/
protected static _addDataFieldMigration(
data: AnyMutableObject,
oldKey: string,
newKey: string,
apply?: (data: AnyMutableObject) => unknown,
): unknown;
// options: not null (destructured where forwarded)
protected static _logDataFieldMigration(
oldKey: string,
newKey: string,
options?: LogCompatibilityWarningOptions,
): void;
/**
* @deprecated since v12, will be removed in v14
* @remarks "The `Document._onCreateDocuments` static method is deprecated in favor of {@link Document._onCreateOperation | `Document._onCreateOperation`}"
*/
// Note: This uses `never` because it's unsound to try to do `Document._onCreateDocuments` directly.
protected static _onCreateDocuments(
documents: never,
context: Document.ModificationContext<Document.Any | null>,
): Promise<void>;
/**
* @deprecated since v12, will be removed in v14
* @remarks "The `Document._onUpdateDocuments` static method is deprecated in favor of {@link Document._onUpdateOperation | `Document._onUpdateOperation`}"
*/
// Note: This uses `never` because it's unsound to try to do `Document._onUpdateDocuments` directly.
protected static _onUpdateDocuments(
documents: never,
context: Document.ModificationContext<Document.Any | null>,
): Promise<unknown>;
/**
* @deprecated since v12, will be removed in v14
* @remarks "The `Document._onDeleteDocuments` static method is deprecated in favor of {@link Document._onDeleteOperation | `Document._onDeleteOperation`}"
*/
// Note: This uses `never` because it's unsound to try to do `Document._onDeleteDocuments` directly.
protected static _onDeleteDocuments(
documents: never,
context: Document.ModificationContext<Document.Any | null>,
): Promise<unknown>;
" fvtt_types_internal_document_name": DocumentName;
" fvtt_types_internal_document_schema": Schema;
" fvtt_types_internal_document_parent": Parent;
}
// An empty schema is the most accurate because index signatures are stripped.
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
declare abstract class AnyDocument extends Document<Document.Type, {}, Document.Any | null> {
constructor(...args: never);
// Note(LukeAbby): This uses `object` instead of `AnyObject` to avoid more thorough evaluation of
// the involved types which can cause a loop.
_source: object;
}
declare namespace Document {
interface Any extends AnyDocument {}
interface AnyStored extends Document.Internal.Stored<Any> {}
interface AnyValid extends AnyDocument {
get invalid(): false;
}
interface AnyInvalid extends Document.Internal.Invalid<AnyValid> {}
interface AnyConstructor extends Identity<typeof AnyDocument> {}
type Type = CONST.ALL_DOCUMENT_TYPES;
type PlaceableType =
| "AmbientLight"
| "AmbientSound"
| "Drawing"
| "MeasuredTemplate"
| "Note"
| "Region"
| "Tile"
| "Token"
| "Wall";
type PrimaryType = CONST.PRIMARY_DOCUMENT_TYPES;
type EmbeddedType = CONST.EMBEDDED_DOCUMENT_TYPES;
type WorldType = CONST.WORLD_DOCUMENT_TYPES;
type CompendiumType = CONST.COMPENDIUM_DOCUMENT_TYPES;
type WithSubTypes = WithSystem | "Folder" | "Macro" | "TableResult";
type WithSystem =
| "ActiveEffect"
| "ActorDelta"
| "Actor"
| "Card"
| "Cards"
| "ChatMessage"
| "Combat"
| "Combatant"
| "CombatantGroup"
| "Item"
| "JournalEntryPage"
| "RegionBehavior";
type CoreTypesForName<Name extends Type> = _CoreTypes<
Name,
string & GetKey<Document.MetadataFor<Name>, "coreTypes", [CONST.BASE_DOCUMENT_TYPE]>[number]
>;
/** @internal */
type _CoreTypes<Name extends Type, Types> = SystemConfig extends { [_ in Name]: { readonly base: "ignore" } }
? Exclude<Types, "base">
: Types;
type ConfiguredSubTypeOf<Name extends Type> = Name extends "ActorDelta"
? ConfiguredSubTypeOf<"Actor">
: // ESLint doesn't know that `DataModelConfig` and `SourceConfig` are meant to be declaration merged into.
// Therefore it hastily thinks the results are always `never`.
// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents, @typescript-eslint/no-duplicate-type-constituents
string & (keyof GetKey<DataModelConfig, Name, unknown> | keyof GetKey<SourceConfig, Name, unknown>);
type SubTypesOf<Name extends Document.Type> = Name extends "ActorDelta"
? SubTypesOf<"Actor">
: Document.CoreTypesForName<Name> | ConfiguredSubTypeOf<Name> | _ModuleSubType<Name>;
/** @internal */
type _ModuleSubType<Name extends Type> = SystemConfig extends {
[_ in Name]: { readonly moduleSubType: "ignore" };
}
? never
: Document.MetadataFor<Name> extends { readonly hasTypeData: true }
? Document.ModuleSubType
: never;
type ModuleSubType = Brand<`${string}.${string}`, "Document.ModuleSubtype">;
type OfType<Name extends WithSubTypes, SubType extends SubTypesOf<Name>> =
| (Name extends "ActiveEffect" ? ActiveEffect.OfType<SubType & ActiveEffect.SubType> : never)
| (Name extends "ActorDelta" ? ActorDelta.OfType<SubType & ActorDelta.SubType> : never)
| (Name extends "Actor" ? Actor.OfType<SubType & Actor.SubType> : never)
| (Name extends "Card" ? Card.OfType<SubType & Card.SubType> : never)
| (Name extends "Cards" ? Cards.OfType<SubType & Cards.SubType> : never)
| (Name extends "ChatMessage" ? ChatMessage.OfType<SubType & ChatMessage.SubType> : never)
| (Name extends "Combat" ? Combat.OfType<SubType & Combat.SubType> : never)
| (Name extends "Combatant" ? Combatant.OfType<SubType & Combatant.SubType> : never)
| (Name extends "CombatantGroup" ? CombatantGroup.OfType<SubType & CombatantGroup.SubType> : never)
| (Name extends "Folder" ? Folder.OfType<SubType & Folder.SubType> : never)
| (Name extends "Item" ? Item.OfType<SubType & Item.SubType> : never)
| (Name extends "JournalEntryPage" ? JournalEntryPage.OfType<SubType & JournalEntryPage.SubType> : never)
| (Name extends "Macro" ? Macro.OfType<SubType & Macro.SubType> : never)
| (Name extends "RegionBehavior" ? RegionBehavior.OfType<SubType & RegionBehavior.SubType> : never)
| (Name extends "TableResult" ? TableResult.OfType<SubType & TableResult.SubType> : never);
/**
* With the existence of custom module subtypes a system can no longer rely on their configured types being the only ones.
* A module can provide its own custom type though it is always of the form `${moduleName}.${subType}` so the `.` is a pretty
* strong indicator.
*
* `UnknownSourceData` covers the case where it's configured without a data model.
* See {@linkcode UnknownSystem} for other possibilities.
*/
interface UnknownSourceData extends AnyObject {
type: ModuleSubType;
}
/**
* With the existence of custom module subtypes a system can no longer rely on their configured types being the only ones.
*/
type UnknownSystem = UnknownSourceData | TypeDataField.UnknownTypeDataModel | DataModel.UnknownDataModel;
// TODO: Probably a way to auto-determine this
type SystemType =
| "ActiveEffect"
| "Actor"
| "Card"
| "Cards"
| "ChatMessage"
| "Combat"
| "Combatant"
| "CombatantGroup"
| "Item"
| "JournalEntryPage"
| "RegionBehavior";
namespace Embedded {
type CollectionNameFor<
Embedded extends Document.Metadata.Embedded,
CollectionName extends Document.Embedded.CollectionName<Embedded>,
> = Extract<GetKey<Metadata.Embedded, CollectionName, CollectionName>, Document.Type>;
type DocumentFor<
Embedded extends Document.Metadata.Embedded,
CollectionName extends Document.Embedded.CollectionName<Embedded>,
> = Document.ImplementationFor<CollectionNameFor<Embedded, CollectionName>>;
type CollectionFor<
Parent extends Document.Any,
Embedded extends Document.Metadata.Embedded,
CollectionName extends Document.Embedded.CollectionName<Embedded>,
> = EmbeddedCollection<DocumentFor<Embedded, CollectionName>, Parent>;
type CollectionName<Embedded extends Document.Metadata.Embedded> = {
[K in keyof Embedded]: K extends Document.Type ? Extract<K | Embedded[K], string> : never;
}[keyof Embedded];
}
/**
* @internal
*/
interface _WorldCollectionMap {
Actor: foundry.documents.collections.Actors.Implementation;
Cards: foundry.documents.collections.CardStacks;
Combat: foundry.documents.collections.CombatEncounters;
FogExploration: foundry.documents.collections.FogExplorations;
Folder: foundry.documents.collections.Folders;
Item: foundry.documents.collections.Items;
JournalEntry: foundry.documents.collections.Journal;
Macro: foundry.documents.collections.Macros;
ChatMessage: foundry.documents.collections.ChatMessages;
Playlist: foundry.documents.collections.Playlists;
Scene: foundry.documents.collections.Scenes;
Setting: foundry.documents.collections.WorldSettings;
RollTable: foundry.documents.collections.RollTables;
User: foundry.documents.collections.Users;
}
type WorldCollectionFor<Name extends Document.WorldType> = _WorldCollectionMap[Name];
type IsParentOf<
ParentDocument extends Document.Internal.Instance.Any,
ChildDocument extends Document.Internal.Instance.Any,
> = ParentDocument extends Internal.ParentFor<ChildDocument> ? true : false;
type SocketRequest<Action extends DatabaseAction> = DocumentSocketRequest<Action>;
type SocketResponse<Action extends DatabaseAction> = DocumentSocketResponse<Action>;
// Documented at https://gist.github.com/LukeAbby/c7420b053d881db4a4d4496b95995c98
namespace Internal {
type Constructor = (abstract new (...args: never) => Instance.Any) & {
documentName: Document.Type;
};
interface Instance<
DocumentName extends Document.Type,
Schema extends DataSchema,
Parent extends Document.Internal.Instance.Any | null,
> {
" fvtt_types_internal_document_name": DocumentName;
" fvtt_types_internal_document_schema": Schema;
" fvtt_types_internal_document_parent": Parent;
}
type DocumentNameFor<ConcreteInstance extends Instance.Any> =
ConcreteInstance[" fvtt_types_internal_document_name"];
type SchemaFor<ConcreteInstance extends Instance.Any> = ConcreteInstance[" fvtt_types_internal_document_schema"];
type ParentFor<ConcreteInstance extends Instance.Any> = ConcreteInstance[" fvtt_types_internal_document_parent"];
namespace Instance {
interface Any extends Instance<any, any, any> {}
type Complete<T extends Any> = T extends Document.Any ? T : never;
}
type ModelMap<Name extends Document.WithSubTypes> = _ModelMap<
Name,
// `{}` is used to avoid `keyof never` issues.
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
GetKey<DataModelConfig, Name, {}>,
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
GetKey<SourceConfig, Name, {}>
> &
// `Document.ModuleSubType` has to be accounted for specially because of its perculiar nature.
Record<Document.ModuleSubType, _ModuleSubTypeFor<Name>>;
type _ModuleSubTypeFor<Name extends Document.WithSubTypes> = SystemConfig extends {
readonly [_ in Name]: { readonly moduleSubtype: "ignore" };
}
? never
: // The `Extract<..., object>` serves a dual purpose:
// 1) Get rid of `| undefined` for optional subtypes.
// 2) Make sure it's obvious to TypeScript `system` is always an object.
Extract<GetKey<GetKey<DataModelConfig, Name, object>, Document.ModuleSubType, Document.UnknownSystem>, object>;
// Note(LukeAbby): This is written this way to preserve any optional modifiers.
type _ModelMap<Name extends Document.WithSubTypes, DataModel, Config> = PrettifyType<
SimpleMerge<
{
[SubType in Document.CoreTypesForName<Name>]: EmptyObject;
},
SimpleMerge<
{
[SubType in keyof DataModel]: EmptyObject;
},
{
[SubType in keyof Config]: EmptyObject;
}
>
>
>;
type SystemMap<Name extends Document.WithSubTypes> = _SystemMap<
Name,
// `{}` is used to avoid `keyof never` issues.
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
GetKey<DataModelConfig, Name, {}>,
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
GetKey<DataConfig, Name, {}>
> &
// `Document.ModuleSubType` has to be accounted for specially because of its perculiar nature.
Record<Document.ModuleSubType, _ModuleSubTypeFor<Name>>;
// Note(LukeAbby): This is written this way to preserve any optional modifiers.
type _SystemMap<Name extends Document.WithSubTypes, DataModel, DataConfig> = PrettifyType<
SimpleMerge<
{
[SubType in Document.CoreTypesForName<Name>]: EmptyObject;
},
SimpleMerge<
{