This commit is contained in:
2025-01-04 00:34:03 +01:00
parent 41829408dc
commit 0ca14bbc19
18111 changed files with 1871397 additions and 0 deletions

View File

@@ -0,0 +1 @@
export default function ServerCompendiumFolderMixin(e){return class extends db.Folder{static _compendium=e;static{Object.defineProperty(this,"_db",{get:()=>e._db})}static get collectionName(){return"folders"}static get sublevel(){return this._db.sublevels.folders}get compendium(){return this.constructor._compendium}static metadata=(()=>foundry.utils.mergeObject(super.metadata,{permissions:{create:this.#e.bind(this),update:this.#e.bind(this),delete:this.#e.bind(this)}},{inplace:!1}))();static async getMany(e,t={}){const i=[];for(let o of e)i.push(await this._compendium.getFolder(o,t));return i}static fromSource(e,t={}){return t.pack=this._compendium.collectionName,super.fromSource(e,t)}static#e(e,t,i){if(((game.compendiumConfiguration||{})[t.pack]||{}).locked??"world"!==t.compendium.package.type)throw new Error(`You may not modify the ${t.pack} Compendium which is currently locked.`);return db.packs.get(t.pack).isOwner(e)}}}

View File

@@ -0,0 +1 @@
export default function EmbeddedDeltaMixin(e){return class extends e{async _preUpdate(e,t,s){if(!1===await super._preUpdate(e,t,s))return!1;const i=this.parent.getEmbeddedCollection(this.parentCollection);i.manages(this.id)||i.set(this.id,this)}batchWrite(e,{restoreDelta:t=!1,...s}={}){const i=this.parent.getEmbeddedCollection(this.parentCollection);t&&!i.manages(this.id)?super.batchDelete(e):super.batchWrite(e,s)}batchDelete(e,{restoreDelta:t=!1}={}){const s=this.parent.getEmbeddedCollection(this.parentCollection);if(!t&&s.isTombstone(this.id)){const t=new foundry.data.TombstoneData({_id:this.id}),{dbKey:s,sublevelName:i}=this;this.constructor.batchWrite(t.toObject(),e,{dbKey:s,sublevelName:i})}else super.batchDelete(e)}}}

View File

@@ -0,0 +1 @@
import fs from"node:fs";import{ClassicLevel}from"classic-level";import SublevelDatabase from"./sublevel-database.mjs";import Semaphore from"../../../common/utils/semaphore.mjs";export default class LevelDatabase extends ClassicLevel{constructor(e,a,{sublevels:t=[],...s}={}){if(!e||!a)throw new Error("You must provide a unique database name and file path location");if(LevelDatabase.#e.has(e))throw new Error(`The database "${e}" is already open and cannot be re-created.`);s.keyEncoding="utf8",s.valueEncoding="json",super(a,s),this.#a=e,LevelDatabase.#e.set(e,this),this.setMaxListeners(Math.max(10,t.length+1));const n={keyEncoding:s.keyEncoding,valueEncoding:s.valueEncoding};for(const e of t)this.#t[e]=this.sublevel(e,n)}static async connect(e,a,{allowRepair:t=!0,...s}={}){const n=new this(e,a,{passive:!0,createIfMissing:!0,...s});try{await n.open(),await n.keys().all(),global.logger.info(`Connected to database "${e}"`)}catch(i){if(await n.close(),i.message=`Failed to connect to database "${e}": ${i.message}`,t)return logger.error(i),LevelDatabase.#s(e,a,s);throw i}return n}static async#s(e,a,t){return logger.warn(`FoundryVTT | Attempting database repair for ${a}`),await this.repair(a),logger.warn(`FoundryVTT | Repair of ${a} complete. Attempting re-connection`),LevelDatabase.connect(e,a,{allowRepair:!1,...t})}semaphore=new Semaphore(1);get name(){return this.#a}#a;get sublevels(){return this.#t}#t={};static get databases(){return LevelDatabase.#e}static#e=new Map;static formatKey(...e){return e.join(".")}async close(...e){if(LevelDatabase.#e.delete(this.#a),"open"===this.status)try{await this.compactFull()}catch(e){e.message=`Unable to compact database ${this.location}: ${e.message}`,logger.error(e)}return super.close(...e)}async clone(e,a){if(this.constructor.databases.has(e)||a===this.location)throw new Error("The cloned database name and location must be unique");const t=await this.constructor.connect(e,a,{sublevels:Object.keys(this.sublevels)}),s=t.batch(),n=this.iterator();for await(const[e,a]of n)s.put(e,a);return await n.close(),await s.write(),t}async destroy(){await this.close(),fs.rmSync(this.location,{recursive:!0})}async compactFull(){const e=this.keys({limit:1,fillCache:!1}),a=await e.next();await e.close();const t=this.keys({limit:1,reverse:!0,fillCache:!1}),s=await t.next();return await t.close(),this.compactRange(a,s,{keyEncoding:"utf8"})}async size(){const e=this.keys({limit:1,fillCache:!1}),a=await e.next();await e.close();const t=this.keys({limit:1,reverse:!0,fillCache:!1}),s=await t.next();return await t.close(),this.approximateSize(a,s,{keyEncoding:"utf8"})}_sublevel(e,a){return new SublevelDatabase(this,e,a)}}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
import fs from"node:fs";import ServerCompendiumFolderMixin from"./compendium-folder.mjs";import{tagModelStats}from"../../core/utils.mjs";import*as CONST from"../../../common/constants.mjs";import{PACKAGE_TYPE_MAPPING}from"../../packages/_module.mjs";export default function ServerCompendiumMixin(e,t){return class extends e{static{this._db=void 0,this._dbState=0,this._dbWait=void 0,this.sublevel=void 0}static packData=t;static folderClass=ServerCompendiumFolderMixin(this);static get package(){return packages[this.packData.packageType.titleCase()].get(this.packData.packageName)}static metadata=(()=>foundry.utils.mergeObject(super.metadata,{permissions:{create:this.#e.bind(this),update:this.#e.bind(this),delete:this.#e.bind(this)}},{inplace:!1}))();static get collectionName(){return this.packData.id}static get implementation(){return db.packs.get(this.collectionName)}static get filename(){return this.packData.absPath}static _getSublevelNames(){const e=super._getSublevelNames();return e.push("folders"),e}static async disconnect(){await super.disconnect(),db.packs.delete(this.collectionName)}static fromSource(e,t={}){return t.pack=this.collectionName,super.fromSource(e,t)}static isOwner(e){const t=CONST.DOCUMENT_OWNERSHIP_LEVELS;return(e.isGM?t.OWNER:this.getUserLevel(e))>=t.OWNER}static getUserLevel(e){const t=CONST.DOCUMENT_OWNERSHIP_LEVELS;let i=t.NONE;const a=(game.compendiumConfiguration||{})[this.collectionName]||{},s=a?.ownership??this.packData?.ownership??{...PACKAGE_TYPE_MAPPING.module.schema.getField("packs.ownership").initial};for(const[a,o]of Object.entries(s))e.hasRole(a)&&(i=Math.max(i,t[o]));return i}static#e(e,t,i){if(((game.compendiumConfiguration||{})[t.collectionName]||{}).locked??"world"!==t.constructor.package.type)throw new Error(`You may not modify the ${t.collectionName} Compendium which is currently locked.`);return db.packs.get(t.collectionName).isOwner(e)}static async deleteCompendium(){await this.disconnect(),await fs.promises.rm(this.filename,{force:!0,recursive:!0}),await fs.promises.rm(`${this.filename}.db`,{force:!0}),logger.info(`Deleted Compendium Pack ${this.collectionName}`)}static async getIndex(e){if(!e)throw new Error("You must provide an array of index fields to retrieve");return this.connected||await this.connect(),this.database.get(this,{query:{},index:!0,indexFields:e})}static async getFolders(){return this.db.sublevels.folders.find()}static async getFolder(e,t={}){const i=await this.db.sublevels.folders.get(e);if(void 0!==i)return this.folderClass.fromSource(i,t);if(!0===t.strict)throw new Error(`The Folder [${e}] does not exist in ${this.collectionName}.`)}static async migrate({user:e,...t}={}){logger.info(`Migrating ${this.collectionName} Compendium to updated system template version.`),this.connected||await this.connect();const i=await this.find(),a=this.db.batch();for(let t of i)this.hasTypeData&&t.updateSource({system:t.migrateSystemData()}),tagModelStats(t,{user:e}),t.batchWrite(a),logger.info(`Migrated ${this.documentName} ${t.name} in Compendium pack ${this.collectionName}`);await a.write(),logger.info(`Migrated all ${i.length} ${this.documentName} Documents in Compendium pack ${this.collectionName}`)}}}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
import{AbstractSublevel}from"abstract-level";import{filterObject,getType,mergeObject,randomID}from"../../../common/utils/helpers.mjs";export default class SublevelDatabase extends AbstractSublevel{async createNewId(){for(;;){const e=randomID(16);if(!await this.has(e))return e}}prefixKey(e,t="utf8"){return super.prefixKey(e,t)}async get(e,t={},r){try{return await super.get(e,t,r)}catch(e){return}}async put(e,t,r={},a){return await super.put(e,t,r,a),t}async has(e){const t=this.keys({gte:e,lte:e,limit:1,fillCache:!1}),r=await t.next();return await t.close(),!!r}async delMany(e=[]){const t=await this.getMany(e,{fillCache:!1}),r=this.batch();for(const[a,s]of t.entries()){const t=e[a];s&&r.del(t)}return await r.write(),t}async find(e,{project:t,map:r,sort:a}={}){e=SublevelDatabase.#e(e);const s=[],i=await this.values({fillCache:!1}).all();for(let a of i)SublevelDatabase.#t(a,e)&&(t&&(a=filterObject(a,t)),r&&(a=await r(a)),s.push(a));return a&&("string"==typeof a&&(a={[a]:1}),s.sort(((e,t)=>SublevelDatabase.#r(e,t,a)))),s}async findOne(e,t){const r=await this.find(e,t);if(r.length)return r.length>1&&global.logger.warn(`Multiple results found for query "${JSON.stringify(e)}"`),r[0]}async findUpdate(e,t){e=SublevelDatabase.#e(e);const r=this.batch(),a=[];for(const[s,i]of await this.iterator({fillCache:!1}).all())SublevelDatabase.#t(i,e)&&(mergeObject(i,t),r.put(s,i),a.push(i));return await r.write(),a}async findDelete(e){e=SublevelDatabase.#e(e);const t=this.batch(),r=[];for(const[a,s]of await this.iterator({fillCache:!1}).all())SublevelDatabase.#t(s,e)&&(t.del(a),r.push(s));return await t.write(),r}static#r(e,t,r={}){for(const[a,s]of Object.entries(r)){const r=e[a],i=t[a];let n=0;if("string"==typeof r?n=r.compare(i)*s:"number"==typeof i&&(n=(r-i)*s),0!==n)return n}return 0}static#e(e){if(!e)return;const t=/^([A-z]+)__([a-z]+)$/;for(const[r,a]of Object.entries(e)){const s=r.match(t);if(s){const[t,i,n]=s;if(delete e[r],"in"===n)e[i]=new QueryOperation(i,a,SublevelDatabase.#a)}else"Object"===getType(a)&&SublevelDatabase.#e(a)}return e}static#t(e,t){if(!t)return!0;for(const[r,a]of Object.entries(t)){const t=e[r];if(a instanceof QueryOperation){if(!a.test(t))return!1}else if("Object"===getType(a)){if(!SublevelDatabase.#t(t,a))return!1}else if(t!==a)return!1}return!0}static#a(e,t){if(!Array.isArray(t))throw new Error("You must provide an array of target values when querying field__in");return t.includes(e)}}class QueryOperation{constructor(e,t,r){Object.defineProperties(this,{key:{value:e,writable:!1},target:{value:t,writable:!1},comparator:{value:r,writable:!1}})}test(e){return this.comparator(e,this.target)}}

View File

@@ -0,0 +1 @@
import"./fields-extensions.mjs";import{COMPENDIUM_DOCUMENT_TYPES,COMPATIBILITY_MODES}from"../../common/constants.mjs";import LevelDatabase from"./backend/level-database.mjs";import ServerDatabaseBackend from"./backend/server-backend.mjs";import ServerCompendiumMixin from"./backend/server-compendium.mjs";import ActiveEffect from"./documents/active-effect.mjs";import Actor from"./documents/actor.mjs";import ActorDelta from"./documents/actor-delta.mjs";import Adventure from"./documents/adventure.mjs";import AmbientLight from"./documents/ambient-light.mjs";import AmbientSound from"./documents/ambient-sound.mjs";import Card from"./documents/card.mjs";import Cards from"./documents/cards.mjs";import ChatMessage from"./documents/chat-message.mjs";import Combat from"./documents/combat.mjs";import Combatant from"./documents/combatant.mjs";import Drawing from"./documents/drawing.mjs";import FogExploration from"./documents/fog.mjs";import Folder from"./documents/folder.mjs";import Item from"./documents/item.mjs";import JournalEntry from"./documents/journal.mjs";import JournalEntryPage from"./documents/journal-page.mjs";import Playlist from"./documents/playlist.mjs";import MeasuredTemplate from"./documents/measured-template.mjs";import Note from"./documents/note.mjs";import Region from"./documents/region.mjs";import RegionBehavior from"./documents/region-behavior.mjs";import Scene from"./documents/scene.mjs";import Setting from"./documents/setting.mjs";import TableResult from"./documents/table-result.mjs";import Tile from"./documents/tile.mjs";import Token from"./documents/token.mjs";import User from"./documents/user.mjs";import Wall from"./documents/wall.mjs";import{Macro,PlaylistSound,RollTable}from"./documents/others.mjs";export const DatabaseBackend=new ServerDatabaseBackend;export const documents=[Actor,Cards,ChatMessage,Combat,FogExploration,Folder,Item,JournalEntry,Macro,Playlist,RollTable,Scene,Setting,User];export function getDocumentClass(e){return global.db[e]}export async function disconnect(){const e=[];for(const t of packs.values())e.push(t.disconnect());packs.clear();for(const t of documents)e.push(t.disconnect()),t.clearSanitizedFields();const t=await Promise.allSettled(e);e.length=0;for(let t of LevelDatabase.databases.values())e.push(t.close());const o=await Promise.allSettled(e);for(const e of t.concat(o))"rejected"===e.status&&global.logger.error(e.reason)}export const packs=new Map;export function defineCompendium(e){_validateCompendiumMetadata(e);const t=global.db[e.type],o=ServerCompendiumMixin(t,e);return db.packs.set(o.collectionName,o),o}function _validateCompendiumMetadata(e={}){if(!("name"in e))throw new Error("Compendium packs must define a canonical name");if(!("path"in e))throw new Error("Compendium packs must specify their relative file path.");if(!("type"in e))throw new Error("Compendium packs must specify the document type they contain.");if(!("absPath"in e))throw new Error("An absolute file path must be provided when a new Compendium is defined.");if(!COMPENDIUM_DOCUMENT_TYPES.includes(e.type))throw new Error(`Compendium ${e.label} is configured for an invalid Document type ${e.type}`);return!0}globalThis.CONFIG={compatibility:{mode:COMPATIBILITY_MODES.WARNING,includePatterns:[],excludePatterns:[]},DatabaseBackend:DatabaseBackend,Actor:{documentClass:Actor},Adventure:{documentClass:foundry.documents.BaseAdventure},Cards:{documentClass:Cards},ChatMessage:{documentClass:ChatMessage},Combat:{documentClass:Combat},FogExploration:{documentClass:FogExploration},Folder:{documentClass:Folder},Item:{documentClass:Item},JournalEntry:{documentClass:JournalEntry},JournalEntryPage:{documentClass:JournalEntryPage},Macro:{documentClass:Macro},Playlist:{documentClass:Playlist},RollTable:{documentClass:RollTable},Scene:{documentClass:Scene},Setting:{documentClass:Setting},User:{documentClass:User},ActiveEffect:{documentClass:ActiveEffect},Card:{documentClass:Card},TableResult:{documentClass:TableResult},PlaylistSound:{documentClass:PlaylistSound},AmbientLight:{documentClass:AmbientLight},AmbientSound:{documentClass:AmbientSound},Combatant:{documentClass:Combatant},Drawing:{documentClass:Drawing},MeasuredTemplate:{documentClass:MeasuredTemplate},Note:{documentClass:Note},Region:{documentClass:Region},RegionBehavior:{documentClass:RegionBehavior},Tile:{documentClass:Tile},Token:{documentClass:Token},Wall:{documentClass:Wall},ActorDelta:{documentClass:ActorDelta}};export{Actor,ActiveEffect,Adventure,AmbientLight,AmbientSound,Card,Cards,ChatMessage,Combat,Combatant,Drawing,FogExploration,Folder,Item,JournalEntry,JournalEntryPage,MeasuredTemplate,Note,Playlist,PlaylistSound,Scene,Setting,Macro,RollTable,Region,RegionBehavior,TableResult,Tile,Token,ActorDelta,User,Wall};

View File

@@ -0,0 +1 @@
import ServerDocumentMixin from"../backend/server-document.mjs";import BaseActiveEffect from"../../../common/documents/active-effect.mjs";import{getType}from"../../../common/utils/helpers.mjs";export default class ActiveEffect extends(ServerDocumentMixin(BaseActiveEffect)){static _migrationRegistry=[...super._migrationRegistry,{fn:migrateSystemDataInChanges,version:12},{fn:migrateOriginOwnedItem,version:12}]}function migrateSystemDataInChanges(e){if(!Array.isArray(e.changes))return!1;let t=!1;for(const r of e.changes)"Object"===getType(r)&&"string"==typeof r.key&&r.key.startsWith("data.")&&(r.key=r.key.replace(/^data\./,"system."),t=!0);return t}function migrateOriginOwnedItem(e){if("string"!=typeof e.origin)return!1;const t=e.origin.split(".").map((e=>"OwnedItem"===e?"Item":e)).join(".");return e.origin!==t&&(e.origin=t,!0)}

View File

@@ -0,0 +1 @@
import ServerDocumentMixin from"../backend/server-document.mjs";import BaseActorDelta from"../../../common/documents/actor-delta.mjs";import*as fields from"../../../common/data/fields.mjs";import Actor from"./actor.mjs";export default class ActorDelta extends(ServerDocumentMixin(BaseActorDelta)){static _migrationRegistry=Actor._migrationRegistry;static isDelta=!0;static _getTemplateFields(e){return e.Actor||{}}async loadRelatedDocuments(){return this.parent.loadRelatedDocuments()}async _preUpdate(e,t,r){if(t.restoreDelta){for(const t of Object.keys(e))delete e[t];for(const t of this.schema)t instanceof fields.DocumentIdField||(t instanceof fields.EmbeddedCollectionField?e[t.name]=[]:e[t.name]=t.initial);e._id=this.id}return super._preUpdate(e,t,r)}_onDelete(e,t){return super._onDelete(e,t),delete this.parent.delta,this.parent.recreateActorDelta()}}

View File

@@ -0,0 +1 @@
import BaseActor from"../../../common/documents/actor.mjs";import ServerDocumentMixin from"../backend/server-document.mjs";import{IMAGE_FILE_EXTENSIONS,VIDEO_FILE_EXTENSIONS}from"../../../common/constants.mjs";import{getType}from"../../../common/utils/helpers.mjs";import Files from"../../files/files.mjs";import Token from"./token.mjs";export default class Actor extends(ServerDocumentMixin(BaseActor)){static _migrationRegistry=[...super._migrationRegistry,{fn:migrateV10Fields,version:12},...Token._migrationRegistry.map((e=>({...e,fn:t=>{const r=t.prototypeToken;return"Object"===getType(r)&&e.fn(r)}})))];static socketListeners(e){e.on("requestTokenImages",this.#e.bind(this,e))}static async#e(e,t,{pack:r=null},o){const s=r?db.packs.get(r):db.Actor;if(!s.ready)return o({error:`The "${r}" database is not yet connected.`});const n=await s.get(t),i=n?.testUserPermission(e.user,"OWNER");if(!e.user.hasPermission("FILES_BROWSE")&&!i)return o({error:`You do not have permission to query wildcard token images for Actor [${t}].`});const a=n.prototypeToken;if(!a.randomImg)return o({files:[a.texture.src]});const{source:c,pattern:m,browseOptions:d}=Files.parseWildcardPath(a.texture.src);d.isAdmin=!0,d.target=m,d.extensions=Object.keys(IMAGE_FILE_EXTENSIONS).concat(Object.keys(VIDEO_FILE_EXTENSIONS)).map((e=>`.${e}`));config.files.storages[c].getFiles(d).then(o).catch((e=>o({error:e.message})))}}function migrateV10Fields(e){const t=Actor._addDataFieldMigration(e,"data","system"),r=Actor._addDataFieldMigration(e,"token","prototypeToken");return t||r}

View File

@@ -0,0 +1 @@
import BaseAdventure from"../../../common/documents/adventure.mjs";import ServerDocumentMixin from"../backend/server-document.mjs";export default class Adventure extends(ServerDocumentMixin(BaseAdventure)){static _migrateEmbeddedRecords(e,r){let t=super._migrateEmbeddedRecords(e,r);for(const[o,n]of Object.entries(this.contentFields)){const s=e[o];if(Array.isArray(s))for(const e of s){const o=n._migrateRecord(e,r);t||=o}}return t}}

View File

@@ -0,0 +1 @@
import BaseAmbientLight from"../../../common/documents/ambient-light.mjs";import ServerDocumentMixin from"../backend/server-document.mjs";import{getType}from"../../../common/utils/helpers.mjs";export default class AmbientLight extends(ServerDocumentMixin(BaseAmbientLight)){static _migrationRegistry=[...super._migrationRegistry,{fn:migrateFieldNameChanges,version:12},{fn:migrateNegativeRadius,version:12},{fn:migrateGradualToAttenuation,version:12},{fn:migrateZeroAngle,version:12},{fn:migrateSourceTypeFlags,version:12},{fn:migrateNegativeLuminosity,version:"12.319"}]}function migrateFieldNameChanges(i){let e=!1;const t={darknessThreshold:"darkness.min",dim:"config.dim",bright:"config.bright",angle:"config.angle",tintColor:"config.color",tintAlpha:"config.alpha",lightAnimation:"config.animation",darkness:"config.darkness"};for(const[n,o]of Object.entries(t)){const t=AmbientLight._addDataFieldMigration(i,n,o);e||=t}return e}function migrateSourceTypeFlags(i){return"t"in i&&("walls"in i||(i.walls="u"!==i.t),"vision"in i||(i.vision="l"!==i.t),delete i.t,!0)}function migrateNegativeRadius(i){const e=i.config;if("Object"!==getType(e))return!1;let t=!1;return e.dim<0&&(e.dim=Math.abs(e.dim),t=!0),e.bright<0&&(e.bright=Math.abs(e.bright),t=!0),!!t&&(e.luminosity=-1*Math.abs(e.luminosity??.5),!0)}function migrateGradualToAttenuation(i){const e=i.config;return"Object"===getType(e)&&("gradual"in e&&(e.attenuation=e.gradual?.5:.3,delete e.gradual,!0))}function migrateZeroAngle(i){return"Object"===getType(i.config)&&(0===i.config.angle&&(i.config.angle=360,!0))}function migrateNegativeLuminosity(i){const e=i.config;return"Object"===getType(e)&&(e.luminosity<0&&(e.luminosity=.5,e.negative=!0,!0))}

View File

@@ -0,0 +1 @@
import BaseAmbientSound from"../../../common/documents/ambient-sound.mjs";import ServerDocumentMixin from"../backend/server-document.mjs";export default class AmbientSound extends(ServerDocumentMixin(BaseAmbientSound)){static _migrationRegistry=[...super._migrationRegistry,{fn:migrateWallAttributes,version:12}]}function migrateWallAttributes(e){return"t"in e&&("walls"in e||(e.walls="l"===e.t),delete e.t,!0)}

View File

@@ -0,0 +1 @@
import BaseCard from"../../../common/documents/card.mjs";import ServerDocumentMixin from"../backend/server-document.mjs";export default class Card extends(ServerDocumentMixin(BaseCard)){static _migrationRegistry=[...super._migrationRegistry,{fn:migrateV10Fields,version:12}]}function migrateV10Fields(e){return Card._addDataFieldMigration(e,"data","system")}

View File

@@ -0,0 +1 @@
import ServerDocumentMixin from"../backend/server-document.mjs";import BaseCards from"../../../common/documents/cards.mjs";export default class Cards extends(ServerDocumentMixin(BaseCards)){static _migrationRegistry=[...super._migrationRegistry,{fn:migrateV10Fields,version:12},{fn:migrateBaseToDeck,version:12}]}function migrateV10Fields(e){return Cards._addDataFieldMigration(e,"data","system")}function migrateBaseToDeck(e){return e.type===CONST.BASE_DOCUMENT_TYPE&&(e.type="deck",!0)}

View File

@@ -0,0 +1 @@
import BaseChatMessage from"../../../common/documents/chat-message.mjs";import ServerDocumentMixin from"../backend/server-document.mjs";export default class ChatMessage extends(ServerDocumentMixin(BaseChatMessage)){static _migrationRegistry=[...super._migrationRegistry,{fn:migrateRolls,version:12}]}function migrateRolls(e){return"roll"in e&&("rolls"in e||(e.rolls=[e.roll]),delete e.roll,!0)}

View File

@@ -0,0 +1 @@
import BaseCombat from"../../../common/documents/combat.mjs";import ServerDocumentMixin from"../backend/server-document.mjs";export default class Combat extends(ServerDocumentMixin(BaseCombat)){static get collection(){return"combat"}_onUpdate(e,t,a){if(super._onUpdate(e,t,a),Number.isNumeric(t.advanceTime)&&!t.worldTime&&(t.worldTime={delta:t.advanceTime}),"object"==typeof t.worldTime){const{delta:e,...m}=t.worldTime;Number.isNumeric(e)&&db.Setting.advanceTime(e,m,a)}}static async _onDeleteScene(e){const t=(await this.find({scene:e.id})).map((e=>e.id));await this.sublevel.delMany(t),db.DatabaseBackend.emit(this.documentName,"delete",t)}}

View File

@@ -0,0 +1 @@
import BaseCombatant from"../../../common/documents/combatant.mjs";import ServerDocumentMixin from"../backend/server-document.mjs";export default class Combatant extends(ServerDocumentMixin(BaseCombatant)){async loadRelatedDocuments(){if(void 0===this.actor){const t=await db.Actor.get(this.actorId);this.actor=t||null}}static async _onCreateOperation(t,a,e){return await Combatant.#t(a),await Combatant.#a(a),super._onCreateOperation(t,a,e)}static async _onUpdateOperation(t,a,e){return await Combatant.#t(a),await Combatant.#a(a),super._onUpdateOperation(t,a,e)}static async _onDeleteOperation(t,a,e){return await Combatant.#t(a),super._onDeleteOperation(t,a,e)}static async#t(t){if("combatTurn"in t){const a=t.parent;if(t.combatTurn===a.turn)return;a.updateSource({turn:t.combatTurn}),await a.save({writeEmbedded:!1})}}static async#a(t){const a=t.parent;a.scene&&a.combatants.some((t=>t.sceneId&&t.sceneId!==a.scene))&&(a.updateSource({scene:null}),await a.save({writeEmbedded:!1}),db.DatabaseBackend.emit(a.documentName,"update",[{_id:a.id,scene:null}]))}}

View File

@@ -0,0 +1 @@
import BaseDrawing from"../../../common/documents/drawing.mjs";import ServerDocumentMixin from"../backend/server-document.mjs";export default class Drawing extends(ServerDocumentMixin(BaseDrawing)){static _migrationRegistry=[...super._migrationRegistry,{fn:migrateV10Fields,version:12},{fn:migrateNullValues,version:12},{fn:migrateInvisible,version:12},{fn:migrateInterface,version:"12.317"}]}function migrateV10Fields(t){const e={t:"r",f:"p"},i=Drawing._addDataFieldMigration(t,"type","shape.type",(t=>e[t.type]??t.type)),r=Drawing._addDataFieldMigration(t,"width","shape.width"),n=Drawing._addDataFieldMigration(t,"height","shape.height"),o=Drawing._addDataFieldMigration(t,"points","shape.points",(t=>Array.isArray(t.points)?t.points.flat():t.points));return i||r||n||o}function migrateNullValues(t){let e=!1;return null===t.fillColor&&(t.fillColor="#ffffff",e=!0),null===t.fontSize&&(t.fontSize=48,e=!0),null===t.strokeColor&&(t.strokeColor="#ffffff",e=!0),null===t.strokeWidth&&(t.strokeWidth=0,e=!0),null===t.textColor&&(t.textColor="#ffffff",e=!0),e}function migrateInvisible(t){let{text:e,textAlpha:i,fillType:r,fillAlpha:n,strokeWidth:o,strokeAlpha:a}=t;e??="",i??=1,r??=CONST.DRAWING_FILL_TYPES.NONE,n??=1,o??=8,a??=1;const l=""!==e&&i>0,f=r!==CONST.DRAWING_FILL_TYPES.NONE&&n>0;return!(l||f||o>0&&a>0)&&(t.strokeWidth=8,t.strokeColor="#ffffff",t.strokeAlpha=1,!0)}function migrateInterface(t){return t.interface=!!t.text,!0}

View File

@@ -0,0 +1 @@
import BaseFogExploration from"../../../common/documents/fog-exploration.mjs";import ServerDocumentMixin from"../backend/server-document.mjs";export default class FogExploration extends(ServerDocumentMixin(BaseFogExploration)){_onCreate(e,o,t){return o.broadcast=!1,super._onCreate(e,o,t)}_onUpdate(e,o,t){return o.broadcast=!1,super._onUpdate(e,o,t)}_onDelete(e,o){return e.broadcast=!1,super._onDelete(e,o)}static async find(e={},o={}){const t=await super.find(e,o);if(!0===o.expireOthers){const e=t.length?t.pop():null;return t.length&&await this.sublevel.delMany(t.map((e=>e._id))),e}return t}static socketListeners(e){e.on("resetFog",this.#e.bind(e))}static async#e(e){if(!this.user.isGM)throw new Error("You do not have permission to reset Fog of War for this scene");const o=(await FogExploration.find({scene:e})).map((e=>e.id));await FogExploration.sublevel.delMany(o),global.logger.info(`Reset ${o.length} FogExploration documents for Scene ${e}`),global.express.io.emit("resetFog",{sceneId:e}),db.DatabaseBackend.emit(FogExploration.documentName,"delete",o)}}

View File

@@ -0,0 +1 @@
import BaseFolder from"../../../common/documents/folder.mjs";import ServerDocumentMixin from"../backend/server-document.mjs";import{SORT_INTEGER_DENSITY}from"../../../common/constants.mjs";export default class Folder extends(ServerDocumentMixin(BaseFolder)){static _migrationRegistry=[...super._migrationRegistry,{fn:migrateParentToFolder,version:12}];async getSubfolders(e=!1,t){if((t=t||new Set).has(this.id))return[];let r=await this.constructor.find({folder:this.id});if(t.add(this.id),e&&r.length)for(let e of r){const i=await e.getSubfolders(!0,t);r=r.concat(i)}return r}async _preCreate(e,t,r){if(!Number.isFinite(this.sort)){const e=await Folder.find({type:this.type});let t=Math.max(...e.map((e=>e.sort||0)));this.updateSource({sort:t+SORT_INTEGER_DENSITY})}if(e.folder){const t=e.pack?db.packs.get(e.pack).folders:db.Folder;let r=await t.get(e.folder);if(!r)return;const i=e.pack?CONST.FOLDER_MAX_DEPTH-1:CONST.FOLDER_MAX_DEPTH;let a=1;for(;r?.folder;)r=await t.get(r.folder),a++;if(a>=i)throw new Error(`You may not nest Folders more than ${i} levels deep.`)}if(this.compendium&&this.compendium.packData.type!==this.type)throw new Error(`Attempted to create a Folder for ${this.type} Documents in a compendium that only allows for ${this.compendium.packData.type} Documents.`);return super._preCreate(e,t,r)}async _preUpdate(e,t,r){if(!1===await super._preUpdate(e,t,r))return!1;if(e.parent&&e.parent===this.id)throw new Error("You cannot assign a Folder to be it's own parent")}async _preDelete(e,t){if(!1===await super._preDelete(e,t))return!1;const r=this.folder||null,{deleteContents:i,deleteSubfolders:a}=e,o=[],n=[],s=await this.getSubfolders(!0);for(let e of s)a?o.push(e.id):n.push(e.id);if(o.length&&(await this.constructor.sublevel.delMany(o),db.DatabaseBackend.emit(this.documentName,"delete",o,{pack:this.pack,render:!0},t)),n.length){await this.constructor.sublevel.findUpdate({_id__in:Array.from(n)},{folder:r});const e=n.map((e=>({_id:e,folder:r})));db.DatabaseBackend.emit(this.documentName,"update",e,{pack:this.pack,render:!0},t)}if("Compendium"===this.type)return;o.push(this.id);const d=this.pack?this.compendium.sublevel:db[this.type].implementation.sublevel;let l=[];if(i)l=await d.findDelete({folder__in:Array.from(o)}),l.length&&db.DatabaseBackend.emit(this.type,"delete",l.map((e=>e._id)),{pack:this.pack,render:!0},t);else{const e=await d.findUpdate({folder__in:Array.from(o)},{folder:r});if(e.length){const i=e.map((e=>({_id:e._id,folder:r})));db.DatabaseBackend.emit(this.type,"update",i,{pack:this.pack,render:!0},t)}}logger.info([`Deleting Folder [${this.id}] also deletes:`,o.length>1?o.length-1+" subfolders":"",l.length?`and ${l.length} ${this.type} documents`:""].filterJoin(" "))}}function migrateParentToFolder(e){return Folder._addDataFieldMigration(e,"parent","folder")}

View File

@@ -0,0 +1 @@
import BaseItem from"../../../common/documents/item.mjs";import ServerDocumentMixin from"../backend/server-document.mjs";export default class Item extends(ServerDocumentMixin(BaseItem)){static _migrationRegistry=[...super._migrationRegistry,{fn:migrateV10Fields,version:12}]}function migrateV10Fields(e){return Item._addDataFieldMigration(e,"data","system")}

View File

@@ -0,0 +1 @@
import ServerDocumentMixin from"../backend/server-document.mjs";import BaseJournalEntryPage from"../../../common/documents/journal-entry-page.mjs";import showdown from"showdown";import{cleanHTML}from"../validators.mjs";export default class JournalEntryPage extends(ServerDocumentMixin(BaseJournalEntryPage)){static _migrationRegistry=[...super._migrationRegistry,{fn:migrateBaseToText,version:12}];static#t=(()=>(Object.entries(CONST.SHOWDOWN_OPTIONS).forEach((([t,e])=>showdown.setOption(t,e))),new showdown.Converter))();async _preCreate(t,e,r){if(!1===await super._preCreate(t,e,r))return!1;this.text.format===CONST.JOURNAL_ENTRY_PAGE_FORMATS.MARKDOWN?this.updateSource({"text.content":this.#e(this.text.markdown)}):this.updateSource({"text.markdown":void 0})}async _preUpdate(t,e,r){if(!1===await super._preUpdate(t,e,r))return!1;if("text"in t){const e="format"in t.text?t.text.format??CONST.JOURNAL_ENTRY_PAGE_FORMATS.HTML:this.text.format;if(e===CONST.JOURNAL_ENTRY_PAGE_FORMATS.MARKDOWN){const r="markdown"in t.text?t.text.markdown:this.text.markdown;t.text.content=this.#e(r,e),void 0!==this.text.content&&(t.text.content||="")}else this.text.markdown?t.text.markdown="":"markdown"in t.text&&delete t.text.markdown}}#e(t,e){const r=CONST.JOURNAL_ENTRY_PAGE_FORMATS,n=(e??this.text.format)===r.MARKDOWN&&t;if("text"!==this.type||!n)return;let o=JournalEntryPage.#t.makeHtml(t);return cleanHTML(o)}}function migrateBaseToText(t){return t.type===CONST.BASE_DOCUMENT_TYPE&&(t.type="text",!0)}

View File

@@ -0,0 +1 @@
import BaseJournalEntry from"../../../common/documents/journal-entry.mjs";import ServerDocumentMixin from"../backend/server-document.mjs";import*as CONST from"../../../common/constants.mjs";export default class JournalEntry extends(ServerDocumentMixin(BaseJournalEntry)){static _migrationRegistry=[...super._migrationRegistry,{fn:migrateJournalEntryPages,version:12}];static socketListeners(e){e.on("showEntry",this.#e.bind(e)),e.on("shareImage",this.#t.bind(e))}static#e(e,{force:t=!1,users:n=[]},r){if(n.length)for(const r of n){const n=game.users.find((e=>e.id===r));n&&n.sockets.forEach((n=>n.emit("showEntry",e,t)))}else this.broadcast.emit("showEntry",e,t);r(!0)}static#t({users:e,...t}={}){if(e?.length)for(const n of e){const e=game.users.find((e=>e.id===n));e&&e.sockets.forEach((e=>e.emit("shareImage",t)))}else this.broadcast.emit("shareImage",t)}}function migrateJournalEntryPages(e){if(!("img"in e)&&!("content"in e))return!1;if(Array.isArray(e.pages)&&e.pages.length>0)return delete e.img,delete e.content,!0;e.pages=[];const t=e.img&&e.content;return e.img&&e.pages.push({name:`${t?"Figure: ":""}${e.name}`,type:"image",src:e.img,title:{show:!1}}),e.content&&e.pages.push({name:e.name,type:"text",title:{show:!1},text:{format:CONST.JOURNAL_ENTRY_PAGE_FORMATS.HTML,content:e.content}}),delete e.img,delete e.content,!0}

View File

@@ -0,0 +1 @@
import BaseMeasuredTemplate from"../../../common/documents/measured-template.mjs";import ServerDocumentMixin from"../backend/server-document.mjs";export default class MeasuredTemplate extends(ServerDocumentMixin(BaseMeasuredTemplate)){static _migrationRegistry=[...super._migrationRegistry,{fn:migrateNegativeDistanceAndWidth,version:12},{fn:migrateNullAngle,version:12},{fn:migrateNullFillColor,version:12}]}function migrateNegativeDistanceAndWidth(e){let t=!1;return"distance"in e&&e.distance<0&&(e.distance=Math.abs(e.distance),t=!0),"width"in e&&e.width<0&&(e.width=Math.abs(e.width),t=!0),t}function migrateNullAngle(e){return null===e.angle&&"cone"===e.t&&(e.angle=90,!0)}function migrateNullFillColor(e){return null===e.fillColor&&(e.fillColor="#ff0000",!0)}

View File

@@ -0,0 +1 @@
import BaseNote from"../../../common/documents/note.mjs";import ServerDocumentMixin from"../backend/server-document.mjs";export default class Note extends(ServerDocumentMixin(BaseNote)){static _migrationRegistry=[...super._migrationRegistry,{fn:migrateTextureData,version:12}];async loadRelatedDocuments(){this.entry||(this.entry=await db.JournalEntry.get(this.entryId))}}function migrateTextureData(t){const e=Note._addDataFieldMigration(t,"icon","texture.src"),r=Note._addDataFieldMigration(t,"iconTint","texture.tint");return e||r}

View File

@@ -0,0 +1 @@
import ServerDocumentMixin from"../backend/server-document.mjs";import BaseMacro from"../../../common/documents/macro.mjs";import BasePlaylistSound from"../../../common/documents/playlist-sound.mjs";import BaseRollTable from"../../../common/documents/roll-table.mjs";export class Macro extends(ServerDocumentMixin(BaseMacro)){}export class PlaylistSound extends(ServerDocumentMixin(BasePlaylistSound)){}export class RollTable extends(ServerDocumentMixin(BaseRollTable)){}

View File

@@ -0,0 +1 @@
import BasePlaylist from"../../../common/documents/playlist.mjs";import ServerDocumentMixin from"../backend/server-document.mjs";import{handleCustomSocket}from"../../server/sockets.mjs";export default class Playlist extends(ServerDocumentMixin(BasePlaylist)){static socketListeners(o){o.on("playAudio",handleCustomSocket.bind(o,"playAudio")),o.on("playAudioPosition",handleCustomSocket.bind(o,"playAudioPosition")),o.on("preloadAudio",(e=>o.broadcast.emit("preloadAudio",e)))}}

View File

@@ -0,0 +1 @@
import BaseRegionBehavior from"../../../common/documents/region-behavior.mjs";import{setProperty}from"../../../common/utils/helpers.mjs";import ServerDocumentMixin from"../backend/server-document.mjs";export default class RegionBehavior extends(ServerDocumentMixin(BaseRegionBehavior)){static _migrationRegistry=[...super._migrationRegistry,{fn:migrateExecuteMacroEveryone,version:"12.326"}]}function migrateExecuteMacroEveryone(e){return"executeMacro"===e.type&&(setProperty(e,"system.everyone",!0),!0)}

View File

@@ -0,0 +1 @@
import BaseRegion from"../../../common/documents/region.mjs";import ServerDocumentMixin from"../backend/server-document.mjs";export default class Region extends(ServerDocumentMixin(BaseRegion)){static socketListeners(e){e.on("regionEvent",this.#e.bind(this))}static#e(e){global.express.io.emit("regionEvent",e)}}

View File

@@ -0,0 +1 @@
import BaseScene from"../../../common/documents/scene.mjs";import ServerDocumentMixin from"../backend/server-document.mjs";import{getProperty,getType,setProperty}from"../../../common/utils/helpers.mjs";export default class Scene extends(ServerDocumentMixin(BaseScene)){static isCached=!0;static _migrationRegistry=[...super._migrationRegistry,{fn:migrateV10Fields,version:12},{fn:migrateLegacyHexFlag,version:12},{fn:migrateRangesInLegacyHex,version:12},{fn:migrateHexGridAlpha,version:12},{fn:migrateOverheadTiles,version:12},{fn:migrateGlobalLightLuminosity,version:"12.325"}];async _preCreate(e,t,r){return this.active&&await this.activate(),super._preCreate(e,t,r)}async _preUpdate(e,t,r){if(!1===await super._preUpdate(e,t,r))return!1;e.active&&await this.activate()}_onDelete(e,t){super._onDelete(e,t),db.Combat._onDeleteScene(this),db.FogExploration.sublevel.findDelete({scene:this.id})}async activate(){logger.info(`Activating scene ${this.name} [${this.id}]`);(await this.constructor.sublevel.findUpdate({active:!0},{active:!1})).forEach((({_id:e})=>{if(e===this.id)return;const t=game.documentCache.get(this.constructor.documentName,e);t&&(t.updateSource({active:!1}),game.documentCache.set(t))}))}static async get(e,t,r){const i=game.documentCache.get(this.documentName,e)??await super.get(e,t,r);return game.documentCache.set(i),i}static async getMany(e,t){const r=[],i=[];for(const t of e){const e=game.documentCache.get(this.documentName,t);e?i.push(e):r.push(t)}return r.length?i.concat(await super.getMany(e,t)):i}static socketListeners(e){e.on("preloadScene",this.#e.bind(e)),e.on("pullToScene",this.#t.bind(e))}static#e(e,t){this.broadcast.emit("preloadScene",e),t(e)}static async#t(e,t){if(!this.user.isGM)return;const r=(await db.User.get(t,{strict:!0})).sockets;r.length&&r.forEach((t=>this.server.to(t.id).emit("pullToScene",e)))}static async migrateSystem(){const{logger:e}=global;e.info("Migrating Scene documents to the latest game system data model");const t=await this.find({},{}),r=this.db.batch();for(const i of t){for(const t of i.tokens){try{await t.loadRelatedDocuments()}catch(t){e.error(t);continue}const i=t.delta?._source.items||[];if(t.actorLink||!i.length)continue;const o=[];for(const t of i)try{if(t._tombstone)return t;const e=db.Item.fromSource(t);e.updateSource({system:e.migrateSystemData()}),o.push(e.toObject())}catch(t){e.error(t)}t.delta._source.items=o,t.batchWrite(r)}i.batchWrite(r,{writeEmbedded:!1})}await r.write(),globalThis.logger.info(`Successfully migrated ${t.length} Scene documents to the latest system data model.`)}}function isHexGrid(e){const{HEXODDR:t,HEXEVENQ:r}=CONST.GRID_TYPES,i=e.grid?.type??e.gridType;return i>=t&&i<=r}function migrateV10Fields(e){let t=!1;"grid"in e&&"Object"!==getType(e.grid)&&(e.grid={size:e.grid},t=!0);const r={gridType:"grid.type",gridColor:"grid.color",gridAlpha:"grid.alpha",gridDistance:"grid.distance",gridUnits:"grid.units",img:"background.src",shiftX:"background.offsetX",shiftY:"background.offsetY"};for(const[i,o]of Object.entries(r)){const r=Scene._addDataFieldMigration(e,i,o);t||=r}return t}function migrateLegacyHexFlag(e){const t=isHexGrid(e),r=getProperty(e,"flags.core.legacyHex");return t&&!0!==r&&!getProperty(e,"_stats.coreVersion")?(setProperty(e,"flags.core.legacyHex",!0),!0):!!(!t&&void 0!==r||t&&!1===r)&&(delete source.flags.core.legacyHex,!0)}function migrateHexGridAlpha(e){if(!isHexGrid(e))return!1;const t=Math.clamp(e.grid?.alpha??.2,0,1),r=Number((t*(2-t)).toFixed(4));return r!==e.grid?.alpha&&(setProperty(e,"grid.alpha",r),!0)}function migrateRangesInLegacyHex(e){if(!(isHexGrid(e)&&getProperty(e,"flags.core.legacyHex")))return!1;const t=2*Math.SQRT1_3;let r=!1;if(Array.isArray(e.lights))for(const i of e.lights)"Object"===getType(i)&&"Object"===getType(i.config)&&(i.config.dim>0&&(i.config.dim*=t,r=!0),i.config.bright>0&&(i.config.bright*=t,r=!0));if(Array.isArray(e.sounds))for(const i of e.sounds)"Object"===getType(i)&&i.radius>0&&(i.radius*=t,r=!0);return r}function migrateOverheadTiles(e){let t=!1;const r=e.grid?.distance??game.system.grid.distance,i=e.foregroundElevation??4*r;if(!Array.isArray(e.tiles))return!1;for(const r of e.tiles)"Object"===getType(r)&&(r.overhead?(r.elevation=i,t=!0):(r.roof&&(r.roof=!1,t=!0),getProperty(r,"occlusion.mode")>CONST.OCCLUSION_MODES.NONE&&(setProperty(r,"occlusion.mode",CONST.OCCLUSION_MODES.NONE),t=!0)));return t}function migrateGlobalLightLuminosity(e){return setProperty(e,"environment.globalLight.luminosity",0),!0}

View File

@@ -0,0 +1 @@
import BaseSetting from"../../../common/documents/setting.mjs";import ServerDocumentMixin from"../backend/server-document.mjs";import{USER_PERMISSIONS,USER_ROLES}from"../../../common/constants.mjs";import{deepClone}from"../../../common/utils/helpers.mjs";export default class Setting extends(ServerDocumentMixin(BaseSetting)){_onCreate(e,t,i){super._onCreate(e,t,i),this.#e(),"core.moduleConfiguration"===this.key&&!1!==t.updateWorld&&game.world?.onUpdateModuleConfiguration(this.value)}_onUpdate(e,t,i){super._onUpdate(e,t,i),this.#e(),"core.moduleConfiguration"===this.key&&!1!==t.updateWorld&&game.world?.onUpdateModuleConfiguration(this.value)}async _preCreate(e,t,i){if(!1===await super._preCreate(e,t,i))return!1;if("core.moduleConfiguration"===this.key&&this.value){const e=deepClone(this.value);this.#t(e),this.updateSource({value:e})}}async _preUpdate(e,t,i){if(!1===await super._preUpdate(e,t,i))return!1;if("core.moduleConfiguration"===this.key&&"value"in e){const t=JSON.parse(e.value);this.#t(t),e.value=JSON.stringify(t)}}#e(){switch(this.key){case"core.permissions":game.permissions=this.value;break;case"core.compendiumConfiguration":game.compendiumConfiguration=this.value;break;case"core.time":game.activity.worldTime=parseInt(this.value)}}#t(e){const t=[...game.world.relationships.requires,...game.world.system.relationships.requires];for(const i of game.world.modules){const s=i.isCompatibleWithSystem(game.world.system),a=t.find((e=>e.id===i.id));s&&a?e[i.id]=!0:s||(e[i.id]=!1)}}static async getValue(e){const t=await this.findOne({key:e});return t?.value}static async set(e,t,i){t="string"==typeof t?t:JSON.stringify(t);let s=await this.find({key:e});return s.length?(s=s.shift(),s.update({value:t},i)):this.create({key:e,value:t},i)}static async getPermissions(){let e=await this.getValue("core.permissions")||{},t=!1;for(let[i,s]of Object.entries(USER_PERMISSIONS))i in e||(e[i]=Object.values(USER_ROLES).reduce(((e,t)=>(s.defaultRole<=t&&e.push(t),e)),[]),t=!0);return t&&await this.set("core.permissions",e),game.permissions=e}static async advanceTime(e,t,i){const s=await this.getValue("core.time")??0,a=await this.set("core.time",s+e);return db.DatabaseBackend.emit(this.documentName,"update",[{_id:a.id,value:a.value}],t,i),a}}

View File

@@ -0,0 +1 @@
import BaseTableResult from"../../../common/documents/table-result.mjs";import ServerDocumentMixin from"../backend/server-document.mjs";export default class TableResult extends(ServerDocumentMixin(BaseTableResult)){static _migrationRegistry=[...super._migrationRegistry,{fn:migrateV10Fields,version:12},{fn:migrateType,version:12}]}function migrateV10Fields(e){const t=TableResult._addDataFieldMigration(e,"collection","documentCollection"),r=TableResult._addDataFieldMigration(e,"resultCollection","documentCollection"),i=TableResult._addDataFieldMigration(e,"resultId","documentId");return t||r||i}function migrateType(e){switch(e.type){case 0:return e.type=CONST.TABLE_RESULT_TYPES.TEXT,!0;case 1:return e.type=CONST.TABLE_RESULT_TYPES.DOCUMENT,!0;case 2:return e.type=CONST.TABLE_RESULT_TYPES.COMPENDIUM,!0}return!1}

View File

@@ -0,0 +1 @@
import BaseTile from"../../../common/documents/tile.mjs";import ServerDocumentMixin from"../backend/server-document.mjs";import{getType,setProperty}from"../../../common/utils/helpers.mjs";export default class Tile extends(ServerDocumentMixin(BaseTile)){static _migrationRegistry=[...super._migrationRegistry,{fn:migrateTextureData,version:12},{fn:migrateOcclusion,version:12}]}function migrateTextureData(e){const t=Tile._addDataFieldMigration(e,"img","texture.src"),i=Tile._addDataFieldMigration(e,"tint","texture.tint");let r=!1;"width"in e&&e.width<0&&(e.width=Math.abs(e.width),setProperty(e,"texture.scaleX",-1),r=!0);let o=!1;return"height"in e&&e.height<0&&(e.height=Math.abs(e.height),setProperty(e,"texture.scaleY",-1),o=!0),t||i||r||o}function migrateOcclusion(e){return"Object"===getType(e.occlusion)&&(2===Number(e.occlusion.mode)&&(e.occlusion.mode=1,e.roof=!0,!0))}

View File

@@ -0,0 +1 @@
import BaseToken from"../../../common/documents/token.mjs";import ServerDocumentMixin from"../backend/server-document.mjs";import{getProperty,getType,hasProperty,setProperty}from"../../../common/utils/helpers.mjs";export default class Token extends(ServerDocumentMixin(BaseToken)){static _migrationRegistry=[...super._migrationRegistry,{fn:migrateLightConfig,version:12},{fn:migrateNegativeLightRadius,version:12},{fn:migrateGradualLightToAttenuation,version:12},{fn:migrateTextureData,version:12},{fn:migrateSight,version:12},{fn:migrateActorData,version:12},{fn:migrateZeroAngles,version:12},{fn:migrate2x2HexShape,version:12},{fn:migrateNonsquareTextureFit,version:12},{fn:migrateNullVisionRanges,version:12},{fn:migrateOcclusionRadiusFlag,version:12},{fn:migrateNegativeLuminosity,version:"12.319"},{fn:migrateTokenRing,version:"12.320"}];async _preUpdate(t,e,i){if(!1===await super._preUpdate(t,e,i))return!1;("x"in t||"y"in t||"elevation"in t)&&(e._priorPosition??={},e._priorPosition[this.id]={x:this.x,y:this.y,elevation:this.elevation}),"_regions"in t&&(e._priorRegions??={},e._priorRegions[this.id]=this._regions)}_onUpdate(t,e,i){if(super._onUpdate(t,e,i),"object"==typeof e.worldTime){const{delta:t,...r}=e.worldTime;Number.isNumeric(t)&&db.Setting.advanceTime(t,r,i)}}async loadRelatedDocuments(){this.baseActor=await db.Actor.get(this.actorId),this.delta||await this.recreateActorDelta();for(const t of Object.values(this.delta.collections))t.initialize({full:!0});this.actorLink?this.actor=this.baseActor:this.actor=this.delta?.constructor.applyDelta(this.delta,this.baseActor)}async recreateActorDelta(){return this.updateSource({delta:new db.ActorDelta({_id:this.id},{parent:this}).toObject()}),this.save()}}function migrateLightConfig(t){let e=!1;const i={dimLight:"light.dim",brightLight:"light.bright",lightAngle:"light.angle",lightColor:"light.color",lightAlpha:"light.alpha",lightAnimation:"light.animation"};for(const[r,a]of Object.entries(i)){const i=Token._addDataFieldMigration(t,r,a);e||=i}return e}function migrateNegativeLightRadius(t){const e=t.light;if("Object"!==getType(e))return!1;let i=!1;return e.dim<0&&(e.dim=Math.abs(e.dim),i=!0),e.bright<0&&(e.bright=Math.abs(e.bright),i=!0),!!i&&(e.luminosity=-1*Math.abs(e.luminosity??.5),!0)}function migrateGradualLightToAttenuation(t){const e=t.light;return"Object"===getType(e)&&("gradual"in e&&(e.attenuation=e.gradual?.5:.3,delete e.gradual,!0))}function migrateTextureData(t){const e=Token._addDataFieldMigration(t,"img","texture.src"),i=Token._addDataFieldMigration(t,"tint","texture.tint");let r=!1,a=t.texture?.scaleX??1,n=t.texture?.scaleY??1;"scale"in t&&(a=n=t.scale,setProperty(t,"texture.scaleX",t.scale),setProperty(t,"texture.scaleY",t.scale),delete t.scale,r=!0);const o=Token._addDataFieldMigration(t,"mirrorX","texture.scaleX",(t=>t.mirrorX?-Math.abs(a):Math.abs(a))),s=Token._addDataFieldMigration(t,"mirrorY","texture.scaleY",(t=>t.mirrorY?-Math.abs(n):Math.abs(n)));return e||i||r||o||s}function migrateSight(t){const e=Token._addDataFieldMigration(t,"sightAngle","sight.angle"),i=Token._addDataFieldMigration(t,"vision","sight.enabled");let r=!1;if("dimSight"in t||"brightSight"in t){const e=t.dimSight??0,i=t.brightSight??0,a=Math.max(e,i);setProperty(t,"sight.range",a),delete t.dimSight,delete t.brightSight,setProperty(t,"sight.brightness",i>=e?1:0),r=!0}return e||i||r}function migrateActorData(t){return"actorData"in t&&("delta"in t||(t.delta=t.actorData,t._id&&"Object"===getType(t.delta)&&(t.delta._id=t._id)),delete t.actorData,!0)}function migrateZeroAngles(t){let e=!1;return"Object"===getType(t.light)&&0===t.light.angle&&(t.light.angle=360,e=!0),"Object"===getType(t.sight)&&0===t.sight.angle&&(t.sight.angle=360,e=!0),e}function migrate2x2HexShape(t){2===t.width&&2===t.height&&(t.hexagonalShape=CONST.TOKEN_HEXAGONAL_SHAPES.ELLIPSE_2)}function migrateNonsquareTextureFit(t){let e;const i=t.width||1,r=t.height||1;if(i<r)e="height";else{if(!(i>r))return!1;e="width"}return setProperty(t,"texture.fit",e),!0}function migrateNullVisionRanges(t){let e=!1;if("Object"===getType(t.sight)&&null===t.sight.range&&(t.sight.range=0,e=!0),Array.isArray(t.detectionModes))for(const i of t.detectionModes)"Object"===getType(i)&&null===i.range&&(i.range=0,e=!0);return e}function migrateOcclusionRadiusFlag(t){if(!hasProperty(t,"flags.core.occlusionRadius"))return!1;const e=Number(t.flags.core.occlusionRadius);return Number.isFinite(e)&&e>0&&(t.occludable={radius:e}),delete t.flags.core.occlusionRadius,!0}function migrateTokenRing(t){const e=getProperty(t,"flags.dnd5e.tokenRing");if(!e)return!1;const{enabled:i,colors:r,effects:a,scaleCorrection:n,textures:o}=e;return t.ring={enabled:i,colors:r,effects:a,subject:{}},o&&(t.ring.subject.texture=o.subject),Number.isFinite(n)&&(t.ring.subject.scale=n),delete t.flags.dnd5e.tokenRing,!0}function migrateNegativeLuminosity(t){const e=t.light;return"Object"===getType(e)&&(e.luminosity<0&&(e.luminosity=.5,e.negative=!0,!0))}

View File

@@ -0,0 +1 @@
import ServerDocumentMixin from"../backend/server-document.mjs";import BaseUser from"../../../common/documents/user.mjs";import sessions from"../../sessions.mjs";import{createPassword}from"../../core/auth.mjs";import{USER_ROLES}from"../../../common/constants.mjs";export default class User extends(ServerDocumentMixin(BaseUser)){static _migrationRegistry=[...super._migrationRegistry,{fn:migratePlainTextPassword,version:12}];get sessions(){return sessions.getUserSessions(this.id)}get sockets(){const e=[];for(let s of config.sockets.values())s.userId===this.id&&e.push(s);return e}get isActive(){return!!this.id&&(game.active&&game.activity&&this.id in game.activity.users)}async _preCreate(e,s,t){await this.#e();if(!1===await super._preCreate(e,s,t))return!1;const{hash:r,salt:a}=createPassword(this.password);this.updateSource({password:r,passwordSalt:a})}_onCreate(e,s,t){super._onCreate(e,s,t),game.users.push(this),delete e.password,delete e.passwordSalt}async _preUpdate(e,s,t){await this.#e();if(!1===await super._preUpdate(e,s,t))return!1;if("password"in e){const{hash:s,salt:t}=createPassword(e.password);e.password=s,e.passwordSalt=t,sessions.deactivateUserSession(this.id)}if("role"in e&&(this.role>t.role||e.role>t.role))throw new Error("You are not authorized to perform this role change.");const r=CONST.USER_ROLES.GAMEMASTER;if("role"in e&&e.role<r&&this.role===r&&!game.users.find((e=>e.role===r&&e.id!==this.id)))throw new Error("You may not demote the only Gamemaster user within the World.")}_onUpdate(e,s,t){super._onUpdate(e,s,t),db.User.get(this.id).then((e=>game.users.findSplice((e=>e.id===this.id),e))),delete e.password,delete e.passwordSalt}async _preDelete(e,s){const t=CONST.USER_ROLES.GAMEMASTER;if(this.role===t&&!game.users.find((e=>e.role===t&&e.id!==this.id)))throw new Error("You may not delete the only Gamemaster user within the World.");await super._preDelete(e,s)}_onDelete(e,s){super._onDelete(e,s),game.users.findSplice((e=>e.id===this.id))}async#e(){if((await User.find({name:this.name})).find((e=>e.id!==this.id)))throw new Error(`User names must be unique, the name ${this.name} is already taken`)}static async dump(e={}){const s=await super.dump(e);return s.forEach((e=>{delete e.password,delete e.passwordSalt})),s.sort(this._sort),s}static fromSource(e,{safe:s=!1,...t}={}){const r=super.fromSource(e,t);return s&&r.updateSource({password:"",passwordSalt:""}),r}static async find(e={},s={}){const t=await super.find(e,s);return t.sort(this._sort),t}static async getUsers(){const e=await User.find({}),s=game.activity.users;for(let t of e){if(!t.id)continue;const e=s[t.id]||{};t.viewedScene=e.sceneId||null}return game.users=e}static _sort(e,s){let t=e.role>=USER_ROLES.ASSISTANT?e.role:1,r=s.role>=USER_ROLES.ASSISTANT?s.role:1;return t!==r?r-t:e.name.compare(s.name)}}function migratePlainTextPassword(e){if("passwordSalt"in e)return!1;const{hash:s,salt:t}=createPassword(e.password||"");return e.password=s,e.paswordSalt=t,!0}

View File

@@ -0,0 +1 @@
import BaseWall from"../../../common/documents/wall.mjs";import ServerDocumentMixin from"../backend/server-document.mjs";export default class Wall extends(ServerDocumentMixin(BaseWall)){static _migrationRegistry=[...super._migrationRegistry,{fn:migrateSense,version:12},{fn:migrateLimited,version:12}]}function migrateSense(e){return"sense"in e&&("sight"in e||(e.sight=e.sense),"light"in e||(e.light=e.sense),delete e.sense,!0)}function migrateLimited(e){const i={1:CONST.WALL_SENSE_TYPES.NORMAL,2:CONST.WALL_SENSE_TYPES.LIMITED},n=["light","move","sight","sound"];let t=!1;for(const s of n){if(!(s in e))continue;const n=e[s];n in i&&(e[s]=i[n],t=!0)}return t}

View File

@@ -0,0 +1 @@
import*as fields from"../../common/data/fields.mjs";import LevelDatabase from"./backend/level-database.mjs";import*as sanitization from"./sanitization.mjs";import EmbeddedDeltaMixin from"./backend/embedded-delta.mjs";import{randomID}from"../../common/utils/helpers.mjs";fields.HTMLField.prototype.sanitize=sanitization.sanitizeHTMLField,fields.FilePathField.prototype.sanitize=sanitization.sanitizeFilePathField,fields.EmbeddedCollectionField.prototype.sanitize=sanitization.sanitizeEmbeddedCollectionField,fields.EmbeddedDocumentField.prototype.sanitize=sanitization.sanitizeEmbeddedDocumentField,Object.defineProperty(fields.EmbeddedCollectionDeltaField.prototype,"model",{get(){return EmbeddedDeltaMixin(this.element.implementation)},configurable:!0}),fields.EmbeddedCollectionField.prototype.expandEmbedded=async function(e,t,i,d){const{logger:a}=global,o=LevelDatabase.formatKey(i,this.name),n=d.sublevels[o],l=e[this.name];if(!l)return[];if(!Array.isArray(l))return a.warn(`Did not retrieve ${this.name} collection from ${this.model.documentName} ${e._id} as it was not an Array.`),[];const r=l.map((e=>LevelDatabase.formatKey(t,e))),s=await n.getMany(r),m=await Promise.all(s.reduce(((e,i)=>(i?._id&&e.push(this.model.expandEmbedded(i,{ldb:d,idPrefix:t,sublevelName:o})),e)),[])),b=s.length-m.length;return b&&a.warn(`${b} embedded ${this.name} records in ${this.model.documentName} ${e._id} were undefined and not retrieved from the ${o} sublevel.`),m},fields.EmbeddedCollectionField.prototype._dbWrite=function(e,t,i,d,{writeEmbedded:a=!0,generateIds:o=!1}={}){const{logger:n}=global,l=LevelDatabase.formatKey(d,this.name),r=e[this.name];return r?Array.isArray(r)?r.reduce(((d,r)=>{if(!r._id){if(!o)return n.warn(`Did not write record in ${this.name} collection for ${this.model.documentName} ${e._id} as it had no ID.`),d;r._id=randomID()}return a&&this.model.batchWrite(r,t,{writeEmbedded:a,idPrefix:i,sublevelName:l}),d.push(r._id),d}),[]):(n.warn(`Did not write ${this.name} collection for ${this.model.documentName} ${e._id} as it was not an Array.`),[]):[]},fields.EmbeddedCollectionField.prototype._dbDelete=function(e,t,i,d){const a=LevelDatabase.formatKey(d,this.name),o=e[this.name];for(const e of o??[])this.model.batchDelete(e,t,{idPrefix:i,sublevelName:a})},fields.EmbeddedCollectionField.prototype._dbDeleteBranch=function(e,t,i){const d=e.getEmbeddedCollection(this.name);for(const e of d)e.batchDelete(t,i)},fields.EmbeddedCollectionDeltaField.prototype._dbDeleteBranch=function(e,t,i){const d=e.getEmbeddedCollection(this.name);for(const a of d._source)d.isTombstone(a._id)?d.documentClass.batchDelete(a,t,{idPrefix:e.dbKey,sublevelName:LevelDatabase.formatKey(e.sublevelName,this.name)}):d.get(a._id).batchDelete(t,i)},fields.EmbeddedDocumentField.prototype.expandEmbedded=async function(e,t,i,d){const{logger:a}=global,o=LevelDatabase.formatKey(i,this.name),n=d.sublevels[o];let l=e[this.name];if(!l)return null;l=LevelDatabase.formatKey(t,l);const r=await n.get(l);return r?(await this.model.expandEmbedded(r,{ldb:d,idPrefix:t,sublevelName:o}),r):(a.warn(`Singleton embedded ${this.name} ${l} in ${this.model.documentName} ${e._id} was undefined and not retrieved from the ${o} sublevel.`),null)},fields.EmbeddedDocumentField.prototype._dbWrite=function(e,t,i,d,{writeEmbedded:a=!0,generateIds:o=!1}={}){const{logger:n}=global,l=LevelDatabase.formatKey(d,this.name),r=e[this.name];if(!r)return null;if(!r._id){if(!o)return n.warn(`Did not write record in ${this.name} field for ${this.model.documentName} ${e._id} as it had no ID.`),null;r._id=randomID()}return a&&this.model.batchWrite(r,t,{writeEmbedded:a,idPrefix:i,sublevelName:l}),r._id},fields.EmbeddedDocumentField.prototype._dbDelete=function(e,t,i,d){const a=LevelDatabase.formatKey(d,this.name),o=e[this.name];o&&this.model.batchDelete(o,t,{idPrefix:i,sublevelName:a})};

View File

@@ -0,0 +1 @@
import fs from"node:fs";import path from"node:path";import{cleanHTML}from"./validators.mjs";import Files from"../files/files.mjs";import{fromUuid}from"../core/utils.mjs";import{randomID}from"../../common/utils/helpers.mjs";import{EmbeddedCollectionField,EmbeddedDocumentField,FilePathField}from"../../common/data/fields.mjs";export function sanitizeFilePathField(e,{assetPath:t,document:i,documentId:n,fieldPath:o,user:a}={}){if(null==e||""===e)return e;if("string"!=typeof e)return null;if(this.base64)return e;const r=e.match(/^data:([a-z]+)\/([a-z0-9]+);base64,(.*)/);if(!r)return e;if(a&&!a.can("FILES_UPLOAD"))throw new Error(`You lack FILES_UPLOAD permission and may not upload base64 data to the ${this.name} field.`);const[d,s]=r.slice(2,4);n??=i._id;const l=`${[n,...o].filterJoin("-")}.${d}`,m=path.join(t,l);return fs.mkdirSync(t,{recursive:!0}),fs.writeFileSync(m,Buffer.from(s,"base64")),global.logger.info(`Extracted base64 asset: "${m}"`),Files.standardizePath(path.relative(global.paths.data,m))}export async function handleDocumentAssetUpload(e,t){const i=await fromUuid(e),n=i.constructor.extractedAssetPath,o=t.name.split("."),a=o.pop();return t.name=`${o.join(".")}-${randomID()}.${a}`,t.name=i.parent?[i.parent.id,i.collectionName,i.id,t.name].join("-"):t.name,fs.mkdirSync(n,{recursive:!0}),Files.standardizePath(path.relative(global.paths.data,n))}export function sanitizeHTMLField(e,t={}){return null==e||""===e?e:"string"!=typeof e?"":cleanHTML(e)}export function sanitizeEmbeddedCollectionField(e,t={}){const i=t.document?.[t.fieldPath.at(-1)];for(const[n,o]of e.entries()){const a=i?.get(o._id);e[n]=this.model.sanitizeUserInput(o,{...t,document:a,documentId:o._id,fieldPath:t.fieldPath.concat([o._id])})}return e}export function sanitizeEmbeddedDocumentField(e,t={}){const i=t.document?.[t.fieldPath.at(-1)];return this.model.sanitizeUserInput(e,{...t,document:i,documentId:e?._id})}

View File

@@ -0,0 +1 @@
import{parseFragment,serialize}from"parse5";import sanitizeHTML from"sanitize-html";import{ALLOWED_HTML_ATTRIBUTES,TRUSTED_IFRAME_DOMAINS}from"../../common/constants.mjs";export function cleanHTML(t){const e=parseFragment(t);return sanitizeHTML(serialize(e),{allowedTags:["header","main","section","article","aside","nav","footer","div","address","h1","h2","h3","h4","h5","h6","hr","br","p","blockquote","summary","details","span","code","pre","a","label","abbr","cite","mark","q","ruby","rp","rt","small","time","dfn","sub","sup","strong","em","b","i","u","s","del","ins","ol","ul","li","dl","dd","dt","table","thead","tbody","tfoot","tr","th","td","col","colgroup","form","input","select","option","button","datalist","fieldset","legend","meter","optgroup","progress","textarea","figure","figcaption","caption","img","video","map","area","track","picture","source","audio","iframe"],allowedAttributes:ALLOWED_HTML_ATTRIBUTES,allowedSchemes:["http","https","data","mailto"],transformTags:{"*":sanitizeTooltips,iframe:sanitizeIframes}})}function sanitizeIframes(t,e){const a=URL.parseSafe(e.src),r=a?.hostname;return TRUSTED_IFRAME_DOMAINS.some((t=>r===t||r?.endsWith(`.${t}`)))?delete e.sandbox:e.sandbox="allow-scripts allow-forms",{tagName:"iframe",attribs:e}}function sanitizeTooltips(t,e){return e["data-tooltip"]&&(e["data-tooltip"]=cleanHTML(e["data-tooltip"])),{tagName:t,attribs:e}}export function stripTags(t){return t.replace(/<[^>]+>/g,"")}