BUILD200 (2026.11.05.1) — закрепить плашку удаления и снимать штраф после гола по ACK vMix
This commit is contained in:
@@ -13,6 +13,30 @@ function extract(name) {
|
||||
assert.ok(end > start, `missing function end ${name}`);
|
||||
return source.slice(start, end + 4);
|
||||
}
|
||||
// Load new transport dependencies from production; existing I/O/DOM edges stay local.
|
||||
function productionClosure(context, roots) {
|
||||
const functions = new Map(), pattern = /^( {0,2})(?:async )?function (\w+)\(/gm;
|
||||
let match;
|
||||
while ((match = pattern.exec(source))) {
|
||||
const start = match.index, lineEnd = source.indexOf('\n', start);
|
||||
const singleLine = /\}\s*;?\s*$/.test(source.slice(start, lineEnd));
|
||||
const end = singleLine ? lineEnd : source.indexOf('\n' + match[1] + '}', start) + match[1].length + 2;
|
||||
assert.ok(end > start, 'Unclosed production helper ' + match[2]);
|
||||
functions.set(match[2], source.slice(start, end));
|
||||
}
|
||||
const needed = new Set(roots);
|
||||
function visit(name) {
|
||||
assert.ok(functions.has(name), 'Missing actual production helper ' + name);
|
||||
for (const candidate of functions.keys()) {
|
||||
if (needed.has(candidate) || typeof context[candidate] === 'function') continue;
|
||||
if (new RegExp('\\b' + candidate + '\\s*\\(').test(functions.get(name))) {
|
||||
needed.add(candidate); visit(candidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
roots.forEach(visit);
|
||||
return [...needed].map(name => functions.get(name)).join('\n');
|
||||
}
|
||||
function runtime(extra = []) {
|
||||
const noop = () => {};
|
||||
const game = { action_id:'hockey_game_timer', type:'timer', props:{mode:'count_down',startTime:'20:00'} };
|
||||
@@ -34,8 +58,18 @@ function runtime(extra = []) {
|
||||
runningShortcutSequences:new Set(),pendingShortcutSequenceRuns:new Map(),data:{}};
|
||||
let allowed = true;
|
||||
const compact = raw => Object.fromEntries(Object.entries(raw).map(([key,value])=>[key,typeof value==='function'?value():value]));
|
||||
const context = {console,performance:{now:()=>1000},state,boot:{mode:'runtime'},setTimeout,clearTimeout,
|
||||
window:{HockeyMatchAccess:{storage:{getItem:()=> 'account-a'}}},
|
||||
const context = {console,Date,AbortController,URLSearchParams,performance:{now:()=>1000},state,boot:{mode:'runtime'},setTimeout,clearTimeout,
|
||||
window:{setTimeout,clearTimeout,HockeyMatchAccess:{storage:{getItem:()=> 'account-a'}}},
|
||||
refreshQuickPanelOnAirClasses:noop,hockeyRuntimeVmixInputTitle:async input=>String(input),
|
||||
async fetch(url,options={}) {
|
||||
assert.equal(options.method,'POST');
|
||||
if(url.startsWith('/api/hockey/agents/devices/OUTPUT-A/apply-mapping?')) return {ok:true,status:200,json:async()=>({ok:true,complete:true,mapping_ack_waited:true,confirmation:'ack',applied:1,
|
||||
text_fields_total:1,text_fields_confirmed:1,missing:[],rule_missing:[],errors:[],warnings:[]})};
|
||||
assert.equal(url,'/api/hockey/vmix/sequence');
|
||||
const body=JSON.parse(options.body);order.push('transport');sent.push(body.commands.map(compact));
|
||||
return {ok:true,status:200,json:async()=>({ok:true,confirmation:'ack',applied:body.commands.length,
|
||||
results:body.commands.map(command=>({ok:true,function:command.Function,ack_waited:true}))})};
|
||||
},
|
||||
padTimer:value=>String(value).padStart(2,'0'),clamp:(n,l,h)=>Math.max(l,Math.min(h,n)),
|
||||
hockeyCanControl:()=>allowed,componentByActionId:id=>id===game.action_id?game:id===boardComponent.action_id?boardComponent:null,
|
||||
isTimerComponent:c=>c===game,ensureTimerState:()=>main,
|
||||
@@ -76,9 +110,10 @@ function runtime(extra = []) {
|
||||
'hockeyScoreboardDelaySeconds','hockeyScoreboardPresentationContext','hockeyObserveScoreboardPresentation','hockeyScoreboardAuxiliaryReady',
|
||||
'hockeyScoreboardAuxiliaryDelay','hockeyScoreboardAuxiliaryTargetReady','hockeyMatchFlags',
|
||||
'hockeyScoreboardTeamStateSettings','hockeyTeamStateSetting','hockeyGameControlLanguage','shortcutVmixStepCommands',
|
||||
'hockeyAuxiliaryGateContext','hockeyScoreboardAuxiliariesEnabled',...extra];
|
||||
'hockeyAuxiliaryGateContext','hockeyScoreboardAuxiliariesEnabled','hockeyPenaltyEntryDeliveryPending', 'hockeyPenaltyDisplayTargetAllowed', 'hockeyPenaltyCaptionTargetAllowed',
|
||||
'hockeyPenaltyTargetDeliveryPending', 'hockeyPenaltyTransportSampler', 'sendRuntimeVmixSequence',...extra];
|
||||
vm.createContext(context);
|
||||
vm.runInContext([...new Set(names)].map(extract).join('\n'),context);
|
||||
vm.runInContext(productionClosure(context,[...new Set(names)]),context);
|
||||
return {context,game,main,board,boardComponent,step,sequence,target,state,sent,events,order,
|
||||
allow:value=>{allowed=value;},clear:()=>{sent.length=events.length=order.length=0;},
|
||||
functions:()=>sent.flat().map(command=>command.Function)};
|
||||
|
||||
Reference in New Issue
Block a user