UNPKG

fvtt-types

Version:
1,188 lines (1,072 loc) 135 kB
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< {