{"version":3,"file":"index.cjs","names":["AsyncLocalStorage","mysql","BoyNames","GirlNames","FamilyNames","EncodeMedium","ObjectData","Migration","StyledText","MigrationModel","logger"],"sources":["../src/classes/Database.ts","../src/classes/Factory.ts","../src/classes/Model.ts","../src/classes/ManyToManyRelation.ts","../src/classes/ManyToOneRelation.ts","../src/classes/OneToManyRelation.ts","../src/classes/Column.ts","../src/decorators/Column.ts","../src/models/Migration.ts","../src/classes/Migration.ts"],"sourcesContent":["import fs from 'fs';\nimport { AsyncLocalStorage } from 'async_hooks';\nimport mysql from 'mysql2/promise';\nimport type {DatabaseStoredValue} from './DatabaseStoredValue.js';\nexport type SQLResultRow = Record<string, DatabaseStoredValue>;\nexport type SQLResultNamespacedRow = Record<string, SQLResultRow>;\n\ntype SelectOptions = { connection?: mysql.PoolConnection; nestTables?: boolean };\nexport type PoolOptions = {\n    debug?: boolean;\n    host?: string;\n    user?: string;\n    password?: string;\n    port?: number;\n    database?: string | null; // set to null to not use a default database\n    connectionLimit?: number;\n    multipleStatements?: boolean;\n    charset?: string;\n    useSSL?: boolean;\n    /** Path to the CA certificate file */\n    ca?: string;\n    /** Path to the client private key file (for mutual TLS) */\n    key?: string;\n    /** Path to the client certificate file (for mutual TLS) */\n    cert?: string;\n};\n\n/// Database is a wrapper arround mysql, because we want to use promises + types\nexport class DatabaseInstance {\n    pool: mysql.Pool;\n    debug = false;\n\n    /// Holds the connection of the transaction that is currently running for the active async context (if any).\n    private transactionStorage = new AsyncLocalStorage<mysql.PoolConnection>();\n\n    /// Holds the instance that replaces the default one for the active async context (if any).\n    private static instanceStorage = new AsyncLocalStorage<DatabaseInstance>();\n    private static defaultInstance: DatabaseInstance | undefined;\n\n    constructor(options: PoolOptions = {}) {\n        this.createPool(options);\n    }\n\n    /// The instance that queries run on when they don't receive one: the instance of the enclosing\n    /// use() call, or the default instance.\n    static get current(): DatabaseInstance {\n        return DatabaseInstance.instanceStorage.getStore() ?? DatabaseInstance.default;\n    }\n\n    /// The instance built from the environment variables. Only created once something uses it, so a\n    /// service that talks to another database doesn't need a default one configured.\n    static get default(): DatabaseInstance {\n        DatabaseInstance.defaultInstance ??= new DatabaseInstance();\n        return DatabaseInstance.defaultInstance;\n    }\n\n    /// Runs the provided method on the given instance: every query inside it that doesn't receive an\n    /// explicit connection runs on that instance instead of the default one.\n    static async use<T>(instance: DatabaseInstance, handler: () => Promise<T>): Promise<T> {\n        return await DatabaseInstance.instanceStorage.run(instance, handler);\n    }\n\n    createPool(options: PoolOptions = {}) {\n        const settings = {\n            host: options.host ?? process.env.DB_HOST ?? 'localhost',\n            user: options.user ?? process.env.DB_USER ?? 'root',\n            password: options.password ?? process.env.DB_PASS ?? 'root',\n            port: options.port ? options.port : parseInt(process.env.DB_PORT ?? '3306'),\n            database: options.database === undefined ? process.env.DB_DATABASE : options.database,\n            connectionLimit: options.connectionLimit ? options.connectionLimit : parseInt(process.env.DB_CONNECTION_LIMIT ?? '10'),\n            multipleStatements: options.multipleStatements ?? ((process.env.DB_MULTIPLE_STATEMENTS ?? 'true') === 'true'),\n            charset: options.charset ?? process.env.DB_CHARSET ?? 'utf8mb4_0900_ai_ci',\n            useSSL: options.useSSL ?? !!process.env.DB_USE_SSL,\n            ca: options.ca ?? process.env.DB_CA,\n            key: options.key ?? process.env.DB_KEY,\n            cert: options.cert ?? process.env.DB_CERT,\n        };\n\n        if (settings.database === undefined) {\n            throw new Error('Environment variable DB_DATABASE is missing');\n        }\n\n        this.pool = mysql.createPool({\n            host: settings.host,\n            user: settings.user,\n            password: settings.password,\n            port: settings.port,\n            database: settings.database ?? undefined,\n            waitForConnections: true,\n            connectionLimit: settings.connectionLimit,\n            queueLimit: 0,\n            multipleStatements: settings.multipleStatements,\n            charset: settings.charset,\n            decimalNumbers: true,\n            jsonStrings: true,\n            ssl: settings.useSSL\n                ? {\n                        ca: settings.ca ? fs.readFileSync(settings.ca) : undefined,\n                        key: settings.key ? fs.readFileSync(settings.key) : undefined,\n                        cert: settings.cert ? fs.readFileSync(settings.cert) : undefined,\n                        rejectUnauthorized: settings.ca ? true : false,\n                    }\n                : undefined,\n        });\n        this.debug = options?.debug ?? false;\n\n        if (this.debug) {\n            this.pool.on('acquire', function (connection) {\n                console.log('Connection %d acquired', connection.threadId);\n            });\n\n            this.pool.on('connection', function (connection) {\n                console.log('Connection %d created', connection.threadId);\n            });\n\n            this.pool.on('enqueue', function () {\n                console.log('Waiting for available connection slot');\n            });\n\n            this.pool.on('release', function (connection) {\n                console.log('Connection %d released', connection.threadId);\n            });\n        }\n    }\n\n    async reload(options: PoolOptions = {}): Promise<void> {\n        await this.pool.end();\n        this.createPool(options);\n    }\n\n    setDebug(enabled = true) {\n        this.debug = enabled;\n    }\n\n    async getConnection(): Promise<mysql.PoolConnection> {\n        // Todo: use the settings here to provide a good connection pool\n        return await this.pool.getConnection();\n    }\n\n    /// Returns the connection of the transaction that is running in the current async context, if any.\n    getTransactionConnection(): mysql.PoolConnection | undefined {\n        return this.transactionStorage.getStore();\n    }\n\n    /// Runs the provided method inside a database transaction. Every query that runs inside the method\n    /// (and that doesn't receive an explicit connection) automatically uses the transaction connection.\n    /// The transaction is committed when the method returns, or rolled back when it throws.\n    async beginTransaction<T>(transaction: () => Promise<T>): Promise<T> {\n        const connection = await this.getConnection();\n        try {\n            await connection.beginTransaction();\n\n            let result: T;\n            try {\n                result = await this.transactionStorage.run(connection, transaction);\n            }\n            catch (error) {\n                try {\n                    await connection.rollback();\n                } catch (e) {\n                    console.error('Failed to rollback transaction', e);\n                }\n                throw error;\n            }\n\n            await connection.commit();\n            return result;\n        }\n        finally {\n            connection.release();\n        }\n    }\n\n    escapeId(value: string): string {\n        return this.pool.escapeId(value);\n    }\n\n    async end(): Promise<void> {\n        return await this.pool.end();\n    }\n\n    startQuery(): [number, number] {\n        return process.hrtime();\n    }\n\n    logQuery(q, hrstart: [number, number]) {\n        if (this.debug) {\n            const hrend = process.hrtime(hrstart);\n            console.warn(q.sql.replace(/\\n+ +/g, '\\n'), 'started at ' + (hrend[0] * 1000 + hrend[1] / 1000000) + 'ms');\n        }\n    }\n\n    finishQuery(q, hrstart: [number, number]) {\n        if (this.debug) {\n            const hrend = process.hrtime(hrstart);\n            console.log(q.sql.replace(/\\s+/g, ' '), 'in ' + (hrend[0] * 1000 + hrend[1] / 1000000) + 'ms');\n        }\n    }\n\n    select(query: string, values?: any, options?: SelectOptions & { nestTables: true }): Promise<[SQLResultNamespacedRow[], mysql.FieldPacket[] | undefined]>;\n    select(query: string, values?: any, options?: SelectOptions & { nestTables: false }): Promise<[SQLResultRow[], mysql.FieldPacket[] | undefined]>;\n    async select(query: string, values?: any, options: SelectOptions = {}): Promise<[(SQLResultNamespacedRow | SQLResultRow)[], mysql.FieldPacket[] | undefined]> {\n        const explicitConnection = options.connection ?? this.getTransactionConnection();\n        const connection: mysql.PoolConnection = explicitConnection ?? (await this.getConnection());\n        try {\n            const q = await connection.query({ sql: query, nestTables: options.nestTables ?? true, values: values });\n            return [\n                q[0] as (SQLResultNamespacedRow | SQLResultRow)[],\n                q[1] as mysql.FieldPacket[] | undefined,\n            ];\n        }\n        finally {\n            if (!explicitConnection) {\n                connection.release();\n            }\n        }\n    }\n\n    async insert(\n        query: string,\n        values?: any,\n        useConnection?: mysql.PoolConnection,\n    ): Promise<[{ insertId: any; affectedRows: number }, mysql.FieldPacket[] | undefined]> {\n        const explicitConnection = useConnection ?? this.getTransactionConnection();\n        const connection: mysql.PoolConnection = explicitConnection ?? (await this.getConnection());\n        try {\n            const q = await connection.query({ sql: query, values: values });\n            return [\n                q[0] as { insertId: any; affectedRows: number },\n                q[1] as mysql.FieldPacket[] | undefined,\n            ];\n        }\n        finally {\n            if (!explicitConnection) {\n                connection.release();\n            }\n        }\n    }\n\n    async update(query: string, values?: any, useConnection?: mysql.PoolConnection): Promise<[\n        { /** @deprecated */ changedRows: number; affectedRows: number }\n        , mysql.FieldPacket[] | undefined]> {\n        const explicitConnection = useConnection ?? this.getTransactionConnection();\n        const connection: mysql.PoolConnection = explicitConnection ?? (await this.getConnection());\n        try {\n            const q = await connection.query({ sql: query, values: values });\n            return [\n                q[0] as { changedRows: number; affectedRows: number },\n                q[1] as mysql.FieldPacket[] | undefined,\n            ];\n        }\n        finally {\n            if (!explicitConnection) {\n                connection.release();\n            }\n        }\n    }\n\n    async delete(query: string, values?: any, useConnection?: mysql.PoolConnection): Promise<[{ affectedRows: number }, mysql.FieldPacket[] | undefined]> {\n        const explicitConnection = useConnection ?? this.getTransactionConnection();\n        const connection: mysql.PoolConnection = explicitConnection ?? (await this.getConnection());\n        try {\n            const q = await connection.query({ sql: query, values: values });\n            return [\n                q[0] as { affectedRows: number },\n                q[1] as mysql.FieldPacket[] | undefined,\n            ];\n        }\n        finally {\n            if (!explicitConnection) {\n                connection.release();\n            }\n        }\n    }\n\n    async statement(query: string, values?: any, useConnection?: mysql.PoolConnection): Promise<[any, any]> {\n        const explicitConnection = useConnection ?? this.getTransactionConnection();\n        const connection: mysql.PoolConnection = explicitConnection ?? (await this.getConnection());\n        try {\n            const q = await connection.query({ sql: query, values: values });\n            return [\n                q[0] as any,\n                q[1] as any,\n            ];\n        }\n        finally {\n            if (!explicitConnection) {\n                connection.release();\n            }\n        }\n    }\n}\n\nexport type DatabaseProxy = DatabaseInstance & {\n    /// The instance the queries of the current async context run on.\n    readonly instance: DatabaseInstance;\n};\n\n/**\n * The database of the current async context, as set by DatabaseInstance.use() - the default instance\n * outside of it.\n *\n * Reading a member off this object (Database.select, Database.pool, ...) resolves it on that instance\n * every time. That only exists to keep the calls that were written against the old singleton working:\n * write Database.instance.select(...) in new code, so the instance the query runs on is visible where\n * the query is.\n */\nexport const Database: DatabaseProxy = new Proxy({} as DatabaseProxy, {\n    get(_target, property) {\n        const current = DatabaseInstance.current;\n        if (property === 'instance') {\n            return current;\n        }\n\n        const value = Reflect.get(current, property, current) as unknown;\n        return typeof value === 'function' ? value.bind(current) : value;\n    },\n    set(_target, property, value) {\n        return Reflect.set(DatabaseInstance.current, property, value);\n    },\n    has(_target, property) {\n        return property === 'instance' || Reflect.has(DatabaseInstance.current, property);\n    },\n});\n","import BoyNames from './data/boys.js';\nimport FamilyNames from './data/family-names.js';\nimport GirlNames from './data/girls.js';\n\nexport abstract class Factory<Options, Model> {\n    options: Options;\n    constructor(options: Options) {\n        this.options = options;\n    }\n    abstract create(): Promise<Model>;\n\n    randomArray(arr: Array<any>): any {\n        const int = Math.floor(Math.random() * arr.length);\n        return arr[int];\n    }\n\n    randomEnum<E extends { [key: number]: string | number }>(e: E): E[keyof E] {\n        return this.randomArray(Object.values(e));\n    }\n\n    randomString(length: number): string {\n        let result = '';\n        const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n        const charactersLength = characters.length;\n        for (let i = 0; i < length; i++) {\n            result += characters.charAt(Math.floor(Math.random() * charactersLength));\n        }\n        return result;\n    }\n\n    randomFirstName(gender: 'Male' | 'Female' | 'Other'): string {\n        let names: string[];\n        switch (gender) {\n            case 'Male':\n                names = BoyNames;\n                break;\n            case 'Female':\n                names = GirlNames;\n                break;\n            case 'Other':\n                names = [...BoyNames, ...GirlNames];\n                break;\n        }\n        return this.randomArray(names);\n    }\n\n    randomLastName(): string {\n        return this.randomArray(FamilyNames);\n    }\n\n    async createMultiple(amount = 40): Promise<Model[]> {\n        const arr: Model[] = [];\n        for (let index = 0; index < amount; index++) {\n            arr.push(await this.create());\n        }\n        return arr;\n    }\n}\n","/* eslint-disable @typescript-eslint/no-unsafe-argument */\nimport type { Column } from './Column.js';\nimport { Database } from './Database.js';\nimport type {DatabaseStoredValue} from './DatabaseStoredValue.js';\nimport type {ManyToManyRelation} from './ManyToManyRelation.js';\nimport type {ManyToOneRelation} from './ManyToOneRelation.js';\nimport type {OneToManyRelation} from './OneToManyRelation.js';\n\ntype SQLWhere = { sign: string; value: string | Date | number | null | (string | null)[] | (number | null)[]; mode?: string };\ntype SQLWhereQuery = { [key: string]: string | Date | number | null | SQLWhere | SQLWhere[] };\n\ntype Listener<Value> = (value: Value) => Promise<void> | void;\n\n/**\n * Controls the fetching and decrypting of members\n */\nexport class ModelEventBus<Value> {\n    protected listeners: Map<any, { listener: Listener<Value> }[]> = new Map();\n\n    addListener(owner: any, listener: Listener<Value>) {\n        const existing = this.listeners.get(owner);\n        if (existing) {\n            existing.push({ listener });\n        }\n        else {\n            this.listeners.set(owner, [{ listener }]);\n        }\n    }\n\n    removeListener(owner: any) {\n        this.listeners.delete(owner);\n    }\n\n    async sendEvent(value: Value) {\n        const values: (Promise<void>)[] = [];\n        for (const owner of this.listeners.values()) {\n            for (const listener of owner) {\n                const v = listener.listener(value);\n                if (v) {\n                    values.push(v);\n                }\n            }\n        }\n        return await Promise.all(values);\n    }\n}\n\nexport type ModelEventType = 'created' | 'updated' | 'deleted';\n\nexport type ModelEvent<M extends Model = Model> = {\n    type: 'created';\n    model: M;\n} | {\n    type: 'updated';\n    model: M;\n    changedFields: Record<string, DatabaseStoredValue>;\n    originalFields: Record<string, DatabaseStoredValue>;\n\n    /**\n     * Use this method to compare changes\n     */\n    getOldModel(): M;\n} | {\n    type: 'deleted';\n    model: M;\n};\n\nexport class Model /* static implements RowInitiable<Model> */ {\n    static primary: Column;\n    static modelEventBus = new ModelEventBus<ModelEvent>();\n\n    /**\n     * Properties that are stored in the table (including foreign keys, but without mapped relations!)\n     */\n    static columns: Map<string, Column>;\n    static debug = false;\n    static showWarnings = false;\n    static table: string; // override this!\n    static relations: ManyToOneRelation<string, Model>[];\n\n    existsInDatabase = false;\n\n    savedProperties = new Map<string, DatabaseStoredValue | ({\n        to: () => DatabaseStoredValue;\n        from: () => unknown;\n    })>();\n\n    /**\n     * Sometimes we have skipUpdate properties that still should get saved on specific occasions.\n     * E.g. update updatedAt field manually if is the only changed field.\n     */\n    forceSaveProperties = new Set<string>();\n\n    constructor() {\n        // Read values\n        if (!this.static.relations) {\n            this.static.relations = [];\n        }\n\n        if (!this.static.columns) {\n            this.static.columns = new Map<string, Column>();\n        }\n    }\n\n    /**\n     * Delete the value of a key from memory\n     */\n    eraseProperty(key: string) {\n        const column = this.static.columns.get(key);\n        if (!column) {\n            throw new Error('Unknown property ' + key);\n        }\n        delete this[key];\n        this.savedProperties.delete(key);\n    }\n\n    /**\n     * Make sure this key will get saved on the next save, even when it is not changed or when it is skipUpdate\n     */\n    forceSaveProperty(key: string) {\n        this.forceSaveProperties.add(key);\n    }\n\n    /**\n     * Mark all properties as changed, so they will get updated on the next save\n     */\n    markAllChanged() {\n        this.savedProperties.clear();\n    }\n\n    /**\n     * Returns the default select to select the needed properties of this table\n     * @param namespace: optional namespace of this select\n     */\n    static getDefaultSelect(namespace: string = this.table): string {\n        return '`' + namespace + '`.*';\n    }\n\n    static selectColumnsWithout(namespace: string = this.table, ...exclude: string[]): string {\n        const properties = Array.from(this.columns.keys()).flatMap(name => (exclude.includes(name) ? [] : [name]));\n\n        if (properties.length === 0) {\n            // todo: check what to do in this case.\n            throw new Error('Not implemented yet');\n        }\n        return '`' + namespace + '`.`' + properties.join('`, `' + namespace + '`.`') + '`';\n    }\n\n    /**\n     * Set a relation to undefined, marking it as not loaded (so it won't get saved in the next save)\n     * @param relation\n     */\n    unloadRelation<Key extends keyof any, Value extends Model>(\n        this: this & Record<Key, Value>,\n        relation: ManyToOneRelation<Key, any>,\n    ): this & Record<Key, undefined> {\n        // Todo: check if relation is nullable?\n        const t = this as any;\n        delete t[relation.modelKey];\n        return t;\n    }\n\n    /**\n     * Set a relation to null, deleting it on the next save (unless unloadRelation is called)\n     * @param relation\n     */\n    unsetRelation<Key extends keyof any, Value extends Model>(\n        this: this & Record<Key, Value>,\n        relation: ManyToOneRelation<Key, any>,\n    ): this & Record<Key, null> {\n        // Todo: check if relation is nullable?\n        return this.setOptionalRelation(relation, null);\n    }\n\n    setOptionalRelation<Key extends keyof any, Value extends Model>(\n        relation: ManyToOneRelation<Key, Value>,\n        value: Value | null,\n    ): this & Record<Key, Value | null> {\n        // Todo: check if relation is nullable?\n\n        if (value !== null && !value.existsInDatabase) {\n            throw new Error('You cannot set a relation to a model that are not yet saved in the database.');\n        }\n        const t = this as any;\n        t[relation.modelKey] = value;\n        t[relation.foreignKey] = value ? value.getPrimaryKey() : null;\n        return t;\n    }\n\n    setRelation<Key extends keyof any, Value extends Model, V extends Value>(relation: ManyToOneRelation<Key, Value>, value: V): this & Record<Key, V> {\n        if (!value.existsInDatabase) {\n            throw new Error('You cannot set a relation to a model that are not yet saved in the database.');\n        }\n        const t = this as any;\n        t[relation.modelKey] = value;\n        t[relation.foreignKey] = value.getPrimaryKey();\n        return t;\n    }\n\n    /**\n     * Set a many relation. Note that this doesn't save the relation! You'll need to use the methods of the relation instead\n     */\n    setManyRelation<Key extends keyof any, Value extends Model>(\n        relation: ManyToManyRelation<Key, any, Value, any> | OneToManyRelation<Key, any, Value>,\n        value: Value[],\n    ): this & Record<Key, Value[]> {\n        value.forEach((v) => {\n            if (!v.existsInDatabase) {\n                throw new Error('You cannot set a relation to models that are not yet saved in the database.');\n            }\n        });\n        const t = this as any;\n        t[relation.modelKey] = value;\n        return t;\n    }\n\n    /**\n     * Set a many relation. Note that this doesn't save the relation! You'll need to use the methods of the relation instead\n     */\n    getManyRelation<Key extends keyof any, Value extends Model>(\n        relation: ManyToManyRelation<Key, any, Value, any> | OneToManyRelation<Key, any, Value>,\n    ): Value[] | null {\n        const t = this as any;\n        return t[relation.modelKey] ?? null;\n    }\n\n    /**\n     * Load the returned properties from a DB response row into the model\n     * If the row's primary key is null, undefined is returned\n     */\n    static fromRow<T extends typeof Model>(this: T, row: Record<string, DatabaseStoredValue>): InstanceType<T> | undefined {\n        if (row === undefined || (this.primary && (row[this.primary.name] === null || row[this.primary.name] === undefined))) {\n            return undefined;\n        }\n\n        const model = new this() as InstanceType<T>;\n        for (const column of this.columns.values()) {\n            if (row[column.name] !== undefined) {\n                const value = column.from(row[column.name]);\n                model[column.name] = value;\n            }\n            else {\n                // Override default value to prevent any saving!\n                model[column.name] = undefined;\n            }\n        }\n\n        model.markSaved(row, { fromMySQL: true }); // note: we don't pass row because that value is unreliable for json values (mysql performs sorting and other opertions on keys)\n        return model;\n    }\n\n    static fromRows<T extends typeof Model>(this: T, rows: Record<string, Record<string, DatabaseStoredValue>>[], namespace: string): InstanceType<T>[] {\n        return rows.flatMap((row) => {\n            const model = this.fromRow(row[namespace]);\n            if (model) {\n                return [model];\n            }\n            return [];\n        });\n    }\n\n    markSaved(fields?: Record<string, DatabaseStoredValue>, options?: { fromMySQL?: boolean }) {\n        this.existsInDatabase = true;\n        this.forceSaveProperties.clear();\n\n        for (const column of this.static.columns.values()) {\n            if ((fields ? fields[column.name] : this[column.name]) !== undefined) {\n                // If undefined: do not update, since we didn't save the value\n                this.savedProperties.set(column.name, fields\n                    ? (column.type === 'json' && options?.fromMySQL\n                            ? {\n                                // We can never use the original value we received from MySQL because the ordering of keys is different in MySQL.\n                                // We use this object to avoid doing expensive CPU operations if we don't need to check if a value has changed\n                                // so we only evaluate this once we actually try to save a model to the database\n                                    to() {\n                                        if (this._to) {\n                                            return this._to;\n                                        }\n                                        const value = this._from ?? column.from(fields[column.name]);\n                                        this._from = value;\n\n                                        const found = column.to(value);\n                                        // Store permanently\n                                        this._to = found;\n                                        return found;\n                                    },\n                                    from() {\n                                        if (this._from) {\n                                            return this._from;\n                                        }\n                                        const from = column.from(fields[column.name]);\n                                        this._from = from;\n                                        return from;\n                                    },\n                                }\n                            : fields[column.name]\n                        )\n                    : column.to(this[column.name]),\n                );\n            }\n        }\n\n        /// Save relation foreign keys (so we can check if the id has changed)\n        for (const relation of this.static.relations) {\n            if (relation.isLoaded(this)) {\n                if (relation.isSet(this)) {\n                    const model = this[relation.modelKey];\n                    this[relation.foreignKey] = model.getPrimaryKey();\n                }\n                else {\n                    this[relation.foreignKey] = null;\n                }\n            }\n        }\n    }\n\n    copyFrom<T extends Model>(this: T, from: T) {\n        for (const column of this.static.columns.values()) {\n            this[column.name] = from[column.name];\n        }\n\n        // Unload all relations\n        for (const relation of this.static.relations) {\n            if (relation.isLoaded(this)) {\n                this[relation.modelKey] = undefined;\n            }\n        }\n\n        this.savedProperties = from.savedProperties;\n        this.forceSaveProperties = from.forceSaveProperties;\n        this.existsInDatabase = from.existsInDatabase;\n    }\n\n    get static(): typeof Model {\n        return this.constructor as typeof Model;\n    }\n\n    getPrimaryKey(): number | string | null {\n        return this[this.static.primary.name];\n    }\n\n    /**\n     * Get a model by its primary key\n     * @param id primary key\n     */\n    static async getByID<T extends typeof Model>(this: T, id: number | string): Promise<InstanceType<T> | undefined> {\n        const [rows] = await Database.select(`SELECT ${this.getDefaultSelect()} FROM \\`${this.table}\\` WHERE \\`${this.primary.name}\\` = ? LIMIT 1`, [id]);\n\n        if (rows.length === 0) {\n            return undefined;\n        }\n\n        // Read member + address from first row\n        const row = rows[0][this.table];\n        return this.fromRow(row);\n    }\n\n    /**\n     * Get multiple models by their ID\n     * @param ids primary key of the models you want to fetch\n     */\n    static async getByIDs<T extends typeof Model>(this: T, ...ids: (number | string)[]): Promise<InstanceType<T>[]> {\n        if (ids.length === 0) {\n            return [];\n        }\n        const [rows] = await Database.select(`SELECT ${this.getDefaultSelect()} FROM \\`${this.table}\\` WHERE \\`${this.primary.name}\\` IN (?) LIMIT ?`, [\n            ids,\n            ids.length,\n        ]);\n\n        // Read member + address from first row\n        return this.fromRows(rows, this.table);\n    }\n\n    static buildWhereOperator(key: string, value: SQLWhere): [string, any[]] {\n        let whereQuery: string;\n        const params: any[] = [];\n\n        switch (value.sign) {\n            case 'MATCH': {\n                let suffix = '';\n                if (value.mode) {\n                    switch (value.mode) {\n                        case 'BOOLEAN':\n                            suffix = ' IN BOOLEAN MODE';\n                            break;\n                        default:\n                            throw new Error('Unknown match mode ' + value.mode);\n                    }\n                }\n                whereQuery = (`MATCH(\\`${this.table}\\`.\\`${key}\\`) AGAINST(?${suffix})`);\n                params.push(value.value);\n                break;\n            }\n\n            case 'LIKE':\n                whereQuery = (`\\`${this.table}\\`.\\`${key}\\` LIKE ?`);\n                params.push(value.value);\n                break;\n\n            case '!=':\n                if (value.value === null) {\n                    whereQuery = (`\\`${this.table}\\`.\\`${key}\\` IS NOT NULL`);\n                }\n                else {\n                    whereQuery = (`\\`${this.table}\\`.\\`${key}\\` != ?`);\n                    params.push(value.value);\n                }\n                break;\n\n            case '<':\n                whereQuery = (`\\`${this.table}\\`.\\`${key}\\` < ?`);\n                params.push(value.value);\n                break;\n\n            case '>':\n                whereQuery = (`\\`${this.table}\\`.\\`${key}\\` > ?`);\n                params.push(value.value);\n                break;\n\n            case '<=':\n                whereQuery = (`\\`${this.table}\\`.\\`${key}\\` <= ?`);\n                params.push(value.value);\n                break;\n\n            case '>=':\n                whereQuery = (`\\`${this.table}\\`.\\`${key}\\` >= ?`);\n                params.push(value.value);\n                break;\n\n            case '=':\n                if (value.value === null) {\n                    whereQuery = (`\\`${this.table}\\`.\\`${key}\\` IS NULL`);\n                }\n                else {\n                    whereQuery = (`\\`${this.table}\\`.\\`${key}\\` = ?`);\n                    params.push(value.value);\n                }\n                break;\n\n            case 'IN':\n                if (!Array.isArray(value.value)) {\n                    throw new Error('Expected an array for IN where query');\n                }\n                if (value.value.includes(null)) {\n                    const filtered = (value.value as (string | number | null)[]).filter(v => v !== null);\n                    if (filtered.length === 0) {\n                        whereQuery = (`\\`${this.table}\\`.\\`${key}\\` IS NULL`);\n                    }\n                    else if (filtered.length === 1) {\n                        // Special query\n                        whereQuery = (`(\\`${this.table}\\`.\\`${key}\\` = ? OR \\`${this.table}\\`.\\`${key}\\` IS NULL)`);\n                        params.push(filtered[0]);\n                    }\n                    else {\n                        // Special query\n                        whereQuery = (`(\\`${this.table}\\`.\\`${key}\\` IN (?) OR \\`${this.table}\\`.\\`${key}\\` IS NULL)`);\n                        params.push(filtered);\n                    }\n                }\n                else {\n                    whereQuery = (`\\`${this.table}\\`.\\`${key}\\` IN (?)`);\n                    params.push(value.value);\n                }\n\n                break;\n\n            default:\n                throw new Error('Sign not supported.');\n        }\n\n        return [whereQuery, params];\n    }\n\n    static buildWhereQuery(where: SQLWhereQuery): [string, any[]] {\n        const whereQuery: string[] = [];\n        const params: any[] = [];\n\n        for (const key in where) {\n            if (Object.hasOwnProperty.call(where, key)) {\n                const value = where[key];\n                if (Array.isArray(value)) {\n                    for (const v of value) {\n                        const [w, p] = this.buildWhereOperator(key, v);\n                        whereQuery.push(w);\n                        params.push(...p);\n                    }\n                }\n                else if (typeof value === 'object' && value !== null && !(value instanceof Date)) {\n                    const [w, p] = this.buildWhereOperator(key, value);\n                    whereQuery.push(w);\n                    params.push(...p);\n                }\n                else {\n                    if (value === null) {\n                        whereQuery.push(`\\`${this.table}\\`.\\`${key}\\` IS NULL`);\n                    }\n                    else {\n                        whereQuery.push(`\\`${this.table}\\`.\\`${key}\\` = ?`);\n                        params.push(value);\n                    }\n                }\n            }\n        }\n\n        return [whereQuery.join(' AND '), params];\n    }\n\n    /**\n     * @deprecated Use the new SQL package instead\n     * Get multiple models by a simple where\n     */\n    static async where<T extends typeof Model>(this: T, where: SQLWhereQuery, extra?: {\n        limit?: number;\n        sort?: (string | { column: string | SQLWhereQuery; direction?: 'ASC' | 'DESC' })[];\n        select?: string;\n    }): Promise<InstanceType<T>[]> {\n        const params: any[] = [];\n\n        let query = `SELECT ${extra?.select ?? this.getDefaultSelect()} FROM \\`${this.table}\\``;\n        if (Object.keys(where).length !== 0) {\n            const [whereQuery, p] = this.buildWhereQuery(where);\n            query += `WHERE ` + whereQuery;\n            params.push(...p);\n        }\n\n        if (extra && extra.sort !== undefined) {\n            const sortQuery: string[] = [];\n\n            for (const item of extra.sort) {\n                if (typeof item === 'string') {\n                    sortQuery.push(Database.escapeId(item) + ' ASC');\n                }\n                else if (typeof item.column === 'string') {\n                    sortQuery.push(Database.escapeId(item.column) + ' ' + (item.direction ?? 'ASC'));\n                }\n                else {\n                    // Repeat use of a where query in the sorting (needed when fulltext searching and need to sort on the score, because limit won't work without this for an unknown reason)\n                    const [w, p] = this.buildWhereQuery(item.column);\n\n                    sortQuery.push('(' + w + ') ' + (item.direction ?? 'ASC'));\n                    params.push(...p);\n                }\n            }\n\n            query += ` ORDER BY ` + sortQuery.join(', ');\n        }\n\n        if (extra && extra.limit !== undefined) {\n            query += ` LIMIT ?`;\n            params.push(extra.limit);\n        }\n\n        const [rows] = await Database.select(query, params);\n\n        return this.fromRows(rows, this.table);\n    }\n\n    /**\n     * Get multiple models by a simple where\n     */\n    static async all<T extends typeof Model>(this: T, limit?: number): Promise<InstanceType<T>[]> {\n        const params: any[] = [];\n        let query = `SELECT ${this.getDefaultSelect()} FROM \\`${this.table}\\``;\n\n        if (limit) {\n            query += ` LIMIT ?`;\n            params.push(limit);\n        }\n\n        const [rows] = await Database.select(query, params);\n        return this.fromRows(rows, this.table);\n    }\n\n    /**\n     * Return an object of all the properties that are changed and their database representation\n     */\n    getChangedDatabaseProperties(): { fields: Record<string, DatabaseStoredValue>; skipUpdate: number } {\n        const set: Record<string, DatabaseStoredValue> = {};\n        let skipUpdate = 0;\n\n        for (const column of this.static.columns.values()) {\n            if (column.primary && column.type === 'integer' && this.static.primary.name === 'id') {\n                // Auto increment: not allowed to set\n                continue;\n            }\n\n            if (!this.existsInDatabase && this[column.name] === undefined) {\n                // In the future we might make some columns optional because they have a default value in the database.\n                // But that could cause inconsitent state, so it would be better to generate default values in code.\n                throw new Error('Tried to create model ' + this.constructor.name + ' with undefined property ' + column.name);\n            }\n\n            if (this[column.name] !== undefined) {\n                const forceSave = this.forceSaveProperties.has(column.name);\n                const oldSavedValue = forceSave ? undefined : this.savedProperties.get(column.name);\n                const saveValue = forceSave ? null : column.to(this[column.name]); // .to is expensive, so avoid calling it when not needed\n                if (forceSave || oldSavedValue === undefined || column.isChanged(typeof oldSavedValue === 'object' && oldSavedValue !== null && 'to' in oldSavedValue ? oldSavedValue.to() : oldSavedValue, saveValue)) {\n                    set[column.name] = forceSave ? column.to(this[column.name]) : saveValue;\n\n                    if (column.skipUpdate && !forceSave) {\n                        skipUpdate++;\n                    }\n                }\n            }\n        }\n\n        return { fields: set, skipUpdate };\n    }\n\n    beforeSave() {\n        for (const column of this.static.columns.values()) {\n            // Run beforeSave\n            if (column.beforeSave) {\n                this[column.name] = column.beforeSave.call(this, this[column.name]);\n            }\n        }\n    }\n\n    /**\n     * Return original value from the database or undefined if not known\n     */\n    getOriginalValue(key: string): unknown | undefined {\n        const column = this.static.columns.get(key);\n        if (!column) {\n            throw new Error('Unknown property ' + key);\n        }\n        const c = this.savedProperties.get(key);\n        if (c === undefined) {\n            return undefined;\n        }\n        if (typeof c === 'object' && c !== null && 'from' in c) {\n            return c.from();\n        }\n        return column.from(c);\n    }\n\n    async save(options?: {skipMarkSaved?: boolean, skipSendEvents?: boolean}): Promise<boolean> {\n        if (!this.static.table) {\n            throw new Error('Table name not set');\n        }\n\n        if (!this.static.primary) {\n            throw new Error('Primary key not set for model ' + this.constructor.name + ' ' + this.static);\n        }\n\n        // Check if relation models were modified\n        for (const relation of this.static.relations) {\n            // If a relation is loaded, we can check if it changed the value of the foreign key\n            if (relation.isLoaded(this)) {\n                // The foreign key is modified (set, changed or cleared)\n                if (relation.isSet(this)) {\n                    const model = this[relation.modelKey] as Model;\n\n                    if (!model.existsInDatabase) {\n                        throw new Error('You cannot set a relation that is not yet saved in the database.');\n                    }\n\n                    if (this[relation.foreignKey] !== model.getPrimaryKey()) {\n                        // Should always match because setRelation will make it match on setting it.\n                        throw new Error(\n                            'You cannot modify the value of a foreign key when the relation is loaded. Unload the relation first or modify the relation with setRelation',\n                        );\n                    }\n                }\n                else {\n                    if (this[relation.foreignKey] !== null) {\n                        // Should always match because setRelation will make it match on setting it.\n                        throw new Error(\n                            'You cannot set a foreign key when the relation is loaded. Unload the relation first or modify the relation with setRelation',\n                        );\n                    }\n                }\n            }\n        }\n\n        const id = this.getPrimaryKey();\n        if (!id) {\n            if (this.existsInDatabase) {\n                throw new Error('Model ' + this.constructor.name + \" was loaded from the Database, but didn't select the ID. Saving not possible.\");\n            }\n        }\n        else {\n            if (!this.existsInDatabase && this.static.primary.type === 'integer' && this.static.primary.name === 'id') {\n                throw new Error(\n                    `PrimaryKey was set programmatically without fetching the model ${this.constructor.name} from the database. This is not allowed for integer primary keys.`,\n                );\n            }\n        }\n\n        this.beforeSave();\n        const { fields, skipUpdate } = this.getChangedDatabaseProperties();\n\n        if (Object.keys(fields).length === 0) {\n            if (Model.showWarnings) console.warn('Tried to update model without any properties modified');\n            return false;\n        }\n\n        if (this.existsInDatabase && skipUpdate === Object.keys(fields).length) {\n            if (Model.showWarnings) console.warn('Tried to update model without any properties modified');\n            return false;\n        }\n\n        if (Model.debug) console.log('Saving ' + this.constructor.name + ' to...', fields);\n\n        // todo: save here\n        if (!this.existsInDatabase) {\n            if (Model.debug) console.log(`Creating new ${this.constructor.name}`);\n\n            const [result] = await Database.insert('INSERT INTO `' + this.static.table + '` SET ?', [fields]);\n\n            if (this.static.primary.type === 'integer' && this.static.primary.name === 'id') {\n                // Auto increment value\n                this[this.static.primary.name] = result.insertId;\n                fields[this.static.primary.name] = result.insertId; // Also mark saved\n                if (Model.debug) console.log(`New id = ${this[this.static.primary.name]}`);\n            }\n        }\n        else {\n            if (Model.debug) console.log(`Updating ${this.constructor.name} where ${this.static.primary.name} = ${id}`);\n\n            const [result] = await Database.update('UPDATE `' + this.static.table + '` SET ? WHERE `' + this.static.primary.name + '` = ?', [fields, id]);\n            if (result.changedRows !== 1 && Model.showWarnings) {\n                console.warn(`Updated ${this.constructor.name}, but it didn't change a row. Check if ID exists.`);\n            }\n        }\n\n        // Emit event\n        if (this.existsInDatabase) {\n            // Keep reference to old properties before marking saved\n            let originalProperties:  {\n                [k: string]: DatabaseStoredValue;\n            };\n            if (!options?.skipSendEvents) {\n                originalProperties = Object.fromEntries(\n                    Array.from(this.savedProperties.entries()).map(([key, value]) => {\n                        if (typeof value === 'object' && value !== null && 'to' in value) {\n                            return [key, value.to()];\n                        }\n                        return [key, value];\n                    }),\n                );\n            }\n\n            // Mark saved before sending the event\n\n            if (!options?.skipMarkSaved) {\n                this.markSaved(fields);\n            }\n\n            if (!options?.skipSendEvents) {\n                await this.static.modelEventBus.sendEvent({\n                    type: 'updated',\n                    model: this,\n                    changedFields: fields,\n                    originalFields: originalProperties!,\n                    getOldModel: () => {\n                        // Build a new model as if it was loaded from the database using the same original properties\n                        const v = this.static.fromRow(originalProperties);\n                        if (v === undefined) {\n                            throw new Error('Failed to create old model: primary key might be missing');\n                        }\n                        return v;\n                    },\n                });\n            }\n        }\n        else {\n            if (!options?.skipMarkSaved) {\n                this.markSaved(fields);\n            }\n            if (!options?.skipSendEvents) {\n                await this.static.modelEventBus.sendEvent({ type: 'created', model: this });\n            }\n        }\n\n        return true;\n    }\n\n    async delete() {\n        const id = this.getPrimaryKey();\n\n        if (!id && this.existsInDatabase) {\n            throw new Error('Model ' + this.constructor.name + \" was loaded from the Database, but didn't select the ID. Deleting not possible.\");\n        }\n\n        if (!id || !this.existsInDatabase) {\n            throw new Error('Model ' + this.constructor.name + \" can't be deleted if it doesn't exist in the database already\");\n        }\n\n        if (Model.debug) console.log(`Deleting ${this.constructor.name} where ${this.static.primary.name} = ${id}`);\n\n        const [result] = await Database.delete('DELETE FROM `' + this.static.table + '` WHERE `' + this.static.primary.name + '` = ?', [id]);\n        if (result.affectedRows !== 1 && Model.showWarnings) {\n            console.warn(`Deleted ${this.constructor.name}, but it didn't change a row. Check if ID exists.`);\n        }\n\n        this.existsInDatabase = false;\n        if (this.static.primary.type === 'integer' && this.static.primary.name === 'id') {\n            this.eraseProperty(this.static.primary.name);\n        }\n        this.savedProperties.clear();\n\n        await this.static.modelEventBus.sendEvent({ type: 'deleted', model: this });\n    }\n}\n","import { Database } from './Database.js';\nimport { Model } from './Model.js';\n\nexport class ManyToManyRelation<Key extends keyof any, A extends Model, B extends Model, Link extends Model | undefined = undefined> {\n    modelA: { new (): A } & typeof Model;\n    modelB: { new (): B } & typeof Model;\n    modelLink?: { new(): Link } & typeof Model;\n\n    /**\n     * E.g. parents\n     */\n    modelKey: Key;\n\n    /**\n     * Sort the loading of this relation\n     */\n    sortKey: string | undefined;\n    sortOrder: 'ASC' | 'DESC' = 'ASC';\n\n    /**\n     * E.g. _models_parents\n     */\n    get linkTable(): string {\n        if (this.modelLink) {\n            return this.modelLink.table;\n        }\n        return '_' + [this.modelA.table, this.modelB.table].sort().join('_');\n    }\n\n    /**\n     * e.g. modelsId\n     */\n    get linkKeyA(): string {\n        return (\n            this.modelA.table\n            + this.modelA.primary.name.charAt(0).toUpperCase()\n            + this.modelA.primary.name.substring(1)\n            + ((this.modelA as any) === this.modelB ? 'A' : '')\n        );\n    }\n\n    /**\n     * e.g. parentsId\n     */\n    get linkKeyB(): string {\n        return (\n            this.modelB.table\n            + this.modelB.primary.name.charAt(0).toUpperCase()\n            + this.modelB.primary.name.substring(1)\n            + ((this.modelA as any) === this.modelB ? 'B' : '')\n        );\n    }\n\n    constructor(modelA: { new(): A } & typeof Model, modelB: { new(): B } & typeof Model, modelKey: Key, modelLink?: { new(): Link } & typeof Model) {\n        this.modelA = modelA;\n        this.modelB = modelB;\n        this.modelKey = modelKey;\n        this.modelLink = modelLink;\n    }\n\n    reverse<Key2 extends keyof any>(modelKey: Key2): ManyToManyRelation<Key2, B, A, Link> {\n        return new ManyToManyRelation<Key2, B, A, Link>(this.modelB, this.modelA, modelKey, this.modelLink);\n    }\n\n    setSort(key: string, order: 'ASC' | 'DESC' = 'ASC'): this {\n        this.sortKey = key;\n        this.sortOrder = order;\n        return this;\n    }\n\n    /// Generate a join query\n    joinQuery(namespaceA: string, namespaceB: string): string {\n        const linkNamespace = namespaceA + '_' + namespaceB;\n        let str = `LEFT JOIN ${this.linkTable} as ${linkNamespace} on ${linkNamespace}.${this.linkKeyA} = ${namespaceA}.${this.modelA.primary.name}\\n`;\n        str += `LEFT JOIN ${this.modelB.table} as ${namespaceB} on ${linkNamespace}.${this.linkKeyB} = ${namespaceB}.${this.modelB.primary.name}`;\n        return str;\n    }\n\n    orderByQuery(namespaceA: string, namespaceB: string): string {\n        if (this.sortKey === undefined) {\n            return '';\n        }\n        const linkNamespace = namespaceA + '_' + namespaceB;\n        let str = `\\nORDER BY ${linkNamespace}.${this.sortKey}`;\n        if (this.sortOrder === 'DESC') {\n            str += ' DESC';\n        }\n        return str + '\\n';\n    }\n\n    /// Load the relation of a model and return the loaded models\n    async load(modelA: A, sorted = true, where?: object, whereLink?: object): Promise<(B & { _link: Link })[]> {\n        const namespaceB = 'B';\n        const linkNamespace = 'A_B';\n        let select = this.modelB.getDefaultSelect(namespaceB);\n\n        if (this.modelLink) {\n            select += ', ' + this.modelLink.getDefaultSelect(linkNamespace);\n        }\n\n        let str = `SELECT ${select} FROM ${this.linkTable} as ${linkNamespace}\\n`;\n        str += `JOIN ${this.modelB.table} as ${namespaceB} on ${linkNamespace}.${this.linkKeyB} = ${namespaceB}.${this.modelB.primary.name}\\n`;\n        str += `WHERE ${linkNamespace}.${this.linkKeyA} = ?`;\n\n        const params = [modelA.getPrimaryKey()];\n        if (where) {\n            for (const key in where) {\n                if (Object.hasOwnProperty.call(where, key)) {\n                    str += ` AND ${namespaceB}.\\`${key}\\` = ?`;\n                    params.push(where[key] as string);\n                }\n            }\n        }\n        if (whereLink) {\n            for (const key in whereLink) {\n                if (Object.hasOwnProperty.call(whereLink, key)) {\n                    str += ` AND ${linkNamespace}.\\`${key}\\` = ?`;\n                    params.push(whereLink[key] as string);\n                }\n            }\n        }\n        if (sorted) {\n            str += this.orderByQuery('A', 'B');\n        }\n\n        const [rows] = await Database.select(str, params);\n        const modelsB = this.modelB.fromRows(rows, namespaceB) as (B & { _link: Link })[];\n\n        // Also load link table if possible\n        if (this.modelLink) {\n            for (const row of rows) {\n                const model = this.modelLink.fromRow(row[linkNamespace]) as Link;\n\n                // Fin modelB\n                const modelB = modelsB.find(m => m.getPrimaryKey() === row[linkNamespace][this.linkKeyB]);\n\n                if (!modelB) {\n                    throw new Error('Unexpected linking');\n                }\n\n                // Save link\n                modelB._link = model;\n            }\n        }\n\n        modelA.setManyRelation(this, modelsB);\n        return modelsB;\n    }\n\n    /// Whether this relation is loaded\n    isLoaded<T extends A>(model: T): model is T & Record<Key, B[]> {\n        // Also not loaded if null, since it should be an empty array or an array if it is loaded\n        return Array.isArray((model as any)[this.modelKey]);\n    }\n\n    async setLinkTable(modelA: A, modelB: B, linkTableValues: { [key: string]: any }) {\n        const str = `UPDATE ${this.linkTable} SET ? WHERE ${Database.escapeId(this.linkKeyA)} = ? AND ${Database.escapeId(this.linkKeyB)} = ?`;\n\n        const [result] = await Database.update(str, [linkTableValues, modelA.getPrimaryKey(), modelB.getPrimaryKey()]);\n        if (result.changedRows !== 1) {\n            // Todo: add option to fail silently\n            throw new Error('Failed to update link table. Check if combination exists');\n        }\n    }\n\n    async linkIds(modelA: string | number, modelsB: (string | number)[], linkTableValues?: { [key: string]: any[] }): Promise<number> {\n        if (modelsB.length === 0) {\n            return 0;\n        }\n        // Nested arrays are turned into grouped lists (for bulk inserts), e.g. [['a', 'b'], ['c', 'd']] turns into ('a', 'b'), ('c', 'd')\n        let result: {\n            affectedRows: number;\n        };\n        if (linkTableValues !== undefined) {\n            const linkTableKeys: string[] = Object.keys(linkTableValues);\n\n            for (const property in linkTableValues) {\n                if (Object.hasOwnProperty.call(linkTableValues, property)) {\n                    if (linkTableValues[property].length !== modelsB.length) {\n                        throw new Error(\n                            'Amount of link table values for key '\n                            + property\n                            + ' ('\n                            + linkTableValues[property].length\n                            + ') are not equal to amount of models linked ('\n                            + modelsB.length\n                            + ')',\n                        );\n                    }\n                }\n            }\n\n            const query = `INSERT INTO ${Database.escapeId(this.linkTable)} (\n                    ${Database.escapeId(this.linkKeyA)}, \n                    ${Database.escapeId(this.linkKeyB)}, \n                    ${linkTableKeys.map(k => Database.escapeId(k)).join(', ')}\n                ) VALUES ?`;\n            [result] = await Database.insert(query, [modelsB.map((modelB, i) => [modelA, modelB, ...linkTableKeys.map(k => linkTableValues[k][i])])]);\n        }\n        else {\n            const query = `INSERT INTO ${Database.escapeId(this.linkTable)} (\n                    ${Database.escapeId(this.linkKeyA)}, \n                    ${Database.escapeId(this.linkKeyB)}\n                ) VALUES ?`;\n            [result] = await Database.insert(query, [modelsB.map(modelB => [modelA, modelB])]);\n        }\n        return result.affectedRows;\n    }\n\n    async link(modelA: A, modelsB: B[], linkTableValues?: { [key: string]: any[] }): Promise<void> {\n        const modelAId = modelA.getPrimaryKey();\n        if (!modelAId) {\n            throw new Error('Cannot link if model is not saved yet');\n        }\n\n        const affectedRows = await this.linkIds(\n            modelAId,\n            modelsB.map((modelB) => {\n                const id = modelB.getPrimaryKey();\n                if (!id) {\n                    throw new Error('Cannot link to a model that is not saved yet');\n                }\n                return id;\n            }),\n            linkTableValues,\n        );\n\n        // If the relation is loaded, also modify the value of the relation\n        if (this.isLoaded(modelA)) {\n            if (affectedRows === modelsB.length) {\n                const arr: B[] = (modelA as any)[this.modelKey];\n                arr.push(...modelsB);\n            }\n            else {\n                // This could happen in race conditions and simultanious requests\n\n                console.warn('Warning: linking expected to affect ' + modelsB.length + ' rows, but only affected ' + affectedRows + ' rows');\n\n                // TODO: Manually correct by doing a query (safest)\n                throw new Error('Fallback behaviour net yet implemented');\n            }\n        }\n    }\n\n    /**\n     * Delete all the links from modelA for this relation\n     * @param modelA\n     */\n    async clearId(modelA: string | number): Promise<void> {\n        const query = `DELETE FROM ${this.linkTable} WHERE ${this.linkKeyA} = ?`;\n\n        // Arrays are turned into list, e.g. ['a', 'b'] turns into 'a', 'b'\n        const [result] = await Database.delete(query, [modelA]);\n\n        if (result.affectedRows === 0 && Model.debug) {\n            console.warn(\"Cleared many to many relation, but didn't deleted any entries\");\n        }\n    }\n\n    /**\n     * Delete all the links from modelA for this relation\n     * @param modelA\n     */\n    async clear(modelA: A): Promise<void> {\n        await this.clearId(modelA.getPrimaryKey()!);\n        modelA.setManyRelation(this, []);\n    }\n\n    /**\n     * Delete all the links from modelA for this relation\n     * @param modelA\n     */\n    async syncId(modelA: string | number, modelsB: (string | number)[], linkTableValues?: { [key: string]: any[] }): Promise<void> {\n        // todo: add transaction\n        await this.clearId(modelA);\n        await this.linkIds(modelA, modelsB, linkTableValues);\n    }\n\n    async unlinkIds(modelA: string | number, ...modelsB: (string | number)[]): Promise<{ affectedRows: number }> {\n        const query = `DELETE FROM ${this.linkTable} WHERE ${this.linkKeyA} = ? AND ${this.linkKeyB} IN (?)`;\n\n        // Arrays are turned into list, e.g. ['a', 'b'] turns into 'a', 'b'\n        const [result] = await Database.delete(query, [modelA, modelsB]);\n\n        return result;\n    }\n\n    async unlink(modelA: A, ...modelsB: B[]): Promise<void> {\n        const result = await this.unlinkIds(modelA.getPrimaryKey()!, ...modelsB.map(modelB => modelB.getPrimaryKey()!));\n\n        if (this.isLoaded(modelA)) {\n            if (result.affectedRows === modelsB.length) {\n                const arr: B[] = (modelA as any)[this.modelKey];\n                const idMap = modelsB.map(model => model.getPrimaryKey());\n                modelA.setManyRelation(\n                    this,\n                    arr.filter((model) => {\n                        return !idMap.includes(model.getPrimaryKey());\n                    }),\n                );\n            }\n            else {\n                // This could happen in race conditions and simultanious requests\n                console.warn('Warning: unlinking expected to affect ' + modelsB.length + ' rows, but only affected ' + result.affectedRows + ' rows');\n\n                // TODO: Manually correct by doing a query (safest)\n                throw new Error('Fallback behaviour net yet implemented');\n            }\n        }\n    }\n}\n","import { Database } from './Database.js';\nimport type { Model } from './Model.js';\n\nexport class ManyToOneRelation<Key extends keyof any, M extends Model> {\n    model: { new (): M } & typeof Model;\n\n    /**\n     * E.g. addressId\n     */\n    foreignKey: string;\n\n    /**\n     * E.g. address\n     */\n    modelKey: Key;\n\n    constructor(model: { new (): M } & typeof Model, modelKey: Key) {\n        this.model = model;\n        this.modelKey = modelKey;\n    }\n\n    /// Whether this relation is loaded\n    isLoaded(model: Model) {\n        return (model as any)[this.modelKey] !== undefined;\n    }\n\n    /// Whether this relation is set\n    isSet(model: Model): boolean /* model is Model & Record<Key, M> */ {\n        return (model as any)[this.modelKey] !== undefined && (model as any)[this.modelKey] !== null;\n    }\n\n    /// Generate a join query\n    joinQuery(namespaceA: string, namespaceB: string): string {\n        return `LEFT JOIN ${this.model.table} as ${namespaceB} on ${namespaceB}.${this.model.primary.name} = ${namespaceA}.${this.foreignKey}\\n`;\n    }\n\n    /// Load the relation of a list of models\n    async load<A extends Model>(modelsA: A[]): Promise<(A & Record<Key, M>)[]> {\n        if (modelsA.length === 0) {\n            return [];\n        }\n        let str = `SELECT ${this.model.getDefaultSelect()} FROM ${this.model.table}\\n`;\n        str += `WHERE ${this.model.primary.name} IN (?)`;\n\n        const [rows] = await Database.select(str, [modelsA.map(m => m[this.foreignKey])]);\n        const modelsB = this.model.fromRows(rows, this.model.table) as M[];\n\n        for (const model of modelsA) {\n            const found = modelsB.find(m => m.getPrimaryKey() === model[this.foreignKey]);\n            if (!found) {\n                throw new Error('Could not load many to one relation: no match found when loading');\n            }\n            model.setRelation(this, found);\n        }\n        return modelsA as (A & Record<Key, M>)[];\n    }\n}\n","import { Database } from './Database.js';\nimport type { Model } from './Model.js';\n\nexport class OneToManyRelation<Key extends keyof any, A extends Model, B extends Model> {\n    modelA: { new (): A } & typeof Model;\n    modelB: { new (): B } & typeof Model;\n\n    /**\n     * Foreign key of the other model that references the current model\n     */\n    foreignKey: keyof B;\n\n    /**\n     * E.g. categories\n     */\n    modelKey: Key;\n\n    /**\n     * Sort the loading of this relation\n     */\n    sortKey: keyof B | undefined;\n    sortOrder: 'ASC' | 'DESC' = 'ASC';\n\n    constructor(modelA: { new (): A } & typeof Model, modelB: { new (): B } & typeof Model, modelKey: Key, foreignKey: keyof B) {\n        this.modelA = modelA;\n        this.modelB = modelB;\n        this.modelKey = modelKey;\n        this.foreignKey = foreignKey;\n    }\n\n    setSort(key: keyof B, order: 'ASC' | 'DESC' = 'ASC'): this {\n        this.sortKey = key;\n        this.sortOrder = order;\n        return this;\n    }\n\n    /// Whether this relation is loaded\n    isLoaded<T extends A>(model: T): model is T & Record<Key, B[]> {\n        // Also not loaded if null, since it should be an empty array or an array if it is loaded\n        return Array.isArray((model as any)[this.modelKey]);\n    }\n\n    /**\n     * Generate a join query\n     * @param namespaceA namespace in the SQL query of modelA\n     * @param namespaceB namespace in the SQL query of modelB\n     */\n    joinQuery(namespaceA: string, namespaceB: string): string {\n        return `LEFT JOIN ${this.modelB.table} as ${namespaceB} on ${namespaceB}.${this.foreignKey.toString()} = ${namespaceA}.${this.modelA.primary.name}\\n`;\n    }\n\n    orderByQuery(namespaceB: string): string {\n        if (this.sortKey === undefined) {\n            return '';\n        }\n        let str = `\\nORDER BY ${namespaceB}.${this.sortKey.toString()}`;\n        if (this.sortOrder === 'DESC') {\n            str += ' DESC';\n        }\n        return str + '\\n';\n    }\n\n    /// Load the relation of a model and return the loaded models\n    async load(modelA: A, sorted = true, where?: object): Promise<B[]> {\n        const namespaceB = 'B';\n        let str = `SELECT ${this.modelB.getDefaultSelect(namespaceB)} FROM ${this.modelB.table} as ${namespaceB}\\n`;\n        str += `WHERE ${namespaceB}.${this.foreignKey.toString()} = ?`;\n\n        const params = [modelA.getPrimaryKey()];\n        if (where) {\n            for (const key in where) {\n                if (Object.hasOwnProperty.call(where, key)) {\n                    str += ` AND ${namespaceB}.\\`${key}\\` = ?`;\n                    params.push(where[key] as string);\n                }\n            }\n        }\n        if (sorted) {\n            str += this.orderByQuery('B');\n        }\n\n        const [rows] = await Database.select(str, params);\n        const modelsB = this.modelB.fromRows(rows, namespaceB) as B[];\n        modelA.setManyRelation(this, modelsB);\n        return modelsB;\n    }\n}\n","import type { Decoder, EncodableObject} from '@simonbackx/simple-encoding';\nimport { EncodeMedium, encodeObject, ObjectData } from '@simonbackx/simple-encoding';\n\nimport type {ColumnType} from './ColumnType.js';\nimport type {DatabaseStoredValue} from './DatabaseStoredValue.js';\n\nexport class Column {\n    type: ColumnType;\n    name: string;\n    nullable = false;\n    primary = false;\n\n    /**\n     * Do not save the model if this is the only field that has changed\n     */\n    skipUpdate = false;\n    decoder: Decoder<any> | undefined;\n    beforeSave?: (value?: any) => any | Promise<any>;\n\n    // Allow handling of new fields etc\n    beforeLoad?: (value?: any) => any;\n\n    private static jsonVersion = 0;\n\n    /**\n     * Set the version used for JSON encoding in simple-encoding\n     */\n    static setJSONVersion(version: number): void {\n        Column.jsonVersion = version;\n    }\n\n    constructor(type: ColumnType, name: string) {\n        this.type = type;\n        this.name = name;\n    }\n\n    /**\n     * @deprecated use to instead\n     */\n    saveProperty(data: unknown): DatabaseStoredValue {\n        return this.to(data);\n    }\n\n    isChanged(old: DatabaseStoredValue, now: DatabaseStoredValue): boolean {\n        if (old instanceof Date) {\n            if (now instanceof Date) {\n                return old.getTime() !== now.getTime();\n            }\n            return true;\n        }\n        return now !== old;\n    }\n\n    /// Convert from database to javascript\n    from(data: DatabaseStoredValue): unknown {\n        if (this.beforeLoad) {\n            data = this.beforeLoad(data);\n        }\n        if (this.nullable && data === null) {\n            return null;\n        }\n        if (!this.nullable && data === null) {\n            throw new Error('Received null value from database. Expected a non-nullable value for ' + this.name);\n        }\n        switch (this.type) {\n            case 'integer':\n                // Mapped correctly by MySQL\n                if (!Number.isInteger(data)) {\n                    throw new Error('Expected integer');\n                }\n                return data;\n\n            case 'number':\n                // Mapped correctly by MySQL\n                if (Number.isNaN(data)) {\n                    throw new Error('Expected number');\n                }\n                return data;\n\n            case 'string':\n                return data;\n\n            case 'boolean':\n                // Mapped correctly by MySQL\n                if (data !== 1 && data !== 0) {\n                    throw new Error('Expected boolean');\n                }\n                return data === 1;\n\n            case 'date':\n                // Correctly mapped by node MySQL\n                return data;\n\n            case 'datetime':\n                // Mapped correctly by node MySQL\n                return data;\n\n            case 'json': {\n                if (typeof data !== 'string') {\n                    throw new Error('Expected string for JSON column');\n                }\n\n                // Mapped correctly by node MySQL\n                let parsed: unknown;\n                try {\n                    parsed = JSON.parse(data);\n                }\n                catch (e) {\n                    // Syntax error. Mark this in the future.\n                    console.error(e);\n                    parsed = {};\n                }\n\n                if (this.decoder) {\n                    if (this.decoder.decodeField) {\n                        if (typeof parsed === 'object' && parsed !== null && 'value' in parsed && typeof (parsed as any).version === 'number') {\n                            return this.decoder.decodeField(parsed.value, { version: (parsed as any).version, medium: EncodeMedium.Database }, this.name);\n                        }\n\n                        // Fallback decoding without version (since we don't know the saved version)\n                        return this.decoder.decodeField(parsed, { version: 0, medium: EncodeMedium.Database }, this.name);\n                    }\n                    if (typeof parsed === 'object' && parsed !== null && 'version' in parsed && 'value' in parsed && typeof parsed.version === 'number') {\n                        return this.decoder.decode(new ObjectData(parsed.value, { version: parsed.version, medium: EncodeMedium.Database }, this.name));\n                    }\n\n                    // Fallback decoding without version (since we don't know the saved version)\n                    return this.decoder.decode(new ObjectData(parsed, { version: 0, medium: EncodeMedium.Database }, this.name));\n                }\n                else {\n                    console.warn('It is recommended to always use a decoder for JSON columns');\n                }\n\n                if (typeof parsed === 'object' && parsed !== null && 'version' in parsed && 'value' in parsed && typeof parsed.version === 'number') {\n                    return parsed.value;\n                }\n\n                // If data comes from before version encoding, fall back to parsed\n                return parsed;\n            }\n\n            default: {\n                // If we get a compile error heres, a type is missing in the switch\n                const t: never = this.type;\n                throw new Error('Type ' + t + ' not supported');\n            }\n        }\n    }\n\n    /// Convert to database from javascript\n    to(data: unknown): DatabaseStoredValue {\n        if (this.nullable && data === null) {\n            return null;\n        }\n        if (!this.nullable && data === null) {\n            throw new Error('Tried to set null to non-nullable value. Expected a non-nullable value');\n        }\n\n        switch (this.type) {\n            case 'integer':\n                // Mapped correctly by MySQL\n                if (typeof data !== 'number') {\n                    throw new Error('Expected integer for ' + this.name + ', received ' + (typeof data));\n                }\n\n                return data;\n\n            case 'number':\n                if (typeof data !== 'number') {\n                    throw new Error('Expected number for ' + this.name + ', received ' + (typeof data));\n                }\n\n                // Mapped correctly by node MySQL\n                return data;\n\n            case 'string':\n                if (typeof data !== 'string') {\n                    throw new Error('Expected string for ' + this.name + ', received ' + (typeof data));\n                }\n\n                return data;\n\n            case 'boolean':\n                if (typeof data !== 'boolean') {\n                    throw new Error('Expected boolean for ' + this.name + ', received ' + (typeof data));\n                }\n\n                return data ? 1 : 0;\n\n            case 'date':\n            case 'datetime':\n                if (!(data instanceof Date)) {\n                    throw new Error('Expected Date for ' + this.name + ', received ' + (typeof data));\n                }\n                // This information cannot be stored in the database - so also update it in JS to keep it in sync\n                data.setMilliseconds(0);\n\n                // Correctly mapped by node MySQL\n                return data;\n\n            case 'json': {\n                const version = Column.jsonVersion;\n\n                return JSON.stringify({\n                    // Warning: keys should be sorted or they will get marked as changed every time\n                    value: encodeObject(data as EncodableObject, { version, medium: EncodeMedium.Database }),\n                    version: version,\n\n                });\n            }\n\n            default: {\n                // If we get a compile error heres, a type is missing in the switch\n                const t: never = this.type;\n                throw new Error('Type ' + t + ' not supported');\n            }\n        }\n    }\n}\n","import type { Decoder } from '@simonbackx/simple-encoding';\n\nimport { Column } from '../classes/Column.js';\nimport type { ManyToOneRelation } from '../classes/ManyToOneRelation.js';\nimport type { Model } from '../classes/Model.js';\nimport type {ColumnType} from '../classes/ColumnType.js';\n\nexport function column<Key extends keyof any, Value extends Model>(settings: {\n    type: ColumnType;\n    primary?: boolean;\n    nullable?: boolean;\n    decoder?: Decoder<any>;\n    /**\n     * Do not save the model if this is the only field that has changed\n     */\n    skipUpdate?: boolean;\n    beforeSave?: (value?: any) => any;\n    beforeLoad?: (value?: any) => any;\n    foreignKey?: ManyToOneRelation<Key, Value>;\n}) {\n    return (target: any /* future typeof Model */, key: string) => {\n        if (!target.constructor.columns) {\n            target.constructor.columns = new Map<string, Column>();\n        }\n\n        if (settings.foreignKey) {\n            settings.foreignKey.foreignKey = key;\n\n            if (!target.constructor.relations) {\n                target.constructor.relations = [];\n            }\n\n            target.constructor.relations.push(settings.foreignKey);\n        }\n\n        const column = new Column(settings.type, key);\n        column.beforeSave = settings.beforeSave;\n        column.beforeLoad = settings.beforeLoad;\n\n        if (settings.decoder) {\n            column.decoder = settings.decoder;\n        }\n\n        if (settings.nullable) {\n            column.nullable = true;\n        }\n\n        if (settings.skipUpdate !== undefined) {\n            column.skipUpdate = settings.skipUpdate;\n        }\n        if (settings.primary) {\n            if (target.constructor.primary) {\n                throw new Error('Duplicate primary column ' + key);\n            }\n            target.constructor.primary = column;\n            column.primary = true;\n        }\n\n        target.constructor.columns.set(key, column);\n    };\n}\n","import { Database } from '../classes/Database.js';\nimport { Model } from '../classes/Model.js';\nimport { column } from '../decorators/Column.js';\n\nexport class Migration extends Model {\n    static table = 'migrations';\n\n    // Columns\n    @column({ primary: true, type: 'integer' })\n    id: number | null = null;\n\n    @column({ type: 'string' })\n    file: string;\n\n    @column({ type: 'datetime' })\n    executedOn: Date;\n\n    private static cleanFileName(file: string) {\n        // Replace .ts with .js, to avoid rerunning same file depending whether ts-node is used\n        file = file.replace('.ts', '.js');\n\n        return file;\n    }\n\n    // Methods\n    static async isExecuted(file: string): Promise<boolean> {\n        file = this.cleanFileName(file);\n\n        const [rows] = await Database.select(`SELECT count(*) as c FROM ${this.table} WHERE \\`file\\` = ? LIMIT 1`, [\n            file,\n        ]);\n\n        if (rows.length === 0) {\n            return false;\n        }\n\n        // Read member + address from first row\n        return rows[0]['']['c'] === 1;\n    }\n\n    static async markAsExecuted(file: string): Promise<void> {\n        if (await this.isExecuted(file)) {\n            return;\n        }\n        file = this.cleanFileName(file);\n        const migration = new Migration();\n        migration.file = file;\n        migration.executedOn = new Date();\n        await migration.save();\n    }\n}\n","import { promises as fs } from 'fs';\n\nimport { logger, StyledText } from '@simonbackx/simple-logging';\nimport { Migration as MigrationModel } from '../models/Migration.js';\nimport { Database, DatabaseInstance } from './Database.js';\n\nexport async function fileExists(file: string): Promise<boolean> {\n    try {\n        await fs.access(file);\n        return true;\n    }\n    catch (e) {\n        return false;\n    }\n}\n\nasync function getProjectRoot() {\n    let path = __dirname;\n    for (let index = 0; index < 5; index++) {\n        if (await fileExists(path + '/migrations')) {\n            return path;\n        }\n        path += '/..';\n    }\n    throw new Error('Could not find migrations root');\n}\n\ntype MigrationFunction = () => Promise<void>;\n\nasync function directoryExists(filePath: string): Promise<boolean> {\n    try {\n        return (await fs.stat(filePath)).isDirectory();\n    }\n    catch (err) {\n        return false;\n    }\n}\n\nexport class Migration {\n    up: MigrationFunction;\n    down: MigrationFunction | undefined;\n\n    constructor(up: MigrationFunction, down?: MigrationFunction) {\n        this.up = up;\n        this.down = down;\n    }\n\n    /***\n     * Given a folder, loop all the folders in that folder and run the migrations in the 'migrations' folder\n     *\n     * The migrations run on the database of the current async context, unless one is passed in\n     * options.database. That database keeps the migration history of the migrations that ran on it,\n     * in its own 'migrations' table.\n     */\n    static async runAll(folder: string, options?: { database?: DatabaseInstance }): Promise<boolean> {\n        if (options?.database) {\n            return await DatabaseInstance.use(options.database, () => this.runAll(folder));\n        }\n\n        const dirname = __dirname;\n\n        // Get the current working directory by removing shared part of folder and dirname\n        const shared = dirname.split('/').filter((part, index) => part === folder.split('/')[index]).join('/');\n        const cwd = folder.replace(shared, '');\n\n        logger.log(\n            new StyledText('[Migration]').addClass('migration', 'prefix').addTag('migration'),\n            ' ',\n            new StyledText('Running all... ').addClass('migration', 'runAll'),\n            new StyledText(cwd).addClass('migration', 'folder').addStyle('dim'),\n        );\n\n        process.env.DB_MULTIPLE_STATEMENTS = 'true';\n\n        // First check if we have migrations table\n        const projectRoot = await getProjectRoot();\n        const setupMigration = await this.getMigration(projectRoot + '/migrations/000000000-setup-migrations.sql');\n        if (!setupMigration) {\n            throw new Error('Setup migration missing');\n        }\n        await setupMigration.up();\n        await MigrationModel.markAsExecuted('000000000-setup-migrations.sql');\n\n        const parts = folder.split('/');\n        const firstPart = parts.shift();\n        if (firstPart === undefined) {\n            throw new Error('Invalid folder path');\n        }\n        let folderQueue: string[] = [firstPart];\n        const migrations: [string, string][] = [];\n\n        for (const part of parts) {\n            if (part === '*') {\n                const newQueue: string[] = [];\n                for (folder of folderQueue) {\n                    // Read all directories\n                    const recursiveFolders = (await fs.readdir(folder, { withFileTypes: true })).filter(dirent => dirent.isDirectory()).map(dirent => folder + '/' + dirent.name);\n                    newQueue.push(...recursiveFolders);\n                }\n                folderQueue = newQueue;\n            }\n            else {\n                folderQueue = folderQueue.map(folder => folder + '/' + part);\n            }\n        }\n        let found = 0;\n        for (const p of folderQueue) {\n            if (await directoryExists(p)) {\n                const folderFiles = await fs.readdir(p);\n\n                for (const file of folderFiles) {\n                    const full = p + '/' + file;\n                    const name = file;\n                    found += 1;\n                    if (!(await MigrationModel.isExecuted(name))) {\n                        migrations.push([name, full]);\n                    }\n                }\n            }\n        }\n\n        if (found === 0) {\n            logger.error(\n                new StyledText('[Migration]').addClass('migration', 'prefix').addTag('migration'),\n                ' ',\n                new StyledText('✗').addClass('migration', 'failed', 'tag'),\n                ' ',\n                new StyledText('No migrations found in ' + folder).addClass('migration', 'failed', 'text'),\n            );\n            return false;\n        }\n\n        // Make sure we run the migrations in order\n        migrations.sort((a, b) => {\n            // It is expected to return a negative value if first argument is less than second argument, zero if they're equal and a positive value otherwise. If omitted, the elements are sorted in ascending, ASCII character order.\n            if (a < b) return -1;\n            if (a > b) return 1;\n            return 0;\n        });\n\n        for (const [name, file] of migrations) {\n            // Check if SQL or TypeScript\n            const migration = await this.getMigration(file);\n            if (!migration) {\n                continue;\n            }\n\n            logger.log(\n                new StyledText('[Migration]').addClass('migration', 'prefix').addTag('migration'),\n                ' ',\n                new StyledText('Running ').addClass('migration', 'start'),\n                new StyledText(name).addClass('migration', 'start', 'name'),\n            );\n\n            try {\n                await logger.setContext({\n                    prefixes: [new StyledText('[Migration]').addClass('migration', 'prefix'), ' '],\n                    tags: ['migration'],\n                }, async () => {\n                    await migration.up();\n                    await MigrationModel.markAsExecuted(name);\n                });\n\n                logger.log(\n                    new StyledText('[Migration]').addClass('migration', 'prefix').addTag('migration'),\n                    ' ',\n                    new StyledText('✓').addClass('migration', 'success', 'tag'),\n                    ' ',\n                    new StyledText('Migration ' + name + ' ran successfully').addClass('migration', 'success', 'text'),\n                );\n            }\n            catch (e) {\n                // Logger.errorWithContext({textColor: ['dim', 'red'], prefix: ' FAILED ', addSpace: true, prefixColor: ['bgRed']}, \"Migration \" + name + \" failed\", e)\n                logger.error(\n                    new StyledText('[Migration]').addClass('migration', 'prefix').addTag('migration'),\n                    ' ',\n                    new StyledText('✗').addClass('migration', 'failed', 'tag'),\n                    ' ',\n                    new StyledText('Migration ' + name + ' failed').addClass('migration', 'failed', 'text'),\n                    ' ',\n                    new StyledText(e).addClass('migration', 'failed', 'error'),\n                );\n                return false;\n            }\n        }\n\n        logger.log(\n            new StyledText('[Migration]').addClass('migration', 'prefix').addTag('migration'),\n            ' ',\n            new StyledText('✨').addClass('migration', 'success', 'tag', 'all'),\n            ' ',\n            new StyledText('All migrations done').addClass('migration', 'success', 'text', 'all'),\n        );\n        return true;\n    }\n\n    static async getMigration(file: string): Promise<Migration | undefined> {\n        let migration: Migration;\n        if (file.endsWith('.sql')) {\n            if (file.endsWith('.down.sql')) {\n                // Ignore. This will contain the downgrade implementation.\n                return;\n            }\n            const sqlStatement = await fs.readFile(file, { encoding: 'utf-8' });\n\n            migration = new Migration(async () => {\n                await Database.statement(sqlStatement);\n            });\n        }\n        else {\n            if (file.includes('.test.')) {\n                return;\n            }\n            if (file.endsWith('.d.ts')) {\n                return;\n            }\n            if (!file.endsWith('.ts') && !file.endsWith('.js')) {\n                return;\n            }\n\n            const imported = await import(file);\n            migration = imported.default?.default ?? imported.default;\n        }\n        return migration;\n    }\n}\n"],"mappings":"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"}