Add queue prewarm candidate selection

This commit is contained in:
Jonathan Sykes
2026-10-07 16:30:01 +08:00
parent b77938ab4a
commit fcd98b6a4d
2 changed files with 63 additions and 0 deletions

37
frontend/prewarm-next.js Normal file
View File

@@ -0,0 +1,37 @@
/* Pure queue candidate selection for stream prewarming. */
(function (root, factory) {
if (typeof module === 'object' && module.exports) module.exports = factory();
else root.PrewarmNext = factory();
})(typeof globalThis !== 'undefined' ? globalThis : this, function () {
'use strict';
function candidates(queue, index, state = {}, n = 2, known = {}) {
if (!Array.isArray(queue) || !queue.length || !Number.isInteger(index) || index < 0 || index >= queue.length) return [];
if (state.loopOne) return [];
const count = Math.max(0, Math.floor(Number(n) || 0));
if (!count) return [];
const repeatAll = state.repeatMode === 'all' || state.repeatAll === true;
const offline = new Set(known.offline || []);
const serverCached = new Set(known.serverCached || []);
const current = queue[index];
const currentId = typeof current === 'string' ? current : current && current.id;
const seen = new Set(currentId ? [currentId] : []);
const ids = [];
for (let step = 1; step < queue.length + (repeatAll ? 1 : 0) && ids.length < count; step++) {
let at = index + step;
if (at >= queue.length) {
if (!repeatAll) break;
at %= queue.length;
}
const item = queue[at];
const id = typeof item === 'string' ? item : item && item.id;
if (!id || seen.has(id)) continue;
seen.add(id);
if (offline.has(id) || serverCached.has(id) || (item && item.serverCached)) continue;
ids.push(id);
}
return ids;
}
return { candidates };
});

View File

@@ -0,0 +1,26 @@
'use strict';
const test = require('node:test');
const assert = require('node:assert/strict');
const { candidates } = require('./prewarm-next.js');
const q = ['a', 'b', 'c', 'd'].map(id => ({ id }));
test('returns the next n queue ids in existing order', () => {
assert.deepEqual(candidates(q, 0, {}, 2), ['b', 'c']);
});
test('repeat all wraps and does not duplicate ids', () => {
assert.deepEqual(candidates(q, 3, { repeatMode: 'all' }, 2), ['a', 'b']);
assert.deepEqual(candidates([{ id: 'x' }, { id: 'x' }], 0, { repeatMode: 'all' }, 3), []);
});
test('repeat off stops at the end and loop one has no candidates', () => {
assert.deepEqual(candidates(q, 3, {}, 2), []);
assert.deepEqual(candidates(q, 0, { loopOne: true, repeatMode: 'all' }, 2), []);
});
test('skips offline and known server cached ids and deduplicates', () => {
assert.deepEqual(candidates(q, 0, {}, 3, { offline: ['b'], serverCached: ['c'] }), ['d']);
assert.deepEqual(candidates([{ id: 'a' }, { id: 'b', serverCached: true }, { id: 'c' }], 0, {}, 2), ['c']);
});
test('shuffle follows the queue order selected before advance', () => {
const shuffled = ['a', 'd', 'b', 'c'].map(id => ({ id }));
assert.deepEqual(candidates(shuffled, 0, { shuffle: true }, 2), ['d', 'b']);
});