export type AleScalar = string|number|boolean|null; export type AleOperatorRequest = | {kind:'command';command:string} | {kind:'global';method:string;args?:AleScalar[]} | {kind:'player';player:string;method:string;args?:AleScalar[]} | {kind:'playerbotAI';player:string;method:string;args?:AleScalar[]} | {kind:'raw';source:string}; const IDENT=/^[A-Za-z_][A-Za-z0-9_]*$/; export function luaString(value:string){ return '"' + value .replace(/\\/g,'\\\\') .replace(/"/g,'\\"') .replace(/\r/g,'\\r') .replace(/\n/g,'\\n') + '"'; } function luaValue(value:AleScalar):string{ if(value===null)return 'nil'; if(typeof value==='string')return luaString(value); if(typeof value==='number'){ if(!Number.isFinite(value))throw new Error('ALE numeric arguments must be finite'); return String(value); } return value?'true':'false'; } function method(name:string){ if(!IDENT.test(name))throw new Error('Invalid ALE method name'); return name; } function args(values:AleScalar[]|undefined){ return (values??[]).map(luaValue).join(','); } export function compileAleOperatorRequest(input:AleOperatorRequest,rawEnabled:boolean){ if(input.kind==='command'){ if(!input.command.trim()||input.command.length>500)throw new Error('Invalid server command'); return `RunCommand(${luaString(input.command.trim())}); return "command dispatched"`; } if(input.kind==='global'){ return `local fn=_G[${luaString(method(input.method))}]; if type(fn)~="function" then error("Unknown global ALE method") end; return fn(${args(input.args)})`; } if(input.kind==='player'){ return `local p=GetPlayerByName(${luaString(input.player)}); if not p then error("Player not found") end; return p:${method(input.method)}(${args(input.args)})`; } if(input.kind==='playerbotAI'){ return `local p=GetPlayerByName(${luaString(input.player)}); if not p then error("Player not found") end; local ai=p:GetBotAI(); if not ai then error("PlayerBotAI not found") end; return ai:${method(input.method)}(${args(input.args)})`; } if(!rawEnabled)throw new Error('Raw ALE execution is disabled. Set ALE_RAW_EXEC_ENABLED=true on the private server to enable the full operator surface.'); if(!input.source.trim()||input.source.length>12000)throw new Error('Invalid raw ALE source'); return input.source; } export function wrapAleCommand(id:string,source:string){ const qid=luaString(id); return `local __command_id=${qid} local function __stringify(v) if v==nil then return "nil" end local t=type(v) if t=="string" or t=="number" or t=="boolean" then return tostring(v) end return "<"..t.."> "..tostring(v) end local __ok,__result=pcall(function() ${source} end) AzerothAIComplete(__command_id,__ok,__stringify(__result)) `; } export const ALE_SURFACE = { gameClasses:['ALEQuery','Achievement','Aura','BattleGround','ChatHandler','Corpse','Creature','GameObject','GemPropertiesEntry','Global','Group','Guild','Item','ItemTemplate','Loot','Map','Object','Pet','Player','Quest','Roll','Spell','SpellEntry','SpellInfo','Ticket','Transport','Unit','Vehicle','WorldObject','WorldPacket'], playerbotClasses:['PlayerBot','PlayerBotAI','PlayerBotFactory','PlayerBotHolder','PlayerBotMgr','PlayerBotsMgr','RandomPlayerBotMgr'], operatorModes:['server-command','global-method','player-method','playerbot-ai-method','raw-lua'], modelAccess:'none' } as const;