// repositories/framework/addons/core/assets/back/addon.js
import onetype from '#framework/load.js';

const assets = onetype.Addon('assets', (addon) =>
{
    addon.Field('id', ['number']);
    addon.Field('order', ['number']);
    addon.Field('path', ['string']);
    addon.Field('content', ['string|function']);
    addon.Field('type', ['string']);
    addon.Field('ignore', ['array', []]);
});

export default assets;

// repositories/framework/addons/core/assets/back/functions/css.js
import assets from '../addon.js';

assets.Fn('css', function()
{
    const css = [];
    const items = Object.values(this.Items()).filter(item => item.Get('type') === 'css').sort((a, b) => a.Get('order') - b.Get('order'));
    
    items.forEach((item) => 
    {
        if(item.Get('content'))
        {
            if(typeof item.Get('content') === 'function')
            {
                css.push(item.Get('content')());
            }
            else 
            {
                css.push(item.Get('content'));
            }
        }
        else if(item.Get('path'))
        {
            const files = assets.Fn('scan.files', item.Get('path'), 'css', item.Get('ignore'));

            css.push(...assets.Fn('utils.read', files));
        }
    })

    return assets.Fn('utils.transform', css, 'css');
});

// repositories/framework/addons/core/assets/back/functions/import.js
import assets from '../addon.js';
import onetype from '#framework/load.js';

assets.Fn('import', function(modules, order = 10)
{
    for (let i = 0; i < modules.length; i++)
    {
        const entry = modules[i];
        const registered = onetype.AssetsGet(entry);

        if (registered)
        {
            if (registered.js)
            {
                assets.Item({ type: 'js', order: order + i, path: registered.js.path, ignore: registered.js.exclude || [] });
            }

            if (registered.css)
            {
                assets.Item({ type: 'css', order: order + i, path: registered.css.path, ignore: registered.css.exclude || [] });
            }
        }
    }
});

// repositories/framework/addons/core/assets/back/functions/js.js
import assets from '../addon.js';

assets.Fn('js', function(context = {})
{
    const js = [];
    const items = Object.values(this.Items()).filter(item => item.Get('type') === 'js').sort((a, b) => a.Get('order') - b.Get('order'));

    items.forEach((item) =>
    {
        if(item.Get('content'))
        {
            if(typeof item.Get('content') === 'function')
            {
                js.push(item.Get('content').call(context));
            }
            else
            {
                js.push(item.Get('content'));
            }
        }
        else if(item.Get('path'))
        {
            const files = assets.Fn('scan.files', item.Get('path'), 'js', item.Get('ignore'));

            js.push(...assets.Fn('utils.read', files));
        }
    })

    return assets.Fn('utils.transform', js, 'js');
});

// repositories/framework/addons/core/assets/back/functions/scan/directories.js
import assets from '../../addon.js';
import fs from 'fs';
import path from 'path';

assets.Fn('scan.directories', function(dirPath, ignore = [], recursive = true)
{
    const dirs = [];

    if (!fs.existsSync(dirPath))
    {
        return dirs;
    }

    const items = fs.readdirSync(dirPath);

    for (const item of items)
    {
        if (item.startsWith('.')) continue;
        if (ignore.some(str => item.includes(str))) continue;

        const itemPath = path.join(dirPath, item);
        const stats = fs.statSync(itemPath);

        if (stats.isDirectory())
        {
            dirs.push(itemPath);

            if (recursive)
            {
                dirs.push(...assets.Fn('scan.directories', itemPath, ignore, recursive));
            }
        }
    }

    return dirs;
});

// repositories/framework/addons/core/assets/back/functions/scan/files.js
import assets from '../../addon.js';
import fs from 'fs';
import path from 'path';

assets.Fn('scan.files', function(dirPath, extension = 'css', ignore = [], recursive = true)
{
    const results = [];

    if (!fs.existsSync(dirPath))
    {
        return results;
    }

    const items = fs.readdirSync(dirPath);
    const folders = [];
    const files = [];

    for (const item of items)
    {
        if (item.startsWith('.')) continue;

        const full = path.join(dirPath, item);
        const stats = fs.statSync(full);

        if (ignore.some(str => full.includes(str))) continue;

        if (stats.isDirectory() && recursive)
        {
            folders.push(full);
        }
        else if (item.endsWith(`.${extension}`) && !item.endsWith(`.back.${extension}`))
        {
            files.push(full);
        }
    }

    const addon = files.find(f => f.endsWith('/addon.js'));

    if (addon)
    {
        results.push(addon);
        files.splice(files.indexOf(addon), 1);
    }

    folders.sort();

    for (const folder of folders)
    {
        results.push(...assets.Fn('scan.files', folder, extension, ignore, recursive));
    }

    files.sort();
    results.push(...files);

    return results;
});

// repositories/framework/addons/core/assets/back/functions/utils/read.js
import assets from '../../addon.js';
import fs from 'fs';

assets.Fn('utils.read', function(files)
{
    const contents = [];

    for (const file of files)
    {
        if(!fs.existsSync(file))
        {
            continue;
        }

        const fileContent = fs.readFileSync(file, 'utf8');
        
        if (fileContent)
        {
            contents.push(fileContent);
        }
    }

    return contents;
});

// repositories/framework/addons/core/assets/back/functions/utils/transform.js
import assets from '../../addon.js';

assets.Fn('utils.transform', function(contents, type = 'js')
{
	if (!Array.isArray(contents))
	{
		return '';
	}

	if (type === 'js')
	{
		contents = contents.map(content =>
		{
			content = content.replace(/^import\s+.*?;?\s*$/gm, '');
			content = content.replace(/^export\s+.*?;?\s*$/gm, '');
			content = content.replace(/^\s*[\r\n]/gm, '');

			return content;
		});

		return '(function(){\n' + contents.filter(content => content?.trim()).join('\n\n') + '\n})();';
	}

	if (type === 'css')
	{
		contents = contents.map(content => content ? content.trim() : '');
	}

	return contents.filter(content => content?.trim()).join('\n');
});

// repositories/framework/addons/core/assets/back/items/commands/css.js
import onetype from "#framework/load.js";
import assets from "#assets/load.js";

onetype.AddonReady('commands', (commands) => 
{
    commands.Item({
        id: 'assets:css',
        exposed: true,
        method: 'GET',
        endpoint: '/assets/build.css',
        type: 'CSS',
        callback: async function(properties, resolve)
        {
            resolve(await assets.Fn('css'));
        }
    });
});

// repositories/framework/addons/core/assets/back/items/commands/js.js
import onetype from "#framework/load.js";
import assets from "#assets/addon.js";

onetype.AddonReady('commands', (commands) => 
{
    commands.Item({
        id: 'assets:js',
        exposed: true,
        method: 'GET',
        endpoint: '/assets/build.js',
        type: 'JS',
        callback: async function(properties, resolve)
        {
            resolve(await assets.Fn('js', {http: this.http}));
        }
    });
});

// repositories/framework/addons/core/assets/back/items/html/css.js
import onetype from "#framework/load.js";

onetype.AddonReady('html', (html) =>
{
    console.log(onetype.Base());
    
    html.Item({
        id: 'assets-css',
        exposed: true,
        tag: 'link',
        position: 'head',
        order: 90,
        attributes: {
            rel: 'stylesheet',
            href: onetype.Base() + '/assets/build.css?v=18'
        }
    });
});

// repositories/framework/addons/core/assets/back/items/html/js.js
import onetype from "#framework/load.js";

onetype.AddonReady('html', (html) =>
{
    html.Item({
        id: 'assets-js',
        exposed: true,
        tag: 'script',
        position: 'head',
        order: 90,
        attributes: {
            src: onetype.Base() + '/assets/build.js?v=18',
            defer: null
        }
    });
});

// repositories/framework/addons/core/assets/back/load.js
import assets from './addon.js';

/* Functions */
import './functions/css.js';
import './functions/js.js';

import './functions/scan/files.js';
import './functions/scan/directories.js';

import './functions/utils/read.js';
import './functions/utils/transform.js';

import './functions/import.js';

/* Items */
import './items/commands/css.js';
import './items/commands/js.js';

import './items/html/css.js';
import './items/html/js.js';


export default assets;

// repositories/framework/addons/core/clients/back/grpc/addon.js
import onetype from '#framework/load.js';

const clientsGRPC = onetype.Addon('clients.grpc', (addon) =>
{
    addon.Field('id', ['string|number']);
    addon.Field('instance', ['object']);
    addon.Field('host', ['string', 'localhost']);
    addon.Field('port', ['number', 50000]);
    addon.Field('secure', ['boolean', true]);
    addon.Field('timeout', ['number', 5]);
    addon.Field('metadata', ['object', {}]);
    
    addon.Field('onError', ['function']);
    addon.Field('onConnect', ['function']);

    addon.Field('onStream', ['function']);
    addon.Field('onStreamError', ['function']);
    addon.Field('onStreamEnd', ['function']);
    addon.Field('onStreamData', ['function']);
    addon.Field('onStreamRequest', ['function']);
    addon.Field('onStreamRespond', ['function']);
});

export default clientsGRPC;

// repositories/framework/addons/core/clients/back/grpc/item/catch/add.js
import clientsGRPC from '#clients/grpc/addon.js';

clientsGRPC.ItemOn('add', function(item)
{
    item.Fn('connect');
});

// repositories/framework/addons/core/clients/back/grpc/item/functions/connect.js
import { fileURLToPath } from 'url'
import { dirname, join } from 'path'

import clientsGRPC from '#clients/grpc/addon.js';

clientsGRPC.Fn('item.connect', async function(item)
{
    if(item.Get('instance'))
    {
        return item.Get('instance');
    }

    try
    {
        const grpcModule = await import('@grpc/grpc-js');
        const protoLoaderModule = await import('@grpc/proto-loader');

        const grpc = grpcModule.default || grpcModule;
        const protoLoader = protoLoaderModule.default || protoLoaderModule;

        const protoPath = join(dirname(fileURLToPath(import.meta.url)), '/../../service.proto');

        const definition = protoLoader.loadSync(protoPath, 
        {
            keepCase: true,
            longs: String,
            enums: String,
            defaults: true,
            oneofs: true
        });

        const universalPackage = grpc.loadPackageDefinition(definition).universal;
        
        const credentials = item.Get('secure') ? grpc.credentials.createSsl() : grpc.credentials.createInsecure();

        const client = new universalPackage.UniversalService(`${item.Get('host')}:${item.Get('port')}`, credentials, {
            'grpc.max_send_message_length': 1024 * 1024 * 100,
            'grpc.max_receive_message_length': 1024 * 1024 * 100,
            'grpc.keepalive_time_ms': 30000,
            'grpc.keepalive_timeout_ms': 10000,
            'grpc.keepalive_permit_without_calls': 1
        });

        item.Set('instance', client);

        await new Promise((resolve, reject) => 
        {
            client.waitForReady(new Date(Date.now() + item.Get('timeout') * 1000), (error) => 
            {
                if(error) 
                {
                    reject(error);
                } 
                else 
                {
                    resolve();
                }
            });
        });

        
        item.Get('onConnect') && item.Get('onConnect').call(item, client);

        return client;
    }
    catch(error)
    {
        item.Get('instance')?.close();
        item.Set('instance', null);
        
        item.Get('onError') && item.Get('onError').call(item, error.message);
    }
});

// repositories/framework/addons/core/clients/back/grpc/item/functions/resolve.js
import clientsGRPC from '#clients/grpc/addon.js';

const promises = {};

clientsGRPC.Fn('item.resolve', function(item, id, payload = null, onChunk = null)
{
    if(payload === null)
    {
        const timeout = setTimeout(() =>
        {
            promises[id].resolve({data: null, message: 'Request timed out after 60 seconds. No response received.', code: 500, id, time: 60000});
            delete(promises[id]);
        }, 60000);

        return new Promise((resolve) =>
        {
            promises[id] = { resolve, timeout, onChunk, time: performance.now() };
        });
    }

    if(!promises[id])
    {
        return;
    }

    if(promises[id].onChunk && !payload.end)
    {
        promises[id].onChunk(payload);
    }

    if(payload.end)
    {
        payload.time = (performance.now() - promises[id].time).toFixed(2);
        promises[id].resolve(payload);
        clearTimeout(promises[id].timeout);

        delete promises[id];
    }
});

// repositories/framework/addons/core/clients/back/grpc/item/functions/stream.js
import clientsGRPC from '#clients/grpc/addon.js';
import onetype from '#framework/load.js';

clientsGRPC.Fn('item.stream', async function(item)
{
    const client = item.Get('instance');

    if(!client)
    {
        return;
    }

    try
    {
        const grpcModule = await import('@grpc/grpc-js');
        const grpc = grpcModule.default || grpcModule;

        const metadata = new grpc.Metadata();

        Object.entries(item.Get('metadata')).forEach(([key, value]) => 
        {
            metadata.add(key, value);
        });

        const stream = client.Stream(metadata);

        stream.request = function(name, data, onChunk = null, id = null)
        {
            id = id || onetype.GenerateUID();

            const extracted = onetype.BinariesExtract(data);

            stream.write({
                data: JSON.stringify({type: 'request', name, data: extracted.data, id}),
                binaries: extracted.binaries
            });

            item.Get('onStreamRequest') && item.Get('onStreamRequest').call(item, stream, {type: 'request', name, data, binaries: extracted.binaries, id});

            return item.Fn('resolve', id, null, onChunk);
        };

        stream.respond = function(data, message, code, end = true, id = null)
        {
            id = id || onetype.GenerateUID();

            const extracted = onetype.BinariesExtract(data);

            stream.write({
                data: JSON.stringify({type: 'respond', data: extracted.data, message, code, id, end}),
                binaries: extracted.binaries
            });

            item.Get('onStreamRespond') && item.Get('onStreamRespond').call(item, stream, {type: 'respond', data, binaries: extracted.binaries, message, code, id, end});
        };

        item.Get('onStream') && item.Get('onStream').call(item, stream);

        stream.on('data', (response) =>
        {
            const payload = JSON.parse(response.data);

            if(payload.data && response.binaries)
            {
                payload.data = onetype.BinariesInject(payload.data, response.binaries);
            }

            if(payload.type === 'respond')
            {
                item.Fn('resolve', payload.id, payload);
            }

            item.Get('onStreamData') && item.Get('onStreamData').call(item, stream, payload);
        });

        stream.on('error', (error) => 
        {
            item.Get('onStreamError') && item.Get('onStreamError').call(item, stream, error.message);
        });

        stream.on('end', () => 
        {
            item.Get('onStreamEnd') && item.Get('onStreamEnd').call(item, stream);

            item.Get('instance').close();
            item.Set('instance', null);
        });

        return stream;
    }
    catch(error)
    {
        if(item.Get('instance'))
        {
            item.Get('instance').close();
            item.Set('instance', null);
        }

        item.Get('onError') && item.Get('onError').call(item, error.message);
    }
});

// repositories/framework/addons/core/clients/back/grpc/load.js
import clientsGRPC from '#clients/grpc/addon.js';

import '#clients/grpc/item/catch/add.js';

import '#clients/grpc/item/functions/connect.js';
import '#clients/grpc/item/functions/stream.js';
import '#clients/grpc/item/functions/resolve.js';

export default clientsGRPC;

// repositories/framework/addons/core/clients/back/http/addon.js
import onetype from '#framework/load.js';

const clients = onetype.Addon('clients', (addon) =>
{
    addon.Field('id', ['string']);
    addon.Field('instance', ['object']);
    addon.Field('type', ['string', 'http']);
    addon.Field('host', ['string', 'localhost']);
    addon.Field('port', ['number', 3000]);
    addon.Field('timeout', ['number', 15]);
    
    addon.Field('onConnect', ['function']);
    addon.Field('onRequest', ['function']);
    addon.Field('onResponse', ['function']);
    addon.Field('onError', ['function']);
    addon.Field('onComplete', ['function']);
});

export default clients;

// repositories/framework/addons/core/clients/back/http/item/catch/add.js
import clients from '#clients/addon.js';

clients.ItemOn('add', function(item)
{
    switch(item.Get('type').toLowerCase())
    {
        case 'http':
            item.Fn('http.create');
            break;
        case 'grpc':
            item.Fn('gRPC.create');
            break;
        default:
            item.Get('onError') && item.Get('onError')('Client type does not exist.');
    }
});

// repositories/framework/addons/core/clients/back/http/item/functions/gRPC/create.js
import * as grpc from '@grpc/grpc-js'
import * as protoLoader from '@grpc/proto-loader'
import { fileURLToPath } from 'url'
import { dirname, join } from 'path'

import onetype from '#framework/load.js';
import clients from '#clients/addon.js';

clients.Fn('item.grpc.create', function(item)
{
    const { host, port, timeout } = item.Get(['host', 'port', 'timeout']);
    
    if(item.Get('instance'))
    {
        item.Get('onError') && item.Get('onError')('gRPC Client already created.');
        return;
    }
    
    this.methods.init = () =>
    {
        const protoPath = join(dirname(fileURLToPath(import.meta.url)), '/../../../service.proto');
        
        const definition = protoLoader.loadSync(protoPath, 
        {
            keepCase: true,
            longs: String,
            enums: String,
            defaults: true,
            oneofs: true
        });

        const universalPackage = grpc.loadPackageDefinition(definition).universal;
        
        const address = `${host}:${port}`;
        
        const client = new universalPackage.UniversalService(address, grpc.credentials.createInsecure(), 
        {
            'grpc.max_send_message_length': 1024 * 1024 * 100,
            'grpc.max_receive_message_length': 1024 * 1024 * 100,
            'grpc.keepalive_time_ms': 30000,
            'grpc.keepalive_timeout_ms': 10000,
            'grpc.keepalive_permit_without_calls': 1,
            'grpc.http2.min_time_between_pings_ms': 10000,
            'grpc.http2.max_pings_without_data': 0
        });
        
        const wrapper = {
            instance: client,
            execute: this.methods.execute,
            ping: this.methods.ping
        };
        
        item.Set('instance', wrapper);
        
        if(item.Get('onConnect'))
        {
            item.Get('onConnect')(wrapper);
        }
        
        onetype.Emit('@clients.grpc.connect', wrapper);
    };
    
    this.methods.execute = async (functionName, data = {}, requestTimeout = timeout) =>
    {
        const client = item.Get('instance');
        
        if(!client)
        {
            throw new Error('gRPC Client not initialized.');
        }
        
        const grpc = {
            id: item.Get('id'),
            client,
            function: functionName,
            data,
            items: {},
            variables: {},
            duration: performance.now(),
            response: {
                data: null,
                message: 'Request not sent.',
                code: 0
            }
        };
        
        try
        {
            onetype.Emit('@clients.grpc.request.before', grpc);
            await onetype.Middleware('clients.grpc.request.before', grpc);
            
            if(item.Get('onRequest'))
            {
                await Promise.resolve(item.Get('onRequest')(grpc));
            }
            
            const response = await new Promise((resolve, reject) => 
            {
                const deadline = new Date(Date.now() + requestTimeout * 1000);
                
                try
                {
                    client.instance.execute({function: functionName, data: JSON.stringify(grpc.data)}, {deadline}, (error, response) => 
                    {
                        if(error) 
                        {
                            return reject(error);
                        }

                        try
                        {
                            const data = JSON.parse(response.data);
                            
                            resolve({
                                data, 
                                code: response.code, 
                                message: response.message
                            });
                        }
                        catch(error)
                        {
                            return reject(new Error('Expected valid JSON from response'));
                        }
                    });
                }
                catch(error)
                {
                    reject(new Error('Failed to execute function "' + functionName + '"'));
                }
            });
            
            grpc.response = response;
            
            onetype.Emit('@clients.grpc.request.after', grpc);
            await onetype.Middleware('clients.grpc.request.after', grpc);
            
            if(item.Get('onResponse'))
            {
                item.Get('onResponse')(grpc);
            }
            
            grpc.duration = (performance.now() - grpc.duration).toFixed(2);
            
            const result = {
                data: grpc.response.data,
                code: grpc.response.code,
                message: grpc.response.message,
                duration: grpc.duration
            };
            
            if(item.Get('onComplete'))
            {
                item.Get('onComplete')(result, grpc);
            }
            
            return result;
        }
        catch(error)
        {
            grpc.error = error.message || 'Request failed';
            grpc.duration = (performance.now() - grpc.duration).toFixed(2);
            
            if(item.Get('onError'))
            {
                item.Get('onError')(grpc.error, grpc);
            }
            
            throw new Error(grpc.error);
        }
    };
    
    this.methods.ping = async (requestTimeout = 3) =>
    {
        try
        {
            const response = await this.methods.execute('ping', {}, requestTimeout);
            return response.data.pong === true;
        }
        catch(error)
        {
            if(item.Get('onError'))
            {
                item.Get('onError')('Ping failed: ' + error.message);
            }
            return false;
        }
    };
    
    this.methods.init();
});

// repositories/framework/addons/core/clients/back/http/item/functions/gRPC/execute.js
import clients from '#clients/addon.js';

clients.Fn('item.grpc.execute', function(item, name, data = {}, requestTimeout)
{
    const instance = item.Get('instance');
    
    if(!instance || !instance.execute)
    {
        throw new Error('gRPC Client not initialized properly.');
    }
    
    return instance.execute(name, data, requestTimeout || item.Get('timeout'));
});

// repositories/framework/addons/core/clients/back/http/item/functions/http/create.js
import onetype from '#framework/load.js';
import clients from '#clients/addon.js';

clients.Fn('item.http.create', function(item)
{
    const { host, port, timeout } = item.Get(['host', 'port', 'timeout']);
    
    if(item.Get('instance'))
    {
        item.Get('onError') && item.Get('onError')('HTTP Client already created.');
        return;
    }
    
    this.methods.init = () =>
    {
        const baseUrl = `http://${host}:${port}`;
        
        const client = {
            baseUrl,
            request: this.methods.request
        };
        
        item.Set('instance', client);
        
        if(item.Get('onConnect'))
        {
            item.Get('onConnect')(client);
        }
        
        onetype.Emit('@clients.http.connect', client);
    };
    
    this.methods.request = async (path, method = 'GET', data = {}, requestTimeout = timeout) =>
    {
        const client = item.Get('instance');
        
        if(!client)
        {
            throw new Error('HTTP Client not initialized.');
        }
        
        const http = {
            id: item.Get('id'),
            client,
            path,
            method,
            data,
            url: `${client.baseUrl}${path}`,
            items: {},
            variables: {},
            duration: performance.now(),
            response: {
                data: null,
                message: 'Request not sent.',
                code: 0
            }
        };

        try
        {
            onetype.Emit('@clients.http.request.before', http);
            await onetype.Middleware('clients.http.request.before', http);
            
            if(item.Get('onRequest'))
            {
                await Promise.resolve(item.Get('onRequest')(http));
            }
            
            const controller = new AbortController();
            const timeoutId = setTimeout(() => controller.abort(), requestTimeout * 1000);
            
            const options = {
                method: http.method,
                headers: {
                    'Content-Type': 'application/json'
                },
                signal: controller.signal
            };
            
            if(http.method !== 'GET' && http.data)
            {
                options.body = JSON.stringify(http.data);
            }
            
            const response = await fetch(http.url, options);
            clearTimeout(timeoutId);
            
            http.response.code = response.status;
            http.response.message = response.statusText;
            
            const contentType = response.headers.get('content-type');
            
            if(contentType && contentType.includes('application/json'))
            {
                http.response.data = await response.json();
            }
            else
            {
                http.response.data = await response.text();
            }
            
            onetype.Emit('@clients.http.request.after', http);
            await onetype.Middleware('clients.http.request.after', http);
            
            if(item.Get('onResponse'))
            {
                item.Get('onResponse')(http);
            }
            
            http.duration = (performance.now() - http.duration).toFixed(2);
            
            const result = {
                data: http.response.data,
                code: http.response.code,
                message: http.response.message,
                duration: http.duration
            };
            
            if(item.Get('onComplete'))
            {
                item.Get('onComplete')(result, http);
            }
            
            return result;
        }
        catch(error)
        {
            http.error = error.message || 'Request failed';
            http.duration = (performance.now() - http.duration).toFixed(2);
            
            if(item.Get('onError'))
            {
                item.Get('onError')(http.error, http);
            }
            
            throw new Error(http.error);
        }
    };
    
    this.methods.init();
});

// repositories/framework/addons/core/clients/back/http/item/functions/http/delete.js
import clients from '#clients/addon.js';

clients.Fn('item.http.delete', function(item, path, data = {}, requestTimeout)
{
    return item.Fn('http.request', path, 'DELETE', data, requestTimeout);
});

// repositories/framework/addons/core/clients/back/http/item/functions/http/get.js
import clients from '#clients/addon.js';

clients.Fn('item.http.get', function(item, path, data = {}, requestTimeout)
{
    return item.Fn('http.request', path, 'GET', data, requestTimeout);
});

// repositories/framework/addons/core/clients/back/http/item/functions/http/post.js
import clients from '#clients/addon.js';

clients.Fn('item.http.post', function(item, path, data = {}, requestTimeout)
{
    return item.Fn('http.request', path, 'POST', data, requestTimeout);
});

// repositories/framework/addons/core/clients/back/http/item/functions/http/put.js
import clients from '#clients/addon.js';

clients.Fn('item.http.put', function(item, path, data = {}, requestTimeout)
{
    return item.Fn('http.request', path, 'PUT', data, requestTimeout);
});

// repositories/framework/addons/core/clients/back/http/item/functions/http/request.js
import clients from '#clients/addon.js';

clients.Fn('item.http.request', function(item, path, method = 'GET', data = {}, requestTimeout)
{
    const instance = item.Get('instance');
    
    if(!instance || !instance.request)
    {
        throw new Error('HTTP Client not initialized properly.');
    }
    
    return instance.request(path, method, data, requestTimeout || item.Get('timeout'));
});

// repositories/framework/addons/core/clients/back/http/load.js
import clients from '#clients/addon.js';

import '#clients/item/catch/add.js';

import '#clients/item/functions/http/create.js';
import '#clients/item/functions/http/request.js';
import '#clients/item/functions/http/get.js';
import '#clients/item/functions/http/post.js';
import '#clients/item/functions/http/put.js';
import '#clients/item/functions/http/delete.js';

import '#clients/item/functions/gRPC/create.js';
import '#clients/item/functions/gRPC/execute.js';

export default clients;

// repositories/framework/addons/core/commands/back/functions/expose.js
import commands from '#commands/core/addon.js';

commands.Fn('expose', function(id, endpoint = null)
{
    const command = commands.ItemGet(id);

    if(!command)
    {
        return;
    }

    command.Set('exposed', true);
    command.Set('endpoint', endpoint || '/api/' + id.replace(/:/g, '/'));
});

// repositories/framework/addons/core/commands/back/functions/find.js
import commands from '#commands/core/addon.js';

commands.Fn('find', function(method, pathname)
{
    const normalizedPathname = pathname.toLowerCase();
    
    const items = Object.values(this.Items());
    
    for (const item of items) 
    {
        if (item.Get('method') !== method) 
        {
            continue;
        }
        
        const endpoint = item.Get('endpoint');
        
        if (endpoint === normalizedPathname) 
        {
            return item;
        }
        
        if ((endpoint + '').includes(':')) 
        {
            const endpointParts = endpoint.split('/');
            const pathParts = normalizedPathname.split('/');
            
            if (endpointParts.length !== pathParts.length) 
            {
                continue;
            }
            
            let matches = true;
            
            for (let i = 0; i < endpointParts.length; i++) 
            {
                if (!endpointParts[i].startsWith(':') && endpointParts[i] !== pathParts[i]) 
                {
                    matches = false;
                    break;
                }
            }
            
            if (matches) 
            {
                return item;
            }
        }
    }

    return items.find((item) => item.Get('endpoint') === '/*');
});

// repositories/framework/addons/core/commands/back/functions/grpc/client.js
import commands from '#commands/core/addon.js';

commands.Fn('grpc.client', async function(host, port, metadata = {}, prefix = 'remote', bidirectional = false, callbacks = {}, options = {})
{
    const grpcClients = (await import('#clients/grpc/load.js')).default;
    const store = new Set();
    const retry = { tries: 0, retries: 20, delay: 15000, timeout: null };

    const connect = () =>
    {
        if(retry.timeout)
        {
            clearTimeout(retry.timeout);
            retry.timeout = null;
        }

        const client = grpcClients.Item(
        {
            host,
            port,
            secure: options.secure !== false,
            metadata,
            onConnect: function(client)
            {
                retry.tries = 0;
                callbacks['onConnect'] && callbacks['onConnect'](client);
                this.Fn('stream');
            },

            onStream: async function(stream)
            {
                callbacks['onStream'] && callbacks['onStream'](stream);

                const result = await stream.request('commands:get:many');

                if(result.code !== 200)
                {
                    return stream.cancel();
                }

                const list = result.data.commands;

                list.forEach((command) =>
                {
                    const id = prefix + ':' + command.id;

                    commands.Item(
                    {
                        id,
                        in: command.data.in,
                        out: command.data.out,
                        callback: async (properties, resolve) =>
                        {
                            const result = await stream.request(command.id, properties, (chunk) =>
                            {
                                callbacks['onStreamChunk'] && callbacks['onStreamChunk'](stream, chunk);
                                resolve(chunk.data, chunk.message, chunk.code, chunk.end);
                            });

                            resolve(result.data, result.message, result.code, result.end);
                        }
                    });

                    store.add(id);
                });

                callbacks['onCommands'] && callbacks['onCommands'](stream, store);
            },

            onStreamData: async function(stream, payload)
            {
                if(!bidirectional)
                {
                    return stream.respond(null, 'Client does not support a bidirectional streaming.', 404, true, payload.id);
                }

                if(payload.type === 'request')
                {
                    try
                    {
                        const command = commands.ItemGet(payload.name);

                        if(!command)
                        {
                            throw onetype.Error(404, 'Command does not exist.');
                        }

                        if(store.has(payload.name))
                        {
                            throw onetype.Error(400, 'Cannot execute remote commands recursively.');
                        }

                        if(payload.name.startsWith(prefix + ':'))
                        {
                            throw onetype.Error(400, 'Cannot execute :prefix: commands recursively.', {prefix});
                        }

                        const result = await command.Fn('run', payload.data, (chunk) =>
                        {
                            callbacks['onStreamChunk'] && callbacks['onStreamChunk'](stream, chunk);
                            stream.respond(chunk.data, chunk.message, chunk.code, chunk.end, payload.id);
                        });

                        callbacks['onStreamRespond'] && callbacks['onStreamRespond'](stream, payload, result.data, result.message, result.code, payload.id);
                        stream.respond(result.data, result.message, result.code, result.end, payload.id);
                    }
                    catch(error)
                    {
                        callbacks['onStreamRespond'] && callbacks['onStreamRespond'](stream, payload, null, error.message, 500, payload.id);
                        stream.respond(null, error.message, 500, true, payload.id);
                    }
                }
            },

            onStreamEnd: function(stream)
            {
                callbacks['onStreamEnd'] && callbacks['onStreamEnd'](stream);

                store.forEach(id =>
                {
                    commands.ItemRemove(id);
                });
                
                store.clear();

                if(retry.tries < retry.retries)
                {
                    retry.tries++;
                    retry.timeout = setTimeout(() => connect(), retry.delay);
                }
            },

            onStreamError: function(stream, message)
            {
                callbacks['onStreamError'] && callbacks['onStreamError'](stream, message);
            },
            
            onError: function(message)
            {
                callbacks['onError'] && callbacks['onError'](message);

                if(retry.tries < retry.retries)
                {
                    client.Remove();

                    retry.tries++;
                    retry.timeout = setTimeout(() => connect(), retry.delay);
                }
            }
        });

        return client;
    };

    return connect();
});

// repositories/framework/addons/core/commands/back/functions/grpc/server.js
import commands from '#commands/core/addon.js';

commands.Fn('grpc.server', async function(port = 50000, callbacks = {}, options = {})
{
    const grpcServers = (await import('#servers/grpc/load.js')).default;
    const server = grpcServers.Item({
        port: port,
        secure: options.secure === true,
        cert: options.cert,
        onStart: function()
        {
            callbacks['onStart'] && callbacks['onStart']();
        },
        onStreamConnect: (stream) => 
        {
            callbacks['onStreamConnect'] && callbacks['onStreamConnect'](stream);
        },
        onStreamData: async (stream, payload) => 
        {
            callbacks['onStreamData'] && callbacks['onStreamData'](stream, payload);

            if(payload.type !== 'request')
            {
                return;
            }

            const command = commands.ItemGet(payload.name);

            try
            {
                if(!command)
                {
                    throw onetype.Error(404, 'Command does not exist.');
                }

                if(!command.Get('exposed'))
                {
                    throw onetype.Error(403, 'Command is not exposed.');
                }

                const result = await command.Fn('run', payload.data, (chunk) =>
                {
                    callbacks['onStreamChunk'] && callbacks['onStreamChunk'](stream, chunk);
                    stream.respond(chunk.data, chunk.message, chunk.code, chunk.end, payload.id);
                });

                callbacks['onStreamRespond'] && callbacks['onStreamRespond'](stream, result);
                stream.respond(result.data, result.message, result.code, result.end, payload.id);
            }
            catch(error)
            {
                callbacks['onStreamRespond'] && callbacks['onStreamRespond'](stream, { data: null, message: error.message, code: 500 });
                return stream.respond(null, error.message, 500, true, payload.id);
            }
        },
        onError: (message) => 
        {
            callbacks['onError'] && callbacks['onError'](message);
        },
        onStreamError: function(stream)
        {
            callbacks['onStreamError'] && callbacks['onStreamError'](stream);
        },
        onStreamEnd: function(stream)
        {
            callbacks['onStreamEnd'] && callbacks['onStreamEnd'](stream);
        }
    });
    
    return server;
});

// repositories/framework/addons/core/commands/back/functions/hide.js
import commands from '#commands/core/addon.js';

commands.Fn('hide', function(id)
{
    const command = commands.ItemGet(id);

    if(!command)
    {
        return;
    }

    command.Set('exposed', false);
    command.Set('endpoint', null);
});

// repositories/framework/addons/core/commands/back/functions/http/server.js
import onetype from '#framework/load.js';
import commands from '#commands/core/addon.js';

commands.Fn('http.server', async function(port = 3000, callbacks = {})
{
    const serversHTTP = (await import('#servers/http/load.js')).default;

    return serversHTTP.Item({
        port,
        onStart: (server) =>
        {
            callbacks['onStart'] && callbacks['onStart'](server);
        },
        onRequest: async (http) =>
        {
            const onRequestResult = callbacks['onRequest'] && await callbacks['onRequest'](http);

            if(onRequestResult === false || http.prevent)
            {
                callbacks['onResponse'] && callbacks['onResponse'](http);
                return;
            }

            const command = commands.Fn('find', http.request.method, http.url.pathname);

            if(!command || !command.Get('exposed'))
            {
                http.respond.code = 404;
                http.respond.message = 'Command not found.';

                callbacks['onResponse'] && callbacks['onResponse'](http);
                return;
            }

            // Extract path parameters from URL
            const endpoint = command.Get('endpoint');
            const params = onetype.RouteParams(endpoint, decodeURIComponent(http.url.pathname));

            if (params)
            {
                Object.assign(http.data, params);
            }

            const type = http.types[command.Get('type')] || http.types.JSON;

            if(http.data.streaming)
            {
                http.data.streaming = 1;
                http.prevent = true;
                http.response.writeHead(200, {
                    'Content-Type': type.contentType,
                    'Transfer-Encoding': 'chunked'
                });
            }

            const response = await command.Fn('run', http.data, (chunk) =>
            {
                if(http.data.streaming)
                {
                    http.data.streaming++;
                    http.response.write(type.formatter(chunk.data) + '\n');
                }

                callbacks['onChunk'] && callbacks['onChunk'](http, chunk);
            }, {http});

            if(http.data.streaming)
            {
                if(http.data.streaming === 1)
                {
                    http.response.write(type.formatter(response.data) + '\n');
                }

                http.response.end();
            }

            http.respond.type = command.Get('type');
            http.respond.data = response.data;
            http.respond.message = response.message;
            http.respond.code = response.code;

            callbacks['onResponse'] && callbacks['onResponse'](http);
        },
        onError: (error) =>
        {
            callbacks['onError'] && callbacks['onError'](error);
        }
    });
});

// repositories/framework/addons/core/commands/core/addon.js
import onetype from '#framework/load.js';

const commands = onetype.Addon('commands', (addon) =>
{
    addon.Field('id', ['string|number']);
    addon.Field('type', ['string', 'JSON']);
    addon.Field('description', ['string']);
    addon.Field('meta', ['object', {}]);
    addon.Field('in', ['object|string']);
    addon.Field('out', ['object|string']);
    addon.Field('exposed', ['boolean', false]);
    addon.Field('callback', ['function']);
    
    addon.Field('method', ['string', 'GET'], null, (value) =>
    {
        return ['GET', 'POST', 'PUT', 'DELETE'].includes(value) ? value : 'GET';
    });

    addon.Field('endpoint', ['string'], null, (value) =>
    {
        if(!value)
        {
            return null;
        }

        if(value === '*')
        {
            return '/*';
        }

        return onetype.Base() + '/' + value.replace(/^\/+/, '').replace(/\/+/g, '/').toLowerCase();
    });
});

export default commands;

// repositories/framework/addons/core/commands/core/functions/run.js
import commands from '#commands/core/addon.js';

commands.Fn('run', async function(id, data = {})
{
	const command = commands.ItemGet(id);

	if(!command)
	{
		throw onetype.Error(404, 'Command :id: not found.', {id});
	}

	return await command.Fn('run', data);
});

// repositories/framework/addons/core/commands/core/item/functions/run.js
import onetype from '#framework/load.js';
import commands from '#commands/core/addon.js';

commands.Fn('item.run', function(item, properties = {}, onChunk = null, context = {})
{
    const startTime = performance.now();

    return new Promise(async (resolve, reject) =>
    {
        if(!item.Get('in'))
        {
            properties = {};
        }

        const callback = (data, message = "Command '{{command}}' executed successfully.", code = 200, end = true) =>
        {
            if(message === null && code === 404)
            {
                message = 'The requested resource cannot be found.';
            }

            if(code >= 200 && code < 300 && item.Get('out'))
            {
                try
                {
                    data = onetype.DataDefine(data, item.Get('out'));
                }
                catch(error)
                {
                    throw onetype.Error(500, 'Command OUT error.', {command: item.Get('id')});
                }
            }

            const result = {
                data,
                message: message?.replace('{{command}}', item.Get('id')),
                code,
                time: (performance.now() - startTime).toFixed(2),
                end
            };

            if(onChunk && !result.end)
            {
                onChunk(result);
            }

            if(result.end)
            {
                resolve(result);
            }
        };
     
        try
        {
            if(item.Get('in'))
            {
                try 
                {
                    properties = onetype.DataDefine(properties, item.Get('in'));
                }
                catch(error)
                {
                    return resolve({
                        data: error.message, 
                        message: 'Request contains invalid parameters.', 
                        code: 400,
                        time: (performance.now() - startTime).toFixed(2),
                        end: true
                    });
                }
            }

            await item.Get('callback').call(context, properties, callback);
        }
        catch(error)
        {
            reject(error);
        }
    })
});

// repositories/framework/addons/core/commands/core/load.js
import onetype from '#framework/load.js';
import commands from '#commands/core/addon.js';

/* Core */
import '#commands/core/item/functions/run.js';
import '#commands/core/functions/run.js';

/* Back */
import '#commands/back/functions/grpc/server.js';
import '#commands/back/functions/grpc/client.js';
import '#commands/back/functions/http/server.js';
import '#commands/back/functions/find.js';
import '#commands/back/functions/expose.js';
import '#commands/back/functions/hide.js';
import '#commands/back/items/self/one.js';
import '#commands/back/items/self/many.js';
import '#commands/back/items/self/run.js';

onetype.$ot.command = async function(id, properties = {}, api = false, context = {}, onChunk = null)
{
	if(api && onetype.environment === 'front')
	{
		return await commands.Fn('api', id, properties);
	}

	const command = commands.ItemGet(id);

	if(!command)
	{
		throw onetype.Error(404, 'Command :id: not found.', {id});
	}

	const result = await command.Fn('run', properties, onChunk, context);

	if(result.code !== 200)
	{
		throw onetype.Error(result.code, result.message);
	}

	return result.data;
};

export default commands;

// repositories/framework/addons/core/commands/front/directives/run.js
onetype.AddonReady('directives', function(directives)
{
	directives.ItemAdd({
		id: 'ot-command',
		icon: 'terminal',
		name: 'Command',
		description: 'Execute a command instantly on render',
		category: 'data',
		trigger: 'node',
		order: 664,
		strict: true,
		tag: 'ot-command',
		attributes: {
			'command': ['string', null, true],
			'bind': ['string', 'command'],
			'_success': ['function'],
			'_error': ['function'],
			'data': ['object', {}],
			'api': ['boolean', false]
		},
		code: function(data, item, compile, node, identifier)
		{
			if(node.tagName.toLowerCase() !== 'ot-command')
			{
				return;
			}

			const config = {};
			const methods = {};

			methods.init = () =>
			{
				methods.config();

				if(compile.data[config.bind] !== undefined)
				{
					return;
				}

				compile.data[config.bind] = null;
				methods.run();
			};

			methods.config = () =>
			{
				config.command = data['command'].value;
				config.bind = data['bind'].value;
				config.onSuccess = data['_success'].value;
				config.onError = data['_error'].value;
				config.data = data['data'].value;
				config.api = data['api'].value;
			};

			methods.run = async () =>
			{
				const state = {
					response: null,
					error: null,
					loading: true
				};

				try
				{
					const result = config.api
						? await commands.Fn('api', config.command, config.data)
						: await commands.Fn('run', config.command, config.data);

					state.response = result;
					state.error    = null;
					state.loading  = false;

					config.onSuccess && config.onSuccess(state);
				}
				catch(error)
				{
					state.response = null;
					state.error    = error.message;
					state.loading  = false;

					config.onError && config.onError(state);
				}
				finally
				{
					compile.data[config.bind] = state;
					compile.data.Update();
				}
			};

			methods.init();
		}
	});
});

// repositories/framework/addons/core/commands/front/directives/submit.js
onetype.AddonReady('directives', function(directives)
{
	directives.ItemAdd({
		id: 'ot-command-submit',
		icon: 'terminal',
		name: 'Command Submit',
		description: 'Submit form data to a command via commands.Fn',
		category: 'data',
		trigger: 'node',
		order: 665,
		strict: true,
		tag: 'ot-command-submit',
		attributes: {
			'command': ['string', null, true],
			'bind': ['string', 'command'],
			'_success': ['function'],
			'_error': ['function'],
			'reset': ['boolean', false],
			'stop': ['boolean', false],
			'data': ['object', {}],
			'api': ['boolean', false]
		},
		code: function(data, item, compile, node, identifier)
		{
			const config = {};
			const methods = {};

			methods.init = () =>
			{
				methods.config();
				methods.state();
				methods.element();
				methods.handler();
			};

			methods.state = () =>
			{
				if(!compile.data[config.bind])
				{
					compile.data[config.bind] = {
						response: null,
						error: null,
						loading: false
					};
				}
			};

			methods.config = () =>
			{
				config.command = data['command'].value;
				config.bind = data['bind'].value;
				config.onSuccess = data['_success'].value;
				config.onError = data['_error'].value;
				config.reset = data['reset'].value;
				config.stop = data['stop'].value;
				config.data = data['data'].value;
				config.api = data['api'].value;
			};

			methods.element = () =>
			{
				config.form = document.createElement('form');
				config.form.setAttribute('autocomplete', 'off');

				while(node.firstChild)
				{
					config.form.appendChild(node.firstChild);
				}

				node.appendChild(config.form);

				requestAnimationFrame(() => onetype.FormSet(config.form, config.data));
			};

			methods.handler = () =>
			{
				config.form.otCommandSubmit = async (event) =>
				{
					event.preventDefault();

					if(config.stop)
					{
						event.stopPropagation();
					}

					const form = event.target.closest('form') || event.target;

					await methods.submit(form);
				};
			};

			methods.submit = async (form) =>
			{
				if(compile.data[config.bind].loading)
				{
					return;
				}

				const formData = onetype.FormGet(form);

				compile.data[config.bind].loading = true;
				compile.data[config.bind].error = null;

				compile.data.Update();

				try
				{
					const result = config.api
						? await commands.Fn('api', config.command, formData)
						: await commands.Fn('run', config.command, formData);

					if(result && result.code && result.code !== 200)
					{
						compile.data[config.bind].response = null;
						compile.data[config.bind].error    = result.message || 'Request failed.';
						compile.data[config.bind].loading  = false;

						config.onError && config.onError(compile.data[config.bind]);
						return;
					}

					compile.data[config.bind].response = result;
					compile.data[config.bind].error    = null;
					compile.data[config.bind].loading  = false;

					config.reset && form.reset();
					config.onSuccess && config.onSuccess(compile.data[config.bind]);
				}
				catch(error)
				{
					compile.data[config.bind].response = null;
					compile.data[config.bind].error    = error.message;
					compile.data[config.bind].loading  = false;

					config.onError && config.onError(compile.data[config.bind]);
				}
				finally
				{
					compile.data.Update();
				}
			};

			methods.init();
		}
	});

	document.addEventListener('submit', function(event)
	{
		let node = event.target;

		while(node && node !== document)
		{
			if('otCommandSubmit' in node)
			{
				node.otCommandSubmit(event);
				break;
			}

			node = node.parentNode;
		}
	});
});

// repositories/framework/addons/core/commands/front/functions/api.js
commands.Fn('api', async function(id, data = {})
{
    try
    {
        const response = await fetch('/api/commands/run', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({ id, data })
        });

        const result = await response.json();

        if(result.code !== 200)
        {
            throw new Error(result.message);
        }

        return { time: result.time, ...result.data };
    }
    catch(error)
    {
        return {
            data: null,
            message: error.message,
            code: 500
        };
    }
});

// repositories/framework/addons/core/database/back/addon.js
import onetype from '#framework/load.js';

const database = onetype.Addon('database', (addon) =>
{
    addon.Field('id', ['string']);
    addon.Field('port', ['number', 5432]);
    addon.Field('connection', ['function|object']);
    addon.Field('hostname', ['string']);
    addon.Field('username', ['string']);
    addon.Field('password', ['string']);
    addon.Field('database', ['string']);

    addon.translations = onetype.Addon('database.translations', (addon) =>
    {
        addon.Table('translations');

        addon.Field('id', ['string']);
        addon.Field('entity', ['string', null, true]);
        addon.Field('entity_id', ['string', null, true]);
        addon.Field('language', ['string', null, true]);
        addon.Field('field', ['string', null, true]);
        addon.Field('value', ['string']);
        addon.Field('updated_at', ['string']);
        addon.Field('created_at', ['string']);
    });
});

export default database;

// repositories/framework/addons/core/database/back/events/addon.init.js
import onetype from '#framework/load.js';

onetype.EmitOn('@addon.init', (addon) =>
{
	addon.database =
	{
		expose: null,
		table: null,
		translations: null
	};

	addon.Table = function(name)
	{
		addon.database.table = { name };
	};

	addon.Expose = function(config)
	{
		addon.database.expose = onetype.DataDefine(config,
		{
			filter: ['array', []],
			sort: ['array', []],
			select: ['array', []],
			find: ['function'],
			create: ['function'],
			update: ['function'],
			delete: ['function']
		});
	};

	addon.Translations = function(config)
	{
		addon.database.translations = config;
	};

	addon.Find = function({connection = 'primary', translation = null} = {})
	{
		translation = onetype.LanguageValidate(translation || onetype.Language());

		return onetype.AddonGet('database').Fn('items.find', {connection, translation, table: addon.database.table, addon});
	};
});

// repositories/framework/addons/core/database/back/events/addon.item.init.js
import onetype from '#framework/load.js';
import database from '#database/addon.js';

onetype.EmitOn('@addon.item.init', (item) =>
{
	item.Create = async function({connection = 'primary', translation = null} = {})
	{
		translation = onetype.LanguageValidate(translation || onetype.Language());

		return database.Fn('item.create', item, {connection, translation});
	};

	item.Update = async function({connection = 'primary', translation = null} = {})
	{
		translation = onetype.LanguageValidate(translation || onetype.Language());

		return database.Fn('item.update', item, {connection, translation});
	};

	item.Delete = async function({connection = 'primary'} = {})
	{
		return database.Fn('item.delete', item, {connection});
	};
});

// repositories/framework/addons/core/database/back/functions/item/create.js
import onetype from '#framework/load.js';
import database from '#database/addon.js';

database.Fn('item.create', async function(item, {connection = 'primary', translation = 'en'} = {})
{
	const fields = {};
	const id = item.Get('id');
	const knex = database.ItemGet(connection)?.Get('connection');

	const { table, translations } = item.addon.database;

	if(!knex)
	{
		throw onetype.Error(400, 'Database :1 not found.', connection);
	}

	if(!table)
	{
		throw onetype.Error(400, 'Addon :1 must have table name set.', item.addon.name);
	}

	Object.values(item.addon.Fields().data).forEach((field) =>
	{
		if(field.name === 'id')
		{
			return;
		}

		try
		{
			fields[field.name] = item.Get(field.name);
		}
		catch(error)
		{
			throw onetype.Error(500, 'Error in field: ' + field.name + ': ' + error.message);
		}
	});

	['updated', 'created', 'updated_at', 'created_at'].forEach((field) =>
	{
		if(item.addon.FieldGet(field))
		{
			fields[field] = new Date().toISOString();
		}
	});

	return knex.transaction(async (trx) =>
	{
		const [record] = await trx(table.name).insert(fields).returning('*');

		Object.entries(record).forEach(([key, value]) =>
		{
			if(value instanceof Date)
			{
				value = value.toISOString();
			}

			item.Set(key, value);
		});

		if(translations)
		{
			const rows = translations
				.filter(field => item.Get(field) !== null && item.Get(field) !== undefined)
				.map(field => ({
					entity: item.addon.name,
					entity_id: String(item.Get('id')),
					language: translation,
					field,
					value: String(item.Get(field)),
					updated_at: new Date().toISOString()
				}));

			if(rows.length)
			{
				await trx('translations')
					.insert(rows)
					.onConflict(['entity', 'entity_id', 'language', 'field'])
					.merge(['value', 'updated_at']);
			}
		}

		item.addon.ItemRemove(id, false);

		const created = item.addon.ItemAdd({id: item.Get('id')}, null, false);

		created.data = item.data;
		created.store = item.store;

		return created;
	});
});

// repositories/framework/addons/core/database/back/functions/item/delete.js
import onetype from '#framework/load.js';
import database from '#database/addon.js';

database.Fn('item.delete', async function(item, {connection = 'primary'} = {})
{
	const knex = database.ItemGet(connection)?.Get('connection');
	const { table, translations } = item.addon.database;

	if(!knex)
	{
		throw onetype.Error(400, 'Database :1 not found.', connection);
	}

	if(!table)
	{
		throw onetype.Error(400, 'Addon :1 must have table name set.', item.addon.name);
	}

	return knex.transaction(async (trx) =>
	{
		if(translations)
		{
			await trx('translations')
				.where({entity: item.addon.name, entity_id: String(item.Get('id'))})
				.del();
		}

		await trx(table.name).where('id', item.Get('id')).del();

		item.Set('id', null);

		return item;
	});
});

// repositories/framework/addons/core/database/back/functions/item/save.js
import database from '#database/addon.js';

database.Fn('item.save', async function(item, {connection = 'primary', translations = null} = {})
{
    if(item.Get('id'))
    {
        return database.Fn('item.update', item, {connection, translations});
    }
    else
    {
        return database.Fn('item.create', item, {connection, translations});
    }
});

// repositories/framework/addons/core/database/back/functions/item/update.js
import onetype from '#framework/load.js';
import database from '#database/addon.js';

database.Fn('item.update', async function(item, {connection = 'primary', translation = 'en'} = {})
{
	const fields = {};
	const knex = database.ItemGet(connection)?.Get('connection');
	const { table, translations } = item.addon.database;

	if(!knex)
	{
		throw onetype.Error(400, 'Database :1 not found.', connection);
	}

	if(!table)
	{
		throw onetype.Error(400, 'Addon :1 must have table name set.', item.addon.name);
	}

	const isDefault = translation === onetype.Language();

	Object.values(item.addon.Fields().data).forEach((field) =>
	{
		if(field.name === 'id')
		{
			return;
		}

		if(translations && !isDefault && translations.includes(field.name))
		{
			return;
		}

		try
		{
			fields[field.name] = item.Get(field.name);
		}
		catch(error)
		{
			throw onetype.Error(500, 'Error in field: ' + field.name + ': ' + error.message);
		}
	});

	['updated', 'updated_at'].forEach((field) =>
	{
		if(item.addon.FieldGet(field))
		{
			fields[field] = new Date().toISOString();
		}
	});

	return knex.transaction(async (trx) =>
	{
		const [record] = await trx(table.name).where('id', item.Get('id')).update(fields).returning('*');

		Object.entries(record).forEach(([key, value]) =>
		{
			if(value instanceof Date)
			{
				value = value.toISOString();
			}

			item.Set(key, value);
		});

		if(translations)
		{
			const rows = translations
				.filter(field => item.Get(field) !== null && item.Get(field) !== undefined)
				.map(field => ({
					entity: item.addon.name,
					entity_id: String(item.Get('id')),
					language: translation,
					field,
					value: String(item.Get(field)),
					updated_at: new Date().toISOString()
				}));

			if(rows.length)
			{
				await trx('translations')
					.insert(rows)
					.onConflict(['entity', 'entity_id', 'language', 'field'])
					.merge(['value', 'updated_at']);
			}
		}

		return item;
	});
});

// repositories/framework/addons/core/database/back/functions/items/builder.js
import database from '#database/addon.js';

database.Fn('items.builder', function()
{
	const builder = {};

	builder.applyFilters = (knexQuery, filters) =>
	{
		if(!filters.length)
		{
			return;
		}

		const groups = {};

		filters.forEach(filter =>
		{
			if(!groups[filter.group])
			{
				groups[filter.group] = { type: 'AND', filters: [] };
			}

			if(filter.groupStart)
			{
				groups[filter.group].type = filter.type;
			}
			else
			{
				groups[filter.group].filters.push(filter);
			}
		});

		knexQuery.where(function()
		{
			let first = true;

			Object.entries(groups).forEach(([groupId, group]) =>
			{
				if(!group.filters.length)
				{
					return;
				}

				const method = first ? 'where' : (group.type === 'OR' ? 'orWhere' : 'andWhere');
				first = false;

				this[method](function()
				{
					group.filters.forEach((filter, i) =>
					{
						builder.applyFilter(this, filter, i);
					});
				});
			});
		});
	};

	builder.applyFilter = (query, filter, index) =>
	{
		const method = index === 0 ? 'where' : (filter.type === 'OR' ? 'orWhere' : 'where');
		const operator = filter.operator.toUpperCase();

		if(operator === 'NULL')
		{
			query[method + 'Null'](filter.field);
		}
		else if(operator === 'NOT NULL')
		{
			query[method + 'NotNull'](filter.field);
		}
		else if(operator === 'BETWEEN')
		{
			query[method + 'Between'](filter.field, filter.value);
		}
		else if(operator === 'NOT BETWEEN')
		{
			query[method + 'NotBetween'](filter.field, filter.value);
		}
		else if(operator === 'CONTAINS')
		{
			const val = Array.isArray(filter.value) ? filter.value : [filter.value];
			const type = typeof val[0] === 'number' ? '::int[]' : '::varchar[]';
			query.whereRaw(`?? @> ARRAY[${val.map(() => '?').join(',')}]${type}`, [filter.field, ...val]);
		}
		else if(operator === 'CONTAINED')
		{
			const val = Array.isArray(filter.value) ? filter.value : [filter.value];
			const type = typeof val[0] === 'number' ? '::int[]' : '::varchar[]';
			query.whereRaw(`?? <@ ARRAY[${val.map(() => '?').join(',')}]${type}`, [filter.field, ...val]);
		}
		else if(operator === 'HAS')
		{
			query.whereRaw(`?? ? ?`, [filter.field, filter.value]);
		}
		else if(['IN', 'NOT IN'].includes(operator))
		{
			const values = Array.isArray(filter.value) ? filter.value : [filter.value];
			query[method](filter.field, operatorToKnex(operator), values);
		}
		else
		{
			query[method](filter.field, operatorToKnex(operator), filter.value);
		}
	};

	const operatorToKnex = (operator) =>
	{
		const map = {
			'EQUALS': '=',
			'NOT EQUALS': '!=',
			'LESS': '<',
			'GREATER': '>',
			'LESS EQUALS': '<=',
			'GREATER EQUALS': '>=',
			'LIKE': 'like',
			'NOT LIKE': 'not like',
			'ILIKE': 'ilike',
			'NOT ILIKE': 'not ilike',
			'IN': 'in',
			'NOT IN': 'not in'
		};

		return map[operator] || operator.toLowerCase();
	};

	builder.applySort = (knexQuery, sort) =>
	{
		if(sort)
		{
			knexQuery.orderBy(sort.field, sort.direction);
		}
	};

	builder.applyPagination = (knexQuery, limit, page) =>
	{
		if(limit > 0)
		{
			knexQuery.limit(limit);

			if(page > 1)
			{
				knexQuery.offset((page - 1) * limit);
			}
		}
	};

	builder.applySelect = (knexQuery, select, distinct) =>
	{
		if(select)
		{
			knexQuery[distinct ? 'distinct' : 'select'](select);
		}
		else
		{
			knexQuery[distinct ? 'distinct' : 'select']('*');
		}
	};

	return builder;
});

// repositories/framework/addons/core/database/back/functions/items/filter.js
import database from '#database/addon.js';

database.Fn('items.filter', function(query, field, value, operator, type, group, returnMethods)
{
	const validation = database.Fn('items.validation');
	const normalizedOperator = operator.toUpperCase();

	if(normalizedOperator === 'NULL' || normalizedOperator === 'NOT NULL')
	{
		validation.field(field);
	}
	else if(normalizedOperator === 'BETWEEN' || normalizedOperator === 'NOT BETWEEN')
	{
		validation.field(field);
		validation.between(value);
	}
	else if(normalizedOperator === 'IN' || normalizedOperator === 'NOT IN')
	{
		validation.field(field);
		validation.operator(normalizedOperator, query.operators.map(op => op.toUpperCase()));
		validation.inList(value);
	}
	else
	{
		validation.field(field);
		validation.operator(normalizedOperator, query.operators.map(op => op.toUpperCase()));
		validation.value(value);
	}

	query.filters.push({ field, value, operator: normalizedOperator, type, group });
	return returnMethods;
});

// repositories/framework/addons/core/database/back/functions/items/filters.js
import database from '#database/addon.js';

database.Fn('items.filters', function(knexQuery, filters = [])
{
	const builder = database.Fn('items.builder');
	builder.applyFilters(knexQuery, filters);
});

// repositories/framework/addons/core/database/back/functions/items/find.js
import database from '#database/addon.js';

database.Fn('items.find', function({connection = 'primary', translation = 'en', table, addon})
{
	const knex = database.ItemGet(connection)?.Get('connection');

	const query = {
		filters: [],
		joins: [],
		sort: null,
		limit: 10,
		page: 1,
		distinct: false,
		select: null,
		table,
		addon,
		knex,
		translation,
		operators: [
			'EQUALS', 'NOT EQUALS', 'LESS', 'GREATER', 'LESS EQUALS', 'GREATER EQUALS',
			'LIKE', 'NOT LIKE', 'ILIKE', 'NOT ILIKE', 'IN', 'NOT IN',
			'BETWEEN', 'NOT BETWEEN', 'NULL', 'NOT NULL',
			'CONTAINS', 'CONTAINED', 'HAS'
		]
	};

	return database.Fn('items.methods', query);
});

// repositories/framework/addons/core/database/back/functions/items/methods/aggregate.js
import database from '#database/addon.js';

database.Fn('items.methods.aggregate', async function(query, type, field)
{
	const validation = database.Fn('items.validation');
	validation.field(field);

	const builder = database.Fn('items.builder');
	const knexQuery = query.knex(query.table.name)[type](`${field} as result`);

	builder.applyFilters(knexQuery, query.filters);

	const result = await knexQuery;
	return parseFloat(result[0]?.result || 0);
});

// repositories/framework/addons/core/database/back/functions/items/methods/count.js
import database from '#database/addon.js';

database.Fn('items.methods.count', async function(query)
{
	const builder = database.Fn('items.builder');
	const knexQuery = query.knex(query.table.name).count('* as count');

	builder.applyFilters(knexQuery, query.filters);

	const result = await knexQuery;
	return parseInt(result[0]?.count || 0);
});

// repositories/framework/addons/core/database/back/functions/items/methods/exists.js
import database from '#database/addon.js';

database.Fn('items.methods.exists', async function(query)
{
	query.limit = 1;
	const count = await database.Fn('items.methods.count', query);
	return count > 0;
});

// repositories/framework/addons/core/database/back/functions/items/methods/many.js
import database from '#database/addon.js';

database.Fn('items.methods.many', async function(query, set = false)
{
	let records = await database.Fn('items.methods.query', query);

	if(query.joins.length)
	{
		records = await database.Fn('items.transform.join', records, query.joins);
	}

	return records.map(data => query.addon.ItemAdd(data, null, false, set));
});

// repositories/framework/addons/core/database/back/functions/items/methods/one.js
import database from '#database/addon.js';

database.Fn('items.methods.one', async function(query, set = false)
{
	query.limit = 1;
	const results = await database.Fn('items.methods.many', query, set);
	return results.length > 0 ? results[0] : null;
});

// repositories/framework/addons/core/database/back/functions/items/methods/plain.js
import database from '#database/addon.js';

database.Fn('items.methods.plain', async function(query)
{
	const builder = database.Fn('items.builder');
	const countQuery = query.knex(query.table.name).count('* as count');

	builder.applyFilters(countQuery, query.filters);

	let [records, countResult] = await Promise.all([
		database.Fn('items.methods.query', query),
		countQuery
	]);

	if(query.joins.length)
	{
		records = await database.Fn('items.transform.join', records, query.joins);
	}

	const total = parseInt(countResult[0]?.count || 0);

	return {
		items: records,
		total,
		page: query.page,
		pages: query.limit > 0 ? Math.ceil(total / query.limit) : 1,
		limit: query.limit
	};
});

// repositories/framework/addons/core/database/back/functions/items/methods/query.js
import database from '#database/addon.js';

database.Fn('items.methods.query', async function(query)
{
	const builder = database.Fn('items.builder');
	const knexQuery = query.knex(query.table.name);

	builder.applySelect(knexQuery, query.select, query.distinct);
	builder.applyFilters(knexQuery, query.filters);
	builder.applySort(knexQuery, query.sort);
	builder.applyPagination(knexQuery, query.limit, query.page);

	const result = await knexQuery;

	const records = result.map((record) =>
	{
		const data = {};

		Object.entries(record).forEach(([key, value]) =>
		{
			if(value instanceof Date)
			{
				value = value.toISOString();
			}

			data[key] = value;
		});

		return data;
	});

	return database.Fn('items.transform.translate', records, query);
});

// repositories/framework/addons/core/database/back/functions/items/methods.js
import onetype from '#framework/load.js';
import database from '#database/addon.js';

database.Fn('items.methods', function(query, context = null, groupId = 'default')
{
	const validation = database.Fn('items.validation');
	const methods = {};

	methods.limit = limit =>
	{
		validation.limit(limit);
		query.limit = limit;
		return methods;
	};

	methods.page = page =>
	{
		validation.page(page);
		query.page = page;
		return methods;
	};

	methods.sort = (field, direction = 'asc') =>
	{
		validation.field(field);
		direction = validation.direction(direction);

		query.sort = { field, direction };
		return methods;
	};

	methods.select = fields =>
	{
		fields = Array.isArray(fields) ? fields : [fields];
		validation.fields(fields);

		query.select = fields;
		return methods;
	};

	methods.distinct = (value = true) =>
	{
		query.distinct = Boolean(value);
		return methods;
	};

	methods.filter = (field, value, operator = 'EQUALS') =>
	{
		return database.Fn('items.filter', query, field, value, operator, 'AND', groupId, methods);
	};

	methods.orFilter = (field, value, operator = 'EQUALS') =>
	{
		return database.Fn('items.filter', query, field, value, operator, 'OR', groupId, methods);
	};

	methods.group = (type = 'AND') =>
	{
		const TID = onetype.GenerateTID();

		if(!query.filters)
		{
			query.filters = [];
		}

		query.filters.push({ groupStart: true, group: TID, type });

		const groupMethods = database.Fn('items.methods', query, methods, TID);
		groupMethods.end = () => context || methods;

		return groupMethods;
	};

	methods.join = (addon, field, output = null) =>
	{
		const config = query.addon.FieldGet(field);

		if(!config)
		{
			throw new Error(`Field '${field}' not found in addon`);
		}

		const parsed = onetype.DataParseConfig(config.define);
		const many = parsed.type.includes('array');

		query.joins.push({ addon, field, output: output || field, many });

		return methods;
	};

	methods.many = async (set = false) =>
	{
		return database.Fn('items.methods.many', query, set);
	};

	methods.plain = async () =>
	{
		return database.Fn('items.methods.plain', query);
	};

	methods.count = async () =>
	{
		return database.Fn('items.methods.count', query);
	};

	methods.one = async (set = false) =>
	{
		return database.Fn('items.methods.one', query, set);
	};

	methods.exists = async () =>
	{
		return database.Fn('items.methods.exists', query);
	};

	methods.sum = async (field) =>
	{
		return database.Fn('items.methods.aggregate', query, 'sum', field);
	};

	methods.avg = async (field) =>
	{
		return database.Fn('items.methods.aggregate', query, 'avg', field);
	};

	methods.min = async (field) =>
	{
		return database.Fn('items.methods.aggregate', query, 'min', field);
	};

	methods.max = async (field) =>
	{
		return database.Fn('items.methods.aggregate', query, 'max', field);
	};

	if(context)
	{
		methods.end = () => context;
	}

	return methods;
});

// repositories/framework/addons/core/database/back/functions/items/transform/join.js
import onetype from '#framework/load.js';
import database from '#database/addon.js';

database.Fn('items.transform.join', async function(records, joins)
{
	if(!joins.length || !records.length)
	{
		return records;
	}

	for(const join of joins)
	{
		const addon = onetype.AddonGet(join.addon);

		if(!addon)
		{
			throw new Error(`Join addon '${join.addon}' not found`);
		}

		const ids = [];

		records.forEach(record =>
		{
			const value = record[join.field];

			if(join.many && Array.isArray(value))
			{
				value.forEach(id => { if(id && !ids.includes(id)) ids.push(id); });
			}
			else if(value && !ids.includes(value))
			{
				ids.push(value);
			}
		});

		if(!ids.length)
		{
			continue;
		}

		const joined = await addon.Find().filter('id', ids, 'IN').limit(ids.length).many();
		const map = {};

		const fields = Object.keys(addon.Fields().data);
		joined.forEach(item => { map[String(item.Get('id'))] = item.Get(fields); });

		records.forEach(record =>
		{
			const value = record[join.field];

			if(join.many && Array.isArray(value))
			{
				record[join.output] = value.map(id => map[String(id)]).filter(Boolean);
			}
			else if(value)
			{
				record[join.output] = map[String(value)] || null;
			}
		});
	}

	return records;
});

// repositories/framework/addons/core/database/back/functions/items/transform/translate.js
import database from '#database/addon.js';

database.Fn('items.transform.translate', async function(records, query)
{
	const translations = query.addon.database.translations;

	if(!translations || !records.length)
	{
		return records;
	}

	const ids = records.map(record => String(record.id)).filter(Boolean);

	if(!ids.length)
	{
		return records;
	}

	const rows = await query.knex('translations')
		.where({entity: query.addon.name, language: query.translation})
		.whereIn('entity_id', ids);

	if(!rows.length)
	{
		return records;
	}

	const map = {};

	rows.forEach(row =>
	{
		if(!map[row.entity_id])
		{
			map[row.entity_id] = {};
		}

		map[row.entity_id][row.field] = row.value;
	});

	records.forEach(record =>
	{
		const translated = map[String(record.id)];

		if(translated)
		{
			for(const field of translations)
			{
				if(translated[field] !== undefined)
				{
					record[field] = translated[field];
				}
			}
		}
	});

	return records;
});

// repositories/framework/addons/core/database/back/functions/items/validation.js
import database from '#database/addon.js';

database.Fn('items.validation', function()
{
	const validation = {};

	validation.field = field =>
	{
		if(field === undefined || field === null)
		{
			throw new Error('Field name cannot be null or undefined');
		}

		if(typeof field !== 'string')
		{
			throw new Error(`Field name must be a string, received: ${typeof field}`);
		}

		if(!(/^[a-zA-Z][a-zA-Z0-9_\.]{0,63}$/.test(field)))
		{
			throw new Error(`Invalid field name format: '${field}'.`);
		}

		return true;
	};

	validation.fields = fields =>
	{
		if(!Array.isArray(fields))
		{
			throw new Error(`Fields must be an array, received: ${typeof fields}`);
		}

		if(fields.length === 0)
		{
			throw new Error('Fields array cannot be empty');
		}

		for(let i = 0; i < fields.length; i++)
		{
			validation.field(fields[i]);
		}

		return true;
	};

	validation.limit = limit =>
	{
		if(typeof limit !== 'number' || !Number.isInteger(limit) || limit <= 0)
		{
			throw new Error(`Limit must be a positive integer, received: ${limit}`);
		}

		return true;
	};

	validation.page = page =>
	{
		if(typeof page !== 'number' || !Number.isInteger(page) || page < 1)
		{
			throw new Error(`Page must be an integer >= 1, received: ${page}`);
		}

		return true;
	};

	validation.direction = direction =>
	{
		if(typeof direction !== 'string')
		{
			throw new Error(`Sort direction must be a string, received: ${typeof direction}`);
		}

		direction = direction.toLowerCase();

		if(!['asc', 'desc'].includes(direction))
		{
			throw new Error(`Invalid sort direction: '${direction}'.`);
		}

		return direction;
	};

	validation.operator = (operator, operators) =>
	{
		if(!operators.includes(operator.toUpperCase()))
		{
			throw new Error(`Invalid operator: '${operator}'.`);
		}

		return true;
	};

	validation.value = value =>
	{
		if(value !== null && !['number', 'string', 'boolean'].includes(typeof value))
		{
			throw new Error(`Value must be a string, number, or boolean. Received: ${typeof value}`);
		}

		return true;
	};

	validation.between = value =>
	{
		if(!Array.isArray(value) || value.length !== 2)
		{
			throw new Error('BETWEEN requires an array of exactly 2 elements.');
		}

		return true;
	};

	validation.inList = value =>
	{
		if(!Array.isArray(value) || value.length === 0)
		{
			throw new Error('IN requires a non-empty array.');
		}

		return true;
	};

	return validation;
});

// repositories/framework/addons/core/database/back/item/catch/add.js
import Knex from 'knex';
import database from '#database/addon.js';

database.ItemOn('add', function(item)
{
    const knex = Knex({
        client: 'pg',
        connection: {
            port: item.Get('port'),
            host: item.Get('hostname'),
            user: item.Get('username'),
            password: item.Get('password'),
            database: item.Get('database'),
        },
        acquireConnectionTimeout: 1000,
        pool: {
            min: 0,   
            max: 25,    
            idleTimeoutMillis: 1000,
            acquireTimeoutMillis: 1000
        }
    });

    item.Set('connection', knex);
});

// repositories/framework/addons/core/database/back/items/commands/create.js
import onetype from '#framework/load.js';

onetype.AddonReady('commands', (commands) =>
{
	commands.Item({
		id: 'database:create',
		exposed: true,
		method: 'POST',
		endpoint: '/api/database/create',
		in: {
			addon: ['string', null, true],
			data: ['object', null, true],
			translation: ['string']
		},
		out: {
			item: ['object']
		},
		callback: async function(properties, resolve)
		{
			const addon = onetype.AddonGet(properties.addon);

			if(!addon)
			{
				return resolve(null, 'Addon not found.', 404);
			}

			const expose = addon.database?.expose;

			if(!expose)
			{
				return resolve(null, 'Addon is not exposed.', 403);
			}

			if(!expose.create)
			{
				return resolve(null, 'Create is not allowed.', 403);
			}

			const item = addon.Item(properties.data);

			const allowed = await expose.create.call({http: this.http, properties, item});

			if(allowed !== true)
			{
				return resolve(null, typeof allowed === 'string' ? allowed : 'Create not allowed.', 400);
			}

			const created = await item.Create({translation: properties.translation});
			const fields = expose.select || Object.keys(addon.Fields().data);

			resolve({item: created.Get(fields)});
		}
	});
});

// repositories/framework/addons/core/database/back/items/commands/delete.js
import onetype from '#framework/load.js';

onetype.AddonReady('commands', (commands) =>
{
	commands.Item({
		id: 'database:delete',
		exposed: true,
		method: 'POST',
		endpoint: '/api/database/delete',
		in: {
			addon: ['string', null, true],
			id: ['string', null, true]
		},
		out: {
			success: ['boolean']
		},
		callback: async function(properties, resolve)
		{
			const addon = onetype.AddonGet(properties.addon);

			if(!addon)
			{
				return resolve(null, 'Addon not found.', 404);
			}

			const expose = addon.database?.expose;

			if(!expose)
			{
				return resolve(null, 'Addon is not exposed.', 403);
			}

			if(!expose.delete)
			{
				return resolve(null, 'Delete is not allowed.', 403);
			}

			if(!properties.id || (typeof properties.id !== 'string' && typeof properties.id !== 'number'))
			{
				return resolve(null, 'Invalid or missing id.', 400);
			}

			const item = await addon.Find().filter('id', properties.id).one(true);

			if(!item)
			{
				return resolve(null, 'Item not found.', 404);
			}

			const allowed = await expose.delete.call({http: this.http, properties, item});

			if(allowed !== true)
			{
				return resolve(null, typeof allowed === 'string' ? allowed : 'Delete not allowed.', 400);
			}

			await item.Delete();

			resolve({success: true});
		}
	});
});

// repositories/framework/addons/core/database/back/items/commands/find.js
import onetype from '#framework/load.js';

onetype.AddonReady('commands', (commands) =>
{
	commands.Item({
		id: 'database:find',
		exposed: true,
		method: 'POST',
		endpoint: '/api/database/find',
		in: {
			addon: ['string', null, true],
			filters: {
				type: 'array',
				each: {
					type: 'object',
					config: 'filter'
				}
			},
			sort_field: ['string'],
			sort_direction: ['string', 'asc'],
			select: {
				type: 'array',
				each: {
					type: 'string'
				}
			},
			page: ['number', 1],
			limit: ['number', 50],
			translation: ['string']
		},
		out: {
			items: {
				type: 'array',
				each: {
					type: 'object'
				}
			},
			total: ['number'],
			page: ['number'],
			limit: ['number']
		},
		callback: async function(properties, resolve)
		{
			const addon = onetype.AddonGet(properties.addon);

			if(!addon)
			{
				return resolve(null, 'Addon not found.', 404);
			}

			const expose = addon.database?.expose;

			if(!expose)
			{
				return resolve(null, 'Addon is not exposed.', 403);
			}

			let query = addon.Find({translation: properties.translation});

			if(properties.filters)
			{
				for(const filter of properties.filters)
				{
					if(!expose.filter.includes(filter.field))
					{
						return resolve(null, 'Filter field "' + filter.field + '" is not allowed.', 400);
					}

					query = query.filter(filter.field, filter.value, filter.operator || 'EQUALS');
				}
			}

			if(properties.sort_field)
			{
				if(!expose.sort.includes(properties.sort_field))
				{
					return resolve(null, 'Sort field "' + properties.sort_field + '" is not allowed.', 400);
				}

				query = query.sort(properties.sort_field, properties.sort_direction || 'asc');
			}

			if(expose.find)
			{
				const allowed = expose.find.call({http: this.http, properties}, query);

				if(allowed !== true)
				{
					return resolve(null, typeof allowed === 'string' ? allowed : 'Find not allowed.', 400);
				}
			}

			const limit = Math.min(properties.limit || 50, 500);
			const page = properties.page || 1;

			const result = await query.page(page).limit(limit).plain();

			if(expose.select)
			{
				const allowed = properties.select?.length ? properties.select.filter(field => expose.select.includes(field)) : expose.select;

				result.items = result.items.map(item =>
				{
					const data = {};

					for(const field of allowed)
					{
						data[field] = item[field];
					}

					return data;
				});
			}

			resolve(result);
		}
	});
});

// repositories/framework/addons/core/database/back/items/commands/update.js
import onetype from '#framework/load.js';

onetype.AddonReady('commands', (commands) =>
{
	commands.Item({
		id: 'database:update',
		exposed: true,
		method: 'POST',
		endpoint: '/api/database/update',
		in: {
			addon: ['string', null, true],
			id: ['string', null, true],
			data: ['object', null, true],
			translation: ['string']
		},
		out: {
			item: ['object']
		},
		callback: async function(properties, resolve)
		{
			const addon = onetype.AddonGet(properties.addon);

			if(!addon)
			{
				return resolve(null, 'Addon not found.', 404);
			}

			const expose = addon.database?.expose;

			if(!expose)
			{
				return resolve(null, 'Addon is not exposed.', 403);
			}

			if(!expose.update)
			{
				return resolve(null, 'Update is not allowed.', 403);
			}

			if(!properties.data?.id || (typeof properties.data.id !== 'string' && typeof properties.data.id !== 'number'))
			{
				return resolve(null, 'Invalid or missing id.', 400);
			}

			const item = await addon.Find({translation: properties.translation}).filter('id', properties.data.id).one(true);

			if(!item)
			{
				return resolve(null, 'Item not found.', 404);
			}

			Object.entries(properties.data).forEach(([key, value]) =>
			{
				item.Set(key, value);
			});

			const allowed = await expose.update.call({http: this.http, properties, item});

			if(allowed !== true)
			{
				return resolve(null, typeof allowed === 'string' ? allowed : 'Update not allowed.', 400);
			}

			await item.Update({translation: properties.translation});
			const fields = expose.select || Object.keys(addon.Fields().data);

			resolve({item: item.Get(fields)});
		}
	});
});

// repositories/framework/addons/core/database/back/load.js
import onetype from '#framework/load.js';

import '#database/events/addon.init.js';
import '#database/events/addon.item.init.js';

import database from '#database/addon.js';

import '#database/functions/items/find.js';
import '#database/functions/item/create.js';
import '#database/functions/item/update.js';
import '#database/functions/item/delete.js';
import '#database/functions/item/save.js';

import '#database/functions/items/filter.js';
import '#database/functions/items/filters.js';
import '#database/functions/items/methods.js';
import '#database/functions/items/builder.js';
import '#database/functions/items/validation.js';

import '#database/functions/items/methods/query.js';
import '#database/functions/items/transform/join.js';
import '#database/functions/items/transform/translate.js';
import '#database/functions/items/methods/many.js';
import '#database/functions/items/methods/one.js';
import '#database/functions/items/methods/count.js';
import '#database/functions/items/methods/plain.js';
import '#database/functions/items/methods/exists.js';
import '#database/functions/items/methods/aggregate.js';

import '#database/item/catch/add.js';

import '#database/items/commands/find.js';
import '#database/items/commands/create.js';
import '#database/items/commands/update.js';
import '#database/items/commands/delete.js';

onetype.DataSchema('filter', {
    field: ['string', null, true],
    value: ['string|number|boolean|array'],
    operator: ['string', 'EQUALS']
});

onetype.DataSchema('join', {
    addon: ['string', null, true],
    field: ['string', null, true],
    output: ['string'],
    select: {
        type: 'array',
        each: {
            type: 'string'
        }
    }
});

onetype.DataSchema('query', {
    filters: {
        type: 'array',
        each: {
            type: 'object',
            config: 'filter'
        }
    },
    page: ['number', 1],
    limit: ['number', 10],
    sort_field: ['string'],
    sort_direction: ['string', 'asc']
});

export default database;

// repositories/framework/addons/core/database/front/addon.js
const database = onetype.Addon('database', (addon) =>
{

});

// repositories/framework/addons/core/database/front/events/addon.init.js
onetype.EmitOn('@addon.init', (addon) =>
{
	addon.database =
	{
		expose: null,
		table: null,
		translations: null
	};

	addon.Table = function() {};
	addon.Expose = function() {};
	addon.Translations = function() {};

	addon.Find = function({translation = null} = {})
	{
		translation = translation ? translation : onetype.Language();

		return database.Fn('find', addon, translation);
	};
});

onetype.EmitOn('@addon.item.init', (item) =>
{
	item.Create = async function({translation = null} = {})
	{
		translation = translation || onetype.Language();

		return database.Fn('create', item, translation);
	};

	item.Update = async function({translation = null} = {})
	{
		translation = translation || onetype.Language();

		return database.Fn('update', item, translation);
	};

	item.Delete = async function()
	{
		return database.Fn('delete', item);
	};
});

// repositories/framework/addons/core/database/front/functions/create.js
database.Fn('create', async function(item, translation)
{
	const result = await $ot.command('database:create', {
		addon: item.addon.name,
		data: item.data,
		translation
	}, true);

	item.SetData(result.data.item, false);
	return item;
});

// repositories/framework/addons/core/database/front/functions/delete.js
database.Fn('delete', async function(item)
{
	const result = await $ot.command('database:delete', {
		addon: item.addon.name,
		id: item.Get('id')
	}, true);

	item.Set('id', null);
	return item;
});

// repositories/framework/addons/core/database/front/functions/find.js
database.Fn('find', function(addon, translation)
{
	const state = {
		addon: addon.name,
		filters: [],
		sort_field: null,
		sort_direction: 'asc',
		page: 1,
		limit: 50,
		translation
	};

	const request = async function()
	{
		const response = await fetch('/api/database/find', {
			method: 'POST',
			headers: { 'Content-Type': 'application/json' },
			body: JSON.stringify(state)
		});

		const result = await response.json();

		if(result.code !== 200)
		{
			throw new Error(result.message);
		}

		return result.data;
	};

	const methods = {};

	methods.filter = function(field, value, operator = 'EQUALS')
	{
		state.filters.push({ field, value, operator });
		return methods;
	};

	methods.sort = function(field, direction = 'asc')
	{
		state.sort_field = field;
		state.sort_direction = direction;
		return methods;
	};

	methods.page = function(page)
	{
		state.page = page;
		return methods;
	};

	methods.limit = function(limit)
	{
		state.limit = limit;
		return methods;
	};

	methods.select = function(fields)
	{
		state.select = Array.isArray(fields) ? fields : [fields];
		return methods;
	};
	
	methods.many = async function(set = false)
	{
		const data = await request();
		return data.items.map(item => addon.ItemAdd(item, null, false, set));
	};

	methods.one = async function(set = false)
	{
		state.limit = 1;
		const items = await methods.many(set);
		return items.length > 0 ? items[0] : null;
	};

	methods.plain = async function()
	{
		return request();
	};

	methods.count = async function()
	{
		const data = await request();
		return data.total;
	};

	methods.exists = async function()
	{
		state.limit = 1;
		const data = await request();
		return data.total > 0;
	};

	return methods;
});

// repositories/framework/addons/core/database/front/functions/update.js
database.Fn('update', async function(item, translation)
{
	const result = await $ot.command('database:update', {
		addon: item.addon.name,
		id: item.Get('id'),
		data: item.data,
		translation
	}, true);

	item.SetData(result.data.item, false);
	return item;
});

// repositories/framework/addons/core/queue/back/addon.js
import onetype from '#framework/load.js';

const queue = onetype.Addon('queue', (addon) =>
{
    addon.Field('id', ['string']);

    addon.Field('concurrency', ['number', 10]);
    addon.Field('timeout', ['number', 5000]);

    addon.Field('onTaskStart', ['function']);
    addon.Field('onTaskEnd', ['function']);
    addon.Field('onEmpty', ['function']);
    addon.Field('onRun', ['function']);
    addon.Field('whileRunning', ['function']);

    addon.Field('running', ['boolean', true]);

    addon.Field('tasks', ['array', []]);
});

export default queue;

// repositories/framework/addons/core/queue/back/functions/calculate/art.js
import queue from '#queue/addon.js';

queue.Fn('calculate.art', function(durations = [])
{
    return {
        10: durations.length >= 10 ? parseFloat((durations.slice(-10).reduce((sum, t) => sum + t, 0) / 10).toFixed(2)) : null,
        100: durations.length >= 100 ? parseFloat((durations.slice(-100).reduce((sum, t) => sum + t, 0) / 100).toFixed(2)) : null,
        250: durations.length >= 250 ? parseFloat((durations.slice(-250).reduce((sum, t) => sum + t, 0) / 250).toFixed(2)) : null,
        500: durations.length >= 500 ? parseFloat((durations.slice(-500).reduce((sum, t) => sum + t, 0) / 500).toFixed(2)) : null,
        1000: durations.length >= 1000 ? parseFloat((durations.slice(-1000).reduce((sum, t) => sum + t, 0) / 1000).toFixed(2)) : null
    };
});

// repositories/framework/addons/core/queue/back/functions/calculate/rps.js
import queue from '#queue/addon.js';

queue.Fn('calculate.rps', function(timestamps = [])
{
    this.methods.calculateRateForWindow = (windowTimestamps) =>
    {
        if(windowTimestamps.length < 2) return 0;
        
        const timeSpanSeconds = (windowTimestamps[windowTimestamps.length - 1] - windowTimestamps[0]) / 1000;
        if(timeSpanSeconds === 0) return 0;
        
        return parseFloat((windowTimestamps.length / timeSpanSeconds).toFixed(2));
    };

    return {
        10: timestamps.length >= 10 ? this.methods.calculateRateForWindow(timestamps.slice(-10)) : null,
        100: timestamps.length >= 100 ? this.methods.calculateRateForWindow(timestamps.slice(-100)) : null,
        250: timestamps.length >= 250 ? this.methods.calculateRateForWindow(timestamps.slice(-250)) : null,
        500: timestamps.length >= 500 ? this.methods.calculateRateForWindow(timestamps.slice(-500)) : null,
        1000: timestamps.length >= 1000 ? this.methods.calculateRateForWindow(timestamps.slice(-1000)) : null,
    };
});

// repositories/framework/addons/core/queue/back/item/catch/add.js
import queue from '#queue/addon.js';

queue.ItemOn('add', (item) => 
{
    item.Fn('start');
})

// repositories/framework/addons/core/queue/back/item/catch/remove.js
import queue from '#queue/addon.js';

queue.ItemOn('remove', (item) => 
{
    item.Set('running', false);
})

// repositories/framework/addons/core/queue/back/item/functions/add.js
import onetype from '#framework/load.js';
import queue from '#queue/addon.js';

queue.Fn('item.add', function(item, id, data, callback)
{
    const tasks = item.Get('tasks');

    if(id === null)
    {
        id = onetype.GenerateUID();
    }

    tasks.push({id, data, callback});
    item.Set('tasks', tasks);
});

// repositories/framework/addons/core/queue/back/item/functions/start.js
import queue from '#queue/addon.js';

queue.Fn('item.start', async function(item)
{
    const batch = {
        active: 0,
        processed: 0,
        succeeded: 0, 
        failed: 0, 
        reset: function()
        {
            item.Get('onReset') && item.Get('onReset')(batch);

            this.active = 0;
            this.processed = 0;
            this.succeeded = 0;
            this.failed = 0;
        }
    };

    this.methods.runTask = (task) => 
    {
        return new Promise((resolve, reject) => 
        {
            let ended = false;

            const timeout = setTimeout(() => 
            { 
                ended = true;
                reject(new Error('Task timeout'));
            }, item.Get('timeout'));

            try
            {
                task.callback(task.data, () => 
                { 
                    clearTimeout(timeout); 

                    if(!ended)
                    {
                        resolve();
                    }
                });
            }
            catch(error)
            {
                clearTimeout(timeout);
                reject(error);
            }
        });
    };

    this.methods.processNextTask = async () => 
    {
        if(!item.Get('tasks').length) 
        {
            return false;
        }
        
        const task = item.Get('tasks').shift();
        const now = performance.now();

        batch.active++;

        item.Get('onTaskStart') && item.Get('onTaskStart')(task, batch);

        try
        {
            await this.methods.runTask(task);

            batch.succeeded++;
            task.success = true;
        }
        catch(error)
        {
            batch.failed++;
            task.success = false;
        }
        finally
        {
            task.duration = performance.now() - now;

            batch.active--;
            batch.processed++;
           
            item.Get('onTaskEnd') && item.Get('onTaskEnd')(task, batch);
            
            if(item.Get('tasks').length && item.Get('running'))
            {
                this.methods.processNextTask();
            }
        }
    };
    
    this.methods.run = async () => 
    {
        batch.reset();

        if(!item.Get('tasks').length)
        {
            item.Get('onEmpty') && item.Get('onEmpty')(batch);
            return false;
        }
        
        item.Get('onRun') && item.Get('onRun')(batch);
        
        const concurrency = Math.min(item.Get('concurrency'), item.Get('tasks').length);

        for(let i = 0; i < concurrency; i++)
        {
            this.methods.processNextTask();
        }
        
        while(batch.active > 0)
        {
            item.Get('whileRunning') && item.Get('whileRunning')(batch);
            await new Promise(resolve => setTimeout(resolve, 1000));
        }

        return item.Get('tasks').length > 0;
    };
    
    while(true)
    {
        if(!item.Get('running'))
        {
            await new Promise(resolve => setTimeout(resolve, 1000));
        }
        else
        {
            const remain = await this.methods.run();
            await new Promise(resolve => setTimeout(resolve, remain ? 10 : 100));
        }
    }
});

// repositories/framework/addons/core/queue/back/load.js
import queue from '#queue/addon.js';

import '#queue/functions/calculate/art.js';
import '#queue/functions/calculate/rps.js';

import '#queue/item/functions/start.js';
import '#queue/item/functions/add.js';

import '#queue/item/catch/add.js';
import '#queue/item/catch/remove.js';

export default queue;

// repositories/framework/addons/core/queue/package.json
{
    "name": "addon-queue",
    "type": "module",
    "imports": {
        "#queue/*": "./back/*",
        "#framework/*": "./lib/framework/*"
    },
    "main": "index.js",
    "dependencies": {
        "@grpc/grpc-js": "^1.13.3",
        "@grpc/proto-loader": "^0.7.15"
    }
}

// repositories/framework/addons/core/servers/back/grpc/addon.js
import onetype from '#framework/load.js';

const serversGRPC = onetype.Addon('servers.grpc', (addon) =>
{
    addon.Field('id', ['string']);
    addon.Field('instance', ['object']);
    addon.Field('port', ['number', 50000]);
    addon.Field('host', ['string', '0.0.0.0']);
    addon.Field('secure', ['boolean', false]);
    addon.Field('cert', ['object']);

    addon.Field('onError', ['function']);
    addon.Field('onStart', ['function']);
    
    addon.Field('onStreamConnect', ['function']);
    addon.Field('onStreamError', ['function']);
    addon.Field('onStreamEnd', ['function']);
    addon.Field('onStreamData', ['function']);
    addon.Field('onStreamRequest', ['function']);
    addon.Field('onStreamRespond', ['function']);
});

export default serversGRPC;

// repositories/framework/addons/core/servers/back/grpc/item/catch/add.js
import serversGRPC from '#servers/grpc/addon.js';

serversGRPC.ItemOn('add', function(item)
{
    item.Fn('start');
});

// repositories/framework/addons/core/servers/back/grpc/item/functions/resolve.js
import serversGRPC from '#servers/grpc/addon.js';

const promises = {};

serversGRPC.Fn('item.resolve', function(item, id, payload = null, onChunk = null)
{
    if(payload === null)
    {
        const timeout = setTimeout(() =>
        {
            promises[id].resolve({data: null, message: 'Request timed out after 60 seconds. No response received.', code: 500, id, time: 60000});
            delete(promises[id]);
        }, 60000);

        return new Promise((resolve) =>
        {
            promises[id] = { resolve, timeout, onChunk, time: performance.now() };
        });
    }

    if(!promises[id])
    {
        return;
    }

    if(promises[id].onChunk && !payload.end)
    {
        promises[id].onChunk(payload);
    }

    if(payload.end)
    {
        payload.time = (performance.now() - promises[id].time).toFixed(2);
        promises[id].resolve(payload);
        clearTimeout(promises[id].timeout);

        delete promises[id];
    }
});

// repositories/framework/addons/core/servers/back/grpc/item/functions/start.js
import { fileURLToPath } from 'url'
import { dirname, join } from 'path'

import onetype from '#framework/load.js';
import serversGRPC from '#servers/grpc/addon.js';

serversGRPC.Fn('item.start', async function(item)
{
    if(item.Get('instance'))
    {
        return;
    }

    try
    {
        const grpcModule = await import('@grpc/grpc-js');
        const protoLoaderModule = await import('@grpc/proto-loader');

        const grpc = grpcModule.default || grpcModule;
        const protoLoader = protoLoaderModule.default || protoLoaderModule;

        const protoPath = join(dirname(fileURLToPath(import.meta.url)), '/../../service.proto');
        
        const definition = protoLoader.loadSync(protoPath, 
        {
            keepCase: true,
            longs: String,
            enums: String,
            defaults: true,
            oneofs: true
        });

        const universalPackage = grpc.loadPackageDefinition(definition).universal;
        
        const server = new grpc.Server({
            'grpc.max_send_message_length': 1024 * 1024 * 100,
            'grpc.max_receive_message_length': 1024 * 1024 * 100
        });
        
        server.addService(universalPackage.UniversalService.service, 
        {
            stream: (...data) => item.Fn('stream', ...data)
        });
        
        let credentials = grpc.ServerCredentials.createInsecure();

        if(item.Get('secure'))
        {
            const cert = item.Get('cert') || {};

            credentials = grpc.ServerCredentials.createSsl(cert.ca || null, [{
                private_key: cert.key,
                cert_chain: cert.cert
            }], cert.checkClientCertificate || false);
        }

        server.bindAsync(`${item.Get('host')}:${item.Get('port')}`, credentials, (error) =>
        {
            if(error) 
            {
                item.Get('onError') && item.Get('onError').call(item, error.message);
                return;
            }
            
            item.Set('instance', server);
            item.Get('onStart') && item.Get('onStart').call(item, server);
            
            onetype.Emit('@servers.grpc.start', server);
        });
    }
    catch(error)
    {
        item.Get('onError') && item.Get('onError').call(item, error.message);
    }
});

// repositories/framework/addons/core/servers/back/grpc/item/functions/stream.js
import onetype from '#framework/load.js';
import serversGRPC from '#servers/grpc/addon.js';

serversGRPC.Fn('item.stream', function(item, stream)
{
    stream.id = onetype.GenerateUID();

    stream.request = function(name, data, onChunk = null, id = null)
    {
        id = id || onetype.GenerateUID();

        const extracted = onetype.BinariesExtract(data);

        stream.write({
            data: JSON.stringify({type: 'request', name, data: extracted.data, id}),
            binaries: extracted.binaries
        });

        item.Get('onStreamRequest') && item.Get('onStreamRequest').call(item, stream, {type: 'request', name, data, binaries: extracted.binaries, id});

        return item.Fn('resolve', id, null, onChunk);
    };

    stream.respond = function(data, message, code, end = true, id = null)
    {
        id = id || onetype.GenerateUID();

        const extracted = onetype.BinariesExtract(data);

        stream.write({
            data: JSON.stringify({type: 'respond', data: extracted.data, message, code, id, end}),
            binaries: extracted.binaries
        });

        item.Get('onStreamRespond') && item.Get('onStreamRespond').call(item, stream, {type: 'respond', data, binaries: extracted.binaries, message, code, end, id});
    };

    stream.on('data', (response) =>
    {
        const payload = JSON.parse(response.data);

        if(payload.data && response.binaries)
        {
            payload.data = onetype.BinariesInject(payload.data, response.binaries);
        }

        if(payload.type === 'respond')
        {
            item.Fn('resolve', payload.id, payload);
        }

        item.Get('onStreamData') && item.Get('onStreamData').call(item, stream, payload);
    });

    stream.on('error', (error) => 
    {
        item.Get('onStreamError') && item.Get('onStreamError').call(item, stream, error.message);
    });

    stream.on('close', () => 
    {
        item.Get('onStreamEnd') && item.Get('onStreamEnd').call(item, stream);
    });

    item.Get('onStreamConnect') && item.Get('onStreamConnect').call(item, stream);
});

// repositories/framework/addons/core/servers/back/grpc/load.js
import serversGRPC from '#servers/grpc/addon.js';

import '#servers/grpc/item/catch/add.js';

import '#servers/grpc/item/functions/start.js';
import '#servers/grpc/item/functions/stream.js';
import '#servers/grpc/item/functions/resolve.js';

export default serversGRPC;

// repositories/framework/addons/core/servers/back/http/addon.js
import onetype from '#framework/load.js';

const serversHTTP = onetype.Addon('servers.http', (addon) =>
{
    addon.Field('id', ['string']);
    addon.Field('instance', ['object']);
    addon.Field('port', ['number', 3000]);

    addon.Field('onStart', ['function']);
    addon.Field('onRequest', ['function']);
    addon.Field('onError', ['function']);
    addon.Field('onComplete', ['function']);
});

export default serversHTTP;

// repositories/framework/addons/core/servers/back/http/functions/extract/data.js
import { json } from 'node:stream/consumers';
import serversHTTP from '#servers/http/addon.js';
import Busboy from 'busboy';

serversHTTP.Fn('extract.data', async function(request)
{
    this.methods = {};

    this.methods.extractQueryParams = (request) =>
    {
        const properties = {};
        const url = new URL(request.url, `https://${request.headers.host}`);

        url.searchParams.forEach((value, key) =>
        {
            properties[key] = value;
        });

        return properties;
    };

    this.methods.extractMultipart = async (request) =>
    {
        const properties = {};

        await new Promise((resolve, reject) =>
        {
            const busboy = Busboy({headers: request.headers});

            busboy.on('field', (name, value) =>
            {
                properties[name] = value;
            });

            busboy.on('file', (name, stream) =>
            {
                const chunks = [];

                stream.on('data', (chunk) =>
                {
                    chunks.push(chunk);
                });

                stream.on('end', () =>
                {
                    properties[name] = Buffer.concat(chunks);
                });
            });

            busboy.on('finish', resolve);
            busboy.on('error', reject);

            request.pipe(busboy);
        });

        return properties;
    };

    this.methods.extractJson = async (request) =>
    {
        try
        {
            const body = await json(request);

            if (body && typeof body === 'object')
            {
                return body;
            }
        }
        catch(error)
        {
            // Invalid JSON, skip body parsing
        }

        return {};
    };

    this.methods.convertNumber = (value) =>
    {
        const num = Number(value);
        return isNaN(num) ? null : num;
    };

    this.methods.convertString = (value) =>
    {
        return String(value);
    };

    this.methods.convertBoolean = (value) =>
    {
        if (['true', '1', true, 1].includes(value))
        {
            return true;
        }

        if (['false', '0', false, 0].includes(value))
        {
            return false;
        }

        return null;
    };

    this.methods.convertArray = (value) =>
    {
        if (Array.isArray(value))
        {
            return value;
        }

        if (typeof value === 'string')
        {
            try
            {
                const parsed = JSON.parse(value);
                return Array.isArray(parsed) ? parsed : value.split(',').map(item => item.trim()).filter(Boolean);
            }
            catch
            {
                return value.split(',').map(item => item.trim()).filter(Boolean);
            }
        }

        return null;
    };

    this.methods.convertObject = (value) =>
    {
        if (typeof value === 'object' && !Array.isArray(value))
        {
            return value;
        }

        if (typeof value === 'string')
        {
            try
            {
                const parsed = JSON.parse(value);
                return (typeof parsed === 'object' && !Array.isArray(parsed)) ? parsed : null;
            }
            catch
            {
                return null;
            }
        }

        return null;
    };

    this.methods.convertBinary = (value) =>
    {
        if (Buffer.isBuffer(value))
        {
            return value;
        }

        if (typeof value === 'string')
        {
            try
            {
                return Buffer.from(value, 'base64');
            }
            catch
            {
                return null;
            }
        }

        return null;
    };

    this.methods.convertValue = (value, hint, base) =>
    {
        if (!hint)
        {
            return {[base]: value};
        }

        if (value === null || value === undefined || value === '')
        {
            return {[base]: null};
        }

        const converters =
        {
            number: this.methods.convertNumber,
            string: this.methods.convertString,
            boolean: this.methods.convertBoolean,
            array: this.methods.convertArray,
            object: this.methods.convertObject,
            binary: this.methods.convertBinary
        };

        const converter = converters[hint.toLowerCase()];

        if (converter)
        {
            return {[base]: converter(value)};
        }

        return {[base]: value};
    };

    const properties = this.methods.extractQueryParams(request);

    if (request.method !== 'GET')
    {
        const contentType = request.headers['content-type'] || '';

        if (contentType.includes('multipart/form-data'))
        {
            const multipart = await this.methods.extractMultipart(request);
            Object.assign(properties, multipart);
        }
        else
        {
            const body = await this.methods.extractJson(request);
            Object.assign(properties, body);
        }
    }

    const result = {};

    for (const [key, value] of Object.entries(properties))
    {
        const [base, hint] = key.split(':');
        const converted = this.methods.convertValue(value, hint, hint ? base : key);
        Object.assign(result, converted);
    }

    return result;
});

// repositories/framework/addons/core/servers/back/http/item/catch/add.js
import serversHTTP from '#servers/http/addon.js';

serversHTTP.ItemOn('add', function(item)
{
    item.Fn('start');
});

// repositories/framework/addons/core/servers/back/http/item/functions/start.js
import http from 'http';
import serversHTTP from '#servers/http/addon.js';
import onetype from '#framework/load.js';

serversHTTP.Fn('item.start', function(item)
{
    if(item.Get('instance'))
    {
        item.Get('onError') && item.Get('onError')('HTTP Server already started.');
        return;
    }

    this.methods.types = {
        JSON: { contentType: 'application/json', formatter: content => JSON.stringify(content) },
        HTML: { contentType: 'text/html', formatter: content => String(content) },
        CSS:  { contentType: 'text/css', formatter: content => String(content) },
        JS:   { contentType: 'application/javascript', formatter: content => String(content) }
    };

    this.methods.createHttpObject = async (request, response) =>
    {
        return {
            id: onetype.GenerateUID(),
            request,
            response,
            error: null,
            streaming: false,
            prevent: false,
            data: await serversHTTP.Fn('extract.data', request),
            url: new URL(request.url, `http://${request.headers.host}`),
            user: this.methods.user(request),
            time: performance.now(),
            types: this.methods.types,
            state: {},
            respond: {
                type: 'JSON',
                data: null,
                message: 'Request processed',
                code: 200
            }
        };
    };

    this.methods.user = (request) => 
    {
        const forwarded = request.headers['x-forwarded-for'];
        const agent = request.headers['user-agent'] || null;
        const referrer = request.headers['referer'] || null;

        const ip = (typeof forwarded === 'string' && forwarded.length) ? forwarded.split(',')[0].trim() : request.socket?.remoteAddress;

        return {
            ip,
            agent,
            forwarded: forwarded || null,
            referrer
        };

    };

    this.methods.respond = (http, response) =>
    {
        if(http.prevent)
        {
            return;
        }

        const type = http.types[http.respond.type] || http.types.JSON;

        http.time = (performance.now() - http.time).toFixed(2);

        if(http.error)
        {
            const code = http.errorCode || 500;

            response.writeHead(code, { 'Content-Type': 'application/json' });
            response.end(JSON.stringify({
                data: http.errorContext || {},
                message: http.error,
                code: code,
                time: http.time
            }));

            return;
        }

        const content = http.respond.type === 'JSON' ? {
            data: http.respond.data || {},
            message: http.respond.message || 'No response message provided.',
            code: http.respond.code,
            time: http.time
        } : http.respond.data || '';

        response.writeHead(http.respond.code, { 'Content-Type': type.contentType });
        response.end(type.formatter(content));

        item.Get('onComplete') && item.Get('onComplete')(http);
    };

    this.methods.init = () => 
    {
        const httpServer = http.createServer(async (request, response) =>
        {
            const http = await this.methods.createHttpObject(request, response);

            await onetype.Middleware('servers.http.request', http);
            onetype.Emit('@servers.http.request', http);

            try
            {
                if(item.Get('onRequest'))
                {
                    await Promise.resolve(item.Get('onRequest')(http));
                }

                await onetype.Middleware('servers.http.respond', http);
                onetype.Emit('@servers.http.respond', http);

                this.methods.respond(http, response);
            }
            catch(error)
            {
                const code = typeof error.code === 'number' ? error.code : 500;

                http.error = error.message || 'Internal server error';
                http.errorCode = code;
                http.errorContext = error.context || {};

                await onetype.Middleware('servers.http.error', http);
                onetype.Emit('@servers.http.error', http);

                item.Get('onError') && item.Get('onError')(http.error);

                await onetype.Middleware('servers.http.respond', http);
                onetype.Emit('@servers.http.respond', http);

                this.methods.respond(http, response);
            }

            await onetype.Middleware('servers.http.complete', http);
            onetype.Emit('@servers.http.complete', http);
        });
        
        httpServer.on('error', (error) =>
        {
            item.Get('onError') && item.Get('onError')(error.message);
        });
        
        httpServer.listen(item.Get('port'), () =>
        {
            item.Set('instance', httpServer);
            onetype.Emit('@servers.http.start', httpServer);
            item.Get('onStart') && item.Get('onStart')(httpServer);
        });
    };

    this.methods.init();
});

// repositories/framework/addons/core/servers/back/http/load.js
import serversHTTP from '#servers/http/addon.js';

import '#servers/http/functions/extract/data.js';

import '#servers/http/item/catch/add.js';
import '#servers/http/item/functions/start.js';

export default serversHTTP;

// repositories/framework/addons/float/overlays/js/addon.js
const overlays = onetype.Addon('overlays', (addon) =>
{
	addon.Field('id', ['string']);
	addon.Field('render', ['function', null]);
	addon.Field('element', ['object', null]);
	addon.Field('target', ['object', null]);
	addon.Field('position', ['object', {x: 'center', y: 'center'}]);
	addon.Field('offset', ['object', {x: 0, y: 0}]);
	addon.Field('flip', ['boolean', true]);
	addon.Field('padding', ['number', 10]);
	addon.Field('index', ['number', 100000]);
	addon.Field('backdrop', ['number', null]);
	addon.Field('closeable', ['boolean', false]);
	addon.Field('escape', ['boolean', false]);
	addon.Field('track', ['boolean', false]);
	addon.Field('onOpen', ['function', null]);
	addon.Field('onClose', ['function', null]);
});

// repositories/framework/addons/float/overlays/js/events/click.js
document.addEventListener('click', (event) =>
{
	const items = Object.values(overlays.Items()).sort((a, b) => b.Get('index') - a.Get('index'));

	/* Collect all overlay content nodes that contain the click target.
	   Any overlay whose content contains the click should NOT be closed,
	   AND no overlay below it (by z-index) should close either if the
	   click happened inside a higher overlay. */

	const insideAny = items.some((item) =>
	{
		const element = item.Get('element');
		const content = element?.querySelector('.content');
		return content && content.contains(event.target);
	});

	for (const item of items)
	{
		if (!item.Get('closeable'))
		{
			continue;
		}

		const element = item.Get('element');

		if (!element)
		{
			continue;
		}

		/* Race-condition guard: if this overlay was opened on this exact click tick,
		   skip closing it. The opener stamps `__justOpened` on the element. */
		if (element.__justOpened)
		{
			element.__justOpened = false;
			continue;
		}

		const content = element.querySelector('.content');

		if (!content)
		{
			continue;
		}

		/* If click is inside this overlay's content, leave it (and everything below) alone */
		if (content.contains(event.target))
		{
			break;
		}

		/* If click is inside SOME other overlay (higher up), only close overlays
		   that are below the clicked one. We're iterating top-down, so if we got
		   here without breaking, this overlay is above the click target overlay
		   (or there is no overlay containing the click). Either way, close it. */
		item.Remove();

		/* Don't break - keep closing lower overlays unless click was inside one of them.
		   But if click WAS inside any overlay, we'd have broken already on that one. */
		if (insideAny)
		{
			break;
		}
	}
});

// repositories/framework/addons/float/overlays/js/events/escape.js
document.addEventListener('keydown', (event) =>
{
	if (event.key !== 'Escape')
	{
		return;
	}

	const items = Object.values(overlays.Items()).sort((a, b) => b.Get('index') - a.Get('index'));

	for (const item of items)
	{
		if (item.Get('escape'))
		{
			item.Remove();
			break;
		}
	}
});

// repositories/framework/addons/float/overlays/js/functions/flip.js
overlays.Fn('flip', function(target, overlay, position, offset, padding)
{
	position = position || {x: 'center', y: 'center'};
	padding = padding || 10;

	const width = overlay.offsetWidth;
	const height = overlay.offsetHeight;
	const viewport = {width: window.innerWidth, height: window.innerHeight};

	const pos = {x: position.x, y: position.y};
	let flipped = {x: false, y: false};

	let result = overlays.Fn('position', target, overlay, pos, offset, padding);

	if (pos.y === 'bottom' && (result.top + height) > (viewport.height - padding))
	{
		pos.y = 'top';
		flipped.y = true;
	}
	else if (pos.y === 'top' && result.top < padding)
	{
		pos.y = 'bottom';
		flipped.y = true;
	}

	if (pos.x === 'right' && (result.left + width) > (viewport.width - padding))
	{
		pos.x = 'left';
		flipped.x = true;
	}
	else if (pos.x === 'left' && result.left < padding)
	{
		pos.x = 'right';
		flipped.x = true;
	}

	if (flipped.x || flipped.y)
	{
		result = overlays.Fn('position', target, overlay, pos, offset, padding);
	}

	result.left = Math.max(padding, Math.min(result.left, viewport.width - width - padding));
	result.top = Math.max(padding, Math.min(result.top, viewport.height - height - padding));

	return {
		left: Math.round(result.left),
		top: Math.round(result.top),
		position: pos,
		flipped
	};
});

// repositories/framework/addons/float/overlays/js/functions/index.js
overlays.Fn('index', function()
{
	const items = overlays.Items();
	let max = 100000;

	for (const id in items)
	{
		const index = items[id].Get('index') || 100000;

		if (index > max)
		{
			max = index;
		}
	}

	return max + 1;
});

// repositories/framework/addons/float/overlays/js/functions/position.js
overlays.Fn('position', function(target, overlay, position, offset, padding)
{
	const body = !target || target === document.body;
	const rect = body
		? {left: 0, top: 0, right: window.innerWidth, bottom: window.innerHeight, width: window.innerWidth, height: window.innerHeight}
		: target.getBoundingClientRect();

	const width = overlay.offsetWidth;
	const height = overlay.offsetHeight;
	const pos = position || {x: 'center', y: 'center'};
	const pad = padding || 0;

	let left = rect.left;
	let top = rect.top;

	if (pos.x === 'left')
	{
		left = body ? pad : rect.left - width;
	}
	else if (pos.x === 'left-in')
	{
		left = rect.left + pad;
	}
	else if (pos.x === 'center')
	{
		left = rect.left + (rect.width / 2) - (width / 2);
	}
	else if (pos.x === 'right-in')
	{
		left = rect.right - width - pad;
	}
	else if (pos.x === 'right')
	{
		left = body ? rect.right - width - pad : rect.right;
	}

	if (pos.y === 'top')
	{
		top = body ? pad : rect.top - height;
	}
	else if (pos.y === 'top-in')
	{
		top = rect.top + pad;
	}
	else if (pos.y === 'center')
	{
		top = rect.top + (rect.height / 2) - (height / 2);
	}
	else if (pos.y === 'bottom-in')
	{
		top = rect.bottom - height - pad;
	}
	else if (pos.y === 'bottom')
	{
		top = body ? rect.bottom - height - pad : rect.bottom;
	}

	left += offset?.x || 0;
	top += offset?.y || 0;

	return {left: Math.round(left), top: Math.round(top)};
});

// repositories/framework/addons/float/overlays/js/functions/reposition.js
overlays.Fn('reposition', function(item)
{
	const element = item.Get('element');
	const content = element?.querySelector('.content');

	if (!content)
	{
		return;
	}

	const target = item.Get('target');
	const position = item.Get('position');
	const offset = item.Get('offset');
	const padding = item.Get('padding');

	let result;

	if (item.Get('flip'))
	{
		result = overlays.Fn('flip', target, content, position, offset, padding);
	}
	else
	{
		result = overlays.Fn('position', target, content, position, offset, padding);
	}

	content.style.left = result.left + 'px';
	content.style.top = result.top + 'px';
});

// repositories/framework/addons/float/overlays/js/item/catch/add.js
overlays.ItemOn('add', function(item)
{
	this.replace = () =>
	{
		const existing = overlays.ItemGet(item.Get('id'));

		if(existing && existing !== item)
		{
			existing.Remove();
		}
	};

	this.compile = () =>
	{
		const render = item.Get('render');

		if(!render)
		{
			return null;
		}

		const name = 'overlay:' + item.Get('id');

		overlays.RenderAdd(name, function()
		{
			return render.call(this, item);
		});

		return overlays.Render(name);
	};

	this.element = (rendered) =>
	{
		const element = document.createElement('div');

		element.className = 'ot-overlay';
		element.setAttribute('data-id', item.Get('id'));
		element.style.zIndex = overlays.Fn('index');
		element.__justOpened = true;

		const backdrop = item.Get('backdrop');

		if(backdrop)
		{
			const bg = document.createElement('div');
			bg.className = 'backdrop';
			bg.style.opacity = backdrop;
			element.appendChild(bg);
		}

		if(rendered?.Element)
		{
			rendered.Element.classList.add('content');
			element.appendChild(rendered.Element);
		}

		item.Set('element', element);
		item.Set('index', parseInt(element.style.zIndex));

		return element;
	};

	this.open = (element) =>
	{
		const callback = item.Get('onOpen');

		if(callback)
		{
			callback(item);
		}

		document.body.appendChild(element);
	};

	this.position = (element) =>
	{
		if(element.classList.contains('ot-modal'))
		{
			return;
		}

		const content = element.querySelector('.content');

		if(!content)
		{
			return;
		}

		const target = item.Get('target') || document.body;
		const position = item.Get('position');
		const offset = item.Get('offset');
		const padding = item.Get('padding');
		const method = item.Get('flip') ? 'flip' : 'position';
		const result = overlays.Fn(method, target, content, position, offset, padding);

		content.style.left = result.left + 'px';
		content.style.top = result.top + 'px';
	};

	this.track = () =>
	{
		if(!item.Get('track') || !item.Get('target'))
		{
			return;
		}

		const reposition = () => overlays.Fn('reposition', item);

		item.StoreSet('scroll', reposition);
		item.StoreSet('resize', reposition);

		window.addEventListener('scroll', reposition, true);
		window.addEventListener('resize', reposition);
	};

	this.replace();

	const rendered = this.compile();
	const element = this.element(rendered);

	this.open(element);
	this.position(element);
	this.track();

	/* Clear __justOpened flag after current event tick completes,
	   so the click that opened this overlay can't immediately close it,
	   but the very next click can. */
	requestAnimationFrame(() =>
	{
		element.__justOpened = false;
	});
});

// repositories/framework/addons/float/overlays/js/item/catch/remove.js
overlays.ItemOn('remove', (item) =>
{
	const scroll = item.StoreGet('scroll');
	const resize = item.StoreGet('resize');

	if (scroll)
	{
		window.removeEventListener('scroll', scroll, true);
	}

	if (resize)
	{
		window.removeEventListener('resize', resize);
	}

	const element = item.Get('element');

	if (element)
	{
		element.remove();
	}

	const onClose = item.Get('onClose');

	if (typeof onClose === 'function')
	{
		onClose(item);
	}
});

// repositories/framework/addons/float/popup/js/addon.js
const popup = onetype.Addon('popup');

onetype.$ot.popup = function(target, render, options)
{
	return popup.Fn('popup', target, render, options);
};

onetype.$ot.popup.open = onetype.$ot.popup;
onetype.$ot.popup.close = (id) => popup.Fn('close', id);

onetype.$ot.modal = function(render, options)
{
	return popup.Fn('modal', render, options);
};

onetype.$ot.modal.open = onetype.$ot.modal;
onetype.$ot.modal.close = (id) => popup.Fn('close', id);

onetype.$ot.tooltip = function(target, text, options)
{
	return popup.Fn('tooltip', target, text, options);
};

onetype.$ot.tooltip.open = onetype.$ot.tooltip;
onetype.$ot.tooltip.close = (id) => popup.Fn('close', id);

onetype.$ot.toast = function(message, options)
{
	return popup.Fn('toast', message, options);
};

onetype.$ot.toast.open = onetype.$ot.toast;
onetype.$ot.toast.close = (id) => popup.Fn('close', id);

onetype.$ot.confirm = function(title, description, options)
{
	return popup.Fn('confirm', title, description, options);
};

// repositories/framework/addons/float/popup/js/functions/close.js
popup.Fn('close', function(id)
{
	if(id)
	{
		const item = overlays.ItemGet(id);

		if(item)
		{
			item.Remove();
			return true;
		}

		return false;
	}

	const items = Object.values(overlays.Items()).sort((a, b) => b.Get('index') - a.Get('index'));

	if(items.length)
	{
		items[0].Remove();
		return true;
	}

	return false;
});

// repositories/framework/addons/float/popup/js/functions/confirm.js
popup.Fn('confirm', function(title, description, options = {})
{
	if(typeof description === 'object')
	{
		options = description;
		description = '';
	}

	return new Promise((resolve) =>
	{
		const id = 'confirm-' + onetype.GenerateUID();
		let resolved = false;
		let overlay = null;

		const finish = (value) =>
		{
			if(resolved)
			{
				return;
			}

			resolved = true;
			resolve(value);

			if(overlay && overlays.ItemGet(overlay.Get('id')))
			{
				overlay.Remove();
			}
		};

		overlay = overlays.Item({
			id: options.id || id,
			position: { x: 'center', y: 'center' },
			backdrop: options.backdrop ?? 0.4,
			closeable: true,
			escape: true,
			onOpen: (item) =>
			{
				const element = item.Get('element');

				if(element)
				{
					element.classList.add('ot-modal');
				}
			},
			onClose: () => finish(options.input ? null : false),
			render: function()
			{
				const initialValue = options.value || '';
				const hasInput = !!options.input;

				/* Internal value holder - NOT bound to render data,
				   so input doesn't re-render and lose focus on every keystroke */
				let currentValue = initialValue;

				this.title = title || '';
				this.description = description || '';
				this.hasInput = hasInput;
				this.hasIcon = !!options.icon;
				this.icon = options.icon || '';
				this.type = options.type || 'default';
				this.confirmText = options.confirm || (hasInput ? 'Continue' : 'Confirm');
				this.cancelText = options.cancel || 'Cancel';
				this.confirmColor = (this.type === 'danger') ? 'red' : 'brand';
				this.placeholder = options.placeholder || '';
				this.initialValue = initialValue;

				this.input = ({ value }) =>
				{
					currentValue = value;
				};

				this.cancel = () =>
				{
					finish(hasInput ? null : false);
				};

				this.submit = () =>
				{
					finish(hasInput ? currentValue : true);
				};

				this.keydown = ({ event }) =>
				{
					if(event.key === 'Enter' && hasInput && currentValue)
					{
						event.preventDefault();
						this.submit();
					}
				};

				return /* html */ `
					<div class="ot-confirm">
						<div ot-if="hasIcon" :class="'icon ' + type"><i>{{ icon }}</i></div>
						<h3 class="title">{{ title }}</h3>
						<p ot-if="description" class="description">{{ description }}</p>
						<div ot-if="hasInput" class="input">
							<e-form-input
								:value="initialValue"
								:placeholder="placeholder"
								:_input="input"
								background="bg-2"
								ot-keydown="keydown"
							></e-form-input>
						</div>
						<div class="actions">
							<e-form-button :text="cancelText" background="bg-2" :_click="cancel"></e-form-button>
							<e-form-button :text="confirmText" :color="confirmColor" :_click="submit"></e-form-button>
						</div>
					</div>
				`;
			}
		});
	});
});

// repositories/framework/addons/float/popup/js/functions/modal.js
popup.Fn('modal', function(render, options = {})
{
	const id = 'modal-' + onetype.GenerateUID();

	return overlays.Item({
		id: options.id || id,
		position: {x: 'center', y: 'center'},
		backdrop: options.backdrop ?? 0.5,
		closeable: options.closeable !== false,
		escape: options.escape !== false,
		onClose: options.onClose,
		onOpen: (item) =>
		{
			const element = item.Get('element');

			if(element)
			{
				element.classList.add('ot-modal');
			}

			if(options.onOpen)
			{
				options.onOpen(item);
			}
		},
		render: typeof render === 'function' ? render : function()
		{
			return render;
		}
	});
});

// repositories/framework/addons/float/popup/js/functions/popup.js
popup.Fn('popup', function(target, render, options = {})
{
	const id = 'popup-' + onetype.GenerateUID();

	return overlays.Item({
		id: options.id || id,
		target,
		position: options.position || {x: 'center', y: 'bottom'},
		offset: options.offset || {x: 0, y: 4},
		flip: true,
		track: true,
		closeable: true,
		escape: true,
		onClose: options.onClose,
		onOpen: options.onOpen,
		render: typeof render === 'function' ? render : function()
		{
			return render;
		}
	});
});

// repositories/framework/addons/float/popup/js/functions/toast.js
popup.Fn('toast', function(message, options = {})
{
	const config = typeof message === 'object' ? message : { message, type: 'success' };
	const id = 'toast-' + onetype.GenerateUID();
	const duration = config.duration ?? options.duration ?? 5000;

	const overlay = overlays.Item({
		id: options.id || id,
		position: options.position || {x: 'right-in', y: 'top-in'},
		padding: options.padding || 16,
		flip: false,
		escape: true,
		onClose: options.onClose,
		onOpen: options.onOpen,
		render: function()
		{
			const icons = {info: 'info', success: 'check_circle', warning: 'warning', error: 'error'};

			this.type = config.type || 'info';
			this.title = config.title || null;
			this.message = config.message || '';
			this.icon = config.icon || icons[this.type] || 'info';
			this.closeable = config.closeable !== false;

			this.close = () =>
			{
				overlay.Remove();
			};

			return `
				<div :class="'ot-toast ' + type">
					<i class="icon">{{ icon }}</i>
					<div class="content">
						<div ot-if="title" class="title">{{ title }}</div>
						<div ot-if="message" class="message">{{ message }}</div>
					</div>
					<button ot-if="closeable" class="close" ot-click="close"><i>close</i></button>
				</div>
			`;
		}
	});

	if (duration > 0)
	{
		setTimeout(() =>
		{
			if (overlays.ItemGet(overlay.Get('id')))
			{
				overlay.Remove();
			}
		}, duration);
	}

	return overlay;
});

// repositories/framework/addons/float/popup/js/functions/tooltip.js
popup.Fn('tooltip', function(target, text, options = {})
{
	const config = typeof text === 'object' ? text : {text};
	const id = 'tooltip-' + onetype.GenerateUID();

	return overlays.Item({
		id: options.id || id,
		target,
		position: options.position || {x: 'center', y: 'top'},
		offset: options.offset || {x: 0, y: -4},
		flip: true,
		track: true,
		escape: true,
		onClose: options.onClose,
		onOpen: options.onOpen,
		render: function()
		{
			this.icon = config.icon || null;
			this.title = config.title || null;
			this.text = config.text || '';
			this.variant = config.variant || 'default';

			return `
				<div :class="'ot-tooltip ' + variant">
					<i ot-if="icon" class="icon">{{ icon }}</i>
					<div class="content">
						<div ot-if="title" class="title">{{ title }}</div>
						<div ot-if="text" class="text">{{ text }}</div>
					</div>
				</div>
			`;
		}
	});
});

// repositories/framework/addons/float/popup/js/items/directives/popup.js
onetype.AddonReady('directives', function(directives)
{
	document.addEventListener('click', function(event)
	{
		let node = event.target;

		while (node && node !== document)
		{
			if ('otPopupConfig' in node)
			{
				if (node.otPopupOverlay)
				{
					node.otPopupOverlay.Remove();
					node.otPopupOverlay = null;
				}
				else
				{
					const config = Object.assign({}, node.otPopupConfig);
					const userOnClose = config.onClose;

					config.onClose = (item) =>
					{
						node.otPopupOverlay = null;

						if (typeof userOnClose === 'function')
						{
							userOnClose(item);
						}
					};

					node.otPopupOverlay = popup.Fn('popup', node, config.render, config);
				}

				break;
			}

			node = node.parentNode;
		}
	});

	directives.ItemAdd({
		id: 'ot-popup',
		icon: 'smart_popup',
		name: 'Popup',
		description: 'Shows popup on click.',
		trigger: 'node',
		order: 600,
		attributes: {
			'ot-popup': ['string|object|function']
		},
		code: function(data, item, compile, node)
		{
			const value = data['ot-popup'].value;

			if (typeof value === 'string')
			{
				node.otPopupConfig = {render: value};
			}
			else if (typeof value === 'function')
			{
				node.otPopupConfig = {render: value};
			}
			else if (typeof value === 'object')
			{
				node.otPopupConfig = value;
			}

			node.otPopupOverlay = null;
		}
	});
});

// repositories/framework/addons/float/popup/js/items/directives/tooltip.js
onetype.AddonReady('directives', function(directives)
{
	document.addEventListener('mouseover', function(event)
	{
		let node = event.target;

		while (node && node !== document)
		{
			if ('otTooltipConfig' in node && !node.otTooltipShow)
			{
				node.otTooltipShow = true;

				const config = node.otTooltipConfig;
				const options = {};

				if (config.position) options.position = config.position;
				if (config.offset)   options.offset   = config.offset;

				const content = {
					text:    config.text    || '',
					title:   config.title   || null,
					icon:    config.icon    || null,
					variant: config.variant || 'default'
				};

				node.otTooltipOverlay = popup.Fn('tooltip', node, content, options);
				break;
			}

			node = node.parentNode;
		}
	});

	document.addEventListener('mouseout', function(event)
	{
		let node = event.target;

		while (node && node !== document)
		{
			if ('otTooltipConfig' in node && node.otTooltipShow)
			{
				if (!node.contains(event.relatedTarget))
				{
					node.otTooltipShow = false;

					if (node.otTooltipOverlay)
					{
						node.otTooltipOverlay.Remove();
						node.otTooltipOverlay = null;
					}
				}

				break;
			}

			node = node.parentNode;
		}
	});

	directives.ItemAdd({
		id: 'ot-tooltip',
		icon: 'info',
		name: 'Tooltip',
		description: 'Shows tooltip on hover.',
		trigger: 'node',
		order: 600,
		attributes: {
			'ot-tooltip': ['string|object']
		},
		code: function(data, item, compile, node)
		{
			const value = data['ot-tooltip'].value;

			if (typeof value === 'string')
			{
				node.otTooltipConfig = {text: value};
			}
			else if (typeof value === 'object')
			{
				node.otTooltipConfig = value;
			}

			node.otTooltipShow = false;
		}
	});
});

// repositories/framework/addons/modules/actions/front/addon.js
import onetype from '#onetype';

const actions = onetype.Addon('actions', (addon) =>
{
    addon.Field('id', ['string']);
    addon.Field('name', ['string', null]);
    addon.Field('group', ['string', null]);
    addon.Field('description', ['string', null]);
    addon.Field('command', ['string|object', null]);
    addon.Field('endpoint', ['string', null]);
    addon.Field('method', ['string', 'POST']);
    addon.Field('headers', ['object', null]);
    addon.Field('body', ['object|string', null]);
    addon.Field('in', ['object', null]);
    addon.Field('out', ['object', null]);
    addon.Field('callback', ['function', null]);
});

export default actions;

// repositories/framework/addons/modules/actions/front/functions/run.js
import onetype from '#onetype';
import actions from '../addon.js';

actions.Fn('run', function(id, props = {})
{
    this.resolve = null;
    this.item = null;
    this.vars = onetype.AddonGet('variables');
    this.commands = onetype.AddonGet('commands');

    this.methods.init = async (resolve) =>
    {
        this.resolve = resolve;
        this.item = this.ItemGet(id);

        if(!this.item)
        {
            return this.methods.done(null, `Action "${id}" not found.`, 404);
        }

        props = this.methods.validate(props, 'in');

        if(props === false)
        {
            return;
        }

        const callback = this.item.Get('callback');
        const command = this.item.Get('command');

        if(callback)
        {
            await this.methods.callback(callback, props);
        }
        else if(command)
        {
            await this.methods.command(command, props);
        }
        else
        {
            await this.methods.http(props);
        }
    };

    this.methods.validate = (data, type) =>
    {
        const schema = this.item.Get(type);

        if(!schema)
        {
            return data;
        }

        try
        {
            return onetype.DataDefine(data, schema);
        }
        catch(error)
        {
            this.methods.done(null, error.message, type === 'in' ? 400 : 500);
            return false;
        }
    };

    this.methods.callback = async (fn, props) =>
    {
        try
        {
            const data = await fn(props);
            return this.methods.done(data, 'Action executed.', 200);
        }
        catch(error)
        {
            return this.methods.done(null, error.message, 500);
        }
    };

    this.methods.command = async (command, props) =>
    {
        if(!this.commands)
        {
            return this.methods.done(null, 'Commands addon not available.', 500);
        }

        const commandId = typeof command === 'string' ? command : command.id;
        const merged = typeof command === 'object' ? { ...command, ...props, id: undefined } : props;

        const item = this.commands.ItemGet(commandId);

        if(!item)
        {
            return this.methods.done(null, `Command "${commandId}" not found.`, 404);
        }

        try
        {
            const result = await item.Fn('run', merged);
            return this.methods.done(result.data, result.message, result.code);
        }
        catch(error)
        {
            return this.methods.done(null, error.message, 500);
        }
    };

    this.methods.http = async (props) =>
    {
        const endpoint = this.item.Get('endpoint');

        if(!endpoint)
        {
            return this.methods.done(null, `Action "${id}" has no callback, command, or endpoint.`, 400);
        }

        try
        {
            const url = this.methods.url(endpoint);
            const opts = this.methods.options(props);
            const res = await fetch(url, opts);

            if(!res.ok)
            {
                return this.methods.done(null, `HTTP ${res.status}: ${res.statusText}`, res.status);
            }

            const data = await res.json();
            return this.methods.done(data, 'Action executed.', 200);
        }
        catch(error)
        {
            return this.methods.done(null, error.message, 500);
        }
    };

    this.methods.url = (endpoint) =>
    {
        return this.methods.process(endpoint);
    };

    this.methods.options = (props) =>
    {
        const method = this.item.Get('method') || 'POST';
        const opts = { method };

        opts.headers = this.methods.headers();
        opts.body = this.methods.body(props);
        opts.headers['Content-Type'] = opts.headers['Content-Type'] || 'application/json';

        return opts;
    };

    this.methods.headers = () =>
    {
        const headers = this.item.Get('headers');
        const result = {};

        if(headers)
        {
            for(const key in headers)
            {
                result[key] = this.methods.process(headers[key]);
            }
        }

        return result;
    };

    this.methods.body = (props) =>
    {
        let body = this.item.Get('body') || props;

        if(typeof body === 'string')
        {
            body = this.methods.process(body);
        }
        else if(typeof body === 'object')
        {
            body = { ...body, ...props };

            for(const key in body)
            {
                if(typeof body[key] === 'string')
                {
                    body[key] = this.methods.process(body[key]);
                }
            }
        }

        return JSON.stringify(body);
    };

    this.methods.process = (value) =>
    {
        if(this.vars && typeof value === 'string')
        {
            return this.vars.Fn('process', value);
        }

        return value;
    };

    this.methods.done = (data, message, code) =>
    {
        if(code >= 200 && code < 300)
        {
            const validated = this.methods.validate(data, 'out');

            if(validated === false)
            {
                return;
            }

            data = validated;
        }

        this.resolve({ data, message, code });
    };

    return new Promise((resolve) =>
    {
        try
        {
            this.methods.init(resolve);
        }
        catch(error)
        {
            resolve({ data: null, message: error.message, code: 500 });
        }
    });
});

// repositories/framework/addons/modules/actions/front/load.js
import actions from './addon.js';

import './functions/run.js';

export default actions;

// repositories/framework/addons/modules/bugs/front/addon.js
import onetype from '#onetype';

const bugs = onetype.Addon('bugs', (addon) =>
{
    addon.Field('id', ['string|number']);
    addon.Field('message', ['string']);
    addon.Field('type', ['string', 'error']);
    addon.Field('source', ['string', null]);
    addon.Field('code', ['number', null]);
    addon.Field('data', ['object', null]);
    addon.Field('time', ['object', null]);
    addon.Field('notify', ['string', 'console']);
    addon.Field('resolved', ['boolean', false]);
});

export default bugs;

// repositories/framework/addons/modules/bugs/front/functions/clear.js
import bugs from '../addon.js';

bugs.Fn('clear', function(source = null)
{
    const items = Object.values(this.Items());

    for(const item of items)
    {
        if(source === null || item.Get('source') === source)
        {
            this.ItemRemove(item.Get('id'));
        }
    }
});

// repositories/framework/addons/modules/bugs/front/functions/push.js
import bugs from '../addon.js';

bugs.Fn('push', function(source, message, code = null, data = null, type = 'error', notify = 'console')
{
    const item = this.Item({
        source,
        message,
        code,
        data,
        type,
        notify
    });

    return item;
});

// repositories/framework/addons/modules/bugs/front/functions/resolve.js
import bugs from '../addon.js';

bugs.Fn('resolve', function(id)
{
    const item = this.ItemGet(id);

    if(item)
    {
        item.Set('resolved', true);
    }

    return item;
});

// repositories/framework/addons/modules/bugs/front/item/catch/add.js
import bugs from '../../addon.js';

bugs.ItemOn('add', function(item)
{
    if(!item.Get('time'))
    {
        item.Set('time', new Date());
    }
});

// repositories/framework/addons/modules/bugs/front/load.js
import bugs from './addon.js';

import './item/catch/add.js';

import './functions/push.js';
import './functions/clear.js';
import './functions/resolve.js';

export default bugs;

// repositories/framework/addons/modules/events/front/addon.js
import onetype from '#onetype';

const events = onetype.Addon('events', (addon) =>
{
    addon.Field('id', ['string']);
    addon.Field('group', ['string', null]);
    addon.Field('description', ['string', null]);
    addon.Field('callbacks', ['array', []]);
});

export default events;

// repositories/framework/addons/modules/events/front/functions/clear.js
import events from '../addon.js';

events.Fn('clear', function()
{
    for(const id in this.items.data)
    {
        this.ItemRemove(id);
    }
});

// repositories/framework/addons/modules/events/front/functions/emit.js
import events from '../addon.js';

events.Fn('emit', async function(id, data = {})
{
    const item = this.ItemGet(id);

    if(!item)
    {
        return [];
    }

    const callbacks = item.Get('callbacks');
    const results = [];
    const remove = [];

    const event = {
        id,
        data,
        stopped: false,
        stop: function() { this.stopped = true; }
    };

    for(let i = 0; i < callbacks.length; i++)
    {
        if(event.stopped)
        {
            break;
        }

        const entry = callbacks[i];

        if(entry.condition && !entry.condition(data, event))
        {
            continue;
        }

        try
        {
            const result = await entry.callback(data, event);
            results.push(result);
        }
        catch(error)
        {
            results.push({ error: error.message });
        }

        if(entry.once)
        {
            remove.push(entry.id);
        }
    }

    if(remove.length > 0)
    {
        const cbs = item.Get('callbacks').filter(cb => !remove.includes(cb.id));
        item.Set('callbacks', cbs);
    }

    return results;
});

// repositories/framework/addons/modules/events/front/functions/off.js
import events from '../addon.js';

events.Fn('off', function(id, callback = null)
{
    const item = this.ItemGet(id);

    if(!item)
    {
        return false;
    }

    if(callback === null)
    {
        item.Set('callbacks', []);
        return true;
    }

    const callbacks = item.Get('callbacks');
    const filtered = callbacks.filter(cb => cb.callback !== callback);
    item.Set('callbacks', filtered);

    return true;
});

// repositories/framework/addons/modules/events/front/functions/on.js
import events from '../addon.js';

events.Fn('on', function(id, callback, opts = {})
{
    const priority = opts.priority || 0;
    const condition = opts.condition || null;
    const once = opts.once || false;

    let item = this.ItemGet(id);

    if(!item)
    {
        item = this.Item({ id });
    }

    const entry = {
        id: Date.now() + Math.random(),
        callback,
        condition,
        once,
        priority
    };

    const callbacks = item.Get('callbacks');
    callbacks.push(entry);
    callbacks.sort((a, b) => b.priority - a.priority);
    item.Set('callbacks', callbacks);

    return () =>
    {
        const cbs = item.Get('callbacks');
        const index = cbs.findIndex(cb => cb.id === entry.id);

        if(index !== -1)
        {
            cbs.splice(index, 1);
            item.Set('callbacks', cbs);
        }
    };
});

// repositories/framework/addons/modules/events/front/functions/once.js
import events from '../addon.js';

events.Fn('once', function(id, callback, opts = {})
{
    return this.Fn('on', id, callback, { ...opts, once: true });
});

// repositories/framework/addons/modules/events/front/load.js
import events from './addon.js';

import './functions/on.js';
import './functions/once.js';
import './functions/emit.js';
import './functions/off.js';
import './functions/clear.js';

export default events;

// repositories/framework/addons/modules/schedules/back/addon.js
import onetype from '#onetype';

const schedules = onetype.Addon('schedules', (addon) =>
{
    addon.Table('schedules');

    addon.Field('id', ['string']);
    addon.Field('name', ['string', null]);
    addon.Field('group', ['string', null]);
    addon.Field('description', ['string', null]);
    addon.Field('type', ['string', 'once']);
    addon.Field('time', ['string|number']);
    addon.Field('command', ['string|object', null]);
    addon.Field('callback', ['function', null]);
    addon.Field('enabled', ['boolean', true]);
    addon.Field('repeat', ['number', null]);
    addon.Field('timezone', ['string', null]);
    addon.Field('runs', ['number', 0]);
    addon.Field('timer', ['any', null]);
    addon.Field('last', ['string', null]);
    addon.Field('next', ['string', null]);
    addon.Field('created_at', ['string', null]);
    addon.Field('updated_at', ['string', null]);
});

export default schedules;

// repositories/framework/addons/modules/schedules/back/functions/disable.js
import schedules from '../addon.js';

schedules.Fn('disable', function(id)
{
    const item = this.ItemGet(id);

    if(!item)
    {
        return false;
    }

    item.Set('enabled', false);

    this.Fn('stop', id);

    return true;
});

// repositories/framework/addons/modules/schedules/back/functions/enable.js
import schedules from '../addon.js';

schedules.Fn('enable', function(id)
{
    const item = this.ItemGet(id);

    if(!item)
    {
        return false;
    }

    item.Set('enabled', true);

    this.Fn('start', id);

    return true;
});

// repositories/framework/addons/modules/schedules/back/functions/next.js
import schedules from '../addon.js';

schedules.Fn('next', function(id)
{
    this.item = this.ItemGet(id);

    if(!this.item)
    {
        return null;
    }

    this.type = this.item.Get('type');
    this.time = this.item.Get('time');
    this.timezone = this.item.Get('timezone');

    this.methods.once = () =>
    {
        const target = typeof this.time === 'string' ? new Date(this.time).getTime() : this.time;

        if(target <= Date.now())
        {
            return null;
        }

        return target;
    };

    this.methods.interval = () =>
    {
        const ms = typeof this.time === 'string' ? parseInt(this.time, 10) : this.time;
        return Date.now() + ms;
    };

    this.methods.cron = () =>
    {
        const parsed = this.methods.parse(this.time);

        if(!parsed)
        {
            return null;
        }

        return this.methods.calculate(parsed);
    };

    this.methods.parse = (expr) =>
    {
        const parts = expr.trim().split(/\s+/);

        if(parts.length !== 5)
        {
            return null;
        }

        return {
            minute: parts[0],
            hour: parts[1],
            day: parts[2],
            month: parts[3],
            weekday: parts[4]
        };
    };

    this.methods.calculate = (cron) =>
    {
        const now = new Date();

        for(let i = 1; i < 525600; i++)
        {
            const next = new Date(now.getTime() + i * 60000);

            if(this.methods.matches(next, cron))
            {
                return next.getTime();
            }
        }

        return null;
    };

    this.methods.matches = (date, cron) =>
    {
        const minute = date.getMinutes();
        const hour = date.getHours();
        const day = date.getDate();
        const month = date.getMonth() + 1;
        const weekday = date.getDay();

        return (
            this.methods.field(cron.minute, minute, 0, 59) &&
            this.methods.field(cron.hour, hour, 0, 23) &&
            this.methods.field(cron.day, day, 1, 31) &&
            this.methods.field(cron.month, month, 1, 12) &&
            this.methods.field(cron.weekday, weekday, 0, 6)
        );
    };

    this.methods.field = (expr, value, min, max) =>
    {
        if(expr === '*')
        {
            return true;
        }

        if(expr.includes(','))
        {
            const values = expr.split(',').map(v => parseInt(v, 10));
            return values.includes(value);
        }

        if(expr.includes('-'))
        {
            const [start, end] = expr.split('-').map(v => parseInt(v, 10));
            return value >= start && value <= end;
        }

        if(expr.includes('/'))
        {
            const [base, step] = expr.split('/');
            const stepNum = parseInt(step, 10);
            const startNum = base === '*' ? min : parseInt(base, 10);
            return (value - startNum) % stepNum === 0 && value >= startNum;
        }

        return parseInt(expr, 10) === value;
    };

    if(this.type === 'once')
    {
        return this.methods.once();
    }
    else if(this.type === 'interval')
    {
        return this.methods.interval();
    }
    else if(this.type === 'cron')
    {
        return this.methods.cron();
    }

    return null;
});

// repositories/framework/addons/modules/schedules/back/functions/start.js
import schedules from '../addon.js';

schedules.Fn('start', function(id)
{
    this.methods.single = (id) =>
    {
        const item = this.ItemGet(id);

        if(!item || !item.Get('enabled'))
        {
            return false;
        }

        if(item.Get('timer'))
        {
            return false;
        }

        const type = item.Get('type');

        if(type === 'once')
        {
            this.methods.once(item);
        }
        else if(type === 'interval')
        {
            this.methods.interval(item);
        }
        else if(type === 'cron')
        {
            this.methods.cron(item);
        }

        return true;
    };

    this.methods.once = (item) =>
    {
        const time = item.Get('time');
        const target = typeof time === 'string' ? new Date(time).getTime() : time;
        const delay = target - Date.now();

        if(delay <= 0)
        {
            this.Fn('trigger', item.Get('id'));
            return;
        }

        const next = new Date(target).toISOString();
        item.Set('next', next);

        const timer = setTimeout(() =>
        {
            this.Fn('trigger', item.Get('id'));
            item.Set('timer', null);
            item.Set('next', null);
        }, delay);

        item.Set('timer', timer);
    };

    this.methods.interval = (item) =>
    {
        const time = item.Get('time');
        const ms = typeof time === 'string' ? parseInt(time, 10) : time;

        const next = new Date(Date.now() + ms).toISOString();
        item.Set('next', next);

        const timer = setInterval(() =>
        {
            if(!item.Get('enabled'))
            {
                clearInterval(item.Get('timer'));
                item.Set('timer', null);
                item.Set('next', null);
                return;
            }

            this.Fn('trigger', item.Get('id'));

            const nextRun = new Date(Date.now() + ms).toISOString();
            item.Set('next', nextRun);
        }, ms);

        item.Set('timer', timer);
    };

    this.methods.cron = (item) =>
    {
        const next = this.Fn('next', item.Get('id'));

        if(!next)
        {
            return;
        }

        const delay = next - Date.now();
        item.Set('next', new Date(next).toISOString());

        const timer = setTimeout(() =>
        {
            this.Fn('trigger', item.Get('id'));
            item.Set('timer', null);

            if(item.Get('enabled'))
            {
                this.methods.cron(item);
            }
        }, delay);

        item.Set('timer', timer);
    };

    if(id)
    {
        return this.methods.single(id);
    }

    for(const itemId in this.items.data)
    {
        this.methods.single(itemId);
    }

    return true;
});

// repositories/framework/addons/modules/schedules/back/functions/stop.js
import schedules from '../addon.js';

schedules.Fn('stop', function(id)
{
    this.methods.single = (id) =>
    {
        const item = this.ItemGet(id);

        if(!item)
        {
            return false;
        }

        const timer = item.Get('timer');

        if(timer)
        {
            clearTimeout(timer);
            clearInterval(timer);
            item.Set('timer', null);
            item.Set('next', null);
        }

        return true;
    };

    if(id)
    {
        return this.methods.single(id);
    }

    for(const itemId in this.items.data)
    {
        this.methods.single(itemId);
    }

    return true;
});

// repositories/framework/addons/modules/schedules/back/functions/trigger.js
import onetype from '#onetype';
import schedules from '../addon.js';

schedules.Fn('trigger', function(id, props = {})
{
    this.resolve = null;
    this.item = null;
    this.events = onetype.AddonGet('events');
    this.commands = onetype.AddonGet('commands');

    this.methods.init = async (resolve) =>
    {
        this.resolve = resolve;
        this.item = this.ItemGet(id);

        if(!this.item)
        {
            return this.methods.done(null, `Schedule "${id}" not found.`, 404);
        }

        if(!this.item.Get('enabled'))
        {
            return this.methods.done(null, `Schedule "${id}" is disabled.`, 403);
        }

        this.methods.emit();
        this.methods.track();

        const callback = this.item.Get('callback');
        const command = this.item.Get('command');

        if(callback)
        {
            await this.methods.callback(callback, props);
        }
        else if(command)
        {
            await this.methods.command(command, props);
        }
        else
        {
            return this.methods.done(null, `Schedule "${id}" has no callback or command.`, 400);
        }
    };

    this.methods.emit = () =>
    {
        if(this.events)
        {
            this.events.Fn('emit', 'schedule.triggered', {
                id,
                type: this.item.Get('type'),
                runs: this.item.Get('runs')
            });
        }
    };

    this.methods.track = () =>
    {
        const runs = this.item.Get('runs') || 0;
        const repeat = this.item.Get('repeat');
        const type = this.item.Get('type');

        this.item.Set('runs', runs + 1);
        this.item.Set('last', new Date().toISOString());

        if(type === 'once')
        {
            this.Fn('disable', id);
        }
        else if(repeat !== null && runs + 1 >= repeat)
        {
            this.Fn('disable', id);
        }
    };

    this.methods.callback = async (fn, props) =>
    {
        try
        {
            const data = await fn(props);
            return this.methods.done(data, 'Schedule executed.', 200);
        }
        catch(error)
        {
            return this.methods.done(null, error.message, 500);
        }
    };

    this.methods.command = async (command, props) =>
    {
        if(!this.commands)
        {
            return this.methods.done(null, 'Commands addon not available.', 500);
        }

        const commandId = typeof command === 'string' ? command : command.id;
        const merged = typeof command === 'object' ? { ...command, ...props, id: undefined } : props;

        const item = this.commands.ItemGet(commandId);

        if(!item)
        {
            return this.methods.done(null, `Command "${commandId}" not found.`, 404);
        }

        try
        {
            const result = await item.Fn('run', merged);
            return this.methods.done(result.data, result.message, result.code);
        }
        catch(error)
        {
            return this.methods.done(null, error.message, 500);
        }
    };

    this.methods.done = (data, message, code) =>
    {
        this.resolve({ data, message, code });
    };

    return new Promise((resolve) =>
    {
        try
        {
            this.methods.init(resolve);
        }
        catch(error)
        {
            resolve({ data: null, message: error.message, code: 500 });
        }
    });
});

// repositories/framework/addons/modules/schedules/back/items/create.js
import onetype from '#onetype';
import schedules from '../addon.js';

onetype.AddonReady('commands', (commands) =>
{
    commands.Item({
        id: 'schedules:create',
        method: 'POST',
        endpoint: '/api/schedules',
        exposed: true,
        description: 'Create a new schedule',
        in: {
            id: ['string', null],
            name: ['string', null],
            group: ['string', null],
            description: ['string', null],
            type: ['string', 'once'],
            time: ['string|number', null, true],
            command: ['string|object', null],
            enabled: ['boolean', true],
            repeat: ['number', null],
            timezone: ['string', null]
        },
        out: {
            schedule: ['object']
        },
        callback: async function(props)
        {
            const id = props.id || onetype.GenerateTID();

            if(schedules.ItemGet(id))
            {
                throw new Error(`Schedule "${id}" already exists.`);
            }

            const item = schedules.Item({
                id,
                name: props.name,
                group: props.group,
                description: props.description,
                type: props.type,
                time: props.time,
                command: props.command,
                enabled: props.enabled,
                repeat: props.repeat,
                timezone: props.timezone,
                runs: 0,
                last: null,
                next: null,
                created_at: new Date().toISOString(),
                updated_at: new Date().toISOString()
            });

            await item.Create();

            if(props.enabled)
            {
                schedules.Fn('start', id);
            }

            return {
                schedule: {
                    id: item.Get('id'),
                    name: item.Get('name'),
                    group: item.Get('group'),
                    description: item.Get('description'),
                    type: item.Get('type'),
                    time: item.Get('time'),
                    command: item.Get('command'),
                    enabled: item.Get('enabled'),
                    repeat: item.Get('repeat'),
                    timezone: item.Get('timezone'),
                    runs: item.Get('runs'),
                    last: item.Get('last'),
                    next: item.Get('next'),
                    created_at: item.Get('created_at'),
                    updated_at: item.Get('updated_at')
                }
            };
        }
    });
});

// repositories/framework/addons/modules/schedules/back/items/delete.js
import onetype from '#onetype';
import schedules from '../addon.js';

onetype.AddonReady('commands', (commands) =>
{
    commands.Item({
        id: 'schedules:delete',
        method: 'DELETE',
        endpoint: '/api/schedules/:id',
        exposed: true,
        description: 'Delete a schedule',
        in: {
            id: ['string', null, true]
        },
        out: {
            deleted: ['boolean']
        },
        callback: async function(props)
        {
            const item = schedules.ItemGet(props.id);

            if(!item)
            {
                throw new Error(`Schedule "${props.id}" not found.`);
            }

            schedules.Fn('stop', props.id);

            await item.Delete();

            schedules.ItemRemove(props.id);

            return {
                deleted: true
            };
        }
    });
});

// repositories/framework/addons/modules/schedules/back/items/many.js
import onetype from '#onetype';
import schedules from '../addon.js';

onetype.AddonReady('commands', (commands) =>
{
    commands.Item({
        id: 'schedules:many',
        method: 'GET',
        endpoint: '/api/schedules',
        exposed: true,
        description: 'Get all schedules',
        in: {
            page: ['number', 1],
            limit: ['number', 50],
            group: ['string', null],
            enabled: ['boolean', null]
        },
        out: {
            schedules: {
                type: 'array',
                each: {
                    type: 'object',
                    config: {
                        id: ['string'],
                        name: ['string'],
                        group: ['string'],
                        description: ['string'],
                        type: ['string'],
                        time: ['string|number'],
                        command: ['string|object'],
                        enabled: ['boolean'],
                        repeat: ['number'],
                        timezone: ['string'],
                        runs: ['number'],
                        last: ['string'],
                        next: ['string'],
                        created_at: ['string'],
                        updated_at: ['string']
                    }
                }
            },
            total: ['number']
        },
        callback: async function(props)
        {
            const result = [];

            for(const id in schedules.items.data)
            {
                const item = schedules.items.data[id];

                if(props.group && item.Get('group') !== props.group)
                {
                    continue;
                }

                if(props.enabled !== null && item.Get('enabled') !== props.enabled)
                {
                    continue;
                }

                result.push({
                    id: item.Get('id'),
                    name: item.Get('name'),
                    group: item.Get('group'),
                    description: item.Get('description'),
                    type: item.Get('type'),
                    time: item.Get('time'),
                    command: item.Get('command'),
                    enabled: item.Get('enabled'),
                    repeat: item.Get('repeat'),
                    timezone: item.Get('timezone'),
                    runs: item.Get('runs'),
                    last: item.Get('last'),
                    next: item.Get('next'),
                    created_at: item.Get('created_at'),
                    updated_at: item.Get('updated_at')
                });
            }

            const start = (props.page - 1) * props.limit;
            const end = start + props.limit;

            return {
                schedules: result.slice(start, end),
                total: result.length
            };
        }
    });
});

// repositories/framework/addons/modules/schedules/back/items/one.js
import onetype from '#onetype';
import schedules from '../addon.js';

onetype.AddonReady('commands', (commands) =>
{
    commands.Item({
        id: 'schedules:one',
        method: 'GET',
        endpoint: '/api/schedules/:id',
        exposed: true,
        description: 'Get a single schedule by ID',
        in: {
            id: ['string', null, true]
        },
        out: {
            schedule: ['object']
        },
        callback: async function(props)
        {
            const item = schedules.ItemGet(props.id);

            if(!item)
            {
                throw new Error(`Schedule "${props.id}" not found.`);
            }

            return {
                schedule: {
                    id: item.Get('id'),
                    name: item.Get('name'),
                    group: item.Get('group'),
                    description: item.Get('description'),
                    type: item.Get('type'),
                    time: item.Get('time'),
                    command: item.Get('command'),
                    enabled: item.Get('enabled'),
                    repeat: item.Get('repeat'),
                    timezone: item.Get('timezone'),
                    runs: item.Get('runs'),
                    last: item.Get('last'),
                    next: item.Get('next'),
                    created_at: item.Get('created_at'),
                    updated_at: item.Get('updated_at')
                }
            };
        }
    });
});

// repositories/framework/addons/modules/schedules/back/items/update.js
import onetype from '#onetype';
import schedules from '../addon.js';

onetype.AddonReady('commands', (commands) =>
{
    commands.Item({
        id: 'schedules:update',
        method: 'PUT',
        endpoint: '/api/schedules/:id',
        exposed: true,
        description: 'Update a schedule',
        in: {
            id: ['string', null, true],
            name: ['string', null],
            group: ['string', null],
            description: ['string', null],
            type: ['string', null],
            time: ['string|number', null],
            command: ['string|object', null],
            enabled: ['boolean', null],
            repeat: ['number', null],
            timezone: ['string', null]
        },
        out: {
            schedule: ['object']
        },
        callback: async function(props)
        {
            const item = schedules.ItemGet(props.id);

            if(!item)
            {
                throw new Error(`Schedule "${props.id}" not found.`);
            }

            const wasEnabled = item.Get('enabled');

            schedules.Fn('stop', props.id);

            if(props.name !== null && props.name !== undefined)
            {
                item.Set('name', props.name);
            }

            if(props.group !== null && props.group !== undefined)
            {
                item.Set('group', props.group);
            }

            if(props.description !== null && props.description !== undefined)
            {
                item.Set('description', props.description);
            }

            if(props.type !== null && props.type !== undefined)
            {
                item.Set('type', props.type);
            }

            if(props.time !== null && props.time !== undefined)
            {
                item.Set('time', props.time);
            }

            if(props.command !== null && props.command !== undefined)
            {
                item.Set('command', props.command);
            }

            if(props.enabled !== null && props.enabled !== undefined)
            {
                item.Set('enabled', props.enabled);
            }

            if(props.repeat !== null && props.repeat !== undefined)
            {
                item.Set('repeat', props.repeat);
            }

            if(props.timezone !== null && props.timezone !== undefined)
            {
                item.Set('timezone', props.timezone);
            }

            item.Set('updated_at', new Date().toISOString());

            await item.Update();

            if(item.Get('enabled'))
            {
                schedules.Fn('start', props.id);
            }

            return {
                schedule: {
                    id: item.Get('id'),
                    name: item.Get('name'),
                    group: item.Get('group'),
                    description: item.Get('description'),
                    type: item.Get('type'),
                    time: item.Get('time'),
                    command: item.Get('command'),
                    enabled: item.Get('enabled'),
                    repeat: item.Get('repeat'),
                    timezone: item.Get('timezone'),
                    runs: item.Get('runs'),
                    last: item.Get('last'),
                    next: item.Get('next'),
                    created_at: item.Get('created_at'),
                    updated_at: item.Get('updated_at')
                }
            };
        }
    });
});

// repositories/framework/addons/modules/schedules/back/load.js
import schedules from './addon.js';

import './functions/start.js';
import './functions/stop.js';
import './functions/trigger.js';
import './functions/enable.js';
import './functions/disable.js';
import './functions/next.js';

import './items/one.js';
import './items/many.js';
import './items/create.js';
import './items/update.js';
import './items/delete.js';

export default schedules;

// repositories/framework/addons/modules/schedules/front/addon.js
import onetype from '#onetype';

const schedules = onetype.Addon('schedules', (addon) =>
{
    addon.Field('id', ['string']);
    addon.Field('name', ['string', null]);
    addon.Field('group', ['string', null]);
    addon.Field('description', ['string', null]);
    addon.Field('type', ['string', 'once']);
    addon.Field('time', ['string|number']);
    addon.Field('action', ['string|object', null]);
    addon.Field('command', ['string|object', null]);
    addon.Field('callback', ['function', null]);
    addon.Field('enabled', ['boolean', true]);
    addon.Field('repeat', ['number', null]);
    addon.Field('timezone', ['string', null]);
    addon.Field('runs', ['number', 0]);
    addon.Field('timer', ['any', null]);
    addon.Field('last', ['string', null]);
    addon.Field('next', ['string', null]);
});

export default schedules;

// repositories/framework/addons/modules/schedules/front/functions/disable.js
import schedules from '../addon.js';

schedules.Fn('disable', function(id)
{
    const item = this.ItemGet(id);

    if(!item)
    {
        return false;
    }

    item.Set('enabled', false);

    this.Fn('stop', id);

    return true;
});

// repositories/framework/addons/modules/schedules/front/functions/enable.js
import schedules from '../addon.js';

schedules.Fn('enable', function(id)
{
    const item = this.ItemGet(id);

    if(!item)
    {
        return false;
    }

    item.Set('enabled', true);

    this.Fn('start', id);

    return true;
});

// repositories/framework/addons/modules/schedules/front/functions/next.js
import schedules from '../addon.js';

schedules.Fn('next', function(id)
{
    this.item = this.ItemGet(id);

    if(!this.item)
    {
        return null;
    }

    this.type = this.item.Get('type');
    this.time = this.item.Get('time');
    this.timezone = this.item.Get('timezone');

    this.methods.once = () =>
    {
        const target = typeof this.time === 'string' ? new Date(this.time).getTime() : this.time;

        if(target <= Date.now())
        {
            return null;
        }

        return target;
    };

    this.methods.interval = () =>
    {
        const ms = typeof this.time === 'string' ? parseInt(this.time, 10) : this.time;
        return Date.now() + ms;
    };

    this.methods.cron = () =>
    {
        const parsed = this.methods.parse(this.time);

        if(!parsed)
        {
            return null;
        }

        return this.methods.calculate(parsed);
    };

    this.methods.parse = (expr) =>
    {
        const parts = expr.trim().split(/\s+/);

        if(parts.length !== 5)
        {
            return null;
        }

        return {
            minute: parts[0],
            hour: parts[1],
            day: parts[2],
            month: parts[3],
            weekday: parts[4]
        };
    };

    this.methods.calculate = (cron) =>
    {
        const now = new Date();

        for(let i = 1; i < 525600; i++)
        {
            const next = new Date(now.getTime() + i * 60000);

            if(this.methods.matches(next, cron))
            {
                return next.getTime();
            }
        }

        return null;
    };

    this.methods.matches = (date, cron) =>
    {
        const minute = date.getMinutes();
        const hour = date.getHours();
        const day = date.getDate();
        const month = date.getMonth() + 1;
        const weekday = date.getDay();

        return (
            this.methods.field(cron.minute, minute, 0, 59) &&
            this.methods.field(cron.hour, hour, 0, 23) &&
            this.methods.field(cron.day, day, 1, 31) &&
            this.methods.field(cron.month, month, 1, 12) &&
            this.methods.field(cron.weekday, weekday, 0, 6)
        );
    };

    this.methods.field = (expr, value, min, max) =>
    {
        if(expr === '*')
        {
            return true;
        }

        if(expr.includes(','))
        {
            const values = expr.split(',').map(v => parseInt(v, 10));
            return values.includes(value);
        }

        if(expr.includes('-'))
        {
            const [start, end] = expr.split('-').map(v => parseInt(v, 10));
            return value >= start && value <= end;
        }

        if(expr.includes('/'))
        {
            const [base, step] = expr.split('/');
            const stepNum = parseInt(step, 10);
            const startNum = base === '*' ? min : parseInt(base, 10);
            return (value - startNum) % stepNum === 0 && value >= startNum;
        }

        return parseInt(expr, 10) === value;
    };

    if(this.type === 'once')
    {
        return this.methods.once();
    }
    else if(this.type === 'interval')
    {
        return this.methods.interval();
    }
    else if(this.type === 'cron')
    {
        return this.methods.cron();
    }

    return null;
});

// repositories/framework/addons/modules/schedules/front/functions/start.js
import schedules from '../addon.js';

schedules.Fn('start', function(id)
{
    this.methods.single = (id) =>
    {
        const item = this.ItemGet(id);

        if(!item || !item.Get('enabled'))
        {
            return false;
        }

        if(item.Get('timer'))
        {
            return false;
        }

        const type = item.Get('type');

        if(type === 'once')
        {
            this.methods.once(item);
        }
        else if(type === 'interval')
        {
            this.methods.interval(item);
        }
        else if(type === 'cron')
        {
            this.methods.cron(item);
        }

        return true;
    };

    this.methods.once = (item) =>
    {
        const time = item.Get('time');
        const target = typeof time === 'string' ? new Date(time).getTime() : time;
        const delay = target - Date.now();

        if(delay <= 0)
        {
            this.Fn('trigger', item.Get('id'));
            return;
        }

        const next = new Date(target).toISOString();
        item.Set('next', next);

        const timer = setTimeout(() =>
        {
            this.Fn('trigger', item.Get('id'));
            item.Set('timer', null);
            item.Set('next', null);
        }, delay);

        item.Set('timer', timer);
    };

    this.methods.interval = (item) =>
    {
        const time = item.Get('time');
        const ms = typeof time === 'string' ? parseInt(time, 10) : time;

        const next = new Date(Date.now() + ms).toISOString();
        item.Set('next', next);

        const timer = setInterval(() =>
        {
            if(!item.Get('enabled'))
            {
                clearInterval(item.Get('timer'));
                item.Set('timer', null);
                item.Set('next', null);
                return;
            }

            this.Fn('trigger', item.Get('id'));

            const nextRun = new Date(Date.now() + ms).toISOString();
            item.Set('next', nextRun);
        }, ms);

        item.Set('timer', timer);
    };

    this.methods.cron = (item) =>
    {
        const next = this.Fn('next', item.Get('id'));

        if(!next)
        {
            return;
        }

        const delay = next - Date.now();
        item.Set('next', new Date(next).toISOString());

        const timer = setTimeout(() =>
        {
            this.Fn('trigger', item.Get('id'));
            item.Set('timer', null);

            if(item.Get('enabled'))
            {
                this.methods.cron(item);
            }
        }, delay);

        item.Set('timer', timer);
    };

    if(id)
    {
        return this.methods.single(id);
    }

    for(const itemId in this.items.data)
    {
        this.methods.single(itemId);
    }

    return true;
});

// repositories/framework/addons/modules/schedules/front/functions/stop.js
import schedules from '../addon.js';

schedules.Fn('stop', function(id)
{
    this.methods.single = (id) =>
    {
        const item = this.ItemGet(id);

        if(!item)
        {
            return false;
        }

        const timer = item.Get('timer');

        if(timer)
        {
            clearTimeout(timer);
            clearInterval(timer);
            item.Set('timer', null);
            item.Set('next', null);
        }

        return true;
    };

    if(id)
    {
        return this.methods.single(id);
    }

    for(const itemId in this.items.data)
    {
        this.methods.single(itemId);
    }

    return true;
});

// repositories/framework/addons/modules/schedules/front/functions/trigger.js
import onetype from '#onetype';
import schedules from '../addon.js';

schedules.Fn('trigger', function(id, props = {})
{
    this.resolve = null;
    this.item = null;
    this.events = onetype.AddonGet('events');
    this.commands = onetype.AddonGet('commands');
    this.actions = onetype.AddonGet('actions');

    this.methods.init = async (resolve) =>
    {
        this.resolve = resolve;
        this.item = this.ItemGet(id);

        if(!this.item)
        {
            return this.methods.done(null, `Schedule "${id}" not found.`, 404);
        }

        if(!this.item.Get('enabled'))
        {
            return this.methods.done(null, `Schedule "${id}" is disabled.`, 403);
        }

        this.methods.emit();
        this.methods.track();

        const callback = this.item.Get('callback');
        const command = this.item.Get('command');
        const action = this.item.Get('action');

        if(callback)
        {
            await this.methods.callback(callback, props);
        }
        else if(command)
        {
            await this.methods.command(command, props);
        }
        else if(action)
        {
            await this.methods.action(action, props);
        }
        else
        {
            return this.methods.done(null, `Schedule "${id}" has no callback, command, or action.`, 400);
        }
    };

    this.methods.emit = () =>
    {
        if(this.events)
        {
            this.events.Fn('emit', 'schedule.triggered', {
                id,
                type: this.item.Get('type'),
                runs: this.item.Get('runs')
            });
        }
    };

    this.methods.track = () =>
    {
        const runs = this.item.Get('runs') || 0;
        const repeat = this.item.Get('repeat');
        const type = this.item.Get('type');

        this.item.Set('runs', runs + 1);
        this.item.Set('last', new Date().toISOString());

        if(type === 'once')
        {
            this.Fn('disable', id);
        }
        else if(repeat !== null && runs + 1 >= repeat)
        {
            this.Fn('disable', id);
        }
    };

    this.methods.callback = async (fn, props) =>
    {
        try
        {
            const data = await fn(props);
            return this.methods.done(data, 'Schedule executed.', 200);
        }
        catch(error)
        {
            return this.methods.done(null, error.message, 500);
        }
    };

    this.methods.command = async (command, props) =>
    {
        if(!this.commands)
        {
            return this.methods.done(null, 'Commands addon not available.', 500);
        }

        const commandId = typeof command === 'string' ? command : command.id;
        const merged = typeof command === 'object' ? { ...command, ...props, id: undefined } : props;

        const item = this.commands.ItemGet(commandId);

        if(!item)
        {
            return this.methods.done(null, `Command "${commandId}" not found.`, 404);
        }

        try
        {
            const result = await item.Fn('run', merged);
            return this.methods.done(result.data, result.message, result.code);
        }
        catch(error)
        {
            return this.methods.done(null, error.message, 500);
        }
    };

    this.methods.action = async (action, props) =>
    {
        if(!this.actions)
        {
            return this.methods.done(null, 'Actions addon not available.', 500);
        }

        const actionId = typeof action === 'string' ? action : action.id;
        const merged = typeof action === 'object' ? { ...action, ...props, id: undefined } : props;

        try
        {
            const result = await this.actions.Fn('run', actionId, merged);
            return this.methods.done(result.data, result.message, result.code);
        }
        catch(error)
        {
            return this.methods.done(null, error.message, 500);
        }
    };

    this.methods.done = (data, message, code) =>
    {
        this.resolve({ data, message, code });
    };

    return new Promise((resolve) =>
    {
        try
        {
            this.methods.init(resolve);
        }
        catch(error)
        {
            resolve({ data: null, message: error.message, code: 500 });
        }
    });
});

// repositories/framework/addons/modules/schedules/front/load.js
import schedules from './addon.js';

import './functions/start.js';
import './functions/stop.js';
import './functions/trigger.js';
import './functions/enable.js';
import './functions/disable.js';
import './functions/next.js';

export default schedules;

// repositories/framework/addons/modules/shortcuts/front/addon.js
import onetype from '#onetype';

const shortcuts = onetype.Addon('shortcuts', (addon) =>
{
    addon.Field('id', ['string']);
    addon.Field('name', ['string', null]);
    addon.Field('group', ['string', null]);
    addon.Field('description', ['string', null]);
    addon.Field('key', ['string']);
    addon.Field('context', ['string', null]);
    addon.Field('target', ['string', null]);
    addon.Field('action', ['string|object', null]);
    addon.Field('command', ['string|object', null]);
    addon.Field('callback', ['function', null]);
    addon.Field('condition', ['string|function', null]);
    addon.Field('enabled', ['boolean', true]);
    addon.Field('prevent', ['boolean', true]);
    addon.Field('stop', ['boolean', false]);
    addon.Field('priority', ['number', 0]);
});

export default shortcuts;

// repositories/framework/addons/modules/shortcuts/front/functions/context.js
import shortcuts from '../addon.js';

shortcuts.Fn('context', function(name)
{
    if(name === undefined)
    {
        return this.StoreGet('context');
    }

    this.StoreSet('context', name);

    return name;
});

// repositories/framework/addons/modules/shortcuts/front/functions/disable.js
import shortcuts from '../addon.js';

shortcuts.Fn('disable', function(id)
{
    const item = this.ItemGet(id);

    if(!item)
    {
        return false;
    }

    item.Set('enabled', false);

    return true;
});

// repositories/framework/addons/modules/shortcuts/front/functions/enable.js
import shortcuts from '../addon.js';

shortcuts.Fn('enable', function(id)
{
    const item = this.ItemGet(id);

    if(!item)
    {
        return false;
    }

    item.Set('enabled', true);

    return true;
});

// repositories/framework/addons/modules/shortcuts/front/functions/register.js
import onetype from '#onetype';
import shortcuts from '../addon.js';

shortcuts.Fn('register', function()
{
    if(this.StoreGet('registered'))
    {
        return;
    }

    this.StoreSet('registered', true);
    this.StoreSet('context', null);

    this.vars = onetype.AddonGet('variables');

    this.methods.handler = (e) =>
    {
        const key = this.methods.parse(e);
        const items = this.methods.match(key);

        for(const item of items)
        {
            if(this.methods.check(item, e))
            {
                if(item.Get('prevent'))
                {
                    e.preventDefault();
                }

                if(item.Get('stop'))
                {
                    e.stopPropagation();
                }

                this.Fn('trigger', item.Get('id'));
            }
        }
    };

    this.methods.parse = (e) =>
    {
        const parts = [];

        if(e.ctrlKey) parts.push('ctrl');
        if(e.altKey) parts.push('alt');
        if(e.shiftKey) parts.push('shift');
        if(e.metaKey) parts.push('meta');

        const name = e.key.toLowerCase();

        if(!['control', 'alt', 'shift', 'meta'].includes(name))
        {
            parts.push(name);
        }

        return parts.join('+');
    };

    this.methods.match = (key) =>
    {
        const items = [];

        for(const id in this.items.data)
        {
            const item = this.items.data[id];
            const itemKey = item.Get('key');

            if(itemKey && itemKey.toLowerCase() === key)
            {
                items.push(item);
            }
        }

        items.sort((a, b) => b.Get('priority') - a.Get('priority'));

        return items;
    };

    this.methods.check = (item, e) =>
    {
        if(!item.Get('enabled'))
        {
            return false;
        }

        if(!this.methods.context(item))
        {
            return false;
        }

        if(!this.methods.target(item))
        {
            return false;
        }

        if(!this.methods.condition(item, e))
        {
            return false;
        }

        return true;
    };

    this.methods.context = (item) =>
    {
        const context = item.Get('context');
        const current = this.StoreGet('context');

        if(context && context !== current)
        {
            return false;
        }

        return true;
    };

    this.methods.target = (item) =>
    {
        const target = item.Get('target');

        if(!target)
        {
            return true;
        }

        const element = document.querySelector(target);

        if(!element || !element.contains(document.activeElement))
        {
            return false;
        }

        return true;
    };

    this.methods.condition = (item, e) =>
    {
        const condition = item.Get('condition');

        if(!condition)
        {
            return true;
        }

        if(typeof condition === 'function')
        {
            return condition(e);
        }

        if(typeof condition === 'string' && this.vars)
        {
            const processed = this.vars.Fn('process', condition);

            try
            {
                return onetype.Function(`return ${processed}`)();
            }
            catch
            {
                return false;
            }
        }

        return true;
    };

    document.addEventListener('keydown', this.methods.handler);

    this.StoreSet('handler', this.methods.handler);
});

// repositories/framework/addons/modules/shortcuts/front/functions/trigger.js
import onetype from '#onetype';
import shortcuts from '../addon.js';

shortcuts.Fn('trigger', function(id, props = {})
{
    this.resolve = null;
    this.item = null;
    this.events = onetype.AddonGet('events');
    this.commands = onetype.AddonGet('commands');
    this.actions = onetype.AddonGet('actions');

    this.methods.init = async (resolve) =>
    {
        this.resolve = resolve;
        this.item = this.ItemGet(id);

        if(!this.item)
        {
            return this.methods.done(null, `Shortcut "${id}" not found.`, 404);
        }

        if(!this.item.Get('enabled'))
        {
            return this.methods.done(null, `Shortcut "${id}" is disabled.`, 403);
        }

        this.methods.emit();

        const callback = this.item.Get('callback');
        const command = this.item.Get('command');
        const action = this.item.Get('action');

        if(callback)
        {
            await this.methods.callback(callback, props);
        }
        else if(command)
        {
            await this.methods.command(command, props);
        }
        else if(action)
        {
            await this.methods.action(action, props);
        }
        else
        {
            return this.methods.done(null, `Shortcut "${id}" has no callback, command, or action.`, 400);
        }
    };

    this.methods.emit = () =>
    {
        if(this.events)
        {
            this.events.Fn('emit', 'shortcut.triggered', {
                id,
                key: this.item.Get('key')
            });
        }
    };

    this.methods.callback = async (fn, props) =>
    {
        try
        {
            const data = await fn(props);
            return this.methods.done(data, 'Shortcut executed.', 200);
        }
        catch(error)
        {
            return this.methods.done(null, error.message, 500);
        }
    };

    this.methods.command = async (command, props) =>
    {
        if(!this.commands)
        {
            return this.methods.done(null, 'Commands addon not available.', 500);
        }

        const commandId = typeof command === 'string' ? command : command.id;
        const merged = typeof command === 'object' ? { ...command, ...props, id: undefined } : props;

        const item = this.commands.ItemGet(commandId);

        if(!item)
        {
            return this.methods.done(null, `Command "${commandId}" not found.`, 404);
        }

        try
        {
            const result = await item.Fn('run', merged);
            return this.methods.done(result.data, result.message, result.code);
        }
        catch(error)
        {
            return this.methods.done(null, error.message, 500);
        }
    };

    this.methods.action = async (action, props) =>
    {
        if(!this.actions)
        {
            return this.methods.done(null, 'Actions addon not available.', 500);
        }

        const actionId = typeof action === 'string' ? action : action.id;
        const merged = typeof action === 'object' ? { ...action, ...props, id: undefined } : props;

        try
        {
            const result = await this.actions.Fn('run', actionId, merged);
            return this.methods.done(result.data, result.message, result.code);
        }
        catch(error)
        {
            return this.methods.done(null, error.message, 500);
        }
    };

    this.methods.done = (data, message, code) =>
    {
        this.resolve({ data, message, code });
    };

    return new Promise((resolve) =>
    {
        try
        {
            this.methods.init(resolve);
        }
        catch(error)
        {
            resolve({ data: null, message: error.message, code: 500 });
        }
    });
});

// repositories/framework/addons/modules/shortcuts/front/functions/unregister.js
import shortcuts from '../addon.js';

shortcuts.Fn('unregister', function()
{
    if(!this.StoreGet('registered'))
    {
        return;
    }

    const handler = this.StoreGet('handler');

    if(handler)
    {
        document.removeEventListener('keydown', handler);
    }

    this.StoreSet('registered', false);
    this.StoreSet('handler', null);
});

// repositories/framework/addons/modules/shortcuts/front/load.js
import shortcuts from './addon.js';

import './functions/register.js';
import './functions/unregister.js';
import './functions/trigger.js';
import './functions/enable.js';
import './functions/disable.js';
import './functions/context.js';

shortcuts.Fn('register');

export default shortcuts;

// repositories/framework/addons/modules/sources/back/addon.js
import onetype from '#onetype';

const sources = onetype.Addon('sources', (addon) =>
{
    addon.Table('sources');

    addon.Field('id', ['string']);
    addon.Field('name', ['string', null]);
    addon.Field('group', ['string', null]);
    addon.Field('description', ['string', null]);
    addon.Field('command', ['string|object', null]);
    addon.Field('endpoint', ['string', null]);
    addon.Field('method', ['string', 'GET']);
    addon.Field('headers', ['object', null]);
    addon.Field('body', ['object|string', null]);
    addon.Field('in', ['object', null]);
    addon.Field('out', ['object', null]);
    addon.Field('callback', ['function', null]);
    addon.Field('created_at', ['string', null]);
    addon.Field('updated_at', ['string', null]);
});

export default sources;

// repositories/framework/addons/modules/sources/back/functions/run.js
import onetype from '#onetype';
import sources from '../addon.js';

sources.Fn('run', function(id, props = {})
{
    this.resolve = null;
    this.item = null;
    this.vars = onetype.AddonGet('variables');
    this.commands = onetype.AddonGet('commands');

    this.methods.init = async (resolve) =>
    {
        this.resolve = resolve;
        this.item = this.ItemGet(id);

        if(!this.item)
        {
            return this.methods.done(null, `Source "${id}" not found.`, 404);
        }

        props = this.methods.validate(props, 'in');

        if(props === false)
        {
            return;
        }

        const callback = this.item.Get('callback');
        const command = this.item.Get('command');

        if(callback)
        {
            await this.methods.callback(callback, props);
        }
        else if(command)
        {
            await this.methods.command(command, props);
        }
        else
        {
            await this.methods.http(props);
        }
    };

    this.methods.validate = (data, type) =>
    {
        const schema = this.item.Get(type);

        if(!schema)
        {
            return data;
        }

        try
        {
            return onetype.DataDefine(data, schema);
        }
        catch(error)
        {
            this.methods.done(null, error.message, type === 'in' ? 400 : 500);
            return false;
        }
    };

    this.methods.callback = async (fn, props) =>
    {
        try
        {
            const data = await fn(props);
            return this.methods.done(data, 'Source executed.', 200);
        }
        catch(error)
        {
            return this.methods.done(null, error.message, 500);
        }
    };

    this.methods.command = async (command, props) =>
    {
        if(!this.commands)
        {
            return this.methods.done(null, 'Commands addon not available.', 500);
        }

        const commandId = typeof command === 'string' ? command : command.id;
        const merged = typeof command === 'object' ? { ...command, ...props, id: undefined } : props;

        const item = this.commands.ItemGet(commandId);

        if(!item)
        {
            return this.methods.done(null, `Command "${commandId}" not found.`, 404);
        }

        try
        {
            const result = await item.Fn('run', merged);
            return this.methods.done(result.data, result.message, result.code);
        }
        catch(error)
        {
            return this.methods.done(null, error.message, 500);
        }
    };

    this.methods.http = async (props) =>
    {
        const endpoint = this.item.Get('endpoint');

        if(!endpoint)
        {
            return this.methods.done(null, `Source "${id}" has no callback, command, or endpoint.`, 400);
        }

        try
        {
            const url = this.methods.url(endpoint, props);
            const opts = this.methods.options(props);
            const res = await fetch(url, opts);

            if(!res.ok)
            {
                return this.methods.done(null, `HTTP ${res.status}: ${res.statusText}`, res.status);
            }

            const data = await res.json();
            return this.methods.done(data, 'Source executed.', 200);
        }
        catch(error)
        {
            return this.methods.done(null, error.message, 500);
        }
    };

    this.methods.url = (endpoint, props) =>
    {
        let url = this.methods.process(endpoint);
        const method = this.item.Get('method') || 'GET';

        if(method === 'GET' && Object.keys(props).length > 0)
        {
            const params = new URLSearchParams();

            for(const key in props)
            {
                if(props[key] !== null && props[key] !== undefined)
                {
                    params.append(key, props[key]);
                }
            }

            url += (url.includes('?') ? '&' : '?') + params.toString();
        }

        return url;
    };

    this.methods.options = (props) =>
    {
        const method = this.item.Get('method') || 'GET';
        const opts = { method };

        opts.headers = this.methods.headers();

        if(method !== 'GET')
        {
            opts.body = this.methods.body(props);
            opts.headers['Content-Type'] = opts.headers['Content-Type'] || 'application/json';
        }

        return opts;
    };

    this.methods.headers = () =>
    {
        const headers = this.item.Get('headers');
        const result = {};

        if(headers)
        {
            for(const key in headers)
            {
                result[key] = this.methods.process(headers[key]);
            }
        }

        return result;
    };

    this.methods.body = (props) =>
    {
        let body = this.item.Get('body') || props;

        if(typeof body === 'string')
        {
            body = this.methods.process(body);
        }
        else if(typeof body === 'object')
        {
            body = { ...body, ...props };

            for(const key in body)
            {
                if(typeof body[key] === 'string')
                {
                    body[key] = this.methods.process(body[key]);
                }
            }
        }

        return JSON.stringify(body);
    };

    this.methods.process = (value) =>
    {
        if(this.vars && typeof value === 'string')
        {
            return this.vars.Fn('process', value);
        }

        return value;
    };

    this.methods.done = (data, message, code) =>
    {
        if(code >= 200 && code < 300)
        {
            const validated = this.methods.validate(data, 'out');

            if(validated === false)
            {
                return;
            }

            data = validated;
        }

        this.resolve({ data, message, code });
    };

    return new Promise((resolve) =>
    {
        try
        {
            this.methods.init(resolve);
        }
        catch(error)
        {
            resolve({ data: null, message: error.message, code: 500 });
        }
    });
});

// repositories/framework/addons/modules/sources/back/items/create.js
import onetype from '#onetype';
import sources from '../addon.js';

onetype.AddonReady('commands', (commands) =>
{
    commands.Item({
        id: 'sources:create',
        method: 'POST',
        endpoint: '/api/sources',
        exposed: true,
        description: 'Create a new source',
        in: {
            id: ['string', null],
            name: ['string', null],
            group: ['string', null],
            description: ['string', null],
            command: ['string|object', null],
            endpoint: ['string', null],
            method: ['string', 'GET'],
            headers: ['object', null],
            body: ['object|string', null],
            in: ['object', null],
            out: ['object', null]
        },
        out: {
            source: ['object']
        },
        callback: async function(props)
        {
            const id = props.id || onetype.GenerateTID();

            if(sources.ItemGet(id))
            {
                throw new Error(`Source "${id}" already exists.`);
            }

            const item = sources.Item({
                id,
                name: props.name,
                group: props.group,
                description: props.description,
                command: props.command,
                endpoint: props.endpoint,
                method: props.method,
                headers: props.headers,
                body: props.body,
                in: props.in,
                out: props.out,
                created_at: new Date().toISOString(),
                updated_at: new Date().toISOString()
            });

            await item.Create();

            return {
                source: {
                    id: item.Get('id'),
                    name: item.Get('name'),
                    group: item.Get('group'),
                    description: item.Get('description'),
                    command: item.Get('command'),
                    endpoint: item.Get('endpoint'),
                    method: item.Get('method'),
                    headers: item.Get('headers'),
                    body: item.Get('body'),
                    in: item.Get('in'),
                    out: item.Get('out'),
                    created_at: item.Get('created_at'),
                    updated_at: item.Get('updated_at')
                }
            };
        }
    });
});

// repositories/framework/addons/modules/sources/back/items/delete.js
import onetype from '#onetype';
import sources from '../addon.js';

onetype.AddonReady('commands', (commands) =>
{
    commands.Item({
        id: 'sources:delete',
        method: 'DELETE',
        endpoint: '/api/sources/:id',
        exposed: true,
        description: 'Delete a source',
        in: {
            id: ['string', null, true]
        },
        out: {
            deleted: ['boolean']
        },
        callback: async function(props)
        {
            const item = sources.ItemGet(props.id);

            if(!item)
            {
                throw new Error(`Source "${props.id}" not found.`);
            }

            await item.Delete();

            sources.ItemRemove(props.id);

            return {
                deleted: true
            };
        }
    });
});

// repositories/framework/addons/modules/sources/back/items/many.js
import onetype from '#onetype';
import sources from '../addon.js';

onetype.AddonReady('commands', (commands) =>
{
    commands.Item({
        id: 'sources:many',
        method: 'GET',
        endpoint: '/api/sources',
        exposed: true,
        description: 'Get all sources',
        in: {
            page: ['number', 1],
            limit: ['number', 50],
            group: ['string', null]
        },
        out: {
            sources: {
                type: 'array',
                each: {
                    type: 'object',
                    config: {
                        id: ['string'],
                        name: ['string'],
                        group: ['string'],
                        description: ['string'],
                        command: ['string|object'],
                        endpoint: ['string'],
                        method: ['string'],
                        headers: ['object'],
                        body: ['object|string'],
                        in: ['object'],
                        out: ['object'],
                        created_at: ['string'],
                        updated_at: ['string']
                    }
                }
            },
            total: ['number']
        },
        callback: async function(props)
        {
            const result = [];

            for(const id in sources.items.data)
            {
                const item = sources.items.data[id];

                if(props.group && item.Get('group') !== props.group)
                {
                    continue;
                }

                result.push({
                    id: item.Get('id'),
                    name: item.Get('name'),
                    group: item.Get('group'),
                    description: item.Get('description'),
                    command: item.Get('command'),
                    endpoint: item.Get('endpoint'),
                    method: item.Get('method'),
                    headers: item.Get('headers'),
                    body: item.Get('body'),
                    in: item.Get('in'),
                    out: item.Get('out'),
                    created_at: item.Get('created_at'),
                    updated_at: item.Get('updated_at')
                });
            }

            const start = (props.page - 1) * props.limit;
            const end = start + props.limit;

            return {
                sources: result.slice(start, end),
                total: result.length
            };
        }
    });
});

// repositories/framework/addons/modules/sources/back/items/one.js
import onetype from '#onetype';
import sources from '../addon.js';

onetype.AddonReady('commands', (commands) =>
{
    commands.Item({
        id: 'sources:one',
        method: 'GET',
        endpoint: '/api/sources/:id',
        exposed: true,
        description: 'Get a single source by ID',
        in: {
            id: ['string', null, true]
        },
        out: {
            source: ['object']
        },
        callback: async function(props)
        {
            const item = sources.ItemGet(props.id);

            if(!item)
            {
                throw new Error(`Source "${props.id}" not found.`);
            }

            return {
                source: {
                    id: item.Get('id'),
                    name: item.Get('name'),
                    group: item.Get('group'),
                    description: item.Get('description'),
                    command: item.Get('command'),
                    endpoint: item.Get('endpoint'),
                    method: item.Get('method'),
                    headers: item.Get('headers'),
                    body: item.Get('body'),
                    in: item.Get('in'),
                    out: item.Get('out'),
                    created_at: item.Get('created_at'),
                    updated_at: item.Get('updated_at')
                }
            };
        }
    });
});

// repositories/framework/addons/modules/sources/back/items/run.js
import onetype from '#onetype';
import sources from '../addon.js';

onetype.AddonReady('commands', (commands) =>
{
    commands.Item({
        id: 'sources:run',
        method: 'POST',
        endpoint: '/api/sources/run',
        exposed: true,
        description: 'Run a source by ID',
        in: {
            id: ['string', null, true],
            props: ['object', {}]
        },
        out: {
            data: ['any'],
            message: ['string'],
            code: ['number']
        },
        callback: async function(props)
        {
            const result = await sources.Fn('run', props.id, props.props || {});

            return result;
        }
    });
});

// repositories/framework/addons/modules/sources/back/items/update.js
import onetype from '#onetype';
import sources from '../addon.js';

onetype.AddonReady('commands', (commands) =>
{
    commands.Item({
        id: 'sources:update',
        method: 'PUT',
        endpoint: '/api/sources/:id',
        exposed: true,
        description: 'Update a source',
        in: {
            id: ['string', null, true],
            name: ['string', null],
            group: ['string', null],
            description: ['string', null],
            command: ['string|object', null],
            endpoint: ['string', null],
            method: ['string', null],
            headers: ['object', null],
            body: ['object|string', null],
            in: ['object', null],
            out: ['object', null]
        },
        out: {
            source: ['object']
        },
        callback: async function(props)
        {
            const item = sources.ItemGet(props.id);

            if(!item)
            {
                throw new Error(`Source "${props.id}" not found.`);
            }

            if(props.name !== null && props.name !== undefined)
            {
                item.Set('name', props.name);
            }

            if(props.group !== null && props.group !== undefined)
            {
                item.Set('group', props.group);
            }

            if(props.description !== null && props.description !== undefined)
            {
                item.Set('description', props.description);
            }

            if(props.command !== null && props.command !== undefined)
            {
                item.Set('command', props.command);
            }

            if(props.endpoint !== null && props.endpoint !== undefined)
            {
                item.Set('endpoint', props.endpoint);
            }

            if(props.method !== null && props.method !== undefined)
            {
                item.Set('method', props.method);
            }

            if(props.headers !== null && props.headers !== undefined)
            {
                item.Set('headers', props.headers);
            }

            if(props.body !== null && props.body !== undefined)
            {
                item.Set('body', props.body);
            }

            if(props.in !== null && props.in !== undefined)
            {
                item.Set('in', props.in);
            }

            if(props.out !== null && props.out !== undefined)
            {
                item.Set('out', props.out);
            }

            item.Set('updated_at', new Date().toISOString());

            await item.Update();

            return {
                source: {
                    id: item.Get('id'),
                    name: item.Get('name'),
                    group: item.Get('group'),
                    description: item.Get('description'),
                    command: item.Get('command'),
                    endpoint: item.Get('endpoint'),
                    method: item.Get('method'),
                    headers: item.Get('headers'),
                    body: item.Get('body'),
                    in: item.Get('in'),
                    out: item.Get('out'),
                    created_at: item.Get('created_at'),
                    updated_at: item.Get('updated_at')
                }
            };
        }
    });
});

// repositories/framework/addons/modules/sources/back/load.js
import sources from './addon.js';

import './functions/run.js';

import './items/one.js';
import './items/many.js';
import './items/create.js';
import './items/update.js';
import './items/delete.js';
import './items/run.js';

export default sources;

// repositories/framework/addons/modules/sources/front/addon.js
import onetype from '#onetype';

const sources = onetype.Addon('sources', (addon) =>
{
    addon.Field('id', ['string']);
    addon.Field('name', ['string', null]);
    addon.Field('group', ['string', null]);
    addon.Field('description', ['string', null]);
    addon.Field('action', ['string|object', null]);
    addon.Field('command', ['string|object', null]);
    addon.Field('endpoint', ['string', null]);
    addon.Field('method', ['string', 'GET']);
    addon.Field('headers', ['object', null]);
    addon.Field('body', ['object|string', null]);
    addon.Field('in', ['object', null]);
    addon.Field('out', ['object', null]);
    addon.Field('callback', ['function', null]);
});

export default sources;

// repositories/framework/addons/modules/sources/front/functions/run.js
import onetype from '#onetype';
import sources from '../addon.js';

sources.Fn('run', function(id, props = {})
{
    this.resolve = null;
    this.item = null;
    this.vars = onetype.AddonGet('variables');
    this.commands = onetype.AddonGet('commands');
    this.actions = onetype.AddonGet('actions');

    this.methods.init = async (resolve) =>
    {
        this.resolve = resolve;
        this.item = this.ItemGet(id);

        if(!this.item)
        {
            return this.methods.done(null, `Source "${id}" not found.`, 404);
        }

        props = this.methods.validate(props, 'in');

        if(props === false)
        {
            return;
        }

        const callback = this.item.Get('callback');
        const command = this.item.Get('command');
        const action = this.item.Get('action');

        if(callback)
        {
            await this.methods.callback(callback, props);
        }
        else if(command)
        {
            await this.methods.command(command, props);
        }
        else if(action)
        {
            await this.methods.action(action, props);
        }
        else
        {
            await this.methods.http(props);
        }
    };

    this.methods.validate = (data, type) =>
    {
        const schema = this.item.Get(type);

        if(!schema)
        {
            return data;
        }

        try
        {
            return onetype.DataDefine(data, schema);
        }
        catch(error)
        {
            this.methods.done(null, error.message, type === 'in' ? 400 : 500);
            return false;
        }
    };

    this.methods.callback = async (fn, props) =>
    {
        try
        {
            const data = await fn(props);
            return this.methods.done(data, 'Source executed.', 200);
        }
        catch(error)
        {
            return this.methods.done(null, error.message, 500);
        }
    };

    this.methods.command = async (command, props) =>
    {
        if(!this.commands)
        {
            return this.methods.done(null, 'Commands addon not available.', 500);
        }

        const commandId = typeof command === 'string' ? command : command.id;
        const merged = typeof command === 'object' ? { ...command, ...props, id: undefined } : props;

        const item = this.commands.ItemGet(commandId);

        if(!item)
        {
            return this.methods.done(null, `Command "${commandId}" not found.`, 404);
        }

        try
        {
            const result = await item.Fn('run', merged);
            return this.methods.done(result.data, result.message, result.code);
        }
        catch(error)
        {
            return this.methods.done(null, error.message, 500);
        }
    };

    this.methods.action = async (action, props) =>
    {
        if(!this.actions)
        {
            return this.methods.done(null, 'Actions addon not available.', 500);
        }

        const actionId = typeof action === 'string' ? action : action.id;
        const merged = typeof action === 'object' ? { ...action, ...props, id: undefined } : props;

        try
        {
            const result = await this.actions.Fn('run', actionId, merged);
            return this.methods.done(result.data, result.message, result.code);
        }
        catch(error)
        {
            return this.methods.done(null, error.message, 500);
        }
    };

    this.methods.http = async (props) =>
    {
        const endpoint = this.item.Get('endpoint');

        if(!endpoint)
        {
            return this.methods.done(null, `Source "${id}" has no callback, command, action, or endpoint.`, 400);
        }

        try
        {
            const url = this.methods.url(endpoint, props);
            const opts = this.methods.options(props);
            const res = await fetch(url, opts);

            if(!res.ok)
            {
                return this.methods.done(null, `HTTP ${res.status}: ${res.statusText}`, res.status);
            }

            const data = await res.json();
            return this.methods.done(data, 'Source executed.', 200);
        }
        catch(error)
        {
            return this.methods.done(null, error.message, 500);
        }
    };

    this.methods.url = (endpoint, props) =>
    {
        let url = this.methods.process(endpoint);
        const method = this.item.Get('method') || 'GET';

        if(method === 'GET' && Object.keys(props).length > 0)
        {
            const params = new URLSearchParams();

            for(const key in props)
            {
                if(props[key] !== null && props[key] !== undefined)
                {
                    params.append(key, props[key]);
                }
            }

            url += (url.includes('?') ? '&' : '?') + params.toString();
        }

        return url;
    };

    this.methods.options = (props) =>
    {
        const method = this.item.Get('method') || 'GET';
        const opts = { method };

        opts.headers = this.methods.headers();

        if(method !== 'GET')
        {
            opts.body = this.methods.body(props);
            opts.headers['Content-Type'] = opts.headers['Content-Type'] || 'application/json';
        }

        return opts;
    };

    this.methods.headers = () =>
    {
        const headers = this.item.Get('headers');
        const result = {};

        if(headers)
        {
            for(const key in headers)
            {
                result[key] = this.methods.process(headers[key]);
            }
        }

        return result;
    };

    this.methods.body = (props) =>
    {
        let body = this.item.Get('body') || props;

        if(typeof body === 'string')
        {
            body = this.methods.process(body);
        }
        else if(typeof body === 'object')
        {
            body = { ...body, ...props };

            for(const key in body)
            {
                if(typeof body[key] === 'string')
                {
                    body[key] = this.methods.process(body[key]);
                }
            }
        }

        return JSON.stringify(body);
    };

    this.methods.process = (value) =>
    {
        if(this.vars && typeof value === 'string')
        {
            return this.vars.Fn('process', value);
        }

        return value;
    };

    this.methods.done = (data, message, code) =>
    {
        if(code >= 200 && code < 300)
        {
            const validated = this.methods.validate(data, 'out');

            if(validated === false)
            {
                return;
            }

            data = validated;
        }

        this.resolve({ data, message, code });
    };

    return new Promise((resolve) =>
    {
        try
        {
            this.methods.init(resolve);
        }
        catch(error)
        {
            resolve({ data: null, message: error.message, code: 500 });
        }
    });
});

// repositories/framework/addons/modules/sources/front/load.js
import sources from './addon.js';

import './functions/run.js';

export default sources;

// repositories/framework/addons/modules/variables/core/addon.js
import onetype from '#framework/load.js';

const variables = onetype.Addon('variables', (addon) =>
{
    addon.Field('id', ['string']);
    addon.Field('value', ['any']);
    addon.Field('type', ['string']);
    addon.Field('group', ['string', null]);
});

export default variables;

// repositories/framework/addons/modules/variables/core/functions/process.js
import onetype from '#framework/load.js';
import variables from '#variables/core/addon.js';

variables.Fn('process', function(string)
{
    if(typeof string !== 'string')
    {
        return string;
    }

    const safe = onetype.environment === 'back';
    const data = {};

    for(const id in this.items.data)
    {
        data[id] = this.items.data[id].Get('value');
    }

    if(!string.includes('${'))
    {
        const result = onetype.Function(string, data, safe);

        return result !== undefined ? result : string;
    }

    const single = string.match(/^\$\{([^}]+)\}$/);

    if(single)
    {
        const result = onetype.Function(single[1], data, safe);

        return result !== undefined ? result : string;
    }

    return string.replace(/\$\{([^}]+)\}/g, (match, expression) =>
    {
        const result = onetype.Function(expression, data, safe);

        if(result === undefined || (typeof result === 'object' && result !== null))
        {
            return match;
        }

        return result;
    });
});

// repositories/framework/addons/modules/variables/core/item/catch/add.js
import variables from '#variables/core/addon.js';

variables.ItemOn('add', function(item)
{
    item.Set('type', typeof item.Get('value'));
});

// repositories/framework/addons/modules/variables/core/load.js
import onetype from '#framework/load.js';
import variables from '#variables/core/addon.js';

import '#variables/core/item/catch/add.js';

import '#variables/core/functions/process.js';

onetype.$ot.var = function(string)
{
    return variables.Fn('process', string);
};

export default variables;

// repositories/framework/addons/render/directives/front/addon.js
const directives = onetype.Addon('directives', (addon) =>
{
    addon.Field('id', ['string']);
    addon.Field('icon', ['string', 'code']);
    addon.Field('name', ['string', '']);
    addon.Field('description', ['string', '']);
    addon.Field('trigger', ['string', 'node']);
    addon.Field('order', ['number', 1]);
    addon.Field('code', ['function']);
    addon.Field('strict', ['boolean', true]);

    addon.Field('tag', ['string']);
    addon.Field('type', ['string']);
    addon.Field('attributes', ['object', {}]);

    /* Trigers */
    
    onetype.EmitOn('@addon.render.compile.before', (item, compile, node, identifier) =>
    {
        if(compile.locale !== false)
        {
            directives.Fn('process.locale', node);
        }

        directives.Fn('process', 'before', item, compile, node, identifier);
    })

    onetype.EmitOn('@addon.render.compile.after', (item, compile, node, identifier) =>
    {
        directives.Fn('process', 'after', item, compile, node, identifier);
    })

    onetype.EmitOn('@addon.render.compile.node', (item, compile, node, identifier) =>
    {
        directives.Fn('process', 'node', item, compile, node, identifier);
    })
});

// repositories/framework/addons/render/directives/front/functions/process/attributes.js
directives.Fn('process.attributes', function(node, compile)
{
    if(node.nodeType !== Node.ELEMENT_NODE || !node.__attributes)
    {
        return;
    }

    if(!node.__attributes.some(attr => attr.name.startsWith(':')))
    {
        return;
    }

    node.__attributes.forEach(attribute =>
    {
        if (!attribute.name.startsWith(':'))
        {
            return;
        }

        const attributeName = attribute.name.substring(1);
        const expression = attribute.value;

        try
        {
            const value = onetype.Function(expression, compile.data, false);

            if (value === null || value === undefined || value === false)
            {
                node.removeAttribute(attributeName);
            }
            else if (value === true)
            {
                node.setAttribute(attributeName, '');
            }
            else if (typeof value === 'string' || typeof value === 'number')
            {
                node.setAttribute(attributeName, value);
            }
            else
            {
                node.removeAttribute(attributeName);
            }

            node.removeAttribute(attribute.name);
        }
        catch(error)
        {
            onetype.Error(500, 'Dynamic attribute error.');
            node.removeAttribute(attribute.name);
        }
    });
});

// repositories/framework/addons/render/directives/front/functions/process/data.js
directives.Fn('process.data', function(attributes, node, compile)
{
    const data = {};

    Object.entries(attributes).forEach(([name, definition]) =>
    {
        name = name.toLowerCase();

        const find = node.__attributes ? node.__attributes.find(attribute =>
        {
            return (attribute.lowerName === name) || attribute.lowerName === (':' + name) || attribute.lowerName.startsWith(name + '.') || attribute.lowerName.startsWith(':' + name + '.');
        }) : null;

        let attribute;

        if (find)
        {
            attribute = {
                original: { name: find.name, value: find.value }, 
                name: find.name,
                value: find.value,
                dynamic: false,
                modifiers: []
            };
        }
        else 
        {
            attribute = {
                original: { name: name, value: null }, 
                name: name,
                value: null,
                dynamic: false,
                modifiers: []
            };
        }

        if(attribute.name.split('.').length > 1)
        {
            const split = attribute.name.split('.');

            attribute.name = split[0];
            attribute.modifiers = split.slice(1);
        }

        if(attribute.name.startsWith(':'))
        {
            attribute.dynamic = true;
            attribute.name = attribute.name.substring(1);
        }

        if(attribute.dynamic)
        {
            try 
            {
                attribute.value = onetype.Function(attribute.value, compile.data, false);
            }
            catch(error)
            {
                attribute.value = null;
            }
        }

        try
        {
            attribute.value = onetype.DataDefineOne(attribute.value, definition);
        }
        catch(error)
        {
            const path = error.path && error.path.length
                ? attribute.name + '.' + error.path.join('.').replace(/\.\[/g, '[')
                : attribute.name;

            onetype.Error(400, 'Directive attribute :path: failed — :reason:', {
                path,
                reason: error.message,
                value: attribute.value
            });

            attribute.value = null;
        }

        data[attribute.name] = attribute;

        if(find && node.removeAttribute)
        {
            node.removeAttribute(find.name);
        }
    });

    return data;
});

// repositories/framework/addons/render/directives/front/functions/process/locale.js
const skip = ['id', 'class', 'style', 'href', 'src', 'action', 'method', 'type', 'name', 'value', 'for', 'role', 'tabindex', 'width', 'height', 'data', 'slot'];

directives.Fn('process.locale', function(node)
{
	if(node.nodeType === 3)
	{
		const text = node.textContent.trim();

		if(text && !text.includes('{{') && node.parentNode?.tagName !== 'I')
		{
			node.textContent = node.textContent.replace(text, onetype.LocaleGet(text));
		}

		return;
	}

	if(node.nodeType === 1)
	{
		for(let i = 0; i < node.attributes.length; i++)
		{
			const attr = node.attributes[i];

			if(attr.name.startsWith(':') || attr.name.startsWith('ot-'))
			{
				continue;
			}

			if(skip.includes(attr.name))
			{
				continue;
			}

			if(!attr.value || !attr.value.trim())
			{
				continue;
			}

			attr.value = onetype.LocaleGet(attr.value);
		}

		for(let i = 0; i < node.childNodes.length; i++)
		{
			directives.Fn('process.locale', node.childNodes[i]);
		}
	}
});

// repositories/framework/addons/render/directives/front/functions/process/match.js
directives.Fn('process.match', function(d, node)
{
    const tag = d.tag;
    const type = d.type;
    const attributes = d.attributes;

    const matches = {
        count: 0,
        total: Object.keys(attributes).length + (tag ? 1 : 0) + (type ? 1 : 0),
    };

    if(tag && node.tagName && node.tagName.toLowerCase() === tag.toLowerCase())
    {
        matches.count++;
    }

    if(type && node.nodeType.toString() === type)
    {
        matches.count++;
    }

    Object.entries(attributes).forEach(([name]) =>
    {
        name = name.toLowerCase();

        const find = node.__attributes && node.__attributes.some(attribute =>
        {
            return (attribute.lowerName === name) || attribute.lowerName === (':' + name) || attribute.lowerName.startsWith(name + '.') || attribute.lowerName.startsWith(':' + name + '.');
        });

        if(find)
        {
            matches.count++;
        }
    });

    if(matches.total === 0 && d.trigger !== 'node')
    {
        return true;
    }

    if(d.strict)
    {
        return matches.count === matches.total;
    }

    return matches.count > 0;
});

// repositories/framework/addons/render/directives/front/functions/process.js
directives.Fn('process', function(trigger, item, compile, node, identifier)
{
    if (!node)
    {
        return;
    }

    if(!directives.StoreHas('sorted'))
    {
        directives.StoreSet('sorted', Object.values(directives.Items()).sort((a, b) => a.Get('order') - b.Get('order')).map(directive => ({
            item: directive,
            trigger: directive.Get('trigger'),
            type: directive.Get('type'),
            tag: directive.Get('tag'),
            attributes: directive.Get('attributes'),
            strict: directive.Get('strict'),
            code: directive.Get('code')
        })));

        directives.StoreSet('fn.match', directives.FnGet('process.match').callback);
        directives.StoreSet('fn.data', directives.FnGet('process.data').callback);
        directives.StoreSet('fn.attributes', directives.FnGet('process.attributes').callback);
    }

    if(node.nodeType === 1)
    {
        const tagLower = node.tagName.toLowerCase();

        if(!node.attributes.length && !tagLower.includes('-') && tagLower !== 'slot' && tagLower !== 'render')
        {
            return;
        }

        if(node.attributes.length && !node.__attributes)
        {
            node.__attributes = Array.from(node.attributes).map(attr => ({
                name: attr.name,
                lowerName: attr.name.toLowerCase(),
                value: attr.value,
                original: attr
            }));
        }
    }
    else if(node.nodeType !== 3)
    {
        return;
    }

    const items = directives.StoreGet('sorted');
    const match = directives.StoreGet('fn.match');
    const data = directives.StoreGet('fn.data');

    for (let i = 0; i < items.length; i++)
    {
        const directive = items[i];

        if(directive.trigger !== trigger)
        {
            continue;
        }

        if(node.nodeType === 3 && directive.type !== '3')
        {
            continue;
        }

        if(!match(directive, node))
        {
            continue;
        }

        const attributes = data(directive.attributes, node, compile);

        try
        {
            const result = directive.code.call({}, attributes, item, compile, node, identifier);

            if(result === false)
            {
                break;
            }
        }
        catch (error)
        {
            onetype.Error(500, 'Directive processing error.');
        }
    }

    if(node.isConnected || node.parentNode)
    {
        directives.StoreGet('fn.attributes')(node, compile);
    }
});

// repositories/framework/addons/render/editor/addon.js
const editor = onetype.Addon('editor', (addon) =>
{
	addon.Field('id', ['string']);
	addon.Field('icon', ['string', 'edit_note']);
	addon.Field('name', ['string', '']);
	addon.Field('category', ['string', '']);
	addon.Field('editable', ['boolean', false]);
	addon.Field('children', ['boolean', false]);
	addon.Field('config', ['object', {}]);
	addon.Field('insert', ['function']);
	addon.Field('update', ['function']);
	addon.Field('remove', ['function']);
});

// repositories/framework/addons/render/editor/functions/block/add.js
onetype.AddonReady('editor', (editor) =>
{
	editor.Fn('block.add', function(instance, type, afterUid, data)
	{
		const block = {
			uid: onetype.GenerateUID(),
			type: type,
			data: data || {},
			children: []
		};

		const container = instance.Element.querySelector('.ot-editor-blocks');

		if(!container)
		{
			return null;
		}

		const node = editor.Fn('render.block', instance, block);

		if(!node)
		{
			return null;
		}

		if(afterUid)
		{
			const index = instance.blocks.findIndex(b => b.uid === afterUid);
			const afterNode = container.querySelector('[data-uid="' + afterUid + '"]');

			if(index !== -1 && afterNode)
			{
				instance.blocks.splice(index + 1, 0, block);
				afterNode.after(node);
			}
			else
			{
				instance.blocks.push(block);
				container.appendChild(node);
			}
		}
		else
		{
			instance.blocks.push(block);
			container.appendChild(node);
		}

		const placeholder = instance.Element.querySelector('.ot-editor-placeholder');

		if(placeholder)
		{
			placeholder.style.display = 'none';
		}

		return block;
	});
});

// repositories/framework/addons/render/editor/functions/block/delete.js
onetype.AddonReady('editor', (editor) =>
{
	editor.Fn('block.delete', function(instance, uid)
	{
		const found = editor.Fn('block.find', instance, uid);

		if(!found)
		{
			return;
		}

		const type = editor.ItemGet(found.block.type);
		const node = instance.Element.querySelector('[data-uid="' + uid + '"]');

		if(type && type.Get('remove'))
		{
			type.Get('remove').call(null, node);
		}

		found.list.splice(found.index, 1);

		if(node)
		{
			node.remove();
		}

		if(!instance.blocks.length)
		{
			editor.Fn('block.add', instance, 'paragraph', null, {});
		}
	});
});

// repositories/framework/addons/render/editor/functions/block/find.js
onetype.AddonReady('editor', (editor) =>
{
	editor.Fn('block.find', function(instance, uid, source)
	{
		source = source || instance.blocks;

		for(let i = 0; i < source.length; i++)
		{
			if(source[i].uid === uid)
			{
				return { block: source[i], list: source, index: i };
			}

			if(source[i].children)
			{
				for(let z = 0; z < source[i].children.length; z++)
				{
					const found = editor.Fn('block.find', instance, uid, source[i].children[z]);

					if(found)
					{
						return found;
					}
				}
			}
		}

		return null;
	});
});

// repositories/framework/addons/render/editor/functions/render/block.js
onetype.AddonReady('editor', (editor) =>
{
	editor.Fn('render.block', function(instance, block)
	{
		const type = editor.ItemGet(block.type);

		if(!type)
		{
			return null;
		}

		const wrapper = document.createElement('div');
		wrapper.className = 'ot-block';
		wrapper.setAttribute('data-uid', block.uid);
		wrapper.setAttribute('data-type', block.type);
		wrapper.setAttribute('tabindex', '-1');

		const gutter = document.createElement('div');
		gutter.className = 'ot-block-gutter';
		gutter.innerHTML = '<button class="ot-block-handle" draggable="true"><i>drag_indicator</i></button><button class="ot-block-add"><i>add</i></button>';

		const content = document.createElement('div');
		content.className = 'ot-block-content';
		content.innerHTML = type.Get('insert').call(null, block.data);

		const actions = document.createElement('div');
		actions.className = 'ot-block-actions';
		actions.innerHTML = '<button class="ot-block-settings"><i>more_vert</i></button>';

		wrapper.appendChild(gutter);
		wrapper.appendChild(content);
		wrapper.appendChild(actions);

		if(type.Get('editable'))
		{
			const target = content.firstElementChild;

			if(target)
			{
				target.setAttribute('contenteditable', 'true');
				target.setAttribute('data-placeholder', instance.placeholder || 'Type something...');

				target.addEventListener('input', () =>
				{
					block.data.content = target.innerHTML;

					if(instance._change)
					{
						instance._change({ value: instance.getValue() });
					}
				});

				target.addEventListener('keydown', (e) =>
				{
					if(e.key === 'Enter' && !e.shiftKey)
					{
						e.preventDefault();
						const newBlock = editor.Fn('block.add', instance, 'paragraph', block.uid, {});

						if(newBlock)
						{
							const newNode = instance.Element.querySelector('[data-uid="' + newBlock.uid + '"]');

							if(newNode)
							{
								const editable = newNode.querySelector('[contenteditable]');
								if(editable) editable.focus();
							}
						}
					}

					if(e.key === 'Backspace' && target.innerHTML === '' && instance.blocks.length > 1)
					{
						e.preventDefault();

						const index = instance.blocks.findIndex(b => b.uid === block.uid);
						const prev = index > 0 ? instance.blocks[index - 1] : null;

						editor.Fn('block.delete', instance, block.uid);

						if(prev)
						{
							const prevNode = instance.Element.querySelector('[data-uid="' + prev.uid + '"]');

							if(prevNode)
							{
								const editable = prevNode.querySelector('[contenteditable]');
								if(editable) editable.focus();
							}
						}
					}
				});
			}
		}

		gutter.querySelector('.ot-block-add').addEventListener('click', () =>
		{
			const newBlock = editor.Fn('block.add', instance, 'paragraph', block.uid, {});

			if(newBlock)
			{
				const newNode = instance.Element.querySelector('[data-uid="' + newBlock.uid + '"]');

				if(newNode)
				{
					const editable = newNode.querySelector('[contenteditable]');
					if(editable) editable.focus();
				}
			}
		});

		return wrapper;
	});
});

// repositories/framework/addons/render/editor/functions/render/blocks.js
onetype.AddonReady('editor', (editor) =>
{
	editor.Fn('render.blocks', function(instance)
	{
		const container = instance.Element.querySelector('.ot-editor-blocks');

		if(!container)
		{
			return;
		}

		container.innerHTML = '';

		instance.blocks.forEach(block =>
		{
			const node = editor.Fn('render.block', instance, block);

			if(node)
			{
				container.appendChild(node);
			}
		});

		const placeholder = instance.Element.querySelector('.ot-editor-placeholder');

		if(placeholder)
		{
			placeholder.style.display = instance.blocks.length ? 'none' : '';
		}
	});
});

// repositories/framework/addons/render/editor/items/elements/editor.js
onetype.AddonReady('elements', (elements) =>
{
	elements.ItemAdd({
		id: 'editor',
		icon: 'edit_note',
		name: 'Block Editor',
		description: 'Block-based content editor.',
		category: 'Form',
		config: {
			value: {
				type: 'array',
				value: []
			},
			placeholder: {
				type: 'string',
				value: 'Type something...'
			},
			readonly: {
				type: 'boolean',
				value: false
			},
			variant: {
				type: 'array',
				value: [],
				options: ['compact', 'bordered']
			},
			_change: {
				type: 'function'
			}
		},
		render: function()
		{
			const addon = onetype.AddonGet('editor');

			this.blocks = [];

			this.getValue = () =>
			{
				return this.blocks.map(block => ({
					type: block.type,
					data: Object.assign({}, block.data),
					children: block.children || []
				}));
			};

			this.OnReady(() =>
			{
				if(this.value && this.value.length)
				{
					this.value.forEach(block =>
					{
						addon.Fn('block.add', this, block.type, null, Object.assign({}, block.data));
					});
				}
				else
				{
					addon.Fn('block.add', this, 'paragraph', null, {});
				}
			});

			return /* html */ `
				<div :class="'ot-editor ' + variant.join(' ')">
					<div class="ot-editor-blocks"></div>
					<div class="ot-editor-placeholder">{{ placeholder }}</div>
				</div>
			`;
		}
	});
});

// repositories/framework/addons/render/elements/front/addon.js
const elements = onetype.Addon('elements', (addon) =>
{
    addon.Field('id', ['string']);
    addon.Field('icon', ['string', 'extension']);
    addon.Field('name', ['string', '']);
    addon.Field('description', ['string', '']);
    addon.Field('category', ['string', '']);
    addon.Field('collection', ['string', '']);
    addon.Field('config', ['object', {}]);
    addon.Field('example', ['array', []]);
    addon.Field('render', ['function']);
});

// repositories/framework/addons/render/elements/front/functions/runtime.js
elements.Fn('runtime', function()
{
	this.methods.render = () =>
	{
		elements.Render('body', () =>
		{
			return document.body.outerHTML;
		});

		const render = elements.Render('body', window);
		document.body.replaceChildren(...render.Element.children);
	};

	if(document.readyState === 'loading')
	{
		document.addEventListener('DOMContentLoaded', () =>
		{
			this.methods.render();
		});
	}
	else
	{
		this.methods.render();
	}
});

// repositories/framework/addons/render/elements/front/functions/types/colors/badge.js
onetype.AddonReady('elements', () => 
{
    const colors = {
        new: 'brand',
        featured: 'brand',
        pro: 'brand',
        premium: 'brand',
        beta: 'blue',
        info: 'blue',
        warning: 'orange',
        danger: 'red',
        success: 'green'
    };

    elements.Fn('type.colors.badge', function(overrides)
    {
        return { ...colors, ...(overrides || {}) };
    });
});

// repositories/framework/addons/render/elements/front/functions/types/colors/status.js
onetype.AddonReady('elements', () => 
{
    const colors = {
        live: 'green',
        published: 'green',
        active: 'green',
        success: 'green',
        approved: 'green',
        pending: 'orange',
        review: 'orange',
        draft: 'neutral',
        archived: 'neutral',
        disabled: 'neutral',
        rejected: 'red',
        error: 'red',
        failed: 'red',
        inactive: 'red'
    };

    elements.Fn('type.colors.status', function(overrides)
    {
        return { ...colors, ...(overrides || {}) };
    });
});

// repositories/framework/addons/render/elements/front/functions/types/escape.js
elements.Fn('type.escape', function(value)
{
	return String(value === null || value === undefined ? '' : value)
		.replace(/&/g, '&amp;')
		.replace(/</g, '&lt;')
		.replace(/>/g, '&gt;')
		.replace(/"/g, '&quot;');
});

// repositories/framework/addons/render/elements/front/functions/types/format/currency.js
elements.Fn('type.format.currency', function(value, currency)
{
	if(value === null || value === undefined || value === '')
	{
		return '—';
	}

	const symbol = currency || '€';

	if(typeof value !== 'number')
	{
		return symbol + value;
	}

	return symbol + value.toLocaleString('en-US', { minimumFractionDigits: 0, maximumFractionDigits: 2 });
});

// repositories/framework/addons/render/elements/front/functions/types/format/date.js
elements.Fn('type.format.date', function(value)
{
	if(!value)
	{
		return '—';
	}

	const date = new Date(value);

	if(isNaN(date))
	{
		return '—';
	}

	return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' });
});

// repositories/framework/addons/render/elements/front/functions/types/format/number.js
elements.Fn('type.format.number', function(value)
{
	if(value === null || value === undefined || value === '')
	{
		return '—';
	}

	if(typeof value !== 'number')
	{
		return String(value);
	}

	if(Math.abs(value) >= 1000000)
	{
		return (value / 1000000).toFixed(1) + 'M';
	}

	if(Math.abs(value) >= 1000)
	{
		return (value / 1000).toFixed(1) + 'k';
	}

	return String(value);
});

// repositories/framework/addons/render/elements/front/functions/types/format/timeago.js
elements.Fn('type.format.timeago', function(value)
{
	if(!value)
	{
		return '—';
	}

	const then = new Date(value).getTime();

	if(isNaN(then))
	{
		return '—';
	}

	const diff = Math.floor((Date.now() - then) / 1000);

	if(diff < 60)
	{
		return 'just now';
	}

	if(diff < 3600)
	{
		return Math.floor(diff / 60) + 'm ago';
	}

	if(diff < 86400)
	{
		return Math.floor(diff / 3600) + 'h ago';
	}

	if(diff < 604800)
	{
		return Math.floor(diff / 86400) + 'd ago';
	}

	if(diff < 2592000)
	{
		return Math.floor(diff / 604800) + 'w ago';
	}

	return Math.floor(diff / 2592000) + 'mo ago';
});

// repositories/framework/addons/render/elements/front/functions/types/render/avatar.js
elements.Fn('type.avatar', function(column, item)
{
	const raw = column.id ? item[column.id] : undefined;
	const src = typeof raw === 'string' ? raw : (raw && raw.src);
	const name = (raw && raw.name) || '';

	return src
		? '<div class="ot-type-avatar"><img src="' + elements.Fn('type.escape', src) + '" alt="' + elements.Fn('type.escape', name) + '"/></div>'
		: '<div class="ot-type-avatar ot-type-avatar-fallback"><i>person</i></div>';
});

// repositories/framework/addons/render/elements/front/functions/types/render/badge.js
elements.Fn('type.badge', function(column, item)
{
	const config = column.config || {};
	const raw = column.id ? item[column.id] : undefined;

	const obj = (typeof raw === 'object' && raw) ? raw : { label: raw };
	const icon = obj.icon || '';
	const label = obj.label !== undefined ? obj.label : obj;
	const colors = elements.Fn('type.colors.badge', config.colors);
	const key = label ? String(label).toLowerCase() : '';
	const color = obj.color || colors[key] || config.color || 'neutral';

	return '<span class="ot-type-badge color-' + elements.Fn('type.escape', color) + '">'
		+ (icon ? '<i>' + elements.Fn('type.escape', icon) + '</i>' : '')
		+ '<span>' + elements.Fn('type.escape', label || '—') + '</span>'
	+ '</span>';
});

// repositories/framework/addons/render/elements/front/functions/types/render/boolean.js
elements.Fn('type.boolean', function(column, item)
{
	const raw = column.id ? item[column.id] : undefined;

	return raw
		? '<i class="ot-type-boolean yes">check_circle</i>'
		: '<i class="ot-type-boolean no">cancel</i>';
});

// repositories/framework/addons/render/elements/front/functions/types/render/chip.js
elements.Fn('type.chip', function(column, item)
{
	const raw = column.id ? item[column.id] : undefined;

	return '<span class="ot-type-chip">' + elements.Fn('type.escape', raw === null || raw === undefined ? '—' : raw) + '</span>';
});

// repositories/framework/addons/render/elements/front/functions/types/render/color.js
elements.Fn('type.color', function(column, item)
{
	const config = column.config || {};
	const raw = column.id ? item[column.id] : undefined;
	const value = config.value !== undefined ? config.value : raw;

	if(!value) return '';

	const escaped = elements.Fn('type.escape', value);

	return '<span class="ot-type-color">'
		+ '<span class="ot-type-color-swatch" style="background:' + escaped + '"></span>'
		+ '<span class="ot-type-color-value">' + escaped + '</span>'
	+ '</span>';
});

// repositories/framework/addons/render/elements/front/functions/types/render/count.js
elements.Fn('type.count', function(column, item)
{
	const config = column.config || {};
	const raw = column.id ? item[column.id] : undefined;
	const value = Array.isArray(raw) ? raw.length : (raw || 0);
	const label = config.label || '';

	return '<span class="ot-type-count">'
		+ '<span class="ot-type-count-value">' + elements.Fn('type.escape', String(value)) + '</span>'
		+ (label ? ' <span class="ot-type-count-label">' + elements.Fn('type.escape', label) + '</span>' : '')
	+ '</span>';
});

// repositories/framework/addons/render/elements/front/functions/types/render/currency.js
elements.Fn('type.currency', function(column, item)
{
	const config = column.config || {};
	const raw = column.id ? item[column.id] : undefined;

	return '<span class="ot-type-number">' + elements.Fn('type.escape', elements.Fn('type.format.currency', raw, config.currency)) + '</span>';
});

// repositories/framework/addons/render/elements/front/functions/types/render/date.js
elements.Fn('type.date', function(column, item)
{
	const raw = column.id ? item[column.id] : undefined;

	return '<span class="ot-type-date">' + elements.Fn('type.escape', elements.Fn('type.format.date', raw)) + '</span>';
});

// repositories/framework/addons/render/elements/front/functions/types/render/description.js
elements.Fn('type.description', function(column, item)
{
	const config = column.config || {};
	const raw = column.id ? item[column.id] : undefined;
	const value = config.value !== undefined ? config.value : raw;
	const max = config.max || 70;

	const string = value === null || value === undefined || value === '' ? '—' : String(value);
	const truncated = string.length > max ? string.slice(0, max) + '…' : string;

	return '<span class="ot-type-description">' + elements.Fn('type.escape', truncated) + '</span>';
});

// repositories/framework/addons/render/elements/front/functions/types/render/group.js
elements.Fn('type.group', function(column, item)
{
	const config = column.config || {};
	const layout = config.layout || 'row';
	const gap = config.gap || 'small';
	const fields = config.fields || [];

	return '<div class="ot-type-group layout-' + layout + ' gap-' + gap + '">'
		+ fields.map(field => elements.Fn('type.render', field, item)).join('')
	+ '</div>';
});

// repositories/framework/addons/render/elements/front/functions/types/render/icon.js
elements.Fn('type.icon', function(column, item)
{
	const config = column.config || {};
	const raw = column.id ? item[column.id] : undefined;
	const value = config.value !== undefined ? config.value : raw;

	return value ? '<i class="ot-type-icon">' + elements.Fn('type.escape', value) + '</i>' : '';
});

// repositories/framework/addons/render/elements/front/functions/types/render/image.js
elements.Fn('type.image', function(column, item)
{
	const config = column.config || {};
	const raw = column.id ? item[column.id] : undefined;
	const src = typeof raw === 'string' ? raw : (raw && raw.src);
	const fit = config.fit === 'cover' ? '' : ' contain';

	return src
		? '<div class="ot-type-image' + fit + '" style="background-image:url(\'' + elements.Fn('type.escape', src) + '\')"></div>'
		: '<div class="ot-type-image empty"></div>';
});

// repositories/framework/addons/render/elements/front/functions/types/render/link.js
elements.Fn('type.link', function(column, item)
{
	const raw = column.id ? item[column.id] : undefined;

	const obj = (typeof raw === 'object' && raw) ? raw : { label: raw, href: '#' };

	return '<a class="ot-type-link" href="' + elements.Fn('type.escape', obj.href || '#') + '">' + elements.Fn('type.escape', obj.label || raw || '—') + '</a>';
});

// repositories/framework/addons/render/elements/front/functions/types/render/media.js
elements.Fn('type.media', function(column, item)
{
	const config = column.config || {};

	const image = item[config.image || 'image'];
	const title = item[config.title || 'name'] || '';
	const subtitle = item[config.subtitle || 'subtitle'] || '';

	return '<div class="ot-type-media">'
		+ (image ? '<div class="ot-type-media-thumb" style="background-image:url(\'' + elements.Fn('type.escape', image) + '\')"></div>' : '<div class="ot-type-media-thumb empty"><i>image</i></div>')
		+ '<div class="ot-type-media-info">'
			+ '<div class="ot-type-media-title">' + elements.Fn('type.escape', title) + '</div>'
			+ (subtitle ? '<div class="ot-type-media-subtitle">' + elements.Fn('type.escape', subtitle) + '</div>' : '')
		+ '</div>'
	+ '</div>';
});

// repositories/framework/addons/render/elements/front/functions/types/render/metric.js
elements.Fn('type.metric', function(column, item)
{
	const raw = column.id ? item[column.id] : undefined;

	const value = typeof raw === 'object' && raw ? raw.value : raw;
	const delta = typeof raw === 'object' && raw ? raw.delta : null;
	const direction = delta && String(delta).startsWith('-') ? 'down' : 'up';

	return '<span class="ot-type-metric">'
		+ '<span class="ot-type-metric-value">' + elements.Fn('type.escape', value === null || value === undefined ? '—' : value) + '</span>'
		+ (delta ? '<span class="ot-type-metric-delta ' + direction + '">' + elements.Fn('type.escape', delta) + '</span>' : '')
	+ '</span>';
});

// repositories/framework/addons/render/elements/front/functions/types/render/number.js
elements.Fn('type.number', function(column, item)
{
	const raw = column.id ? item[column.id] : undefined;

	return '<span class="ot-type-number">' + elements.Fn('type.escape', elements.Fn('type.format.number', raw)) + '</span>';
});

// repositories/framework/addons/render/elements/front/functions/types/render/progress.js
elements.Fn('type.progress', function(column, item)
{
	const config = column.config || {};
	const raw = column.id ? item[column.id] : undefined;

	const percent = Math.max(0, Math.min(100, Number(raw) || 0));
	const color = config.color || 'brand';

	return '<div class="ot-type-progress">'
		+ '<div class="ot-type-progress-track"><div class="ot-type-progress-bar color-' + elements.Fn('type.escape', color) + '" style="width:' + percent + '%"></div></div>'
		+ '<span class="ot-type-progress-label">' + percent + '%</span>'
	+ '</div>';
});

// repositories/framework/addons/render/elements/front/functions/types/render/status.js
elements.Fn('type.status', function(column, item)
{
	const config = column.config || {};
	const raw = column.id ? item[column.id] : undefined;

	const colors = elements.Fn('type.colors.status', config.colors);
	const key = raw ? String(raw).toLowerCase() : '';
	const color = colors[key] || config.color || 'neutral';
	const label = raw ? String(raw).charAt(0).toUpperCase() + String(raw).slice(1) : '—';

	return '<span class="ot-type-status color-' + color + '"><span class="ot-type-status-dot"></span>' + elements.Fn('type.escape', label) + '</span>';
});

// repositories/framework/addons/render/elements/front/functions/types/render/tag.js
elements.Fn('type.tag', function(column, item)
{
	const raw = column.id ? item[column.id] : undefined;

	return '<span class="ot-type-tag">' + elements.Fn('type.escape', raw === null || raw === undefined ? '—' : raw) + '</span>';
});

// repositories/framework/addons/render/elements/front/functions/types/render/tags.js
elements.Fn('type.tags', function(column, item)
{
	const raw = column.id ? item[column.id] : undefined;

	if(!Array.isArray(raw) || !raw.length)
	{
		return '<span class="ot-type-text">—</span>';
	}

	return '<div class="ot-type-tags">' + raw.map(tag => '<span class="ot-type-tag">' + elements.Fn('type.escape', tag) + '</span>').join('') + '</div>';
});

// repositories/framework/addons/render/elements/front/functions/types/render/text.js
elements.Fn('type.text', function(column, item)
{
	const config = column.config || {};
	const raw = column.id ? item[column.id] : undefined;
	const value = config.value !== undefined ? config.value : raw;

	return '<span class="ot-type-text">' + elements.Fn('type.escape', value === null || value === undefined || value === '' ? '—' : value) + '</span>';
});

// repositories/framework/addons/render/elements/front/functions/types/render/timeago.js
elements.Fn('type.timeago', function(column, item)
{
	const raw = column.id ? item[column.id] : undefined;

	return '<span class="ot-type-date">' + elements.Fn('type.escape', elements.Fn('type.format.timeago', raw)) + '</span>';
});

// repositories/framework/addons/render/elements/front/functions/types/render.js
elements.Fn('type.render', function(column, item)
{
	if(column.render)
	{
		return column.render(item, column);
	}

	const type = column.type || 'text';

	if(elements.FnGet('type.' + type))
	{
		return elements.Fn('type.' + type, column, item);
	}

	return elements.Fn('type.text', column, item);
});

// repositories/framework/addons/render/elements/front/item/catch/add.js
elements.ItemOn('add', (item) =>
{
    elements.RenderAdd(item.Get('id'), function()
    {
        this.Define(item.Get('config'));

        return item.Get('render').call(this);
    });
})

// repositories/framework/addons/render/elements/front/items/directives/element.js
onetype.AddonReady('directives', function(directives)
{
	directives.ItemAdd({
		id: 'ot-element',
		icon: 'auto_awesome',
		name: 'Element Load',
		description: 'Load and render elements using custom tag syntax. Automatically detects <e-{name}> tags and renders corresponding elements.',
		trigger: 'node',
		order: 2500,
		strict: false,
		type: '1',
		code: function(data, item, compile, node, identifier)
		{
			if (!node.tagName || !node.tagName.toLowerCase().startsWith('e-') || node.tagName.toLowerCase() === 'e-bind')
			{
				return;
			}

			const tagName = node.tagName.toLowerCase();
			const elementName = tagName.substring(2);

			const attributes = {
				wrapper: {},
				data: {}
			};

			const toCamel = (name) =>
			{
				return name.replace(/-([a-z])/g, (match, letter) => letter.toUpperCase());
			};

			for (let i = 0; i < node.attributes.length; i++)
			{
				const attr = node.attributes[i];

				if (attr.name.startsWith('#'))
				{
					attributes.wrapper[toCamel(attr.name.substring(1))] = attr.value;
				}
				else if(attr.name.startsWith(':'))
				{
					attributes.data[toCamel(attr.name.substring(1))] = onetype.Function(attr.value, compile.data, false);
				}
				else
				{
					attributes.data[toCamel(attr.name)] = attr.value;
				}
			}

			const slots = {};

			if(node.hasChildNodes())
			{
				Array.from(node.childNodes).forEach(child =>
				{
					if(child.nodeType === Node.ELEMENT_NODE)
					{
						const slot = child.getAttribute('slot');

						if(slot)
						{
							child.removeAttribute('slot');
							slots[slot] = { html: child.outerHTML, data: compile.data };
						}
					}
				});
			}

			const key = attributes.wrapper['ot-key'] || attributes.data['ot-key'] || identifier;

			delete attributes.wrapper['ot-key'];
			delete attributes.data['ot-key'];

			const render = onetype.Addon('elements').Render(elementName, attributes.data, attributes.wrapper, slots);

			if(render)
			{
				render.Element.__otExternal = { render, name: elementName, key, data: attributes.data };
				node.replaceWith(render.Element);
			}
			else
			{
				node.innerText = 'Element ' + elementName + ' does not exist.';
			}
		}
	});
});

// repositories/framework/addons/render/html/addon.js
import onetype from '#framework/load.js';

const html = onetype.Addon('html', (addon) =>
{
    addon.Field('id', ['string']);
    addon.Field('tag', ['string', 'div']);
    addon.Field('position', ['string', 'head']);
    addon.Field('parent', ['string']);
    addon.Field('content', ['string']);
    addon.Field('attributes', ['object', {}]);
});

export default html;

// repositories/framework/addons/render/html/functions/render.js
import html from '#html/addon.js';

html.Fn('render', function(context = {})
{
    this.methods.init = () =>
    {
        const sections = { head: [], body: [] };
        const sortedItems = this.methods.getSortedItems();

        sortedItems.forEach(item => this.methods.processItem(item, sections));

        if (typeof context.head === 'function') sections.head.push(context.head());
        if (typeof context.body === 'function') sections.body.push(context.body());

        return this.methods.buildHTML(sections);
    };

    this.methods.getSortedItems = () =>
    {
        return Object.values(this.Items()).sort((a, b) =>
            (a.Get('priority') || 0) - (b.Get('priority') || 0)
        );
    };

    this.methods.processItem = (item, sections) =>
    {
        const position = item.Get('position').toLowerCase();
        const content = item.Fn('render');

        if (content && sections[position])
        {
            sections[position].push(content);
        }
    };

    this.methods.resolveAttributeValue = (item, attrName, value, visited = new Set()) =>
    {
        if (value !== 'inherit')
        {
            return value;
        }

        if (visited.has(item.Get('id')))
        {
            return undefined;
        }

        visited.add(item.Get('id'));

        const parentId = item.Get('parent');

        if (!parentId)
        {
            return undefined;
        }

        const parentItem = this.ItemGet(parentId);

        if (!parentItem)
        {
            return undefined;
        }

        const parentAttrs = parentItem.Get('attributes');
        const parentValue = parentAttrs[attrName];

        return this.methods.resolveAttributeValue(parentItem, attrName, parentValue, visited);
    };

    this.methods.buildHTML = (sections) =>
    {
        return `<!DOCTYPE html>
<html lang="en">
    <head>
        ${sections.head.join('')}
    </head>

    <body>
        ${sections.body.join('')}
    </body>
</html>`;
    };

    return this.methods.init();
});

// repositories/framework/addons/render/html/item/functions/render.js
import html from '#html/addon.js';
import tags from '#tags/load.js';

html.Fn('item.render', function(item)
{
    this.methods.init = () =>
    {
        const tag = tags.ItemGet(item.Get('tag'));

        if (!tag)
        {
            return '';
        }

        return this.methods.element(item, tag);
    };

    this.methods.element = (item, tag) =>
    {
        const parts = [];

        parts.push('<');
        parts.push(tag.Get('id'));
        parts.push(this.methods.attributes(item));
        parts.push('>');

        if (tag.Get('closeable'))
        {
            const content = item.Get('content');

            if (content)
            {
                parts.push(content);
            }
            
            parts.push('</');
            parts.push(tag.Get('id'));
            parts.push('>');
        }

        return parts.join('');
    };

    this.methods.attributes = (item) =>
    {
        const attrs = item.Get('attributes');
        const parts = [];

        Object.entries(attrs).forEach(([key, value]) =>
        {
            parts.push(' ');
            parts.push(this.onetype.StringSanitize(key));
            
            if (value !== null && value !== undefined && value !== '')
            {
                parts.push('="');
                parts.push(this.onetype.StringSanitize(value));
                parts.push('"');
            }
        });

        return parts.join('');
    };

    return this.methods.init();
});

// repositories/framework/addons/render/html/items/assets/favicon.js
import html from '#html/addon.js';

html.Item({
    id: 'asset-favicon',
    tag: 'link',
    position: 'head',
    priority: -60,
    attributes: {
        rel: 'icon',
        href: 'https://images.onetype.ai/96752e47-1bea-4313-025c-5b76dc174200/public'
    }
});

// repositories/framework/addons/render/html/items/fonts/outfit.js
import html from '#html/addon.js';

html.Item({
    id: 'font-outfit',
    tag: 'link',
    position: 'head',
    priority: -40,
    attributes: {
        href: 'https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@200..800&display=swap',
        rel: 'stylesheet'
    }
});

// repositories/framework/addons/render/html/items/fonts/preconnect.js
import html from '#html/addon.js';

html.Item({
    id: 'font-preconnect-api',
    tag: 'link',
    position: 'head',
    priority: -50,
    attributes: {
        rel: 'preconnect',
        href: 'https://fonts.googleapis.com'
    }
});

html.Item({
    id: 'font-preconnect-static',
    tag: 'link',
    position: 'head',
    priority: -49,
    attributes: {
        rel: 'preconnect',
        href: 'https://fonts.gstatic.com',
        crossorigin: null
    }
});

// repositories/framework/addons/render/html/items/meta/charset.js
import html from '#html/addon.js';

html.Item({
    id: 'meta-charset',
    tag: 'meta',
    position: 'head',
    priority: -100,
    attributes: {
        charset: 'UTF-8'
    }
});

// repositories/framework/addons/render/html/items/meta/description.js
import html from '#html/addon.js';

html.Item({
    id: 'meta-description',
    tag: 'meta',
    position: 'head',
    priority: -70,
    attributes: {
        name: 'description',
        content: 'Modern web application built with OneType'
    }
});

// repositories/framework/addons/render/html/items/meta/title.js
import html from '#html/addon.js';

html.Item({
    id: 'meta-title',
    tag: 'title',
    content: 'OneType App',
    position: 'head',
    priority: -80
});

// repositories/framework/addons/render/html/items/meta/viewport.js
import html from '#html/addon.js';

html.Item({
    id: 'meta-viewport',
    tag: 'meta',
    position: 'head',
    priority: -90,
    attributes: {
        name: 'viewport',
        content: 'width=device-width, initial-scale=1.0'
    }
});

// repositories/framework/addons/render/html/items/seo/robots.js
import html from '#html/addon.js';

html.Item({
    id: 'seo-robots',
    tag: 'meta',
    position: 'head',
    priority: -30,
    attributes: {
        name: 'robots',
        content: 'index, follow'
    }
});

// repositories/framework/addons/render/html/load.js
import html from '#html/addon.js';

/* Functions */
import '#html/functions/render.js';

/* Item Functions */
import '#html/item/functions/render.js';

/* Essential Items - Meta */
import '#html/items/meta/charset.js';
import '#html/items/meta/viewport.js';
// import '#html/items/meta/title.js';
// import '#html/items/meta/description.js';

/* Essential Items - Assets */
import '#html/items/assets/favicon.js';

/* Essential Items - Fonts */
import '#html/items/fonts/preconnect.js';
import '#html/items/fonts/outfit.js';

/* Essential Items - SEO */
import '#html/items/seo/robots.js';

export default html;

// repositories/framework/addons/render/pages/core/addon.js
import onetype from '#framework/load.js';

const pages = onetype.Addon('pages', (addon) =>
{
	addon.Field('id', ['string|number']);
	addon.Field('route', ['string|array'], null, (value) =>
	{
		const base = onetype.Base();

		if(!base)
		{
			return value;
		}

		if(Array.isArray(value))
		{
			return value.map(route => base + route);
		}

		return base + value;
	});
	addon.Field('title', ['string|function']);
	addon.Field('meta', ['object', {}]);
	addon.Field('data', ['function']);

	addon.Field('grid', {
		type: 'object',
		value: {},
		config: {
			template: ['string', '"main"'],
			columns: ['string', '1fr'],
			rows: ['string', '1fr'],
			gap: ['string', '0']
		}
	});

	addon.Field('areas', ['object', {}]);

	addon.Field('onBeforeLeave', ['function']);
	addon.Field('onLeave', ['function']);
	addon.Field('onBeforeEnter', ['function']);
	addon.Field('onEnter', ['function']);

	addon.Field('element', ['object']);
	addon.Field('404', ['boolean', false]);
});

export default pages;

// repositories/framework/addons/render/pages/core/load.js
import onetype from '#framework/load.js';
import pages from './addon.js';

onetype.$ot.page = function(route, parameters = {})
{
	return pages.Fn('change', null, route, parameters);
};

export default pages;

// repositories/framework/addons/render/pages/front/events/click.js
document.addEventListener('click', (e) =>
{
	const a = e.target.closest('a');

	if(!a || !a.href)
	{
		return;
	}

	const url = new URL(a.href);

	if(url.origin !== window.location.origin)
	{
		return;
	}

	const match = pages.Fn('match', url.pathname);

	if(!match)
	{
		return;
	}

	e.preventDefault();

	pages.Fn('change', match.page.Get('id'), null, match.parameters, true, url.search);
});

// repositories/framework/addons/render/pages/front/events/load.js
const match = () =>
{
	requestAnimationFrame(() =>
	{
		const resolved = pages.Fn('resolve', null, onetype.RouteCurrent());

		if(resolved)
		{
			pages.Fn('open', resolved.id, resolved.parameters);
		}
	});
};

onetype.EmitOn('@document.load', match);
onetype.EmitOn('@history.popstate', match);

// repositories/framework/addons/render/pages/front/functions/change.js
pages.Fn('change', async function(id, path = null, parameters = {}, push = true, search = '')
{
	this.methods.route = (page) =>
	{
		const route = page.Get('route');

		if(!Array.isArray(route))
		{
			return route;
		}

		return route.findLast(pattern => (pattern.match(/:(\w+)/g) || []).every(param => Object.keys(parameters).includes(param.slice(1)))) || route[0];
	};

	const resolved = this.Fn('resolve', id, path, parameters);

	if(!resolved)
	{
		return null;
	}

	if(push !== false)
	{
		history.pushState(null, '', onetype.RouteBuild(this.methods.route(resolved.page), resolved.parameters) + search);
	}

	const result = await this.Fn('open', resolved.id, resolved.parameters);

	return result === false ? null : resolved.page;
});

// repositories/framework/addons/render/pages/front/functions/match.js
pages.Fn('match', function(path)
{
	const items = Object.values(pages.Items());

	for(const item of items)
	{
		if(item.Get('404'))
		{
			continue;
		}

		const route = item.Get('route');

		if(!route)
		{
			continue;
		}

		const routes = Array.isArray(route) ? route : [route];

		for(const part of routes)
		{
			const result = onetype.RouteMatch(part, path || onetype.RouteCurrent());

			if(result.match)
			{
				return { page: item, parameters: result.params };
			}
		}
	}

	return null;
});

// repositories/framework/addons/render/pages/front/functions/open.js
pages.Fn('open', async function(id, parameters = {})
{
	const page = pages.ItemGet(id);

	if(!page)
	{
		return false;
	}

	const data = page.Get('data') ? await page.Get('data').call(page, parameters) : null;

	const active = pages.StoreGet('active');

	if(active && active.Fn('leave') === false)
	{
		return false;
	}

	return await page.Fn('enter', parameters, data);
});

// repositories/framework/addons/render/pages/front/functions/resolve.js
pages.Fn('resolve', function(id, path = null, parameters = {})
{
	if(path)
	{
		const match = this.Fn('match', path);

		if(match)
		{
			return { page: match.page, id: match.page.Get('id'), parameters: { ...match.parameters, ...parameters } };
		}
	}
	else if(id)
	{
		const page = this.ItemGet(id);

		if(page)
		{
			return { page, id, parameters };
		}
	}

	const page = Object.values(this.Items()).find(p => p.Get('404'));

	if(page)
	{
		return { page, id: page.Get('id'), parameters };
	}

	return null;
});

// repositories/framework/addons/render/pages/front/item/catch/add.js
pages.ItemOn('add', (item) =>
{
	const areas = item.Get('areas');

	if(areas)
	{
		for(const [name, value] of Object.entries(areas))
		{
			if(typeof value === 'function')
			{
				pages.RenderAdd(item.Get('id') + ':' + name, function(context)
				{
					const parameters = context?.parameters || {};
					const data = context?.data || {};

					this.Define({
						parameters: ['object', {}],
						data: ['object', {}],
					});

					return value.call(this, {parameters, data});
				});
			}
		}
	}
});

// repositories/framework/addons/render/pages/front/item/catch/remove.js
pages.ItemOn('removed', (item) =>
{
	const areas = item.Get('areas');

	if(areas)
	{
		for(const [name, value] of Object.entries(areas))
		{
			if(typeof value === 'function')
			{
				pages.RenderRemove(item.Get('id') + ':' + name);
			}
		}
	}
});

// repositories/framework/addons/render/pages/front/item/functions/enter.js
pages.Fn('item.enter', async function(item, parameters = {}, data = null)
{
	if(item.Get('onBeforeEnter') && item.Get('onBeforeEnter').call(item, parameters, data) === false)
	{
		return false;
	}

	pages.StoreSet('active', item);

	onetype.StateSet('page', { 
		id: item.Get('id'), 
		route: item.Get('route'), 
		meta: item.Get('meta'),
		parameters,
		data, 
		'404': item.Get('404') 
	});

	const title = item.Get('title');

	if(title)
	{
		document.title = typeof title === 'function' ? title.call(item, parameters, data) : title;
	}

	const element = item.Fn('render', parameters, data);
	item.Set('element', element);

	document.body.appendChild(element);

	item.Get('onEnter') && item.Get('onEnter').call(item, parameters, data);

	onetype.Emit('@pages.enter', { page: item, parameters, data });

	return true;
});

// repositories/framework/addons/render/pages/front/item/functions/leave.js
pages.Fn('item.leave', function(item)
{
	if(item.Get('onBeforeLeave') && item.Get('onBeforeLeave').call(item) === false)
	{
		return false;
	}

	pages.StoreSet('active', null);

	onetype.StateSet('page', null);

	item.Get('onLeave') && item.Get('onLeave').call(item);

	onetype.Emit('@pages.leave', { page: item });

	const element = item.Get('element');

	if(element)
	{
		element.remove();
		item.Set('element', null);
	}

	return true;
});

// repositories/framework/addons/render/pages/front/item/functions/render.js
pages.Fn('item.render', function(item, parameters = {}, data = null)
{
	const grid = item.Get('grid');
	const element = document.createElement('div');

	element.classList.add('ot-page');
	element.setAttribute('data-page', item.Get('id'));
	element.style.cssText = [
		`grid-template-areas: ${grid.template}`,
		`grid-template-columns: ${grid.columns}`,
		`grid-template-rows: ${grid.rows}`,
		`gap: ${grid.gap}`
	].join('; ');

	const template = grid.template;
	const areas = [...new Set(template.match(/[\w-]+/g) || [])];

	areas.forEach(name =>
	{
		const container = document.createElement('div');

		container.classList.add('ot-page-area');
		container.setAttribute('data-area', name);
		container.style.gridArea = name;

		const render = pages.Render(item.Get('id') + ':' + name, {parameters, data});

		container.appendChild(render.Element);
		element.appendChild(container);
	});

	return element;
});

// repositories/framework/addons/render/pages/front/items/commands/change.js
onetype.AddonReady('commands', (commands) =>
{
	commands.Item({
		id: 'pages:change',
		method: 'POST',
		endpoint: '/api/pages/change',
		exposed: true,
		description: 'Navigate to an application page by ID or path',
		in: {
			id: {
				type: 'string',
				description: 'Page ID to navigate to'
			},
			path: {
				type: 'string',
				description: 'URL path to navigate to'
			},
			parameters: {
				type: 'object',
				value: {},
				description: 'Route parameters'
			},
			push: {
				type: 'boolean',
				value: true,
				description: 'Push to browser history'
			}
		},
		out: {
			page: {
				type: 'object',
				config: {
					id: ['string'],
					route: ['string|array'],
					meta: ['object'],
					'404': ['boolean']
				}
			}
		},
		callback: async function(properties, resolve)
		{
			if(!properties.id && !properties.path)
			{
				resolve({ page: null });
				return;
			}

			const page = await pages.Fn('change', properties.id, properties.path, properties.parameters, properties.push);

			resolve({
				page: page ? {
					id: page.Get('id'),
					route: page.Get('route'),
					meta: page.Get('meta'),
					'404': page.Get('404')
				} : null
			});
		}
	});
});

// repositories/framework/addons/render/pages/front/items/commands/list.js
onetype.AddonReady('commands', (commands) =>
{
	commands.Item({
		id: 'pages:list',
		method: 'GET',
		endpoint: '/api/pages/list',
		exposed: true,
		description: 'List all application pages with their routes and metadata',
		out: {
			pages: {
				type: 'array',
				each: {
					type: 'object',
					config: {
						id: ['string'],
						route: ['string|array'],
						meta: ['object'],
						'404': ['boolean']
					}
				}
			}
		},
		callback: function(properties, resolve)
		{
			const items = Object.values(pages.Items());

			resolve({
				pages: items.map(item =>
				({
					id: item.Get('id'),
					route: item.Get('route'),
					meta: item.Get('meta'),
					'404': item.Get('404')
				}))
			});
		}
	});
});

// repositories/framework/addons/render/pages/front/items/directives/change.js
onetype.AddonReady('directives', function(directives)
{
	directives.ItemAdd({
		id: 'ot-page',
		icon: 'file',
		name: 'Page',
		description: 'Navigate to a page on render',
		category: 'navigation',
		trigger: 'node',
		order: 666,
		strict: true,
		tag: 'ot-page',
		attributes: {
			'route': ['string', null, true],
			'parameters': ['object', {}],
			'history': ['boolean', true],
			'timeout': ['number', 0]
		},
		code: function(data, item, compile, node, identifier)
		{
			const route = data['route'].value;
			const parameters = data['parameters'].value;
			const push = data['history'].value;
			const timeout = data['timeout'].value;

			const change = () =>
			{
				pages.Fn('change', null, route, parameters, push);
			};

			if(timeout > 0)
			{
				setTimeout(change, timeout);
			}
			else
			{
				change();
			}
		}
	});
});

// repositories/framework/addons/render/tags/addon.js
import onetype from '#framework/load.js';

const tags = onetype.Addon('tags', (addon) =>
{
    addon.Field('id', ['string']);
    addon.Field('closeable', ['boolean', true]);
    addon.Field('text', ['boolean', true]);
    addon.Field('nest', ['object', {}]);
});

export default tags;

// repositories/framework/addons/render/tags/functions/groups.js
import tags from '#tags/addon.js';

tags.Fn('groups', function()
{
    return {
        'text-inline': ['a', 'abbr', 'b', 'bdi', 'bdo', 'br', 'cite', 'code', 'em', 'i', 'img', 'kbd', 'mark', 'q', 'small', 'span', 'strong', 'sub', 'sup', 'time', 'u', 'var', 'del', 'ins'],
        'form': ['input', 'textarea', 'select', 'option', 'optgroup', 'form', 'button', 'label', 'fieldset', 'legend', 'datalist', 'output'],
        'block': ['div', 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'pre', 'ul', 'ol', 'li', 'table', 'section', 'article', 'aside', 'nav', 'header', 'footer', 'main', 'figure'],
        'table': ['table', 'thead', 'tbody', 'tfoot', 'tr', 'th', 'td', 'caption', 'colgroup', 'col'],
        'media': ['img', 'video', 'audio', 'source', 'track', 'embed', 'iframe'],
        'void': ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'source', 'track', 'wbr']
    };
});

// repositories/framework/addons/render/tags/functions/validate.js
import tags from '#tags/addon.js';

tags.Fn('validate', function(parentTag, childTag)
{
    const parent = this.ItemGet(parentTag);
    const child = this.ItemGet(childTag);
    
    if(!parent || !child)
    {
        return false;
    }
    
    const nest = parent.Get('nest');
    const groups = this.Fn('groups');
    
    this.methods.check = (rules, tag) =>
    {
        return rules.some(rule =>
        {
            if(rule === '*')
            {
                return true;
            }
            
            if(rule.startsWith('@'))
            {
                const groupName = rule.substring(1);
                return groups[groupName] && groups[groupName].includes(tag);
            }
            
            return rule === tag;
        });
    };
    
    if(nest.allowed)
    {
        if(nest.allowed.includes('*'))
        {
            return !nest.disallowed || !this.methods.check(nest.disallowed, childTag);
        }
        
        return this.methods.check(nest.allowed, childTag);
    }
    
    if(nest.disallowed)
    {
        return !this.methods.check(nest.disallowed, childTag);
    }
    
    return true;
});

// repositories/framework/addons/render/tags/items/block.js
import tags from '#tags/addon.js';

// Headings
tags.Item({
    id: 'h1',
    closeable: true,
    text: true,
    nest: {
        allowed: ['@text-inline']
    }
});

tags.Item({
    id: 'h2',
    closeable: true,
    text: true,
    nest: {
        allowed: ['@text-inline']
    }
});

tags.Item({
    id: 'h3',
    closeable: true,
    text: true,
    nest: {
        allowed: ['@text-inline']
    }
});

tags.Item({
    id: 'h4',
    closeable: true,
    text: true,
    nest: {
        allowed: ['@text-inline']
    }
});

tags.Item({
    id: 'h5',
    closeable: true,
    text: true,
    nest: {
        allowed: ['@text-inline']
    }
});

tags.Item({
    id: 'h6',
    closeable: true,
    text: true,
    nest: {
        allowed: ['@text-inline']
    }
});

// Text blocks
tags.Item({
    id: 'p',
    closeable: true,
    text: true,
    nest: {
        allowed: ['@text-inline']
    }
});

tags.Item({
    id: 'blockquote',
    closeable: true,
    text: true,
    nest: {
        allowed: ['@text-inline', '@block']
    }
});

tags.Item({
    id: 'pre',
    closeable: true,
    text: true,
    nest: {
        allowed: ['@text-inline']
    }
});

tags.Item({
    id: 'hr',
    closeable: false,
    text: false,
    nest: {
        disallowed: ['*']
    }
});

// Generic containers
tags.Item({
    id: 'div',
    closeable: true,
    text: true,
    nest: {
        allowed: ['*']
    }
});

// Lists
tags.Item({
    id: 'ul',
    closeable: true,
    text: false,
    nest: {
        allowed: ['li']
    }
});

tags.Item({
    id: 'ol',
    closeable: true,
    text: false,
    nest: {
        allowed: ['li']
    }
});

tags.Item({
    id: 'li',
    closeable: true,
    text: true,
    nest: {
        allowed: ['*']
    }
});

// repositories/framework/addons/render/tags/items/form.js
import tags from '#tags/addon.js';

tags.Item({
    id: 'form',
    closeable: true,
    text: true,
    nest: {
        allowed: ['*'],
        disallowed: ['form']
    }
});

tags.Item({
    id: 'fieldset',
    closeable: true,
    text: true,
    nest: {
        allowed: ['*']
    }
});

tags.Item({
    id: 'legend',
    closeable: true,
    text: true,
    nest: {
        allowed: ['@text-inline']
    }
});

tags.Item({
    id: 'label',
    closeable: true,
    text: true,
    nest: {
        allowed: ['@text-inline', '@form'],
        disallowed: ['label']
    }
});

tags.Item({
    id: 'input',
    closeable: false,
    text: false,
    nest: {
        disallowed: ['*']
    }
});

tags.Item({
    id: 'textarea',
    closeable: true,
    text: true,
    nest: {
        disallowed: ['*']
    }
});

tags.Item({
    id: 'select',
    closeable: true,
    text: false,
    nest: {
        allowed: ['option', 'optgroup']
    }
});

tags.Item({
    id: 'option',
    closeable: true,
    text: true,
    nest: {
        disallowed: ['*']
    }
});

tags.Item({
    id: 'optgroup',
    closeable: true,
    text: false,
    nest: {
        allowed: ['option']
    }
});

tags.Item({
    id: 'datalist',
    closeable: true,
    text: false,
    nest: {
        allowed: ['option']
    }
});

tags.Item({
    id: 'button',
    closeable: true,
    text: true,
    nest: {
        allowed: ['@text-inline'],
        disallowed: ['@form']
    }
});

tags.Item({
    id: 'output',
    closeable: true,
    text: true,
    nest: {
        allowed: ['@text-inline']
    }
});

// repositories/framework/addons/render/tags/items/media.js
import tags from '#tags/addon.js';

tags.Item({
    id: 'img',
    closeable: false,
    text: false,
    nest: {
        disallowed: ['*']
    }
});

tags.Item({
    id: 'video',
    closeable: true,
    text: true,
    nest: {
        allowed: ['source', 'track']
    }
});

tags.Item({
    id: 'audio',
    closeable: true,
    text: true,
    nest: {
        allowed: ['source', 'track']
    }
});

tags.Item({
    id: 'source',
    closeable: false,
    text: false,
    nest: {
        disallowed: ['*']
    }
});

tags.Item({
    id: 'track',
    closeable: false,
    text: false,
    nest: {
        disallowed: ['*']
    }
});

tags.Item({
    id: 'embed',
    closeable: false,
    text: false,
    nest: {
        disallowed: ['*']
    }
});

tags.Item({
    id: 'iframe',
    closeable: true,
    text: true,
    nest: {
        disallowed: ['*']
    }
});

// repositories/framework/addons/render/tags/items/meta.js
import tags from '#tags/addon.js';

tags.Item({
    id: 'meta',
    closeable: false,
    text: false,
    nest: {
        disallowed: ['*']
    }
});

tags.Item({
    id: 'link',
    closeable: false,
    text: false,
    nest: {
        disallowed: ['*']
    }
});

tags.Item({
    id: 'script',
    closeable: true,
    text: true,
    nest: {
        disallowed: ['*']
    }
});

// repositories/framework/addons/render/tags/items/semantic.js
import tags from '#tags/addon.js';

// Document structure
tags.Item({
    id: 'main',
    closeable: true,
    text: true,
    nest: {
        allowed: ['*'],
        disallowed: ['main']
    }
});

tags.Item({
    id: 'header',
    closeable: true,
    text: true,
    nest: {
        allowed: ['*'],
        disallowed: ['header', 'footer']
    }
});

tags.Item({
    id: 'footer',
    closeable: true,
    text: true,
    nest: {
        allowed: ['*'],
        disallowed: ['header', 'footer']
    }
});

tags.Item({
    id: 'nav',
    closeable: true,
    text: true,
    nest: {
        allowed: ['*']
    }
});

tags.Item({
    id: 'section',
    closeable: true,
    text: true,
    nest: {
        allowed: ['*']
    }
});

tags.Item({
    id: 'article',
    closeable: true,
    text: true,
    nest: {
        allowed: ['*']
    }
});

tags.Item({
    id: 'aside',
    closeable: true,
    text: true,
    nest: {
        allowed: ['*']
    }
});

// Content grouping
tags.Item({
    id: 'figure',
    closeable: true,
    text: true,
    nest: {
        allowed: ['*']
    }
});

tags.Item({
    id: 'figcaption',
    closeable: true,
    text: true,
    nest: {
        allowed: ['@text-inline']
    }
});

tags.Item({
    id: 'details',
    closeable: true,
    text: true,
    nest: {
        allowed: ['*']
    }
});

tags.Item({
    id: 'summary',
    closeable: true,
    text: true,
    nest: {
        allowed: ['@text-inline']
    }
});

tags.Item({
    id: 'dialog',
    closeable: true,
    text: true,
    nest: {
        allowed: ['*']
    }
});

// repositories/framework/addons/render/tags/items/table.js
import tags from '#tags/addon.js';

tags.Item({
    id: 'table',
    closeable: true,
    text: false,
    nest: {
        allowed: ['caption', 'colgroup', 'thead', 'tbody', 'tfoot', 'tr']
    }
});

tags.Item({
    id: 'caption',
    closeable: true,
    text: true,
    nest: {
        allowed: ['@text-inline']
    }
});

tags.Item({
    id: 'colgroup',
    closeable: true,
    text: false,
    nest: {
        allowed: ['col']
    }
});

tags.Item({
    id: 'col',
    closeable: false,
    text: false,
    nest: {
        disallowed: ['*']
    }
});

tags.Item({
    id: 'thead',
    closeable: true,
    text: false,
    nest: {
        allowed: ['tr']
    }
});

tags.Item({
    id: 'tbody',
    closeable: true,
    text: false,
    nest: {
        allowed: ['tr']
    }
});

tags.Item({
    id: 'tfoot',
    closeable: true,
    text: false,
    nest: {
        allowed: ['tr']
    }
});

tags.Item({
    id: 'tr',
    closeable: true,
    text: false,
    nest: {
        allowed: ['th', 'td']
    }
});

tags.Item({
    id: 'th',
    closeable: true,
    text: true,
    nest: {
        allowed: ['*'],
        disallowed: ['@form']
    }
});

tags.Item({
    id: 'td',
    closeable: true,
    text: true,
    nest: {
        allowed: ['*']
    }
});

// repositories/framework/addons/render/tags/items/text-inline.js
import tags from '#tags/addon.js';

// Basic text formatting
tags.Item({
    id: 'span',
    closeable: true,
    text: true,
    nest: {
        allowed: ['@text-inline']
    }
});

tags.Item({
    id: 'strong',
    closeable: true,
    text: true,
    nest: {
        allowed: ['@text-inline']
    }
});

tags.Item({
    id: 'em',
    closeable: true,
    text: true,
    nest: {
        allowed: ['@text-inline']
    }
});

tags.Item({
    id: 'b',
    closeable: true,
    text: true,
    nest: {
        allowed: ['@text-inline']
    }
});

tags.Item({
    id: 'i',
    closeable: true,
    text: true,
    nest: {
        allowed: ['@text-inline']
    }
});

tags.Item({
    id: 'u',
    closeable: true,
    text: true,
    nest: {
        allowed: ['@text-inline']
    }
});

tags.Item({
    id: 's',
    closeable: true,
    text: true,
    nest: {
        allowed: ['@text-inline']
    }
});

tags.Item({
    id: 'small',
    closeable: true,
    text: true,
    nest: {
        allowed: ['@text-inline']
    }
});

tags.Item({
    id: 'mark',
    closeable: true,
    text: true,
    nest: {
        allowed: ['@text-inline']
    }
});

tags.Item({
    id: 'del',
    closeable: true,
    text: true,
    nest: {
        allowed: ['@text-inline']
    }
});

tags.Item({
    id: 'ins',
    closeable: true,
    text: true,
    nest: {
        allowed: ['@text-inline']
    }
});

// Code and technical
tags.Item({
    id: 'code',
    closeable: true,
    text: true,
    nest: {
        allowed: ['@text-inline']
    }
});

tags.Item({
    id: 'kbd',
    closeable: true,
    text: true,
    nest: {
        allowed: ['@text-inline']
    }
});

tags.Item({
    id: 'var',
    closeable: true,
    text: true,
    nest: {
        allowed: ['@text-inline']
    }
});

// Semantic text
tags.Item({
    id: 'abbr',
    closeable: true,
    text: true,
    nest: {
        allowed: ['@text-inline']
    }
});

tags.Item({
    id: 'cite',
    closeable: true,
    text: true,
    nest: {
        allowed: ['@text-inline']
    }
});

tags.Item({
    id: 'dfn',
    closeable: true,
    text: true,
    nest: {
        allowed: ['@text-inline']
    }
});

tags.Item({
    id: 'q',
    closeable: true,
    text: true,
    nest: {
        allowed: ['@text-inline']
    }
});

tags.Item({
    id: 'time',
    closeable: true,
    text: true,
    nest: {
        allowed: ['@text-inline']
    }
});

// Subscript and superscript
tags.Item({
    id: 'sub',
    closeable: true,
    text: true,
    nest: {
        allowed: ['@text-inline']
    }
});

tags.Item({
    id: 'sup',
    closeable: true,
    text: true,
    nest: {
        allowed: ['@text-inline']
    }
});

// Links
tags.Item({
    id: 'a',
    closeable: true,
    text: true,
    nest: {
        allowed: ['@text-inline'],
        disallowed: ['a', '@form']
    }
});

// Line breaks (void elements)
tags.Item({
    id: 'br',
    closeable: false,
    text: false,
    nest: {
        disallowed: ['*']
    }
});

// repositories/framework/addons/render/tags/load.js
import tags from '#tags/addon.js';

/* Functions */
import '#tags/functions/groups.js';
import '#tags/functions/validate.js';

/* Items */
import '#tags/items/text-inline.js';
import '#tags/items/block.js';
import '#tags/items/semantic.js';
import '#tags/items/table.js';
import '#tags/items/form.js';
import '#tags/items/media.js';
import '#tags/items/meta.js';

export default tags;

// repositories/framework/addons/render/transforms/js/addon.js
const transforms = onetype.Addon('transforms', (addon) =>
{
    addon.Field('id', ['string']);
    addon.Field('icon', ['string', 'sync_alt']);
    addon.Field('name', ['string', '']);
    addon.Field('description', ['string', '']);
    addon.Field('js', ['array', []]);
    addon.Field('css', ['array', []]);
    addon.Field('config', ['object', {}]);
    addon.Field('metadata', ['object', {}]);
    addon.Field('tags', ['array', []]);
    addon.Field('code', ['function']);
    addon.Field('destroy', ['function', null]);
    addon.Field('visible', ['function', null]);
    addon.Field('resize', ['function', null]);
    addon.Field('scroll', ['function', null]);
    addon.Field('hover', ['function', null]);
    addon.Field('click', ['function', null]);
    addon.Field('structure', ['function', null]);
});

// repositories/framework/addons/render/transforms/js/functions/data.js
transforms.Fn('data', function(config, node)
{
    const data = {};

    Object.entries(config).forEach(([name, definition]) =>
    {
        name = name.toLowerCase();

        const attribute = 'ot-' + name;
        const raw = node.getAttribute(attribute);
        let value = raw;

        if(raw !== null)
        {
            const type = Array.isArray(definition) ? definition[0] : (definition && definition.type);

            if(type !== 'string')
            {
                try
                {
                    const evaluated = onetype.Function(raw, {}, false);

                    if(evaluated !== undefined)
                    {
                        value = evaluated;
                    }
                }
                catch(e) {}
            }
        }

        data[name] = onetype.DataDefineOne(value, definition);

        if(raw !== null)
        {
            node.removeAttribute(attribute);
        }
    });

    return data;
});

// repositories/framework/addons/render/transforms/js/functions/run.js
transforms.Fn('run', function(id, node, data = null)
{
    const item = transforms.ItemGet(id);

    if(!item)
    {
        return;
    }

    item.Fn('run', node, data);
});

// repositories/framework/addons/render/transforms/js/functions/runtime.js
transforms.Fn('runtime', function()
{
    this.methods.pending = new WeakSet();

    this.methods.process = (node) =>
    {
        const id = node.getAttribute('ot');

        if(!id || node.hasAttribute('ot-init') || this.methods.pending.has(node))
        {
            return;
        }

        this.methods.pending.add(node);
        transforms.Fn('run', id, node);
    };

    this.methods.scan = () =>
    {
        document.querySelectorAll('[ot]').forEach(node =>
        {
            this.methods.process(node);
        });
    };

    this.methods.observe = () =>
    {
        new MutationObserver((mutations) =>
        {
            for(const mutation of mutations)
            {
                for(const node of mutation.addedNodes)
                {
                    if(node.nodeType !== 1)
                    {
                        continue;
                    }

                    if(node.hasAttribute('ot'))
                    {
                        this.methods.process(node);
                    }

                    node.querySelectorAll('[ot]').forEach(child =>
                    {
                        this.methods.process(child);
                    });
                }
            }
        }).observe(document.body, { childList: true, subtree: true });
    };

    if(document.readyState === 'loading')
    {
        document.addEventListener('DOMContentLoaded', () =>
        {
            this.methods.scan();
            this.methods.observe();
        });
    }
    else
    {
        this.methods.scan();
        this.methods.observe();
    }
});

// repositories/framework/addons/render/transforms/js/item/functions/load.js
transforms.Fn('item.load', function(item)
{
    const loaded = transforms.StoreGet('loaded') || {};

    transforms.StoreSet('loaded', loaded);

    this.methods.script = (url) =>
    {
        if(loaded[url])
        {
            return loaded[url];
        }

        loaded[url] = new Promise((resolve, reject) =>
        {
            const element = document.createElement('script');

            element.src = url;
            element.async = true;
            element.onload = resolve;
            element.onerror = () => reject(new Error(`Failed to load: ${url}`));

            document.head.appendChild(element);
        });

        return loaded[url];
    };

    this.methods.style = (url) =>
    {
        if(loaded[url])
        {
            return loaded[url];
        }

        loaded[url] = new Promise((resolve, reject) =>
        {
            const element = document.createElement('link');

            element.rel = 'stylesheet';
            element.href = url;
            element.onload = resolve;
            element.onerror = () => reject(new Error(`Failed to load: ${url}`));

            document.head.appendChild(element);
        });

        return loaded[url];
    };

    const js = (item.Get('js') || []).map(url => this.methods.script(url));
    const css = (item.Get('css') || []).map(url => this.methods.style(url));

    return Promise.all([...js, ...css]);
});

// repositories/framework/addons/render/transforms/js/item/functions/run.js
transforms.Fn('item.run', function(item, node, data = null)
{
	if(data === null)
	{
		data = transforms.Fn('data', item.Get('config'), node);
	}

	item.Fn('load').then(() =>
	{
		if(!document.contains(node))
		{
			return;
		}

		node.setAttribute('ot-init', '');

		const context = {
			scroll: { progress: 0, direction: 'down', speed: 0, top: 0, bottom: 0, visible: false },
			hover: { active: false, x: 0, y: 0, offset: 0 },
			click: { x: 0, y: 0 }
		};

		item.Get('code').call(context, data, node, item);

		if(item.Get('visible'))
		{
			onetype.ObserverVisible(node, () =>
			{
				item.Get('visible').call(context, data, node, item);
			});
		}

		if(item.Get('resize'))
		{
			onetype.ObserverResize(node, () =>
			{
				item.Get('resize').call(context, data, node, item);
			});
		}

		if(item.Get('scroll'))
		{
			onetype.ObserverScroll(node, (scroll) =>
			{
				context.scroll = scroll;
				item.Get('scroll').call(context, data, node, item);
			});
		}

		if(item.Get('hover'))
		{
			onetype.ObserverHover(node, (hover) =>
			{
				context.hover = hover;
				item.Get('hover').call(context, data, node, item);
			}, context.hover.offset);
		}

		if(item.Get('click'))
		{
			onetype.ObserverClick(node, (click) =>
			{
				context.click = click;
				item.Get('click').call(context, data, node, item);
			});
		}

		const listener = onetype.EmitOn('@dom.remove', (removed) =>
		{
			if(removed !== node && document.contains(node))
			{
				return;
			}

			onetype.EmitOff('@dom.remove', listener);
			onetype.ObserverUnvisible(node);
			onetype.ObserverUnresize(node);
			onetype.ObserverUnscroll(node);
			onetype.ObserverUnhover(node);
			onetype.ObserverUnclick(node);

			if(item.Get('destroy'))
			{
				item.Get('destroy').call(context, data, node, item);
			}
		});

		requestAnimationFrame(() =>
		{
			node.removeAttribute('ot');
			node.removeAttribute('ot-init');
		});
	});
});

// repositories/framework/addons/services/cloudflare/images/back/addon.js
import onetype from '#framework/load.js';

const images = onetype.Addon('cloudflare.images', (addon) =>
{
	addon.Table('cloudflare_images');

	addon.Field('id', ['string']);
	addon.Field('team_id', ['string', null, true]);
	addon.Field('site_id', ['string', null, true]);
	addon.Field('cloudflare_id', ['string', null, true]);
	addon.Field('filename', ['string', null, true]);
	addon.Field('url', ['string']);
	addon.Field('variants', ['object', {}]);
	addon.Field('alt', ['string']);
	addon.Field('size', ['number']);
	addon.Field('type', ['string']);
	addon.Field('width', ['number']);
	addon.Field('height', ['number']);
	addon.Field('updated_at', ['string']);
	addon.Field('created_at', ['string']);
});

export default images;

// repositories/framework/addons/services/cloudflare/images/back/expose.js
import images from '#cloudflare-images/addon.js';

images.Expose({
	filter: ['team_id', 'site_id', 'type'],
	sort: ['filename', 'created_at'],
	select: [
		'id', 'team_id', 'site_id', 'cloudflare_id', 'filename', 'url', 'variants', 'alt', 'size', 'type', 'width', 'height', 'updated_at', 'created_at'
	],
	callback: function(query)
	{
		const user = this.http.state.user;

		if(user)
		{
			query.filter('team_id', user.team.id);
		}
		else
		{
			query.filter('id', null, 'NULL');
		}
	}
});

// repositories/framework/addons/services/cloudflare/images/back/functions/api.js
import images from '#cloudflare-images/addon.js';

images.Fn('api', async function(method, path, body, headers = {})
{
	const url = process.env.CLOUDFLARE_API_URL;
	const account = process.env.CLOUDFLARE_ACCOUNT_ID;
	const token = process.env.CLOUDFLARE_API_TOKEN;

	if(!url || !account || !token)
	{
		throw new Error('Cloudflare credentials not configured.');
	}

	const endpoint = `${url}/${account}/images/v1${path}`;

	const options = {
		method,
		headers: {
			'Authorization': 'Bearer ' + token,
			...headers
		}
	};

	if(body)
	{
		if(body instanceof FormData)
		{
			options.body = body;
		}
		else
		{
			options.headers['Content-Type'] = 'application/json';
			options.body = JSON.stringify(body);
		}
	}

	const response = await fetch(endpoint, options);
	const result = await response.json();

	if(!result.success)
	{
		const message = result.errors?.[0]?.message || 'Cloudflare API error.';
		throw new Error(message);
	}

	return result.result;
});

// repositories/framework/addons/services/cloudflare/images/back/functions/meta/detect.js
import images from '#cloudflare-images/addon.js';

images.Fn('meta.detect', function(buffer)
{
	const bytes = new Uint8Array(buffer);

	if(bytes[0] === 0x89 && bytes[1] === 0x50 && bytes[2] === 0x4E && bytes[3] === 0x47)
	{
		return 'image/png';
	}

	if(bytes[0] === 0xFF && bytes[1] === 0xD8 && bytes[2] === 0xFF)
	{
		return 'image/jpeg';
	}

	if(bytes[0] === 0x47 && bytes[1] === 0x49 && bytes[2] === 0x46 && bytes[3] === 0x38)
	{
		return 'image/gif';
	}

	if(bytes[0] === 0x52 && bytes[1] === 0x49 && bytes[2] === 0x46 && bytes[3] === 0x46 && bytes[8] === 0x57 && bytes[9] === 0x45 && bytes[10] === 0x42 && bytes[11] === 0x50)
	{
		return 'image/webp';
	}

	return null;
});

// repositories/framework/addons/services/cloudflare/images/back/functions/meta/dimensions/gif.js
import images from '#cloudflare-images/addon.js';

images.Fn('meta.dimensions.gif', function(buffer)
{
	const view = new DataView(buffer.buffer || buffer);

	return {
		width: view.getUint16(6, true),
		height: view.getUint16(8, true)
	};
});

// repositories/framework/addons/services/cloudflare/images/back/functions/meta/dimensions/jpeg.js
import images from '#cloudflare-images/addon.js';

images.Fn('meta.dimensions.jpeg', function(buffer)
{
	const bytes = new Uint8Array(buffer);
	let offset = 2;

	while(offset < bytes.length)
	{
		if(bytes[offset] !== 0xFF)
		{
			break;
		}

		const marker = bytes[offset + 1];

		if(marker >= 0xC0 && marker <= 0xCF && marker !== 0xC4 && marker !== 0xC8 && marker !== 0xCC)
		{
			return {
				width: (bytes[offset + 7] << 8) | bytes[offset + 8],
				height: (bytes[offset + 5] << 8) | bytes[offset + 6]
			};
		}

		const length = (bytes[offset + 2] << 8) | bytes[offset + 3];

		offset += 2 + length;
	}

	return { width: 0, height: 0 };
});

// repositories/framework/addons/services/cloudflare/images/back/functions/meta/dimensions/png.js
import images from '#cloudflare-images/addon.js';

images.Fn('meta.dimensions.png', function(buffer)
{
	const view = new DataView(buffer.buffer || buffer);

	return {
		width: view.getUint32(16),
		height: view.getUint32(20)
	};
});

// repositories/framework/addons/services/cloudflare/images/back/functions/meta/dimensions/webp.js
import images from '#cloudflare-images/addon.js';

images.Fn('meta.dimensions.webp', function(buffer)
{
	const bytes = new Uint8Array(buffer);
	const view = new DataView(buffer.buffer || buffer);
	const chunk = String.fromCharCode(bytes[12], bytes[13], bytes[14], bytes[15]);

	if(chunk === 'VP8 ')
	{
		return {
			width: view.getUint16(26, true) & 0x3FFF,
			height: view.getUint16(28, true) & 0x3FFF
		};
	}

	if(chunk === 'VP8L')
	{
		const bits = view.getUint32(21, true);

		return {
			width: (bits & 0x3FFF) + 1,
			height: ((bits >> 14) & 0x3FFF) + 1
		};
	}

	if(chunk === 'VP8X')
	{
		return {
			width: (bytes[24] | (bytes[25] << 8) | (bytes[26] << 16)) + 1,
			height: (bytes[27] | (bytes[28] << 8) | (bytes[29] << 16)) + 1
		};
	}

	return { width: 0, height: 0 };
});

// repositories/framework/addons/services/cloudflare/images/back/functions/meta.js
import images from '#cloudflare-images/addon.js';

images.Fn('meta', function(buffer)
{
	const type = this.Fn('meta.detect', buffer);

	if(!type)
	{
		return { type: null, width: 0, height: 0 };
	}

	const format = type.split('/')[1];
	const dimensions = this.Fn('meta.dimensions.' + format, buffer);

	return { type, width: dimensions.width, height: dimensions.height };
});

// repositories/framework/addons/services/cloudflare/images/back/items/commands/delete.js
import commands from '@onetype/framework/commands';
import images from '#cloudflare-images/addon.js';

commands.Item({
	id: 'cloudflare:images:delete',
	exposed: true,
	method: 'DELETE',
	endpoint: '/api/images',
	in: {
		id: ['string', null, true]
	},
	callback: async function(properties, resolve)
	{
		const user = this.http?.state?.user;

		if(!user || !user.team)
		{
			return resolve(null, 'Not authenticated.', 401);
		}

		const item = images.Item(properties.id);

		if(!item)
		{
			return resolve(null, 'Image not found.', 404);
		}

		if(item.Get('team_id') !== user.team.id)
		{
			return resolve(null, 'Not authorized.', 403);
		}

		await images.Fn('api', 'DELETE', '/' + item.Get('cloudflare_id'));
		await item.Delete();

		resolve(null, 'Image deleted.');
	}
});

// repositories/framework/addons/services/cloudflare/images/back/items/commands/get.js
import commands from '@onetype/framework/commands';
import images from '#cloudflare-images/addon.js';

commands.Item({
	id: 'cloudflare:images:get',
	exposed: true,
	method: 'GET',
	endpoint: '/api/images/:id',
	in: {
		id: ['string', null, true]
	},
	out: {
		image: {
			type: 'object',
			config: {
				id: ['string'],
				team_id: ['string'],
				site_id: ['string'],
				cloudflare_id: ['string'],
				filename: ['string'],
				url: ['string'],
				variants: ['object'],
				alt: ['string'],
				size: ['number'],
				type: ['string'],
				width: ['number'],
				height: ['number'],
				updated_at: ['string'],
				created_at: ['string']
			}
		}
	},
	callback: async function(properties, resolve)
	{
		const user = this.http?.state?.user;

		if(!user || !user.team)
		{
			return resolve(null, 'Not authenticated.', 401);
		}

		const item = await images.Find()
			.filter('id', properties.id)
			.filter('team_id', user.team.id)
			.one();

		if(!item)
		{
			return resolve(null, 'Image not found.', 404);
		}

		resolve({
			image: item.Get(['id', 'team_id', 'site_id', 'cloudflare_id', 'filename', 'url', 'variants', 'alt', 'size', 'type', 'width', 'height', 'updated_at', 'created_at'])
		});
	}
});

// repositories/framework/addons/services/cloudflare/images/back/items/commands/list.js
import commands from '@onetype/framework/commands';
import images from '#cloudflare-images/addon.js';

commands.Item({
	id: 'cloudflare:images:list',
	exposed: true,
	method: 'GET',
	endpoint: '/api/images',
	in: {
		site_id: ['string', null, true],
		page: ['number'],
		limit: ['number']
	},
	out: {
		images: ['array', null, true],
		total: ['number']
	},
	callback: async function(properties, resolve)
	{
		const user = this.http?.state?.user;

		if(!user || !user.team)
		{
			return resolve(null, 'Not authenticated.', 401);
		}

		const query = images.Find()
			.filter('team_id', user.team.id)
			.filter('site_id', properties.site_id)
			.sort('created_at', 'desc');

		if(properties.limit)
		{
			query.limit(properties.limit);
		}

		if(properties.page)
		{
			query.page(properties.page);
		}

		const result = await query.plain();

		resolve({
			images: result.items,
			total: result.total
		});
	}
});

// repositories/framework/addons/services/cloudflare/images/back/items/commands/upload.js
import commands from '@onetype/framework/commands';
import images from '#cloudflare-images/addon.js';

commands.Item({
	id: 'cloudflare:images:upload',
	exposed: true,
	method: 'POST',
	endpoint: '/api/images',
	in: {
		site_id: ['string', null, true],
		file: ['binary', null, true],
		filename: ['string', null, true],
		alt: ['string']
	},
	out: {
		image: {
			type: 'object',
			config: {
				id: ['string'],
				team_id: ['string'],
				site_id: ['string'],
				cloudflare_id: ['string'],
				filename: ['string'],
				url: ['string'],
				variants: ['object'],
				alt: ['string'],
				size: ['number'],
				type: ['string'],
				width: ['number'],
				height: ['number'],
				updated_at: ['string'],
				created_at: ['string']
			}
		}
	},
	callback: async function(properties, resolve)
	{
		const user = this.http?.state?.user;

		if(!user || !user.team)
		{
			return resolve(null, 'Not authenticated.', 401);
		}

		const { file, filename, site_id, alt } = properties;
		const meta = images.Fn('meta', file);

		if(!meta.type)
		{
			return resolve(null, 'Unsupported image format.', 400);
		}

		const form = new FormData();

		form.append('file', new Blob([file]), filename);

		const result = await images.Fn('api', 'POST', '', form);

		const item = images.Item({
			team_id: user.team.id,
			site_id,
			cloudflare_id: result.id,
			filename,
			url: result.variants?.[0] || '',
			variants: result.variants || [],
			alt: alt || '',
			size: file.length,
			type: meta.type,
			width: meta.width,
			height: meta.height
		});

		await item.Create();

		resolve({
			image: item.Get(['id', 'team_id', 'site_id', 'cloudflare_id', 'filename', 'url', 'variants', 'alt', 'size', 'type', 'width', 'height', 'updated_at', 'created_at'])
		});
	}
});

// repositories/framework/addons/services/cloudflare/images/back/load.js
import images from '#cloudflare-images/addon.js';

/* Expose */
import '#cloudflare-images/expose.js';

/* Functions */
import '#cloudflare-images/functions/api.js';
import '#cloudflare-images/functions/meta.js';
import '#cloudflare-images/functions/meta/detect.js';
import '#cloudflare-images/functions/meta/dimensions/png.js';
import '#cloudflare-images/functions/meta/dimensions/jpeg.js';
import '#cloudflare-images/functions/meta/dimensions/gif.js';
import '#cloudflare-images/functions/meta/dimensions/webp.js';

/* Commands */
import '#cloudflare-images/items/commands/upload.js';
import '#cloudflare-images/items/commands/delete.js';
import '#cloudflare-images/items/commands/list.js';
import '#cloudflare-images/items/commands/get.js';

export default images;

// repositories/framework/addons/services/cloudflare/images/front/items/elements/browse/browse.js
elements.ItemAdd({
	id: 'cloudflare-images',
	icon: 'image',
	name: 'Images',
	description: 'Image browser with upload and management.',
	category: 'Editor',
	author: 'OneType',
	config: {
		site: {
			type: 'object',
			value: null
		},
		_pick: {
			type: 'function'
		}
	},
	render: function()
	{
		this.items = [];
		this.loading = true;
		this.uploading = false;
		this.dragging = false;

		const load = () =>
		{
			if(!this.site)
			{
				return;
			}

			$ot.command('cloudflare:images:list', { site_id: this.site.id }, true).then(result =>
			{
				this.items = result.data.images;
				this.loading = false;
			});
		};

		load();

		this.upload = (files) =>
		{
			if(!files.length || !this.site)
			{
				return;
			}

			this.uploading = true;

			const pending = Array.from(files).map(file =>
			{
				const form = new FormData();

				form.append('file', file);
				form.append('site_id', this.site.id);
				form.append('filename', file.name);

				return fetch('/api/images', { method: 'POST', body: form });
			});

			Promise.all(pending).then(responses =>
			{
				return Promise.all(responses.map(r => r.json()));
			}).then(results =>
			{
				this.uploading = false;

				const failed = results.filter(r => r.code !== 200);

				if(failed.length)
				{
					$ot.toast({ type: 'error', message: failed[0].message || 'Upload failed.' });
				}
				else
				{
					$ot.toast({ type: 'success', message: 'Images uploaded.' });
				}

				load();
			});
		};

		this.pick = (item) =>
		{
			if(this._pick)
			{
				this._pick(item);
			}
		};

		this.remove = (event, item) =>
		{
			event.stopPropagation();

			$ot.command('cloudflare:images:delete', { id: item.id }, true).then(result =>
			{
				if(result.data.code && result.data.code !== 200)
				{
					$ot.toast({ type: 'error', message: result.data.message || 'Delete failed.' });
				}

				load();
			});
		};

		this.browse = () =>
		{
			const input = document.createElement('input');

			input.type = 'file';
			input.accept = 'image/*';
			input.multiple = true;

			input.onchange = () =>
			{
				this.upload(input.files);
			};

			input.click();
		};

		this.drop = (event) =>
		{
			event.preventDefault();
			this.dragging = false;
			this.upload(event.dataTransfer.files);
		};

		this.over = (event) =>
		{
			event.preventDefault();
			this.dragging = true;
		};

		this.leave = () =>
		{
			this.dragging = false;
		};

		this.size = (bytes) =>
		{
			if(bytes < 1024)
			{
				return bytes + ' B';
			}

			if(bytes < 1048576)
			{
				return (bytes / 1024).toFixed(1) + ' KB';
			}

			return (bytes / 1048576).toFixed(1) + ' MB';
		};

		this.close = () =>
		{
			$ot.modal.close();
		};

		return `
			<div class="holder">
				<div class="header">
					<div class="left">
						<i class="icon">image</i>
						<span class="title">Images</span>
						<span class="count">{{ items.length }}</span>
					</div>
					<div class="right">
						<e-form-button text="Upload" icon="upload" :variant="['bg-1', 'border', 'size-s']" :_click="browse" :loading="uploading"></e-form-button>
						<button class="close" ot-click="close"><i>close</i></button>
					</div>
				</div>
				<div :class="'body' + (dragging ? ' dragging' : '')" ot-drop="drop" ot-dragover="over" ot-dragleave="leave">
					<e-status-loading ot-if="loading" :variant="['brand']"></e-status-loading>
					<div ot-if="!loading && !items.length && !uploading" class="empty">
						<e-status-empty icon="image" title="No images" description="Upload images by dragging them here or clicking upload." action="Upload" :_click="browse"></e-status-empty>
					</div>
					<div ot-if="items.length" class="grid">
						<div ot-for="item in items" class="card" ot-click="() => pick(item)">
							<div class="preview">
								<img :src="item.url" :alt="item.alt || item.filename" loading="lazy" />
							</div>
							<div class="info">
								<span class="name">{{ item.filename }}</span>
								<span class="meta">{{ size(item.size) }}</span>
							</div>
							<button class="delete" ot-click="(e) => remove(e, item)"><i>delete</i></button>
						</div>
					</div>
				</div>
			</div>
		`;
	}
});

// repositories/framework/addons/services/cloudflare/images/front/items/elements/input/input.js
elements.ItemAdd({
	id: 'cloudflare-input',
	icon: 'image',
	name: 'Image Input',
	description: 'Image picker input that opens browse modal.',
	category: 'Form',
	author: 'OneType',
	config: {
		value: {
			type: 'string',
			value: ''
		},
		name: {
			type: 'string',
			value: ''
		},
		site: {
			type: 'object',
			value: null
		},
		multiple: {
			type: 'boolean',
			value: false
		},
		placeholder: {
			type: 'string',
			value: 'Select image...'
		},
		disabled: {
			type: 'boolean',
			value: false
		},
		variant: {
			type: 'array',
			value: ['bg-2', 'border', 'size-m'],
			options: ['bg-1', 'bg-2', 'bg-3', 'bg-4', 'border', 'size-s', 'size-m', 'size-l']
		},
		_change: {
			type: 'function'
		}
	},
	render: function()
	{
		const self = this;

		this.images = () =>
		{
			if(!this.value)
			{
				return [];
			}

			return this.multiple ? this.value.split(',').filter(Boolean) : [this.value];
		};

		this.browse = () =>
		{
			if(this.disabled)
			{
				return;
			}

			const site = self.site || $ot.get('site');

			$ot.modal(function()
			{
				this.site = site;
				this.pick = (item) =>
				{
					$ot.modal.close();
					callback(item.url);
				};

				return `<e-cloudflare-images :site="site" :_pick="pick"></e-cloudflare-images>`;
			});
		};

		const callback = (url) =>
		{
			if(this.multiple)
			{
				const current = this.value ? this.value.split(',').filter(Boolean) : [];

				current.push(url);
				this.value = current.join(',');
			}
			else
			{
				this.value = url;
			}

			if(this._change)
			{
				this._change({ value: this.value });
			}
		};

		this.remove = (event, url) =>
		{
			event.stopPropagation();

			if(this.multiple)
			{
				const current = this.value.split(',').filter(v => v !== url);

				this.value = current.join(',');
			}
			else
			{
				this.value = '';
			}

			if(this._change)
			{
				this._change({ value: this.value });
			}
		};

		this.clear = (event) =>
		{
			event.stopPropagation();
			this.value = '';

			if(this._change)
			{
				this._change({ value: this.value });
			}
		};

		return `
			<div :class="'holder ' + variant.join(' ')">
				<input type="hidden" :name="name" :value="value" />
				<div ot-if="!images().length" class="empty" ot-click="browse">
					<i class="icon">image</i>
					<span class="text">{{ placeholder }}</span>
				</div>
				<div ot-if="images().length" class="preview" ot-click="browse">
					<div ot-for="url in images()" class="thumb">
						<img :src="url" loading="lazy" />
						<button class="remove" ot-click="(e) => remove(e, url)"><i>close</i></button>
					</div>
					<button ot-if="value" class="clear" ot-click="clear"><i>delete</i></button>
				</div>
			</div>
		`;
	}
});

// repositories/framework/lib/boot/load.js
const otboot =
{
	ready: () =>
	{
		window.onetype.Emit('@document.ready');
	},

	load: () =>
	{
		window.onetype.Emit('@document.load');
	},

	resize: () =>
	{
		window.onetype.Emit('@window.resize', {
			width: window.innerWidth,
			height: window.innerHeight
		});
	},

	scroll: () =>
	{
		window.onetype.Emit('@window.scroll', {
			x: window.scrollX,
			y: window.scrollY
		});
	},

	visibility: () =>
	{
		window.onetype.Emit('@document.visibility', {
			hidden: document.hidden,
			state: document.visibilityState
		});
	},

	beforeunload: () =>
	{
		window.onetype.Emit('@window.beforeunload');
	},

	focus: () =>
	{
		window.onetype.Emit('@window.focus');
	},

	blur: () =>
	{
		window.onetype.Emit('@window.blur');
	},

	orientation: () =>
	{
		window.onetype.Emit('@window.orientation', {
			angle: screen.orientation?.angle || window.orientation,
			type: screen.orientation?.type || 'unknown'
		});
	},

	online: () =>
	{
		window.onetype.Emit('@navigator.online');
	},

	offline: () =>
	{
		window.onetype.Emit('@navigator.offline');
	},

	popstate: (event) =>
	{
		window.onetype.Emit('@history.popstate', {
			state: event.state,
			pathname: window.location.pathname
		});
	}
};

document.addEventListener('visibilitychange', otboot.visibility);

window.addEventListener('resize', otboot.resize);
window.addEventListener('scroll', otboot.scroll);
window.addEventListener('beforeunload', otboot.beforeunload);
window.addEventListener('focus', otboot.focus);
window.addEventListener('blur', otboot.blur);
window.addEventListener('orientationchange', otboot.orientation);
window.addEventListener('online', otboot.online);
window.addEventListener('offline', otboot.offline);
window.addEventListener('popstate', otboot.popstate);

if(document.readyState === 'loading')
{
	document.addEventListener('DOMContentLoaded', otboot.ready);
	window.addEventListener('load', otboot.load);
}
else if(document.readyState === 'interactive')
{
	otboot.ready();
	window.addEventListener('load', otboot.load);
}
else
{
	otboot.ready();
	otboot.load();
}

// repositories/framework/lib/browser.js
window.onetype = new OneType();
window.$ot = onetype.$ot;

if(onetype.Base())
{
    const original = window.fetch;

    window.fetch = function(url, options)
    {
        if(typeof url === 'string' && url.startsWith('/'))
        {
            url = onetype.Base() + url;
        }

        return original.call(this, url, options);
    };
}

// repositories/framework/lib/load.js
import OneType from './src/onetype.js';

const onetype = new OneType();

/* Framework */
onetype.Assets('framework', import.meta.url, { js: { path: '.', exclude: ['load.js', 'styles', 'boot'] } });
onetype.Assets('boot', import.meta.url, { js: 'boot' });
onetype.Assets('styles', import.meta.url, { css: 'styles' });

/* Core */
onetype.Assets('commands', import.meta.url, { js: { path: '../addons/core/commands', exclude: ['../addons/core/commands/back'] } });
onetype.Assets('database', import.meta.url, { js: '../addons/core/database/front' });

/* Modules */
onetype.Assets('actions', import.meta.url, { js: '../addons/modules/actions/front' });
onetype.Assets('variables', import.meta.url, { js: '../addons/modules/variables' });
onetype.Assets('bugs', import.meta.url, { js: '../addons/modules/bugs/front' });
onetype.Assets('events', import.meta.url, { js: '../addons/modules/events/front' });
onetype.Assets('schedules', import.meta.url, { js: '../addons/modules/schedules/front' });
onetype.Assets('shortcuts', import.meta.url, { js: '../addons/modules/shortcuts/front' });
onetype.Assets('sources', import.meta.url, { js: '../addons/modules/sources/front' });

/* Render */
onetype.Assets('directives', import.meta.url, { js: { path: '../addons/render/directives/front', exclude: ['../addons/render/directives/front/items/self'] } });
onetype.Assets('directives/items', import.meta.url, { js: '../addons/render/directives/front/items/self' });
onetype.Assets('transforms', import.meta.url, { js: { path: '../addons/render/transforms/js', exclude: ['../addons/render/transforms/js/items/self'] }, css: '../addons/render/transforms/css' });
onetype.Assets('transforms/items', import.meta.url, { js: '../addons/render/transforms/js/items/self' });
onetype.Assets('pages', import.meta.url, { js: '../addons/render/pages', css: '../addons/render/pages/front' });
onetype.Assets('elements', import.meta.url, { js: { path: '../addons/render/elements/front', exclude: ['../addons/render/elements/front/items/self'] }, css: { path: '../addons/render/elements/front', exclude: ['../addons/render/elements/front/items/self'] } });
onetype.Assets('elements/items', import.meta.url, { js: '../addons/render/elements/front/items/self', css: '../addons/render/elements/front/items/self' });
onetype.Assets('editor', import.meta.url, { js: '../addons/render/editor', css: '../addons/render/editor' });

/* Float */
onetype.Assets('float', import.meta.url, { js: '../addons/float', css: '../addons/float' });

/* AI */
onetype.Assets('ai', import.meta.url, { js: '../addons/ai', css: '../addons/ai' });

/* Services */
onetype.Assets('cloudflare/images', import.meta.url, { js: '../addons/services/cloudflare/images/front', css: '../addons/services/cloudflare/images/front' });

global.$ot = onetype.$ot;

process.on('SIGINT', async () =>
{
    await onetype.Middleware('sigint');
    await onetype.Middleware('shutdown');

    process.exit(0);
});

process.on('SIGTERM', async () =>
{
    await onetype.Middleware('sigterm');
    await onetype.Middleware('shutdown');

    process.exit(0);
});

process.on('uncaughtException', async (error) =>
{
    console.error('Uncaught Exception:', error);

    await onetype.Middleware('uncaughtException', { error });
    await onetype.Middleware('shutdown');

    process.exit(1);
});

process.on('unhandledRejection', async (reason, promise) =>
{
    console.error('Unhandled Rejection:', reason);

    await onetype.Middleware('unhandledRejection', { reason, promise });
    await onetype.Middleware('shutdown');

    process.exit(1);
});

export default onetype;

// repositories/framework/lib/src/classes/addon/class.js
import AddonGet from './mixins/get.js';
import AddonStore from './mixins/store.js';
import AddonFields from './mixins/fields.js';
import AddonItems from './mixins/items.js';
import AddonRemove from './mixins/remove.js';
import AddonFunctions from './mixins/functions.js';
import AddonRender from './mixins/render.js';

class OneTypeAddon
{
    constructor(onetype, name)
    {
        this.onetype = onetype;
        this.name = name;

        this.store = {};

        this.fields =
        {
            data: {},
            callbacks:
            {
                add: [],
                remove: []
            }
        };

        this.functions =
        {
            data: {},
            callbacks:
            {
                before: [],
                after: [],
                add: [],
                remove: []
            }
        };

        this.items =
        {
            data: {},
            map: {},
            id: 1,
            callbacks:
            {
                add: [],
                added: [],
                remove: [],
                removed: [],
                modify: [],
                modified: [],
                get: []
            }
        };
    }
};

Object.assign(OneTypeAddon.prototype, AddonGet);
Object.assign(OneTypeAddon.prototype, AddonStore);
Object.assign(OneTypeAddon.prototype, AddonFields);
Object.assign(OneTypeAddon.prototype, AddonItems);
Object.assign(OneTypeAddon.prototype, AddonRemove);
Object.assign(OneTypeAddon.prototype, AddonFunctions);
Object.assign(OneTypeAddon.prototype, AddonRender);

export default OneTypeAddon;

// repositories/framework/lib/src/classes/addon/classes/item/class.js
import AddonItemGet from './mixins/get.js';
import AddonItemSet from './mixins/set.js';
import AddonItemRemove from './mixins/remove.js';
import AddonItemFunctions from './mixins/functions.js';
import AddonItemStore from './mixins/store.js';

class OneTypeAddonItem
{
    constructor(addon, data)
    {
        this.addon = addon;
        this.data = data;
        this.store = {};
    }
}

Object.assign(OneTypeAddonItem.prototype, AddonItemGet);
Object.assign(OneTypeAddonItem.prototype, AddonItemSet);
Object.assign(OneTypeAddonItem.prototype, AddonItemRemove);
Object.assign(OneTypeAddonItem.prototype, AddonItemFunctions);
Object.assign(OneTypeAddonItem.prototype, AddonItemStore);

export default OneTypeAddonItem;

// repositories/framework/lib/src/classes/addon/classes/item/mixins/functions.js
const AddonItemFunctions =
{
    Fn(name, ...data)
    {
        return this.addon.FnRun('item.' + name, this, ...data);
    }
};

export default AddonItemFunctions;

// repositories/framework/lib/src/classes/addon/classes/item/mixins/get.js
const AddonItemGet =
{
    Get(key, callback = true)
    {
        if(Array.isArray(key))
        {
            const data = {};

            key.forEach((k) => 
            {
                data[k] = this.Get(k, callback);
            });

            return data;
        }

        const field = this.addon.FieldGet(key);
        const any = this.addon.FieldGet('*');

        if(!field && !any && key !== 'id')
        {
            return null;
        }

        let value = this.data[key];

        field?.get.forEach(callback =>
        {
            value = callback(value, this);
        });

        any?.get.forEach(callback =>
        {
            value = callback(value, this);
        });

        try 
        {
            if(field?.define)
            {
                value = this.addon.onetype.DataDefineOne(value, field.define)
            }
            else if(any?.define)
            {
                value = this.addon.onetype.DataDefineOne(value, any.define)
            }
        }
        catch(error)
        {
            throw this.addon.onetype.Error(500, 'Error getting value on field :field:.', {field: key});
        }

        if(callback)
        {
            this.addon.items.callbacks.get.forEach(callback =>
            {
                try
                {
                    callback(this, key, value);
                }
                catch(error)
                {
                    this.addon.onetype.Error(500, 'Error in item get callback.');
                }
            });
            
            this.addon.onetype.Emit('@addon.item.get', this, key, value);
        }

        return value;
    },

    GetData(callback = true)
    {
        let data = {};

        Object.keys(this.data).forEach((key) =>
        {
            data[key] = this.Get(key, callback);
        })

        return data;
    }
};

export default AddonItemGet;

// repositories/framework/lib/src/classes/addon/classes/item/mixins/remove.js
const AddonItemRemove =
{
    Remove(callback = true)
    {
        this.addon.ItemRemove(this.data.id, callback);
    },
};

export default AddonItemRemove;

// repositories/framework/lib/src/classes/addon/classes/item/mixins/set.js
const AddonItemSet =
{
    Set(key, value, callback = true)
    {
        const field = this.addon.FieldGet(key);
        const any = this.addon.FieldGet('*');

        if(!field && !any)
        {
            return;
        }

        const prevValue = this.data[key];

        field?.set?.forEach(callback =>
        {
            value = callback(value, prevValue, this);
        });

        any?.set?.forEach(callback =>
        {
            value = callback(value, prevValue, this);
        });

        try 
        {
            if(field?.define)
            {
                value = this.addon.onetype.DataDefineOne(value, field.define)
            }
            else if(any?.define)
            {
                value = this.addon.onetype.DataDefineOne(value, any.define)
            }
        }
        catch(error)
        {
            throw this.addon.onetype.Error(500, 'Error setting value on field :field:.', {field: key});
        }

        if(callback)
        {
            this.addon.items.callbacks.modify.forEach(callback =>
            {
                try
                {
                    callback(this, key, value, prevValue);
                }
                catch(error)
                {
                    this.addon.onetype.Error(500, 'Error in item modify callback.');
                }
            });

            this.addon.onetype.Emit('@addon.item.modify', this, key, value, prevValue);
        }

        this.data[key] = value;

        if(callback)
        {
            this.addon.items.callbacks.modified.forEach(callback =>
            {
                try
                {
                    callback(this, key, value, prevValue);
                }
                catch(error)
                {
                    this.addon.onetype.Error(500, 'Error in item modified callback.');
                }
            });
            
            this.addon.onetype.Emit('@addon.item.modified', this, key, value, prevValue);
        }
    },

    SetData(data, callback = true)
    {
        Object.entries(data).forEach(([key, value]) =>
        {
            this.Set(key, value, callback);
        })
    }
};

export default AddonItemSet;

// repositories/framework/lib/src/classes/addon/classes/item/mixins/store.js
const AddonItemStore =
{
    StoreGet(key, value = undefined)
    {
        if(key === undefined)
        {
            return this.store;
        }

        if(value !== undefined)
        {
            return this.addon.onetype.DataDefineOne(this.store[key], [typeof value]);
        }

        return this.store[key];
    },

    StoreSet(key, value)
    {
        if(typeof key === 'object' && key !== null && value === undefined)
        {
            Object.assign(this.store, key);
            return this;
        }

        this.store[key] = value;
        return this;
    },

    StoreHas(key)
    {
        return key in this.store;
    },

    StoreRemove(key)
    {
        if(Array.isArray(key))
        {
            key.forEach(k => delete this.store[k]);
        }
        else
        {
            delete this.store[key];
        }
        
        return this;
    },

    StoreClear()
    {
        this.store = {};
        return this;
    }
};

export default AddonItemStore;

// repositories/framework/lib/src/classes/addon/classes/render/class.js
import RenderGet from './mixins/get.js';
import RenderSet from './mixins/set.js';
import RenderCompile from './mixins/compile.js';
import RenderDOM from './mixins/dom.js';
import RenderProcess from './mixins/process.js';
import RenderMethods from './mixins/methods.js';
import RenderEvents from './mixins/events.js';

class OneTypeAddonRender
{
    constructor(addon, name, callback)
    {
        this.Addon = addon;
        this.Name = name;
        this.Callback = callback;

        this.UpdateFrame = null;
        this.Data = { state: addon.onetype.StateGet() };
        this.Attributes = {};
        this.Item = null;
        this.Slots = {};

        this.Element = null;
        this.Nodes = {};
        this.Html = '';
        this.Time = 0;

        this.Events = {
            init: [],
            render: [],
            mount: [],
            mounted: [],
            unmount: [],
            unmounted: [],
            destroy: [],
            error: [],
            ready: [],
            compile: []
        };

        this.State = {
            ready: false,
            initialized: false,
            rendered: false,
            mounted: false,
            destroyed: false,
            rendering: false
        };

        this.Emitters = [];

        const proxy = new Proxy(this, {
            get(target, prop)
            {
                if (typeof target[prop] !== 'undefined' || typeof prop === 'symbol')
                {
                    return target[prop];
                }

                return target.Data[prop];
            },

            set(target, prop, value)
            {
                if (prop in target || typeof prop === 'symbol')
                {
                    target[prop] = value;
                }
                else
                {
                    const changed = target.Data[prop] !== value;
                    target.Data[prop] = value;

                    if(changed && target.State.ready && !target.State.rendering && !target.UpdateFrame)
                    {
                        target.UpdateFrame = requestAnimationFrame(() =>
                        {
                            target.UpdateFrame = null;
                            target.Update();
                        });
                    }
                }

                return true;
            }
        });

        return proxy;
    }

}

Object.assign(OneTypeAddonRender.prototype, RenderGet);
Object.assign(OneTypeAddonRender.prototype, RenderSet);
Object.assign(OneTypeAddonRender.prototype, RenderCompile);
Object.assign(OneTypeAddonRender.prototype, RenderDOM);
Object.assign(OneTypeAddonRender.prototype, RenderProcess);
Object.assign(OneTypeAddonRender.prototype, RenderMethods);
Object.assign(OneTypeAddonRender.prototype, RenderEvents);

export default OneTypeAddonRender;

// repositories/framework/lib/src/classes/addon/classes/render/mixins/compile.js
import onetype from '#framework/load.js';

const RenderCompile =
{
    Compile(html, data = null, options = {})
    {
        const element = this.DOMCreateElement(html);

        if(element.nodeType === Node.ELEMENT_NODE)
        {
            Object.entries(this.GetAttributes()).forEach(([key, value]) =>
            {
                element.setAttribute(key, value);
            });

            const prefix = this.GetAddon().GetName().charAt(0).toLowerCase();

            element.classList.add(prefix + '-' + onetype.GenerateHash(this.GetAddon().GetName() + '-' + this.GetName()));
        }

        const compile = {
            element,
            data: this.GetData(),
            nodes: {},
            time: 0,
            walk: true,
            children: true,
            locale: options.locale !== undefined ? options.locale : true
        };

        if(data)
        {
            compile.data = Object.assign({}, compile.data, data);
        }

        const start = performance.now();

        onetype.Emit('@addon.render.compile.before', this, compile, element, '0');

        this.CompileWalkNodes(element, '0', compile);

        onetype.Emit('@addon.render.compile.after', this, compile, element, '0');

        compile.time = performance.now() - start;

        return compile;
    },

    CompileWalkNodes(node, identifier, compile)
    {
        if(!compile.walk || !node)
        {
            return;
        }

        compile.nodes[identifier] = node;

        const parent = node.parentNode;

        onetype.Emit('@addon.render.compile.node', this, compile, node, identifier);

        if(node.parentNode !== parent)
        {
            return;
        }

        if(node.hasChildNodes() && compile.children)
        {
            const childNodes = Array.from(node.childNodes);

            for(let i = 0; i < childNodes.length; i++)
            {
                this.CompileWalkNodes(childNodes[i], identifier + '-' + i, compile);
            }
        }
        else if(node.hasChildNodes())
        {
            compile.children = true;
        }
    }
};

export default RenderCompile;

// repositories/framework/lib/src/classes/addon/classes/render/mixins/dom.js
import onetype from '#framework/load.js';

const RenderDOM =
{
    DOMCreateElement(html)
    {
        const div = document.createElement('div');
        div.innerHTML = html.trim();
        return div;
    },

    DOMPatchElement(current, target)
    {
        return onetype.DOMPatch(current, target);
    },
};

export default RenderDOM;

// repositories/framework/lib/src/classes/addon/classes/render/mixins/events.js
import onetype from '#framework/load.js';

const RenderEvents =
{
    EventOn(event, callback)
    {
        if(!(event in this.Events))
        {
            throw onetype.Error(400, 'Event :event: doesn\'t exist.', {event});
        }

        const run = {
            init:    () => this.State.initialized,
            mount:   () => this.State.mounted,
            mounted: () => this.State.mounted,
            ready:   () => this.State.ready
        };

        if(run[event] && run[event]())
        {
            callback.call(this);
        }
        else
        {
            this.Events[event].push(callback);
        }

        return this;
    },

    EventEmit(event, ...args)
    {
        if(!(event in this.Events))
        {
            throw onetype.Error(400, 'Event :event: doesn\'t exist.', {event});
        }

        const skip = {
            init:      () => this.State.initialized,
            mount:     () => this.State.mounted,
            unmount:   () => !this.State.mounted,
            unmounted: () => !this.State.mounted,
            destroy:   () => this.State.destroyed,
            ready:     () => this.State.ready
        };

        if(skip[event] && skip[event]())
        {
            return this;
        }

        this.Events[event].forEach(callback => callback.call(this, ...args));

        const after = {
            init:      () => { this.State.initialized = true; },
            mount:     () => { this.State.mounted = true; },
            render:    () => { this.State.rendered = true; },
            ready:     () => { this.State.ready = true; },
            destroy:   () => { this.State.destroyed = true; this.EventCleanup(); },
            mounted:   () => { this.EventEmit('ready'); },
            unmounted: () => { this.State.mounted = false; }
        };

        if(after[event])
        {
            after[event]();
        }

        return this;
    },

    EventOff(event, callback)
    {
         if(!(event in this.Events))
        {
            throw onetype.Error(400, 'Event :event: doesn\'t exist.', {event});
        }

        if(!callback)
        {
            this.Events[event] = [];
            return this;
        }

        const index = this.Events[event].indexOf(callback);
        if(index > -1)
        {
            this.Events[event].splice(index, 1);
        }

        return this;
    },

    EventOnce(event, callback)
    {
        const onceWrapper = (...args) =>
        {
            callback.call(this, ...args);
            this.EventOff(event, onceWrapper);
        };

        return this.EventOn(event, onceWrapper);
    },

    EventCleanup()
    {
        /* Global emitters */
        for(const emitter of this.Emitters)
        {
            onetype.EmitOff(emitter.name, emitter.id);
        }

        this.Emitters = [];

        /* Visibility / Resize observers */
        if(this.Element)
        {
            onetype.ObserverUnvisible(this.Element);
            onetype.ObserverUnresize(this.Element);
        }

        /* Update frame */
        if(this.UpdateFrame)
        {
            cancelAnimationFrame(this.UpdateFrame);
            this.UpdateFrame = null;
        }

        /* Local events */
        for(const event in this.Events)
        {
            this.Events[event] = [];
        }
    }
};

export default RenderEvents;

// repositories/framework/lib/src/classes/addon/classes/render/mixins/get.js
const RenderGet =
{
    GetName()
    {
        return this.Name;
    },

    GetCallback()
    {
        return this.Callback;
    },
    
    GetAddon()
    {
        return this.Addon;
    },

    GetData()
    {
        return this.Data;
    },
    
    GetAttributes()
    {
        return this.Attributes;
    },

    GetItem()
    {
        return this.Item;
    },
    
    GetElement()
    {
        return this.Element;
    },
    
    GetNodes()
    {
        return this.Nodes;
    },

    GetReady()
    {
        return this.State.ready;
    }
}

export default RenderGet;

// repositories/framework/lib/src/classes/addon/classes/render/mixins/methods.js
import onetype from '#framework/load.js';

const RenderMethods =
{
    Mount(target)
    {
        try
        {
            if(!this.Element)
            {
                throw onetype.Error(400,'Cannot mount - render not processed');
            }

            if(this.State.mounted)
            {
                throw onetype.Error(400,'Already mounted');
            }

            /* Before mount */
            this.EventEmit('mount');

            /* Insert into DOM */
            target = typeof target === 'string' ? document.querySelector(target) : target;

            if(!target)
            {
                throw onetype.Error(400,'Target element not found');
            }

            target.appendChild(this.Element);

            /* After mount */
            this.EventEmit('mounted');

            return this;
        }
        catch(error)
        {
            this.EventEmit('error', error);
            return this;
        }
    },

    Unmount()
    {
        try
        {
            if(!this.State.mounted)
            {
                return this;
            }

            /* Before unmount */
            this.EventEmit('unmount');

            /* Remove from DOM */
            if(this.Element && this.Element.parentNode)
            {
                this.Element.parentNode.removeChild(this.Element);
            }

            /* After unmount */
            this.EventEmit('unmounted');

            return this;
        }
        catch(error)
        {
            this.EventEmit('error', error);
            return this;
        }
    },

    Destroy()
    {
        try
        {
            /* Unmount first */
            this.Unmount();

            /* Emit destroy */
            this.EventEmit('destroy');

            /* Clear references */
            this.Element = null;
            this.Nodes = {};

            return this;
        }
        catch(error)
        {
            this.EventEmit('error', error);
            return this;
        }
    },

    /* State Management */

    Define(config)
    {
        const data = onetype.DataDefine(this, config);

        for(const key in data)
        {
            this.Data[key] = data[key];
        }
    },

    UpdateData(data)
    {
        let changed = false;

        for(const key in data)
        {
            if(key === 'state')
            {
                continue;
            }

            const value = data[key];

            if(typeof value === 'function')
            {
                this.Data[key] = value;
                continue;
            }

            if(this.Data[key] !== value)
            {
                this.Data[key] = value;
                changed = true;
            }
        }

        if(changed && this.State.ready && !this.State.rendering && !this.UpdateFrame)
        {
            this.UpdateFrame = requestAnimationFrame(() =>
            {
                this.UpdateFrame = null;
                this.Update();
            });
        }
    },

    Update()
    {
        try
        {
            if(!this.Element)
            {
                throw onetype.Error(400,'Cannot update - element not rendered');
            }

            const compiled = this.Compile(this.Html);

            onetype.DOMPatch(this.Element, compiled.element);

            this.Nodes = compiled.nodes;
            this.Time = compiled.time;

            return this;
        }
        catch(error)
        {
            this.EventEmit('error', error);
            return this;
        }
    },

    /* Lifecycle Hooks */

    OnInit(callback)
    {
        return this.EventOn('init', callback);
    },

    OnMount(callback)
    {
        return this.EventOn('mount', callback);
    },

    OnMounted(callback)
    {
        return this.EventOn('mounted', callback);
    },

    OnUnmount(callback)
    {
        return this.EventOn('unmount', callback);
    },

    OnDestroy(callback)
    {
        return this.EventOn('destroy', callback);
    },

    OnReady(callback)
    {
        return this.EventOn('ready', callback);
    },

    OnVisible(callback)
    {
        const render = this;

        this.EventOn('ready', () =>
        {
            onetype.ObserverVisible(render.Element, (entry) =>
            {
                callback.call(render, entry);
            });
        });
    },

    OnResize(callback)
    {
        const render = this;

        this.EventOn('ready', () =>
        {
            onetype.ObserverResize(render.Element, (entry) =>
            {
                callback.call(render, entry);
            });
        });
    },

    OnError(callback)
    {
        return this.EventOn('error', callback);
    },

    /* Global Emitter */

    Emit(name, ...args)
    {
        onetype.Emit(name, ...args);
    },

    On(name, callback)
    {
        const render = this;

        const id = onetype.EmitOn(name, function(...args)
        {
            callback.call(render, ...args);
        });

        this.Emitters.push({ name, id });
    },
};

export default RenderMethods;

// repositories/framework/lib/src/classes/addon/classes/render/mixins/process.js
import onetype from '#framework/load.js';

const RenderProcess =
{
    Process()
    {
        try
        {
            this.EventEmit('init');

            this.ProcessContext();

            const html = this.ProcessHtml();

            this.Html = html;

            this.EventEmit('render', html);

            const compiled = this.Compile(html);

            this.EventEmit('compile', compiled, false);

            this.Element = compiled.element;
            this.Element.__add = () => this.EventEmit('mount');
            this.Element.__remove = () => this.Destroy();
            this.Nodes = compiled.nodes;
            this.Time = compiled.time;

            this.EventEmit('ready');

            return this;
        }
        catch(error)
        {
            const compiled = this.Compile(this.ProcessError(error.message));

            this.EventEmit('compile', compiled, true);

            this.Element = compiled.element;
            this.Nodes = compiled.nodes;
            this.Time = compiled.time;

            this.EventEmit('error', error);

            return this;
        }
    },

    ProcessContext()
    {
        const proto = Object.getPrototypeOf(this);
        const methods = Object.getOwnPropertyNames(proto);

        methods.forEach(method =>
        {
            if(typeof this[method] === 'function' && method !== 'constructor')
            {
                this[method] = this[method].bind(this);
            }
        });
    },

    ProcessHtml()
    {
        this.State.rendering = true;
        const html = this.GetCallback().call(this, this.GetData());
        this.State.rendering = false;

        if(html && typeof html.then === 'function')
        {
            html.then(resolved =>
            {
                if(typeof resolved !== 'string')
                {
                    throw onetype.Error(400, 'Render function must return a string.');
                }

                this.Html = resolved;
                this.Update();
            });

            return '';
        }

        if(typeof html !== 'string')
        {
            throw onetype.Error(400, 'Render function must return a string.');
        }

        return html;
    },

    ProcessError(message)
    {
        const html = `
            <div class="ot-render-error" style="padding: 20px; background: rgb(255 0 0 / 10%); border: 1px solid rgb(255 0 0 / 20%); border-radius: 4px; color: #FF0000; font-family: monospace;">
                <strong>Render Error | Name: ${this.GetName()} | Addon: ${this.GetAddon().GetName()} </strong><br>
                ${onetype.StringSanitize(message)}
            </div>
        `;
        
       return html;
    }
};

export default RenderProcess;

// repositories/framework/lib/src/classes/addon/classes/render/mixins/set.js
import onetype from '#framework/load.js';

const RenderSet =
{
    SetData(data)
    {
        this.Data = Object.assign({ state: onetype.StateGet() }, data);
        return this;
    },

    SetAttributes(attributes)
    {
        this.Attributes = attributes;
        return this;
    },

    SetItem(item)
    {
        this.Item = item;
        return this;
    },

    SetSlots(slots)
    {
        this.Slots = slots;
        return this;
    },

    SetCallback(callback)
    {
        this.Callback = callback;
        return this;
    }
};

export default RenderSet;

// repositories/framework/lib/src/classes/addon/mixins/fields.js
const AddonFields =
{
    Field(name, define = null, get = null, set = null, map = false, callback = true)
    {
        const field = this.FieldGet(name);

        if(field)
        {
            return field;
        }

        return this.FieldAdd(name, define, get, set, map, callback);
    },

    Fields()
    {
        return this.fields;
    },

    FieldAdd(name, define = null, get = null, set = null, map = false, callback = true)
    {
        if(!(name in this.fields.data))
        {
            this.fields.data[name] = {name, define: null, get: [], set: [], map};
        }

        if(map)
        {
            if(!(name in this.items.map))
            {
                this.items.map[name] = {};
            }
        }

        if(define !== null) { this.fields.data[name].define = define; }
        if(get !== null)    { this.fields.data[name].get.push(get); }
        if(set !== null)    { this.fields.data[name].set.push(set); }

        if(callback)
        {
            this.fields.callbacks.add.forEach(callback => callback.call(this, this.fields.data[name]));
            this.onetype.Emit('@addon.field.add', this, this.fields.data[name]);
        }

        return this.fields.data[name];
    },

    FieldGet(name)
    {
        return this.fields.data[name];
    },

    FieldRemove(name, callback = true)
    {
        if(!(name in this.fields))
        {
            return;
        }

        if(callback)
        {
            this.fields.callbacks.remove.forEach(callback => callback.call(this, this.fields.data[name]));
            this.onetype.Emit('@addon.field.remove', this, this.fields.data[name]);
        }

        delete this.fields.data[name];
    },

    FieldOn(type, callback)
    {
        if(!(type in this.fields.callbacks))
        {
            this.onetype.Error(400, 'Field catcher not found.', {name});
            return this;
        }

        this.fields.callbacks[type].push(callback);
    }
};

export default AddonFields;

// repositories/framework/lib/src/classes/addon/mixins/functions.js
const AddonFunctions =
{
    FnAdd(name, callback)
    {
        if(typeof name === 'object')
        {
            Object.entries(name).forEach(([key, value]) => this.FnAdd(key, value));
            return this;
        }

        name = name.toLowerCase();

        if (typeof callback !== 'function')
        {
            this.onetype.Error(400, 'Function :name: callback is not a function.', {name});
            return this;
        }

        this.functions.data[name] = {callback, name};

        try
        {
            this.functions.callbacks.add.forEach(callback => callback.call(this, this.functions.data[name]));
            this.onetype.Emit('@addon.function.add', name);
        }
        catch(error)
        {
            this.onetype.Error(500, 'Error in function :name: callback add.', {name});
        }

        return this;
    },

    FnGet(name)
    {
        name = name.toLowerCase();
        return this.functions.data[name] || null;
    },

    FnRun(name, ...data)
    {
        name = name.toLowerCase();

        const fn = this.FnGet(name);

        if (!fn)
        {
            this.onetype.Error(404, 'Function not found.', {name});
            return null;
        }

        let response = null;

        const context = Object.create(this);
        context.methods = {};

        try
        {
            this.functions.callbacks.before.forEach(callback => callback.call(context, fn, ...data));
            this.onetype.Emit('@addon.function.before', this, fn, response, ...data);
        }
        catch(error)
        {
            this.onetype.Error(500, 'Error in function callback before.', {name});
        }
       
        response = fn.callback.call(context, ...data);

        try
        {
            this.functions.callbacks.after.forEach(callback => callback.call(context, fn, response, ...data));
            this.onetype.Emit('@addon.function.after', context, fn, response, ...data);
        }
        catch(error)
        {
            this.onetype.Error(500, 'Error in function callback after.', {name});
        }

        return response;
    },
    
    FnRemove(name)
    {
        const fn = this.FnGet(name);

        if (!fn)
        {
            return this;
        }

        try
        {
            this.functions.callbacks.remove.forEach(callback => callback.call(this, fn));
            this.onetype.Emit('@addon.function.remove', this, fn);
        }
        catch(error)
        {
            this.onetype.Error(500, 'Error in function callback remove.', {name: fn.name});
        }

        delete this.functions.data[fn.name];

        return this;
    },

    FnOn(type, callback)
    {
        if(!(type in this.functions.callbacks))
        {
            return this.onetype.Error(400, 'Function catcher not found.', {type});
        }

        this.functions.callbacks[type].push(callback);
    },

    Fn(name, ...data)
    {
        if(!this.FnGet(name) && data.length && typeof data[0] === 'function')
        {
            return this.FnAdd(name, ...data);
        }
        else
        {
            return this.FnRun(name, ...data);
        }
    }
};

export default AddonFunctions;

// repositories/framework/lib/src/classes/addon/mixins/get.js
const AddonGet =
{
    GetName()
    {
        return this.name;
    },
};

export default AddonGet;

// repositories/framework/lib/src/classes/addon/mixins/items.js
import OneTypeAddonItem from '../classes/item/class.js';

const AddonItems =
{
    Item(identifierOrObject, map = null, then = null, callback = true)
    {
        if(identifierOrObject && typeof identifierOrObject === 'object')
        {
            return this.ItemAdd(identifierOrObject, then, callback);
        }
        else
        {
            return this.ItemGet(identifierOrObject, map, then);
        }
    },

    Items()
    {
        return this.items.data;
    },

    ItemGet(identifier, map = null, then = null)
    {
        if(map)
        {
            if(!(map in this.items.map) || !(identifier in this.items.map[map])) 
            {
                return null;
            }

            return this.ItemGet(this.items.map[map][identifier], null, then);
        }
        
        if(!(identifier in this.items.data))
        {
            return null;
        }

        then && then(this.items.data[identifier]);

        return this.items.data[identifier];
    },

    ItemGetByMap(identifier, then = null)
    {
        if(!(identifier in this.items.data))
        {
            return null;
        }

        then && then(this.items.data[identifier]);

        return this.items.data[identifier];
    },

    ItemAdd(object, then = null, callback = true, set = true)
    {
        if(!this.FieldGet('id'))
        {
            this.FieldAdd('id', ['number']);
        }

        let item = new OneTypeAddonItem(this, {});

        this.onetype.Emit('@addon.item.init', item);

        if(!('id' in object))
        {
            object.id = this.items.id++;
        }

        item.SetData(object, false);

        for(const [name, field] of Object.entries(this.fields.data))
        {
            if(field.map && this.items.map[name])
            {
                this.items.map[name][item.data[name]] = item.data.id;
            }
        }

        if(callback)
        {
            this.items.callbacks.add.forEach(callback =>
            {
                try
                {
                    callback.call({}, item);
                }
                catch(error)
                {
                    this.onetype.Error(500, 'Error in item add callback.');
                }
            });
            
            this.onetype.Emit('@addon.item.add', item);
        }

        then && then(item);

        if(set)
        {
            this.items.data[item.Get('id') || this.items.id++] = item;
        }

        if(callback)
        {
            this.items.callbacks.added.forEach(callback =>
            {
                try
                {
                    callback.call({}, item);
                }
                catch(error)
                {
                    this.onetype.Error(500, 'Error in item added callback.');
                }
            });

            this.onetype.Emit('@addon.item.added', item);
        }

        return item;
    },

    ItemsAdd(items, then = null, callback = true)
    {
        const added = [];

        for (const item of items)
        {
            added.push(this.ItemAdd(item, null, callback));
        }

        then && then(added);

        return added;
    },

    ItemRemove(id, callback = true)
    {
        const item = this.ItemGet(id);

        if(!item)
        {
            return;
        }

        Object.values(this.fields.data).forEach((field) =>
        {
            if(field.map)
            {
                delete this.items.map[field.name][item.Get(field.name)]
            }
        })

        if(callback)
        {
            this.items.callbacks.remove.forEach(callback =>
            {
                try
                {
                    callback.call({}, item);
                }
                catch(error)
                {
                    this.onetype.Error(500, 'Error in item remove callback.');
                }
            });

            this.onetype.Emit('@addon.item.remove', item);
        }

        delete this.items.data[id];

        if(callback)
        {
            this.items.callbacks.removed.forEach(callback =>
            {
                try
                {
                    callback.call({}, item);
                }
                catch(error)
                {
                    this.onetype.Error(500, 'Error in item removed callback.');
                }
            });

            this.onetype.Emit('@addon.item.removed', item);
        }
    },


    ItemsRemove(callback = true)
    {
        if(!callback)
        {
            this.items.data = {};
        }
        else
        {
            for(let id in this.items.data)
            {
                this.ItemRemove(id, callback);
            }
        }
    },

    ItemOn(type, callback, id = null)
    {
        if(!(type in this.items.callbacks))
        {
            this.onetype.Error(400, 'Item catcher not found for :type:.', {type});
            return this;
        }

        if(id)
        {
            this.items.callbacks[type] = this.items.callbacks[type].filter(cb => cb._id !== id);
            callback._id = id;
        }

        this.items.callbacks[type].push(callback);
    }
};

export default AddonItems;

// repositories/framework/lib/src/classes/addon/mixins/remove.js
const AddonRemove =
{
    Remove(callback = true)
    {
        this.onetype.AddonRemove(this.name, callback);
    }
};

export default AddonRemove;

// repositories/framework/lib/src/classes/addon/mixins/render.js
import OneTypeAddonRender from '../classes/render/class.js';

const AddonRender =
{
    Render(name, data = {}, attributes = {}, slots = {}, item = null)
    {
        if(typeof data === 'function')
        {
            return this.RenderAdd(name, data);
        }
        else
        {
            return this.RenderRun(name, data, attributes, slots, item);
        }
    },

    RenderAdd(name, callback)
    {
        this.FnAdd('render.' + name, function(data, attributes, slots, item)
        {
            const render = new OneTypeAddonRender(this, name, callback);

            render.SetData(data).SetAttributes(attributes).SetSlots(slots).SetItem(item);

            return render.Process();
        });

        return this;
    },

    RenderRun(name, data = {}, attributes = {}, slots = {}, item = null)
    {
        const fn = this.FnGet('render.' + name);

        if(!fn)
        {
            throw this.onetype.Error(404, 'Render :name: doesn\'t exist.', {name});
        }

        return fn.callback.call(this, data, attributes, slots, item);
    },

    RenderRemove(name)
    {
        this.FnRemove('render.' + name);
        return this;
    },
    
    RenderExists(name)
    {
        return !!this.FnGet('render.' + name);
    }
};

export default AddonRender;

// repositories/framework/lib/src/classes/addon/mixins/store.js
import onetype from "#framework/load.js";

const AddonStore =
{
    StoreGet(key, value = undefined)
    {
        if(key === undefined)
        {
            return this.store;
        }

        if(value !== undefined)
        {
            return onetype.DataDefineOne(this.store[key], [typeof value]);
        }

        return this.store[key];
    },

    StoreSet(key, value)
    {
        if(typeof key === 'object')
        {
            Object.assign(this.store, key);
            return this;
        }

        this.store[key] = value;
        return this;
    },

    StoreHas(key)
    {
        return key in this.store;
    },

    StoreDelete(key)
    {
        delete this.store[key];
        return this;
    },

    StoreClear()
    {
        this.store = {};
        return this;
    }
};

export default AddonStore;

// repositories/framework/lib/src/classes/error/class.js
class OneTypeErrorClass extends Error
{
    constructor(code, message, context)
    {
        super(message);

        this.name = 'OneTypeError';
        this.code = typeof code === 'number' ? code : 500;
        this.context = context && typeof context === 'object' ? context : {};
    }

    toJSON()
    {
        return {
            name: this.name,
            code: this.code,
            message: this.message,
            context: this.context
        };
    }
}

export default OneTypeErrorClass;

// repositories/framework/lib/src/mixins/addons.js
import OneTypeAddon from '../classes/addon/class.js';

const OneTypeAddons =
{
    Addon(name, then = null, callback = true)
    {
        if(this.AddonGet(name))
        {
            return this.AddonGet(name, then);
        }
        else
        {
            return this.AddonAdd(name, then, callback);
        }
    },

    AddonAdd(name, then = null, callback = true)
    {
        name = name.toLowerCase();

        const addon = new OneTypeAddon(this, name);

        this.Emit('@addon.init', addon);

        this.addons.data[name] = addon;

        then && then(addon);

        if(callback)
        {
            this.addons.callbacks.add.forEach(callback =>
            {
                try
                {
                    callback(addon);
                }
                catch(error)
                {
                    this.Error(500, 'Error while performing addon add callback.', {addon: name});
                }
            });

            this.Emit('@addon.add', addon);
        }

        return this.AddonGet(name);
    },

    AddonGet(name, then = null)
    {
        name = name.toLowerCase();

        if(!(name in this.addons.data))
        {
            return null;
        }

        then && then(this.addons.data[name]);

        return this.addons.data[name];
    },

    Addons()
    {
        return this.addons.data;
    },

    AddonRemove(name, callback = true)
    {
        const addon = this.AddonGet(name);

        if(!addon)
        {
            return;
        }

        if(callback)
        {
            this.addons.callbacks.remove.forEach(callback =>
            {
                try
                {
                    callback(addon);
                }
                catch(error)
                {
                    this.Error(500, 'Error while performing addon remove callback.', {addon: name});
                }
            });

            this.Emit('@addon.remove', addon);
        }

        addon.ItemsRemove(callback);
        delete this.addons.data[name];
    },

    AddonsRemove(callback = true)
    {
        for(let name in this.addons.data)
        {
            this.AddonRemove(name, callback);
        }
    },

    AddonOn(name, callback)
    {
        if(!(name in this.addons.callbacks))
        {
            return this.Error(400, 'Addon catcher not found.', {addon: name});
        }

        this.addons.callbacks[name].push(callback);
    },

    AddonReady(name, callback)
    {
        const addon = this.AddonGet(name);

        if(addon)
        {
            return callback(addon);
        }

        this.AddonOn('add', (addon) =>
        {
            if(addon.GetName() === name)
            {
                callback(addon);
            }
        })
    }
};

export default OneTypeAddons;

// repositories/framework/lib/src/mixins/assets.js
import { dirname, resolve } from 'path';
import { fileURLToPath } from 'url';

const schema =
{
    js:  { type: 'object', config: { path: ['string', null, true], exclude: ['array', []] } },
    css: { type: 'object', config: { path: ['string', null, true], exclude: ['array', []] } }
};

const OneTypeAssets =
{
    Assets(id, url, config)
    {
        if (config)
        {
            return this.AssetsSet(id, url, config);
        }

        return this.AssetsGet(id);
    },

    AssetsGet(id)
    {
        if (id) return this.assets[id] || null;

        return this.assets;
    },

    AssetsSet(id, url, config)
    {
        const base = dirname(fileURLToPath(url));
        const entry = {};

        for (const type in config)
        {
            if (!(type in schema))
            {
                throw this.Error(400, 'Assets type :type: is not supported. Expected: js, css.', {type});
            }

            const value = config[type];

            if (typeof value === 'string')
            {
                entry[type] = { path: resolve(base, value), exclude: [] };
            }
            else
            {
                const resolved = this.DataDefine(
                    { path: value.path, exclude: value.exclude },
                    schema[type].config
                );

                resolved.path = resolve(base, resolved.path);
                resolved.exclude = resolved.exclude.map(e => resolve(base, e));

                entry[type] = resolved;
            }
        }

        this.assets[id] = entry;
    }
};

export default OneTypeAssets;

// repositories/framework/lib/src/mixins/base.js
const OneTypeBase =
{
	Base(value)
	{
		if(typeof value === 'undefined')
		{
			return this.StateGet('base', '');
		}

		this.StateSet('base', value);
	}
};

export default OneTypeBase;

// repositories/framework/lib/src/mixins/binaries.js
const OneTypeBinaries =
{
    BinariesExtract(data)
    {
        const binaries = {};
        const cleaned = this.BinariesExtractWalk(data, binaries, '');
        return {data: cleaned, binaries};
    },

    BinariesInject(data, binaries)
    {
        return this.BinariesInjectWalk(data, binaries, '');
    },

    BinariesExtractWalk(value, binaries, path)
    {
        if(value === null || value === undefined)
        {
            return value;
        }

        if(Buffer.isBuffer(value))
        {
            binaries[path] = value;
            return undefined;
        }

        if(Array.isArray(value))
        {
            return value.map((item, i) =>
                this.BinariesExtractWalk(item, binaries, path ? `${path}.${i}` : `${i}`)
            );
        }

        if(typeof value === 'object')
        {
            const result = {};

            for(let [key, val] of Object.entries(value))
            {
                const fieldPath = path ? `${path}.${key}` : key;
                result[key] = this.BinariesExtractWalk(val, binaries, fieldPath);
            }

            return result;
        }

        return value;
    },

    BinariesInjectWalk(value, binaries, path)
    {
        if(binaries[path])
        {
            return binaries[path];
        }

        if(value === null || value === undefined)
        {
            return value;
        }

        if(Array.isArray(value))
        {
            return value.map((item, i) =>
                this.BinariesInjectWalk(item, binaries, path ? `${path}.${i}` : `${i}`)
            );
        }

        if(typeof value === 'object')
        {
            const result = {};

            for(let [key, val] of Object.entries(value))
            {
                const fieldPath = path ? `${path}.${key}` : key;
                result[key] = this.BinariesInjectWalk(val, binaries, fieldPath);
            }

            return result;
        }

        return value;
    }
};

export default OneTypeBinaries;

// repositories/framework/lib/src/mixins/cookie.js
const OneTypeCookie =
{
    CookieGet(name, request)
    {
        const header = this.environment === 'front' ? document.cookie : (request?.headers?.cookie || '');
        const match = header.match(new RegExp('(?:^|;\\s*)' + name + '=([^;]*)'));

        if(!match)
        {
            return null;
        }

        return decodeURIComponent(match[1]);
    },

    CookieSet(name, value, options, response)
    {
        const parts = [encodeURIComponent(name) + '=' + encodeURIComponent(value)];

        if(options?.path)
        {
            parts.push('Path=' + options.path);
        }

        if(options?.maxAge)
        {
            parts.push('Max-Age=' + options.maxAge);
        }

        if(options?.domain)
        {
            parts.push('Domain=' + options.domain);
        }

        if(options?.sameSite)
        {
            parts.push('SameSite=' + options.sameSite);
        }

        if(this.environment === 'front')
        {
            if(options?.secure)
            {
                parts.push('Secure');
            }

            document.cookie = parts.join('; ');
            return;
        }

        if(options?.httpOnly !== false)
        {
            parts.push('HttpOnly');
        }

        if(options?.secure !== false)
        {
            parts.push('Secure');
        }

        const existing = response.getHeader('Set-Cookie') || [];
        const cookies = Array.isArray(existing) ? existing : [existing];
        cookies.push(parts.join('; '));
        response.setHeader('Set-Cookie', cookies);
    },

    CookieClear(name, options, response)
    {
        this.CookieSet(name, '', { path: options?.path || '/', maxAge: 0 }, response);
    }
};

export default OneTypeCookie;

// repositories/framework/lib/src/mixins/crypto.js
import crypto from 'crypto';

const OneTypeCrypto =
{
    Encrypt(data, key)
    {
        const buffer = Buffer.from(key, 'hex');
        const iv = crypto.randomBytes(12);
        const cipher = crypto.createCipheriv('aes-256-gcm', buffer, iv);

        let encrypted = cipher.update(JSON.stringify(data), 'utf8', 'base64');
        encrypted += cipher.final('base64');

        const tag = cipher.getAuthTag();

        return iv.toString('base64') + ':' + encrypted + ':' + tag.toString('base64');
    },

    Decrypt(ciphertext, key)
    {
        const buffer = Buffer.from(key, 'hex');
        const [ivB64, encryptedB64, tagB64] = ciphertext.split(':');

        const iv = Buffer.from(ivB64, 'base64');
        const encrypted = Buffer.from(encryptedB64, 'base64');
        const tag = Buffer.from(tagB64, 'base64');

        const decipher = crypto.createDecipheriv('aes-256-gcm', buffer, iv);
        decipher.setAuthTag(tag);

        let decrypted = decipher.update(encrypted, null, 'utf8');
        decrypted += decipher.final('utf8');

        return JSON.parse(decrypted);
    }
};

export default OneTypeCrypto;

// repositories/framework/lib/src/mixins/data.js
const OneTypeData =
{
    DataValidateObject(object, name = 'parameter')
    {
        if(!object || typeof object !== 'object')
        {
            throw this.Error(400, ':name: must be a non-null object.', {name});
        }

        return object;
    },

    DataValidateString(string, name = 'parameter')
    {
        if(!string || typeof string !== 'string')
        {
            throw this.Error(400, ':name: must be a non-empty string.', {name});
        }

        return string;
    },

    DataDefine(data, config, depth = 0)
    {
        if (depth > 10)
        {
            throw this.Error(400, 'Maximum nesting depth of 10 exceeded in data definition.');
        }

        this.DataValidateObject(data, 'Data');

        config = this.DataConfig(config);

        for (let [key, value] of Object.entries(config))
        {
            try
            {
                data[key] = this.DataDefineOne(data[key], value, depth + 1);
            }
            catch (error)
            {
                error.path = [key].concat(error.path || []);
                throw error;
            }
        }

        return data;
    },

    DataDefineOne(value, config, depth = 0)
    {
        this.DataValidateObject(config);

        try
        {
            const parsed = this.DataParseConfig(config);

            if(!this.DataTypeMatch(value, parsed.type))
            {
                if(parsed.value !== undefined && parsed.value !== null && this.DataTypeMatch(parsed.value, parsed.type))
                {
                    value = parsed.value;
                }
                else if(value === null && !parsed.required)
                {
                    value = null;
                }
                else
                {
                    if(parsed.required)
                    {
                        throw this.Error(400, 'Expected :type:, got :got:.', {
                            type: parsed.type,
                            got: value === null ? 'null' : (value === undefined ? 'undefined' : (Array.isArray(value) ? 'array' : typeof value)),
                            value: value
                        });
                    }

                    value = value === null ? null : undefined;
                }
            }

            if(parsed.options && value !== null && value !== undefined)
            {
                const values = Array.isArray(value) ? value : [value];

                for(let i = 0; i < values.length; i++)
                {
                    if(!parsed.options.includes(values[i]))
                    {
                        throw this.Error(400, 'Expected one of :options:.', {options: parsed.options.join(', ')});
                    }
                }
            }

            if(Array.isArray(value))
            {
                if(parsed.each)
                {
                    for (let i = 0; i < value.length; i++)
                    {
                        try
                        {
                            value[i] = this.DataDefineOne(value[i], parsed.each, depth + 1);
                        }
                        catch (error)
                        {
                            error.path = ['[' + i + ']'].concat(error.path || []);
                            throw error;
                        }
                    }
                }

                value = value.filter(v => v !== null && v !== undefined);
            }
            else if(typeof value === 'object' && value !== null && value !== undefined)
            {
                if(parsed.config)
                {
                    value = this.DataDefine(value, parsed.config);
                }
            }

            return value;
        }
        catch (error)
        {
            throw error;
        }
    },

    DataTypeMatch(value, type)
    {
        return type.split('|').map(t => t.trim().toLowerCase()).some(type =>
        {
            if (type === 'any')
            {
                return true;
            }

            if (type === 'array')
            {
                return Array.isArray(value);
            }

            if (type === 'object')
            {
                return typeof value === 'object' && !Array.isArray(value) && value !== null && value !== undefined;
            }

            if (type === 'binary')
            {
                return Buffer.isBuffer(value);
            }

            return type === typeof value;
        });
    },

    DataPrimitiveValue(value, defaultValue = null)
    {
        if (value === undefined || value === null)
        {
            return this.DataCloneDefault(defaultValue);
        }

        if (typeof value !== typeof defaultValue || Array.isArray(value) !== Array.isArray(defaultValue))
        {
            return this.DataCloneDefault(defaultValue);
        }

        return value;
    },

    DataCloneDefault(value)
    {
        if (value === null || value === undefined)
        {
            return value;
        }

        if (Array.isArray(value))
        {
            return value.slice();
        }

        if (typeof value === 'object')
        {
            return { ...value };
        }

        return value;
    },

    DataConfig(config)
    {
        if(typeof config === 'string')
        {
            config = this.DataSchema(config);
        }

        this.DataValidateObject(config, 'Config');

        const result = {};

        for (let [key, data] of Object.entries(config))
        {
            try
            {
                if(key.charAt(0) === '@')
                {
                    result[key.substring(1)] = this.DataConfig(this.DataSchema(key));
                    continue;
                }

                result[key] = this.DataParseConfig(data);
            }
            catch (error)
            {
                throw this.Error(400, 'Error in field :field:.', {field: key});
            }
        }

        return result;
    },

    DataParseConfig(config)
    {
        const parsed = {
            type: null,
            value: undefined,
            required: undefined,
            config: undefined,
            options: undefined,
            description: undefined
        };

        if(Array.isArray(config))
        {
            parsed.type = config[0];
            parsed.value = config[1];
            parsed.required = typeof config[2] === 'boolean' ? config[2] : undefined;
        }
        else if(config && typeof config === 'object')
        {
            parsed.type = config.type;
            parsed.value = config.value;
            parsed.required = typeof config.required === 'boolean' ? config.required : undefined;
            parsed.config = config.config;
            parsed.each = config.each;
            parsed.options = Array.isArray(config.options) ? config.options : undefined;
            parsed.description = typeof config.description === 'string' ? config.description : undefined;
        }
        else 
        {
            parsed.type = typeof config;
            parsed.value = config;
        }

        parsed.value = parsed.value === null ? undefined : parsed.value;

        if(typeof parsed.type !== 'string')
        {
            throw this.Error(400, 'Type property is missing in config object.');
        }

        parsed.type = parsed.type.toLowerCase();

        if(parsed.config)
        {
            if(typeof parsed.config === 'string')
            {
                parsed.config = this.DataSchema(parsed.config);
            }

            for (let [configKey, configValue] of Object.entries(parsed.config))
            {
                try
                {
                    parsed.config[configKey] = this.DataParseConfig(configValue);
                }
                catch (error)
                {
                    throw this.Error(400, 'Error in field :field:.', {field: configKey});
                }
            }
        }

        parsed.type.split('|').forEach((type) =>
        {
            if(!['number', 'string', 'boolean', 'object', 'array', 'function', 'binary', 'any'].includes(type))
            {
                throw this.Error(400, 'Invalid config type. Expected number, string, boolean, object, array, function or binary.');
            }
        })

        if(parsed.each) 
        { 
            parsed.each = this.DataParseConfig(parsed.each); 
        }

        if(typeof parsed.value === 'undefined')       { delete parsed.value;       }
        if(typeof parsed.required === 'undefined')    { delete parsed.required;    }
        if(typeof parsed.config === 'undefined')      { delete parsed.config;      }
        if(typeof parsed.each === 'undefined')        { delete parsed.each;        }
        if(typeof parsed.options === 'undefined')     { delete parsed.options;     }
        if(typeof parsed.description === 'undefined') { delete parsed.description; }

        return parsed;
    },

    DataSchema(name, config = null)
    {
        this.DataValidateString(name, 'Schema');

        const command = this.GenerateCommand(name);

        name = command.name
        name = name.charAt(0) === '@' ? name.substring(1) : name;
        
        if(config === null)
        {
            if(!(name in this.data.schemas))
            {
                throw this.Error(400, 'Schema :name: doesn\'t exist.', {name});
            }

            const schema = this.DataConfig(structuredClone(this.data.schemas[name]));

            command.options.forEach((option) => 
            {
                if(option.name === 'skip')
                {
                    delete schema[option.value];
                }
                else if(option.name === 'optional')
                {
                    Object.values(schema).forEach((value) => delete value.required);
                }
            });

            return schema;
        }

        this.data.schemas[name] = config;
    }
};

export default OneTypeData;

// repositories/framework/lib/src/mixins/dom.js
const OneTypeDOM =
{
	DOMCreate(html)
	{
		const wrapper = document.createElement('div');

		wrapper.innerHTML = html.trim();

		if(wrapper.childNodes.length === 1)
		{
			return wrapper.firstChild;
		}

		const fragment = document.createDocumentFragment();

		while(wrapper.firstChild)
		{
			fragment.appendChild(wrapper.firstChild);
		}

		return fragment;
	},

	DOMKey(node)
	{
		if(node.nodeType !== Node.ELEMENT_NODE)
		{
			return null;
		}

		return node.__otExternal?.key || node.getAttribute('ot-key') || null;
	},

	DOMChildren(node)
	{
		return Array.from(node.childNodes).filter(child => child.nodeType !== Node.TEXT_NODE || child.textContent.trim());
	},

	DOMReplace(current, target)
	{
		if(current.parentNode)
		{
			current.parentNode.replaceChild(target, current);
		}

		return target;
	},

	DOMPatch(current, target)
	{
		if(current === target || !current || !target)
		{
			return current || target;
		}

		if(this.DOMExternal(current, target))
		{
			return current;
		}

		if(target.__otExternal || current.nodeType !== target.nodeType || current.nodeName !== target.nodeName)
		{
			return this.DOMReplace(current, target);
		}

		if(current.nodeType === Node.TEXT_NODE)
		{
			if(current.textContent !== target.textContent)
			{
				current.textContent = target.textContent;
			}

			return current;
		}

		if(current.nodeType === Node.ELEMENT_NODE)
		{
			this.DOMPatchAttributes(current, target);
			this.DOMPatchChildren(current, target);
			this.DOMPatchHandlers(current, target);
		}

		return current;
	},

	DOMExternal(current, target)
	{
		if(!current.__otExternal || !target.__otExternal)
		{
			return false;
		}

		const a = current.__otExternal;
		const b = target.__otExternal;

		if(a.name !== b.name || a.key !== b.key)
		{
			return false;
		}

		if(a.render && b.data)
		{
			a.render.UpdateData(b.data);
		}

		return true;
	},

	DOMPatchAttributes(current, target)
	{
		for(const attr of target.attributes)
		{
			if(current.getAttribute(attr.name) !== attr.value)
			{
				current.setAttribute(attr.name, attr.value);
			}
		}

		for(let i = current.attributes.length - 1; i >= 0; i--)
		{
			const name = current.attributes[i].name;

			if(!target.hasAttribute(name))
			{
				current.removeAttribute(name);
			}
		}
	},

	DOMPatchChildren(current, target)
	{
		const children = this.DOMChildren(current);
		const targets = this.DOMChildren(target);
		const map = new Map();

		for(const child of children)
		{
			const key = this.DOMKey(child);

			if(key)
			{
				map.set(key, child);
			}
		}

		const length = Math.max(children.length, targets.length);

		for(let i = 0; i < length; i++)
		{
			const child = children[i];
			const goal = targets[i];

			if(!goal)
			{
				current.removeChild(child);
				continue;
			}

			if(!child)
			{
				current.appendChild(goal);
				continue;
			}

			const key = this.DOMKey(goal);
			const match = key && map.get(key);

			if(match && match !== child)
			{
				current.insertBefore(match, child);
				this.DOMPatch(match, goal);
			}
			else
			{
				this.DOMPatch(child, goal);
			}
		}
	},

	DOMPatchHandlers(current, target)
	{
		for(const key in target)
		{
			if(key.startsWith('ot') && typeof target[key] === 'function')
			{
				current[key] = target[key];
			}
		}
	},

	DOMWalk(element, callback, depth = 0)
	{
		if(callback(element, depth) === false)
		{
			return;
		}

		if(element.childNodes)
		{
			for(let i = 0; i < element.childNodes.length; i++)
			{
				this.DOMWalk(element.childNodes[i], callback, depth + 1);
			}
		}
	}
};

export default OneTypeDOM;

// repositories/framework/lib/src/mixins/emitter.js
const OneTypeEmitter =
{
    Emit(name, ...args)
    {
        const listeners = this.emitters[name];

        if(!listeners || !listeners.length)
        {
            return false;
        }

        for(let i = 0; i < listeners.length; i++)
        {
            const emitter = listeners[i];

            try
            {
                emitter.callback(...args);

                if(emitter.once)
                {
                    listeners.splice(i, 1);
                    i--;
                }
            }
            catch(error)
            {
                this.Error(500, 'Error in emitter callback :name: — :reason:', {
                    name,
                    reason: error.message,
                    stack: error.stack
                });
            }
        }

        return true;
    },

    EmitOn(name, callback)
    {
        if(!this.emitters[name])
        {
            this.emitters[name] = [];
        }

        const id = this.GenerateUID();

        this.emitters[name].push({id, once: false, callback});

        return id;
    },

    EmitOnce(name, callback)
    {
        if(!this.emitters[name])
        {
            this.emitters[name] = [];
        }

        const id = this.GenerateUID();

        this.emitters[name].push({id, once: true, callback});

        return id;
    },

    EmitOff(name, id)
    {
        if(!(name in this.emitters))
        {
            return;
        }

        this.emitters[name] = this.emitters[name].filter(emitter => emitter.id !== id);
    }
};

export default OneTypeEmitter;

// repositories/framework/lib/src/mixins/error.js
import OneTypeErrorClass from '../classes/error/class.js';

const OneTypeError =
{
    Error(code, message, context)
    {
        if(context && typeof context === 'object')
        {
            for(const [key, value] of Object.entries(context))
            {
                message = message.replace(':' + key + ':', value);
            }
        }

        const error = new OneTypeErrorClass(code, message, context);

        if(!this.emitting)
        {
            this.emitting = true;
            this.Emit('@error', error);
            this.emitting = false;
        }

        return error;
    }
};

export default OneTypeError;

// repositories/framework/lib/src/mixins/form.js
const OneTypeForm =
{
    FormGet(element)
    {
        const data = {};
        const inputs = element.querySelectorAll('input[name], textarea[name], select[name]');

        inputs.forEach(input =>
        {
            const name = input.getAttribute('name');

            if(!name)
            {
                return;
            }

            if(input.type === 'checkbox')
            {
                data[name] = input.checked;
            }
            else if(input.type === 'radio')
            {
                if(input.checked)
                {
                    data[name] = input.value;
                }
            }
            else
            {
                data[name] = input.value;
            }
        });

        return data;
    },

    FormSet(element, data)
    {
        if(!data || typeof data !== 'object')
        {
            return;
        }

        Object.entries(data).forEach(([name, value]) =>
        {
            const inputs = element.querySelectorAll(`[name="${name}"]`);

            inputs.forEach(input =>
            {
                if(input.type === 'checkbox')
                {
                    input.checked = !!value;
                    input.dispatchEvent(new Event('change', { bubbles: true }));
                }
                else if(input.type === 'radio')
                {
                    input.checked = input.value === value;
                    input.dispatchEvent(new Event('change', { bubbles: true }));
                }
                else
                {
                    input.value = value;
                    input.dispatchEvent(new Event('input', { bubbles: true }));
                    input.dispatchEvent(new Event('change', { bubbles: true }));
                }
            });
        });
    }
};

export default OneTypeForm;

// repositories/framework/lib/src/mixins/function.js
const OneTypeFunction =
{
    FunctionCache: new Map(),

    Function(expression, data = {}, safe = true)
    {
        if (!this.FunctionExpressionValid(expression, safe))
        {
            return undefined;
        }

        try
        {
            const keys = Object.keys(data);
            const cacheKey = keys.length > 0 ? expression + ':' + keys.join(',') : expression;

            let evaluator = this.FunctionCache.get(cacheKey);

            if (!evaluator)
            {
                const FunctionConstructor = Function;
                evaluator = new FunctionConstructor(...keys, `return ${expression};`);
                this.FunctionCache.set(cacheKey, evaluator);
            }

            return keys.length > 0 ? evaluator(...Object.values(data)) : evaluator();
        }
        catch (error)
        {
            this.Error(500, 'Function error on :expression: — :reason:', {
                expression,
                reason: error.message,
                data: Object.keys(data),
                stack: error.stack
            });
            return undefined;
        }
    },

    FunctionExpressionValid(expression, safe = true)
    {
        if (expression.includes(';') ||
            expression.includes('`') ||
            expression.includes('return '))
        {
            return false;
        }

        if (!safe)
        {
            return true;
        }

        if (expression.includes('=>'))
        {
            return false;
        }

        const safePattern = /^[a-zA-Z0-9_\[\]\.\'"+-\/*%<>=!&|\s()?,:\d:]*$/;

        if (!safePattern.test(expression))
        {
            return false;
        }
        
        const dangerousKeywords = [
            'eval', 'Function', 'setTimeout', 'setInterval', 'execScript',
            'constructor', 'prototype', '__proto__', 'global', 'process',
            'require', 'import', 'export', 'module', 'window', 'document',
            'localStorage', 'sessionStorage', 'fetch', 'XMLHttpRequest',
            '__dirname', '__filename', 'Buffer', 'Promise', 'arguments',
            'callee', 'caller', 'debugger', 'new', 'this', 'delete',
            'bind', 'apply', 'call', 'Reflect', 'Proxy', 'revoke'
        ];

        const withoutQuotes = expression.replace(/'[^']*'|"[^"]*"/g, '');

        return !dangerousKeywords.some(keyword =>
        {
            const regex = new RegExp(`\\b${keyword}\\b`, 'i');
            return regex.test(withoutQuotes);
        });
    }
};

export default OneTypeFunction;

// repositories/framework/lib/src/mixins/generate.js
const OneTypeGenerate =
{
    GenerateHash(name)
    {
        let hash = 0;

        for (let i = 0; i < name.length; i++)
        {
            hash = (hash << 5) - hash + name.charCodeAt(i);
            hash |= 0;
        }

        return Math.abs(hash).toString(16);
    },

    GenerateUID()
    {
        return Date.now().toString(36) + Math.random().toString(36).slice(2, 8);
    },

    GenerateString(length = 10, charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789')
    {
        let result = '';
        const charsetLength = charset.length;

        for (let i = 0; i < length; i++)
        {
            result += charset.charAt(Math.floor(Math.random() * charsetLength));
        }

        return result;
    },

    GenerateTID()
    {
        let timestamp = Date.now();
        let uniquePart = Math.floor(Math.random() * 1000000).toString().padStart(6, '0');

        return parseInt(`${timestamp}${uniquePart}`);
    },

    GenerateCommand(string)
    {
        const segments = string.trim().toLowerCase().split(' ').filter(segment => segment !== '');
        
        if(!segments.length)
        {
            return { name: '', options: [] };
        }
        
        const [name, ...parameters] = segments;
        
        const options = parameters.filter(parameter => parameter.startsWith('--')).map(parameter => 
        {
            const option = parameter.substring(2);
            
            if (option.includes('='))
            {
                const [identifier, content] = option.split('=');

                return { name: identifier, value: content };
            }
            
            return { name: option, value: null };
        });
    
        return { name, options };
    }
};

export default OneTypeGenerate;

// repositories/framework/lib/src/mixins/helper.js
const OneTypeHelper =
{
    HelperDebounce(callback, delay = 300)
    {
        let timer;
        
        return (...args) =>
        {
            clearTimeout(timer);
            timer = setTimeout(() => callback(...args), delay);
        };
    },
    
    HelperThrottle(callback, delay = 300)
    {
        let waiting = false;
        let lastArgs;
        
        const timeoutFunc = () =>
        {
            if(lastArgs)
            {
                callback(...lastArgs);
                lastArgs = null;
                setTimeout(timeoutFunc, delay);
            }
            else
            {
                waiting = false;
            }
        };
        
        return (...args) =>
        {
            if(waiting)
            {
                lastArgs = args;
                return;
            }
            
            callback(...args);
            waiting = true;
            setTimeout(timeoutFunc, delay);
        };
    },
    
    HelperSleep(ms)
    {
        return new Promise(resolve => setTimeout(resolve, ms));
    },
    
    HelperRetry(fn, retries = 3, delay = 1000)
    {
        return new Promise((resolve, reject) =>
        {
            const attempt = async (remaining) =>
            {
                try
                {
                    const result = await fn();
                    resolve(result);
                }
                catch(error)
                {
                    if(remaining <= 0)
                    {
                        reject(error);
                        return;
                    }
                    
                    await this.HelperSleep(delay);
                    attempt(remaining - 1);
                }
            };
            
            attempt(retries);
        });
    },
    
    HelperMemoize(fn)
    {
        const cache = new Map();
        
        return (...args) =>
        {
            const key = JSON.stringify(args);
            
            if(cache.has(key))
            {
                return cache.get(key);
            }
            
            const result = fn(...args);
            cache.set(key, result);
            
            return result;
        };
    },
    
    HelperPipe(...fns)
    {
        return (value) => fns.reduce((v, fn) => fn(v), value);
    },
    
    HelperCompose(...fns)
    {
        return (value) => fns.reduceRight((v, fn) => fn(v), value);
    },
    
    HelperPick(obj, keys)
    {
        return keys.reduce((result, key) =>
        {
            if(key in obj)
            {
                result[key] = obj[key];
            }
            return result;
        }, {});
    },
    
    HelperOmit(obj, keys)
    {
        const keysSet = new Set(keys);
        
        return Object.entries(obj).reduce((result, [key, value]) =>
        {
            if(!keysSet.has(key))
            {
                result[key] = value;
            }
            return result;
        }, {});
    },
    
    HelperDeepClone(value)
    {
        return structuredClone(value);
    },

    HelperDeepMerge(target, ...sources)
    {
        if(!sources.length) return target;
        
        const source = sources.shift();
        
        if(this.HelperIsObject(target) && this.HelperIsObject(source))
        {
            for(const key in source)
            {
                if(this.HelperIsObject(source[key]))
                {
                    if(!target[key]) Object.assign(target, { [key]: {} });
                    this.HelperDeepMerge(target[key], source[key]);
                }
                else
                {
                    Object.assign(target, { [key]: source[key] });
                }
            }
        }
        
        return this.HelperDeepMerge(target, ...sources);
    },
    
    HelperIsObject(item)
    {
        return item && typeof item === 'object' && !Array.isArray(item);
    },
    
    HelperIsEmpty(value)
    {
        if(value == null) return true;
        if(typeof value === 'boolean') return false;
        if(typeof value === 'number') return false;
        if(value instanceof Date) return false;
        if(value instanceof Error) return false;
        if(Array.isArray(value)) return value.length === 0;
        if(typeof value === 'string') return value.length === 0;
        if(value instanceof Map || value instanceof Set) return value.size === 0;
        if(typeof value === 'object') return Object.keys(value).length === 0;
        
        return false;
    },
    
    HelperGroupBy(array, key)
    {
        return array.reduce((groups, item) =>
        {
            const group = typeof key === 'function' ? key(item) : item[key];
            
            if(!groups[group])
            {
                groups[group] = [];
            }
            
            groups[group].push(item);
            
            return groups;
        }, {});
    },
    
    HelperChunk(array, size)
    {
        const chunks = [];
        
        for(let i = 0; i < array.length; i += size)
        {
            chunks.push(array.slice(i, i + size));
        }
        
        return chunks;
    },
    
    HelperFlatten(array, depth = 1)
    {
        if(depth <= 0) return array.slice();
        
        return array.reduce((flat, item) =>
        {
            if(Array.isArray(item))
            {
                return flat.concat(this.HelperFlatten(item, depth - 1));
            }
            
            return flat.concat(item);
        }, []);
    },
    
    HelperUnique(array, key)
    {
        if(!key)
        {
            return [...new Set(array)];
        }
        
        const seen = new Set();
        
        return array.filter(item =>
        {
            const k = typeof key === 'function' ? key(item) : item[key];
            
            if(seen.has(k))
            {
                return false;
            }
            
            seen.add(k);
            return true;
        });
    },

    HelperRegex(pattern, defaultFlags = '')
    {
        if (typeof pattern !== 'string')
        {
            return null;
        }

        if (pattern.startsWith('/') && pattern.lastIndexOf('/') > 0)
        {
            const lastSlashIndex = pattern.lastIndexOf('/');
            const regexPattern = pattern.substring(1, lastSlashIndex);
            const flags = pattern.substring(lastSlashIndex + 1) || defaultFlags;

            try
            {
                return new RegExp(regexPattern, flags);
            }
            catch (e)
            {
                return null;
            }
        }

        try
        {
            return new RegExp(pattern, defaultFlags);
        }
        catch (e)
        {
            return null;
        }
    }
};

export default OneTypeHelper;

// repositories/framework/lib/src/mixins/language.js
const OneTypeLanguage =
{
	Language(value)
	{
		if(typeof value === 'undefined')
		{
			return this.LanguageGet();
		}

		this.LanguageSet(value);
	},

	LanguageGet()
	{
		return this.StateGet('language', 'EN');
	},

	LanguageSet(value)
	{
		this.LanguageValidate(value);
		this.StateSet('language', value);
	},

	Languages(value)
	{
		if(typeof value === 'undefined')
		{
			return this.LanguagesGet();
		}

		this.LanguagesSet(value);
	},

	LanguagesGet()
	{
		return this.StateGet('languages', []);
	},

	LanguagesSet(value)
	{
		this.StateSet('languages', value);
	},

	LanguageValidate(code)
	{
		if(typeof code !== 'string' || !/^[A-Z]{2}$/.test(code))
		{
			throw this.Error(400, 'Language must be a 2-letter uppercase code, received: :code:.', {code});
		}

		return code;
	}
};

export default OneTypeLanguage;

// repositories/framework/lib/src/mixins/locale.js
const OneTypeLocale =
{
	LocaleGet(text, key = null)
	{
		const language = this.Language();
		const hash = this.GenerateHash(key ? key + ':' + text : text);
		const locales = this.LocalesGet(language);

		if(hash in locales)
		{
			return locales[hash];
		}

		this.LocaleSet(text, key);

		return text;
	},

	LocaleSet(text, key = null)
	{
		const language = this.Language();
		const hash = this.GenerateHash(key ? key + ':' + text : text);
		const locales = this.LocalesGet(language);

		locales[hash] = text;
		this.LocalesSet(language, locales);
	},

	LocalesGet(language)
	{
		const locales = this.StateGet('locales', {});

		if(language in locales)
		{
			return locales[language]
		}

		return {};
	},

	LocalesSet(language, values)
	{
		const locales = this.StateGet('locales', {});
		locales[language] = values;
		this.StateSet('locales', locales);
	}
};

export default OneTypeLocale;

// repositories/framework/lib/src/mixins/markdown.js
const OneTypeMarkdown =
{
	Markdown(content)
	{
		if (!content)
		{
			return '';
		}

		let html = '';
		let buffer = [];
		let block = false;

		const lines = content.split('\n');

		for (let i = 0; i < lines.length; i++)
		{
			const trimmed = lines[i].trim();

			if (trimmed.startsWith('```'))
			{
				buffer.push(lines[i]);
				block = !block;

				if (!block)
				{
					flush();
				}

				continue;
			}

			if (block)
			{
				buffer.push(lines[i]);
				continue;
			}

			if (trimmed.startsWith('|') || /^[\t ]*[-*] .+/.test(trimmed) || /^[\t ]*\d+\. .+/.test(trimmed) || trimmed.startsWith('> '))
			{
				buffer.push(lines[i]);
				continue;
			}

			flush();

			if (!trimmed)
			{
				continue;
			}

			html += `<div class="line">${transform(trimmed)}</div>`;
		}

		flush();

		return html;

		function flush()
		{
			if (!buffer.length)
			{
				return;
			}

			html += `<div class="line">${transform(buffer.join('\n'))}</div>`;
			buffer = [];
		}

		function transform(text)
		{
			text = code(text);
			text = media(text);
			text = heading(text);
			text = rule(text);
			text = quote(text);
			text = format(text);
			text = list(text);
			text = table(text);
			text = paragraph(text);

			return text;
		}

		function code(text)
		{
			text = text.replace(/```(\w*)\n([\s\S]*?)```/g, (_, lang, content) =>
			{
				content = content.replace(/</g, '&lt;').replace(/>/g, '&gt;').trim();
				return `<pre class="codeblock"><code${lang ? ` data-lang="${lang}"` : ''}>${content}</code></pre>`;
			});

			return text.replace(/`([^`]+)`/g, '<code class="inline">$1</code>');
		}

		function media(text)
		{
			text = text.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, '<img src="$2" alt="$1" />');
			return text.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener">$1</a>');
		}

		function heading(text)
		{
			text = text.replace(/^#### (.+)$/gm, '<h4>$1</h4>');
			text = text.replace(/^### (.+)$/gm, '<h3>$1</h3>');
			text = text.replace(/^## (.+)$/gm, '<h2>$1</h2>');
			return text.replace(/^# (.+)$/gm, '<h1>$1</h1>');
		}

		function rule(text)
		{
			return text.replace(/^---$/gm, '<hr />');
		}

		function quote(text)
		{
			text = text.replace(/^> (.+)$/gm, '<blockquote>$1</blockquote>');
			return text.replace(/<\/blockquote>\n<blockquote>/g, '\n');
		}

		function format(text)
		{
			text = text.replace(/\*\*\*(.+?)\*\*\*/g, '<strong><em>$1</em></strong>');
			text = text.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
			text = text.replace(/\*(.+?)\*/g, '<em>$1</em>');
			return text.replace(/~~(.+?)~~/g, '<del>$1</del>');
		}

		function list(text)
		{
			text = text.replace(/^[\t ]*[-*] (.+)$/gm, '<uli>$1</uli>');
			text = text.replace(/((?:<uli>.*<\/uli>\n?)+)/g, (match) =>
			{
				return `<ul>${match.replace(/<\/?uli>/g, (t) => t.replace('uli', 'li'))}</ul>`;
			});

			text = text.replace(/^[\t ]*\d+\. (.+)$/gm, '<oli>$1</oli>');
			text = text.replace(/((?:<oli>.*<\/oli>\n?)+)/g, (match) =>
			{
				return `<ol>${match.replace(/<\/?oli>/g, (t) => t.replace('oli', 'li'))}</ol>`;
			});

			return text;
		}

		function table(text)
		{
			return text.replace(/^(\|.+\|)\n(\|[-| :]+\|)\n((?:\|.+\|\n?)+)/gm, (_, header, _align, body) =>
			{
				const heads = header.split('|').filter(c => c.trim()).map(c => `<th>${c.trim()}</th>`).join('');
				const rows = body.trim().split('\n').map(row =>
				{
					const cells = row.split('|').filter(c => c.trim()).map(c => `<td>${c.trim()}</td>`).join('');
					return `<tr>${cells}</tr>`;
				}).join('');

				return `<table><thead><tr>${heads}</tr></thead><tbody>${rows}</tbody></table>`;
			});
		}

		function paragraph(text)
		{
			text = text.replace(/(<\/(table|ul|ol|blockquote|pre|h[1-4])>)(.)/g, '$1\n$3');
			text = text.replace(/(.)(<(table|ul|ol|blockquote|pre|h[1-4]|hr|img)[\s>])/g, '$1\n$2');

			const pattern = /^<(h[1-4]|ul|ol|pre|blockquote|hr|table|img|li|\/)/;

			return text.split('\n').map(line =>
			{
				const trimmed = line.trim();

				if (!trimmed || pattern.test(trimmed))
				{
					return trimmed;
				}

				return `<p>${trimmed}</p>`;
			}).filter(line => line).join('\n');
		}
	}
};

export default OneTypeMarkdown;

// repositories/framework/lib/src/mixins/middleware.js
const OneTypeMiddleware =
{
    async Middleware(name, value)
    {
        let index = -1;

        const context = {
            value: value,
            errors: [],
            next: async () =>
            {
                index++;

                if (index < this.middleware[name].length)
                {
                    try
                    {
                        await this.middleware[name][index](context);
                    }
                    catch(error)
                    {
                        this.Error(500, 'Error in middleware callback :name:.', {name});
                    }
                }

                return context;
            }
        };

        if (!this.middleware[name])
        {
            this.middleware[name] = [];
        }

        return await context.next();
    },

    MiddlewareIntercept(name, callback)
    {
        if(!this.middleware[name])
        {
            this.middleware[name] = [];
        }

        this.middleware[name].push(callback);
        return this;
    }
};

export default OneTypeMiddleware;

// repositories/framework/lib/src/mixins/observer.js
const OneTypeObserver =
{
	ObserverStart()
	{
		if(this.environment !== 'front')
		{
			return;
		}

		this.observer =
		{
			mutation: null,
			visibility: null,
			resize: null
		};

		const start = () =>
		{
			this.observer.mutation = new MutationObserver((mutations) =>
			{
				for(const mutation of mutations)
				{
					for(const node of mutation.addedNodes)
					{
						if(node.nodeType !== 1)
						{
							continue;
						}

						this.ObserverNode(node, 'add');
						this.ObserverWalk(node, 'add');
					}

					for(const node of mutation.removedNodes)
					{
						if(node.nodeType !== 1)
						{
							continue;
						}

						this.ObserverNode(node, 'remove');
						this.ObserverWalk(node, 'remove');
					}
				}
			});

			this.observer.mutation.observe(document.body, { childList: true, subtree: true });

			this.observer.visibility = new IntersectionObserver((entries) =>
			{
				for(const entry of entries)
				{
					if(entry.isIntersecting && entry.target.__visible)
					{
						entry.target.__visible(entry);
					}
				}
			}, { threshold: 0.1 });

			this.observer.resize = new ResizeObserver((entries) =>
			{
				for(const entry of entries)
				{
					if(entry.target.__resize)
					{
						entry.target.__resize(entry);
					}
				}
			});
		};

		if(document.readyState === 'loading')
		{
			document.addEventListener('DOMContentLoaded', start);
		}
		else
		{
			start();
		}
	},

	ObserverNode(node, type)
	{
		this.Emit('@dom.' + type, node);

		if(type === 'add' && node.__add)
		{
			node.__add();
		}

		if(type === 'remove' && node.__remove)
		{
			node.__remove();
		}
	},

	ObserverWalk(node, type)
	{
		if(!node.hasChildNodes())
		{
			return;
		}

		for(let i = 0; i < node.childNodes.length; i++)
		{
			const child = node.childNodes[i];

			if(child.nodeType !== 1)
			{
				continue;
			}

			this.ObserverNode(child, type);
			this.ObserverWalk(child, type);
		}
	},

	ObserverVisible(element, callback)
	{
		if(!this.observer || !this.observer.visibility)
		{
			return;
		}

		element.__visible = callback;
		this.observer.visibility.observe(element);
	},

	ObserverUnvisible(element)
	{
		if(!this.observer || !this.observer.visibility)
		{
			return;
		}

		delete element.__visible;
		this.observer.visibility.unobserve(element);
	},

	ObserverResize(element, callback)
	{
		if(!this.observer || !this.observer.resize)
		{
			return;
		}

		element.__resize = callback;
		this.observer.resize.observe(element);
	},

	ObserverUnresize(element)
	{
		if(!this.observer || !this.observer.resize)
		{
			return;
		}

		delete element.__resize;
		this.observer.resize.unobserve(element);
	},

	ObserverScroll(element, callback)
	{
		if(!this.observer)
		{
			return;
		}

		if(!this.observer.scroll)
		{
			this.observer.scroll = new Set();
			this.observer.scrollPrevious = window.scrollY;
			this.observer.scrollTimestamp = Date.now();

			window.addEventListener('scroll', () =>
			{
				const now = Date.now();
				const current = window.scrollY;
				const delta = now - this.observer.scrollTimestamp;
				const direction = current >= this.observer.scrollPrevious ? 'down' : 'up';
				const speed = delta > 0 ? Math.abs(current - this.observer.scrollPrevious) / (delta / 1000) : 0;
				const height = window.innerHeight;

				for(const node of this.observer.scroll)
				{
					if(!node.__scroll)
					{
						continue;
					}

					const rect = node.getBoundingClientRect();
					const visible = rect.bottom > 0 && rect.top < height;

					if(!visible)
					{
						continue;
					}

					node.__scroll({
						progress: Math.min(1, Math.max(0, (height - rect.top) / (height + rect.height))),
						direction: direction,
						speed: speed,
						top: rect.top,
						bottom: height - rect.bottom,
						visible: true
					});
				}

				this.observer.scrollPrevious = current;
				this.observer.scrollTimestamp = now;
			}, { passive: true });
		}

		element.__scroll = callback;
		this.observer.scroll.add(element);
	},

	ObserverUnscroll(element)
	{
		if(!this.observer || !this.observer.scroll)
		{
			return;
		}

		delete element.__scroll;
		this.observer.scroll.delete(element);
	},

	ObserverHover(element, callback, offset = 0)
	{
		if(!this.observer)
		{
			return;
		}

		if(!this.observer.hover)
		{
			this.observer.hover = new Set();

			document.addEventListener('mousemove', (event) =>
			{
				for(const node of this.observer.hover)
				{
					if(!node.__hover)
					{
						continue;
					}

					const rect = node.getBoundingClientRect();
					const pad = node.__hoverOffset;
					const inside = event.clientX >= rect.left - pad && event.clientX <= rect.right + pad && event.clientY >= rect.top - pad && event.clientY <= rect.bottom + pad;

					if(inside || node.__hoverActive)
					{
						node.__hover({
							active: inside,
							enter: inside && !node.__hoverActive,
							leave: !inside && node.__hoverActive,
							x: (event.clientX - rect.left) / rect.width,
							y: (event.clientY - rect.top) / rect.height
						});

						node.__hoverActive = inside;
					}
				}
			}, { passive: true });
		}

		element.__hover = callback;
		element.__hoverActive = false;
		element.__hoverOffset = offset;
		this.observer.hover.add(element);
	},

	ObserverUnhover(element)
	{
		if(!this.observer || !this.observer.hover)
		{
			return;
		}

		delete element.__hover;
		delete element.__hoverActive;
		this.observer.hover.delete(element);
	},

	ObserverClick(element, callback)
	{
		if(!this.observer)
		{
			return;
		}

		if(!this.observer.click)
		{
			this.observer.click = new Set();

			document.addEventListener('click', (event) =>
			{
				for(const node of this.observer.click)
				{
					if(!node.__click)
					{
						continue;
					}

					const rect = node.getBoundingClientRect();
					const inside = event.clientX >= rect.left && event.clientX <= rect.right && event.clientY >= rect.top && event.clientY <= rect.bottom;

					if(inside)
					{
						node.__click({
							x: (event.clientX - rect.left) / rect.width,
							y: (event.clientY - rect.top) / rect.height
						});
					}
				}
			});
		}

		element.__click = callback;
		this.observer.click.add(element);
	},

	ObserverUnclick(element)
	{
		if(!this.observer || !this.observer.click)
		{
			return;
		}

		delete element.__click;
		this.observer.click.delete(element);
	}
};

export default OneTypeObserver;

// repositories/framework/lib/src/mixins/overrides.js
const OneTypeOverrides =
{
    OverrideLog()
    {
        let start = performance.now();
        let log = console.log

        console.log = function(message, ...keys)
        {
            if(typeof message !== 'string')
            {
                return log(message, ...keys);
            }

            const now = performance.now();
            const elapsed = now - start;
            const timestamp = new Date().toISOString();
            
            if (!message.endsWith('.')) 
            {
                message = message + '.';
            }

            keys.forEach((key, index) => 
            {
                message = message.replace(':' + (index + 1), key);
            })
            
            log(`${timestamp} [+${elapsed.toFixed(2)}ms] - ${message}`);
            start = now;
        };
    },
};

export default OneTypeOverrides;

// repositories/framework/lib/src/mixins/request.js
const OneTypeRequest = {
    Request(name, value)
    {
        name = name.toLowerCase();

        const data = {value};

        this.Emit('@request.' + name, data);

        if(typeof data !== 'object' || !('value' in data) || typeof data.value !== typeof value)
        {
            return value;
        }

        return data.value;
    },

    RequestCatch(name, callback)
    {
        name = name.toLowerCase();

        this.EmitOn('@request.' + name, callback);
    }
};

export default OneTypeRequest;

// repositories/framework/lib/src/mixins/route.js
const OneTypeRoute =
{
    RouteNormalize(pathname)
    {
        if (!pathname || pathname === '/')
        {
            return '/';
        }

        let normalized = pathname.toLowerCase();

        if (normalized.endsWith('/') && normalized.length > 1)
        {
            normalized = normalized.slice(0, -1);
        }

        if (!normalized.startsWith('/'))
        {
            normalized = '/' + normalized;
        }

        return normalized;
    },

    RouteMatch(pattern, pathname)
    {
        const normalized = this.RouteNormalize(pattern);
        const path = this.RouteNormalize(pathname);

        if (normalized === path)
        {
            return { match: true, params: {} };
        }

        if (normalized.includes('*'))
        {
            const segments = normalized.split('/');
            const parts = path.split('/');

            if (segments[segments.length - 1] === '*')
            {
                if (parts.length < segments.length - 1)
                {
                    return { match: false, params: {} };
                }

                for (let i = 0; i < segments.length - 1; i++)
                {
                    if (segments[i] !== parts[i])
                    {
                        return { match: false, params: {} };
                    }
                }

                return { match: true, params: {} };
            }
        }

        if (normalized.includes(':') || normalized.includes('*'))
        {
            const segments = normalized.split('/');
            const parts = path.split('/');

            if (segments.length !== parts.length)
            {
                return { match: false, params: {} };
            }

            let matches = true;
            const params = {};

            for (let i = 0; i < segments.length; i++)
            {
                if (segments[i] === '*')
                {
                    continue;
                }
                else if (segments[i].startsWith(':'))
                {
                    const name = segments[i].substring(1);
                    params[name] = parts[i];
                }
                else if (segments[i] !== parts[i])
                {
                    matches = false;
                    break;
                }
            }

            if (matches)
            {
                return { match: true, params };
            }
        }

        return { match: false, params: {} };
    },

    RouteParams(pattern, pathname)
    {
        const result = this.RouteMatch(pattern, pathname);
        return result.match ? result.params : null;
    },

    RouteBuild(pattern, params = {})
    {
        let route = pattern;

        for (const [key, value] of Object.entries(params))
        {
            route = route.replace(':' + key, value);
        }

        return this.RouteNormalize(route);
    },

    RouteQuery(search)
    {
        const query = search || (typeof window !== 'undefined' ? window.location.search : '');

        if (!query)
        {
            return {};
        }

        const params = {};
        const cleaned = query.startsWith('?') ? query.substring(1) : query;

        if (!cleaned)
        {
            return {};
        }

        cleaned.split('&').forEach(pair =>
        {
            const [key, value] = pair.split('=');

            if (key)
            {
                params[decodeURIComponent(key)] = value ? decodeURIComponent(value) : '';
            }
        });

        return params;
    },

    RouteHash()
    {
        if (typeof window === 'undefined')
        {
            return '';
        }

        const hash = window.location.hash;
        return hash ? hash.substring(1) : '';
    },

    RouteCurrent()
    {
        if (typeof window === 'undefined')
        {
            return '/';
        }

        return this.RouteNormalize(window.location.pathname);
    },

    RouteNavigate(pathname, state = {})
    {
        if (typeof window === 'undefined')
        {
            return;
        }

        const normalized = this.RouteNormalize(pathname);

        if (window.history && window.history.pushState)
        {
            window.history.pushState(state, '', normalized);
            window.dispatchEvent(new PopStateEvent('popstate', { state }));
        }
        else
        {
            window.location.pathname = normalized;
        }
    }
};

export default OneTypeRoute;

// repositories/framework/lib/src/mixins/state.js
const OneTypeState =
{
    StateGet(key, value)
    {
        const state = this.environment === 'front' ? window.__STATE__ : global.__STATE__;

        if(!state)
        {
            return key ? value : {};
        }

        const result = key ? state[key] : state;

        if(result === undefined || result === null)
        {
            return value;
        }

        if(value !== undefined && typeof result !== typeof value)
        {
            return value;
        }

        return result;
    },

    StateSet(key, value)
    {
        if(this.environment === 'front')
        {
            if(!window.__STATE__)
            {
                window.__STATE__ = {};
            }

            window.__STATE__[key] = value;
        }
        else
        {
            if(!global.__STATE__)
            {
                global.__STATE__ = {};
            }

            global.__STATE__[key] = value;
        }

        this.Emit('@state.change', key, value);
    }
};

export default OneTypeState;

// repositories/framework/lib/src/mixins/string.js
const OneTypeString =
{
    StringSanitize(input)
    {
        const string = typeof input !== 'string' ? JSON.stringify(input) : input;

        const htmlEntities = {
            '&': '&amp;',
            '<': '&lt;',
            '>': '&gt;',
            '"': '&quot;',
            "'": '&#39;'
        };

        return string.replace(/[&<>"']/g, char => htmlEntities[char]);
    },

    StringHasTemplateKey(template, key)
    {
        if (typeof template !== 'string' || typeof key !== 'string')
        {
            return false;
        }

        const regex = /\{\{\s*([^}]+)\s*\}\}/g;

        const keyPattern = new RegExp(`(^|[^\\w.])${key.replace(/\./g, '\\.')}($|[^\\w])`);

        let match;

        while ((match = regex.exec(template)) !== null)
        {
            const expression = match[1].trim();

            if (keyPattern.test(expression))
            {
                return true;
            }
        }

        return false;
    },

    StringCompileTemplate(template, data)
    {
        if (typeof template !== 'string')
        {
            return '';
        }

        if(!data || typeof data !== 'object')
        {
            return template;
        }

        return template.replace(/\{\{\s*([^}]+)\s*\}\}/g, (match, key) =>
        {
            try
            {
                const keys = key.trim().split('.');
                let value = data;
                let depth = 0;
                const MAX_DEPTH = 5;

                for (const k of keys)
                {
                    if (value === undefined || value === null || depth > MAX_DEPTH)
                    {
                        return match;
                    }

                    value = value[k];
                    depth++;
                }

                return value !== undefined && value !== null ? (value + '') : match;
            }
            catch(error)
            {
                return match;
            }
        });
    },

    StringExtractUnit(string, unit = 'px')
    {
        const strValue = (string + '');
        const match = strValue.match(/^(\d+)(%|px|em|rem|vh|vw|pt)?$/);

        if (match)
        {
            return {
                value: parseInt(match[1], 10),
                unit: match[2] || unit
            };
        }

        return {
            value: 0,
            unit: unit
        };
    },

    StringTruncate(string, length = 100, append = '...')
    {
        if (typeof string !== 'string')
        {
            string = (string + '');
        }

        if (string.length <= length)
        {
            return string;
        }

        return string.slice(0, length - append.length) + append;
    },

    StringCapitalize(string)
    {
        if (typeof string !== 'string' || !string.length)
        {
            return '';
        }

        return string.charAt(0).toUpperCase() + string.slice(1);
    },

    StringToCamelCase(string)
    {
        if (typeof string !== 'string')
        {
            return '';
        }

        return string.replace(/[^\w\s]/g, ' ').split(/\s+/).filter(word => word.length).map((word, index) =>
        {
            return index === 0 ? word.toLowerCase() : this.StringCapitalize(word.toLowerCase());
        })
        .join('');
    },

    StringStripHtml(html)
    {
        if (typeof html !== 'string')
        {
            return '';
        }

        return html.replace(/<style[^>]*>.*?<\/style>/gs, '')
            .replace(/<script[^>]*>.*?<\/script>/gs, '')
            .replace(/<[^>]+>/g, '')
            .replace(/&nbsp;/g, ' ')
            .replace(/&amp;/g, '&')
            .replace(/&lt;/g, '<')
            .replace(/&gt;/g, '>')
            .replace(/&quot;/g, '"')
            .replace(/&#039;/g, "'")
            .replace(/\s+/g, ' ')
            .trim();
    },

    StringSlugify(string)
    {
        if (typeof string !== 'string')
        {
            return '';
        }

        return string.toLowerCase()
            .trim()
            .replace(/\s+/g, '-')
            .replace(/[^\w-]+/g, '')
            .replace(/--+/g, '-')
            .replace(/^-+|-+$/g, '');
    },

    StringFormatNumber(number, decimals = 2, decPoint = '.', thousandsSep = ',')
    {
        if (isNaN(number))
        {
            return '0';
        }

        const string = parseFloat(number).toFixed(decimals);
        const parts = string.split('.');

        parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, thousandsSep);

        return parts.join(decPoint);
    }
};

export default OneTypeString;

// repositories/framework/lib/src/mixins/validate.js
const OneTypeValidate =
{
    ValidateEmail(email)
    {
        if (typeof email !== 'string')
        {
            return false;
        }

        const pattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
        
        return pattern.test(email) && email.length <= 254 && !email.includes('..') &&!email.startsWith('.') && !email.endsWith('.');
    },

    ValidatePassword(password, options = {})
    {
        if (typeof password !== 'string')
        {
            return { valid: false, errors: ['Password must be a string'] };
        }

        const config = {
            minLength: options.minLength || 8,
            maxLength: options.maxLength || 128,
            requireUppercase: options.requireUppercase !== false,
            requireLowercase: options.requireLowercase !== false,
            requireNumbers: options.requireNumbers !== false,
            requireSpecial: options.requireSpecial !== false,
            specialChars: options.specialChars || '!@#$%^&*()_+-=[]{}|;:,.<>?'
        };

        const errors = [];

        if (password.length < config.minLength)
        {
            errors.push(`Password must be at least ${config.minLength} characters long`);
        }

        if (password.length > config.maxLength)
        {
            errors.push(`Password must be no more than ${config.maxLength} characters long`);
        }

        if (config.requireUppercase && !/[A-Z]/.test(password))
        {
            errors.push('Password must contain at least one uppercase letter');
        }

        if (config.requireLowercase && !/[a-z]/.test(password))
        {
            errors.push('Password must contain at least one lowercase letter');
        }

        if (config.requireNumbers && !/\d/.test(password))
        {
            errors.push('Password must contain at least one number');
        }

        if (config.requireSpecial)
        {
            const specialRegex = new RegExp(`[${config.specialChars.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}]`);

            if (!specialRegex.test(password))
            {
                errors.push('Password must contain at least one special character');
            }
        }

        return {
            valid: errors.length === 0,
            errors: errors
        };
    }
};

export default OneTypeValidate;

// repositories/framework/lib/src/onetype.js
import OneTypeAddons from './mixins/addons.js';
import OneTypeGenerate from './mixins/generate.js';
import OneTypeRequest from './mixins/request.js';
import OneTypeData from './mixins/data.js';
import OneTypeBinaries from './mixins/binaries.js';
import OneTypeString from './mixins/string.js';
import OneTypeEmitter from './mixins/emitter.js';
import OneTypeMiddleware from './mixins/middleware.js';
import OneTypeFunction from './mixins/function.js';
import OneTypeOverrides from './mixins/overrides.js';
import OneTypeDOM from './mixins/dom.js';
import OneTypeValidate from './mixins/validate.js';
import OneTypeHelper from './mixins/helper.js';
import OneTypeRoute from './mixins/route.js';
import OneTypeError from './mixins/error.js';
import OneTypeCrypto from './mixins/crypto.js';
import OneTypeForm from './mixins/form.js';
import OneTypeCookie from './mixins/cookie.js';
import OneTypeState from './mixins/state.js';
import OneTypeAssets from './mixins/assets.js';
import OneTypeObserver from './mixins/observer.js';
import OneTypeMarkdown from './mixins/markdown.js';
import OneTypeBase from './mixins/base.js';
import OneTypeLanguage from './mixins/language.js';
import OneTypeLocale from './mixins/locale.js';

class OneType
{
    constructor()
    {
        this.environment = typeof window !== 'undefined' ? 'front' : 'back';
        this.emitters = {};
        this.middleware = {};

        this.addons =
        {
            data: {},
            callbacks:
            {
                add: [],
                remove: []
            }
        };

        this.data =
        {
            schemas: {},
        };

        this.assets = {};

        this.$ot =
        {
            get: (key, value = undefined) => this.StateGet(key, undefined),
            set: (key, value) => this.StateSet(key, value),
            t: (text, key) => this.LocaleGet(text, key)
        };

        this.ObserverStart();
    }
}

Object.assign(OneType.prototype, OneTypeAddons);
Object.assign(OneType.prototype, OneTypeEmitter);
Object.assign(OneType.prototype, OneTypeGenerate);
Object.assign(OneType.prototype, OneTypeRequest);
Object.assign(OneType.prototype, OneTypeData);
Object.assign(OneType.prototype, OneTypeBinaries);
Object.assign(OneType.prototype, OneTypeString);
Object.assign(OneType.prototype, OneTypeMiddleware);
Object.assign(OneType.prototype, OneTypeFunction);
Object.assign(OneType.prototype, OneTypeOverrides);
Object.assign(OneType.prototype, OneTypeDOM);
Object.assign(OneType.prototype, OneTypeValidate);
Object.assign(OneType.prototype, OneTypeHelper);
Object.assign(OneType.prototype, OneTypeRoute);
Object.assign(OneType.prototype, OneTypeError);
Object.assign(OneType.prototype, OneTypeCrypto);
Object.assign(OneType.prototype, OneTypeForm);
Object.assign(OneType.prototype, OneTypeCookie);
Object.assign(OneType.prototype, OneTypeState);
Object.assign(OneType.prototype, OneTypeAssets);
Object.assign(OneType.prototype, OneTypeObserver);
Object.assign(OneType.prototype, OneTypeMarkdown);
Object.assign(OneType.prototype, OneTypeBase);
Object.assign(OneType.prototype, OneTypeLanguage);
Object.assign(OneType.prototype, OneTypeLocale);

export default OneType;

// repositories/framework/package.json
{
    "name": "@onetype/framework",
    "version": "2.0.58",
    "description": "OneType Framework — Full-stack isomorphic JavaScript framework built from scratch. One addon abstraction powers databases, servers, commands, pages, directives, queues, and more.",
    "type": "module",
    "main": "lib/load.js",
    "browser": "lib/browser.js",
    "exports": {
        ".": "./lib/load.js",
        "./browser": "./lib/browser.js",
        "./database": "./addons/core/database/back/load.js",
        "./servers/grpc": "./addons/core/servers/back/grpc/load.js",
        "./servers/http": "./addons/core/servers/back/http/load.js",
        "./clients/grpc": "./addons/core/clients/back/grpc/load.js",
        "./clients/http": "./addons/core/clients/back/http/load.js",
        "./commands": "./addons/core/commands/core/load.js",
        "./queue": "./addons/core/queue/back/load.js",
        "./assets": "./addons/core/assets/back/load.js",
        "./pages": "./addons/render/pages/core/load.js",
        "./html": "./addons/render/html/load.js",
        "./tags": "./addons/render/tags/load.js",
        "./sources": "./addons/modules/sources/back/load.js",
        "./variables": "./addons/modules/variables/core/load.js",
        "./ai": "./addons/ai/load.js",
        "./services/cloudflare/images": "./addons/services/cloudflare/images/back/load.js"
    },
    "imports": {
        "#framework/*": "./lib/*",
        "#database/*": "./addons/core/database/back/*",
        "#servers/*": "./addons/core/servers/back/*",
        "#clients/*": "./addons/core/clients/back/*",
        "#commands/*": "./addons/core/commands/*",
        "#queue/*": "./addons/core/queue/back/*",
        "#assets/*": "./addons/core/assets/back/*",
        "#html/*": "./addons/render/html/*",
        "#directives/*": "./addons/render/directives/*",
        "#pages/*": "./addons/render/pages/*",
        "#elements/*": "./addons/render/elements/*",
        "#tags/*": "./addons/render/tags/*",
        "#sources/*": "./addons/modules/sources/*",
        "#variables/*": "./addons/modules/variables/*",
        "#ai/*": "./addons/ai/*",
        "#cloudflare-images/*": "./addons/services/cloudflare/images/back/*"
    },
    "dependencies": {
        "@grpc/grpc-js": "^1.13.3",
        "@grpc/proto-loader": "^0.7.15",
        "bcrypt": "^5.1.1",
        "busboy": "^1.6.0",
        "dotenv": "^16.4.7",
        "knex": "^3.1.0",
        "linkedom": "^0.18.9",
        "pg": "^8.13.3",
        "terser": "^5.37.0"
    },
    "author": "Dejan Tomic <hi@iamdejan.com>",
    "license": "MIT",
    "repository": {
        "type": "git",
        "url": "https://github.com/tomic-d/framework"
    },
    "keywords": [
        "framework",
        "fullstack",
        "isomorphic",
        "addon",
        "orm",
        "grpc",
        "reactivity",
        "dom-diffing",
        "directives"
    ],
    "engines": {
        "node": ">=18.0.0"
    }
}
