Implement

This commit is contained in:
barlind
2026-06-18 00:40:22 -07:00
committed by Bret Mogilefsky
parent 41e62af5e9
commit 853db94887
18 changed files with 1853 additions and 93 deletions
+5 -5
View File
@@ -50,7 +50,7 @@ test('diagnostics export expected compatibility shim surfaces', () => {
assert.equal(expected.some(entry => entry.capability === 'jobs'), false);
});
test('diagnostics include active playback but exclude deferred and documentation-only future core domains', () => {
test('diagnostics include active playback and jobs but exclude deferred future core domains', () => {
const window = loadCapabilities();
const api = window.slopsmith.capabilities;
api.registerParticipant('playback_probe', {
@@ -74,14 +74,14 @@ test('diagnostics include active playback but exclude deferred and documentation
const playback = pipelines.find(entry => entry.name === 'playback');
assert.ok(playback, 'playback should be part of the active runtime graph when participants register');
assert.equal(playback.review.lifecycle, 'active');
const jobs = pipelines.find(entry => entry.name === 'jobs');
assert.ok(jobs, 'jobs should be part of the active runtime graph when participants register');
assert.equal(jobs.review.lifecycle, 'active');
// `visualization` (cap:6 slice) and `note-detection` (spec 009 slice)
// left this list when their domains were promoted.
const futureDomains = [
'ui.navigation', 'ui.plugin-screens', 'settings',
'backend.routes', 'ui.player-controls',
'ui.player-panels', 'ui.player-overlays', 'plugins', 'jobs', 'midi-control',
'tempo-clock',
'ui.navigation', 'ui.plugin-screens', 'settings', 'backend.routes', 'ui.player-controls', 'ui.player-panels', 'ui.player-overlays', 'plugins', 'midi-control', 'tempo-clock',
];
for (const domain of futureDomains) {
@@ -65,6 +65,9 @@ function loadInspector(snapshot, options = {}) {
playback: options.playbackSnapshot ? {
snapshot: () => options.playbackSnapshot,
} : undefined,
jobs: options.jobsSnapshot ? {
snapshot: () => options.jobsSnapshot,
} : undefined,
},
navigator: { clipboard: { writeText: async () => {} } },
document: {
@@ -114,6 +117,41 @@ test('capability inspector renders playback session route loop bridges and outco
assert.match(content, /seek:completed/);
});
test('capability inspector renders jobs support state and active domain graph', () => {
const snapshot = {
pipelines: [{ name: 'jobs', review: { lifecycle: 'active', label: 'Active contract', tone: 'clean', summary: 'Jobs control plane.' }, participants: [{ pluginId: 'core.jobs', kind: 'provider-coordinator', roles: ['owner'], commands: ['enqueue', 'list', 'inspect'], operations: ['job.enqueue'], events: ['queued', 'completed'], runtime: true, availability: 'available', ownership: 'multi-provider', safety: 'privileged' }], conflicts: [] }],
participants: [{ pluginId: 'core.jobs' }],
compatibilityShims: [],
expectedCompatibilityShims: [],
};
const jobsSnapshot = {
schema: 'slopsmith.jobs.diagnostics.v1',
providers: [{ providerId: 'provider.cache', label: 'Cache Builder', availability: 'available', jobTypes: ['cache-build'] }],
selectedProviders: [{ jobType: 'cache-build', providerId: 'provider.cache', source: 'user-selected' }],
jobs: {
active: [{ jobId: 'job-1', safeLabel: 'Build cache', providerId: 'provider.cache', state: 'running', progress: { percent: 42 } }],
queued: [{ jobId: 'job-2', safeLabel: 'Later cache', providerId: 'provider.cache', priority: 'background-maintenance' }],
paused: [],
recentTerminal: [{ jobId: 'job-0', safeLabel: 'Old cache', providerId: 'provider.cache', state: 'completed', terminalOutcome: { status: 'completed' } }],
},
outcomes: [{ operation: 'enqueue', status: 'queued' }, { operation: 'complete', status: 'completed' }],
bridgeHits: [{ bridgeId: 'jobs.legacy-plugin-queue', operation: 'enqueue', diagnosticsOnly: true }],
};
const { elements } = loadInspector(snapshot, { jobsSnapshot });
const content = elements.get('capability-inspector-content').innerHTML;
const filter = elements.get('capability-inspector-filter').innerHTML;
assert.match(content, /data-jobs-support/);
assert.match(content, /Providers: Cache Builder:available:cache-build/);
assert.match(content, /Selected: cache-build:provider.cache:user-selected/);
assert.match(content, /Active: Build cache:provider.cache:42%/);
assert.match(content, /Queued: Later cache:provider.cache:background-maintenance/);
assert.match(content, /Bridges: jobs.legacy-plugin-queue:enqueue:diagnostics-only/);
assert.match(content, /Outcomes: enqueue:queued, complete:completed/);
assert.match(content, /data-domain-graph="jobs"/);
assert.match(filter, /jobs/);
});
test('capability inspector renders shims inside their capability domain', () => {
const snapshot = {
pipelines: [
+58
View File
@@ -0,0 +1,58 @@
const { test } = require('node:test');
const assert = require('node:assert/strict');
const { loadJobs, dispatch, makeProvider, enqueuePayload, diagnosticsSnapshot, captureEvents } = require('./jobs_test_harness');
test('legacy bridge hits are recorded as diagnostics-only compatibility shims', async () => {
const window = loadJobs();
const events = captureEvents(window);
const { provider } = makeProvider({ providerId: 'provider.bridge' });
await dispatch(window, 'register-provider', { provider });
const enqueued = await dispatch(window, 'enqueue', enqueuePayload({ providerId: 'provider.bridge', logicalJobKey: 'legacy-key' }));
const result = await dispatch(window, 'record-bridge-hit', {
bridgeId: 'jobs.legacy-plugin-queue',
legacySurface: 'legacy-plugin-queue',
pluginId: 'legacy_plugin',
operation: 'enqueue',
logicalJobKey: 'legacy-key',
safeReason: 'legacy queue adapter used',
});
const runtimeSnapshot = window.slopsmith.capabilities.snapshotDiagnostics();
const diagnostics = diagnosticsSnapshot(window);
assert.equal(result.status, 'applied');
assert.equal(result.payload.bridge.jobId, enqueued.payload.job.jobId);
assert.equal(result.payload.bridge.diagnosticsOnly, true);
assert.ok(runtimeSnapshot.compatibilityShims.some(shim => shim.capability === 'jobs' && shim.status === 'used'));
assert.equal(diagnostics.bridgeHits.length, 1);
assert.ok(events.some(event => event.event === 'bridge-hit'));
});
test('duplicate logical jobs are suppressed and remain tied to the active job', async () => {
const window = loadJobs();
const { provider } = makeProvider({ providerId: 'provider.duplicate' });
await dispatch(window, 'register-provider', { provider });
const first = await dispatch(window, 'enqueue', enqueuePayload({ providerId: 'provider.duplicate', logicalJobKey: 'same-job' }));
const second = await dispatch(window, 'enqueue', enqueuePayload({ providerId: 'provider.duplicate', logicalJobKey: 'same-job' }));
assert.equal(second.status, 'applied');
assert.equal(second.payload.duplicate, true);
assert.equal(second.payload.job.jobId, first.payload.job.jobId);
assert.equal(diagnosticsSnapshot(window).jobs.active.length, 1);
});
test('unsupported provider actions return unsupported-operation instead of invoking callbacks', async () => {
const window = loadJobs();
const { calls, provider } = makeProvider({ providerId: 'provider.limited', actions: ['enqueue', 'inspect'] });
await dispatch(window, 'register-provider', { provider });
const enqueued = await dispatch(window, 'enqueue', enqueuePayload({ providerId: 'provider.limited' }));
calls.length = 0;
const pause = await dispatch(window, 'pause', { jobId: enqueued.payload.job.jobId });
const retry = await dispatch(window, 'retry', { jobId: enqueued.payload.job.jobId, authorization: 'user-action' });
assert.equal(pause.status, 'unsupported-operation');
assert.equal(retry.status, 'stale');
assert.equal(calls.length, 0);
});
+73
View File
@@ -0,0 +1,73 @@
const { test } = require('node:test');
const assert = require('node:assert/strict');
const { loadJobs, dispatch, makeProvider, enqueuePayload, diagnosticsSnapshot, storageEntries } = require('./jobs_test_harness');
test('diagnostics schema redacts raw payloads, paths, command lines, and provider-private fields', async () => {
const window = loadJobs();
const { provider } = makeProvider();
await dispatch(window, 'register-provider', { provider });
const enqueued = await dispatch(window, 'enqueue', enqueuePayload({
safeLabel: 'Generate cache',
target: { path: '/Users/example/Music/Secret Artist - Secret Song_p.psarc', filename: 'Secret Song_p.psarc' },
inputs: { token: 'abc123', rawPayload: 'never export', commandLine: 'ffmpeg -i secret.wav out.ogg', safeFingerprint: 'fingerprint-public' },
}));
window.slopsmith.jobs.log(provider.providerId, enqueued.payload.job.jobId, 'ran ffmpeg -i /Users/example/secret.wav with token=abc123');
window.slopsmith.jobs.fail(provider.providerId, enqueued.payload.job.jobId, { safeReason: 'failed near /Users/example/private/path', retryable: true });
const json = JSON.stringify(diagnosticsSnapshot(window));
assert.match(json, /Generate cache/);
assert.match(json, /fingerprint-public/);
assert.doesNotMatch(json, /Secret Artist|Secret Song|secret\.wav|abc123|rawPayload|commandLine|never export|ffmpeg -i/);
});
test('recoverable job references are the only active state persisted across reloads', async () => {
const window = loadJobs();
const { provider } = makeProvider({ providerId: 'provider.recover', recoverySupport: { queued: true, running: true, paused: false } });
await dispatch(window, 'register-provider', { provider });
await dispatch(window, 'enqueue', enqueuePayload({ logicalJobKey: 'recoverable-running', safeLabel: 'Recoverable' }));
const entries = storageEntries(window);
assert.ok(entries['slopsmith.jobs.recoverableRefs.v1']);
assert.doesNotMatch(entries['slopsmith.jobs.recoverableRefs.v1'], /operationHandlers|rawPayload|token/);
const sameWindow = window;
sameWindow.slopsmith.jobs.resetForTests({ clearStorage: false });
assert.equal(sameWindow.slopsmith.jobs._test.pendingRecoverableRefs.size, 1);
await dispatch(sameWindow, 'register-provider', { provider });
const snapshot = diagnosticsSnapshot(sameWindow);
assert.equal(snapshot.jobs.active.length + snapshot.jobs.queued.length, 1);
assert.equal(snapshot.jobs.active[0]?.safeLabel || snapshot.jobs.queued[0]?.safeLabel, 'Recoverable');
});
test('reload marks non-recoverable jobs orphaned or provider-unavailable without restoring raw payloads', async () => {
const window = loadJobs();
const { provider } = makeProvider({ providerId: 'provider.no-recover', recoverySupport: { queued: false, running: false, paused: false } });
await dispatch(window, 'register-provider', { provider });
const enqueued = await dispatch(window, 'enqueue', enqueuePayload({ providerId: 'provider.no-recover', logicalJobKey: 'no-recover' }));
window.slopsmith.jobs.simulateReload();
const inspected = await dispatch(window, 'inspect', { jobId: enqueued.payload.job.jobId });
assert.equal(inspected.payload.job.state, 'orphaned');
assert.equal(inspected.payload.job.terminalOutcome.retryable, false);
assert.doesNotMatch(JSON.stringify(inspected.payload.job), /operationHandlers|rawPayload/);
});
test('diagnostics enforce per-job history and bounded snapshot size with terminal minimum retained', async () => {
const window = loadJobs();
const { provider } = makeProvider({ capacity: { maxRunning: 1, maxQueued: 100 } });
await dispatch(window, 'register-provider', { provider });
let lastJobId = null;
for (let index = 0; index < 8; index += 1) {
const enqueued = await dispatch(window, 'enqueue', enqueuePayload({ logicalJobKey: `terminal-${index}`, safeLabel: `Terminal ${index}` }));
lastJobId = enqueued.payload.job.jobId;
for (let line = 0; line < 80; line += 1) window.slopsmith.jobs.log(provider.providerId, lastJobId, `line ${line} /Users/example/private/file-${line}.psarc`);
window.slopsmith.jobs.complete(provider.providerId, lastJobId, { resultSummary: 'done' });
}
const snapshot = diagnosticsSnapshot(window);
assert.ok(snapshot.snapshotBytes <= snapshot.limits.snapshotBudgetBytes + 1024);
assert.ok(snapshot.jobs.recentTerminal.length >= 5);
assert.ok(snapshot.jobs.recentTerminal.every(job => job.history.length <= 50));
});
+78
View File
@@ -0,0 +1,78 @@
const { test } = require('node:test');
const assert = require('node:assert/strict');
const { loadJobs, dispatch, makeProvider, enqueuePayload, diagnosticsSnapshot, diagnosticsContributions } = require('./jobs_test_harness');
test('jobs domain is active and registers the core provider coordinator owner', async () => {
const window = loadJobs();
const snapshot = window.slopsmith.capabilities.snapshotDiagnostics();
const jobs = snapshot.pipelines.find(pipeline => pipeline.name === 'jobs');
assert.ok(jobs);
assert.equal(jobs.review.lifecycle, 'active');
assert.equal(jobs.review.tone, 'clean');
assert.ok(jobs.participants.some(participant => participant.pluginId === 'core.jobs' && participant.kind === 'provider-coordinator' && participant.safety === 'privileged'));
const result = await dispatch(window, 'list-providers');
assert.equal(result.status, 'applied');
assert.equal(result.payload.providers.length, 0);
});
test('provider registration exposes safe metadata and rejects incompatible versions', async () => {
const window = loadJobs();
const { provider } = makeProvider({ providerId: 'provider.cache', label: 'Cache Builder', jobTypes: ['cache-build'] });
const registered = await dispatch(window, 'register-provider', { provider });
assert.equal(registered.status, 'applied');
assert.equal(registered.payload.provider.providerId, 'provider.cache');
assert.deepEqual(Array.from(registered.payload.provider.jobTypes), ['cache-build']);
const incompatible = await dispatch(window, 'register-provider', { provider: { ...provider, providerId: 'provider.old', version: 2, safeReason: 'requires runtime v2' } });
assert.equal(incompatible.status, 'incompatible-version');
assert.equal(incompatible.payload.provider.availability, 'incompatible');
});
test('privileged enqueue requires approval before provider work starts', async () => {
const window = loadJobs();
const { calls, provider } = makeProvider();
await dispatch(window, 'register-provider', { provider });
const result = await dispatch(window, 'enqueue', enqueuePayload({ authorization: 'background' }));
assert.equal(result.status, 'user-action-required');
assert.equal(calls.length, 0);
assert.equal(diagnosticsSnapshot(window).jobs.active.length, 0);
assert.equal(diagnosticsContributions(window).jobs.schema, 'slopsmith.jobs.diagnostics.v1');
});
test('approved enqueue queues and starts with redaction-safe public job fields', async () => {
const window = loadJobs();
const { calls, provider } = makeProvider({ capacity: { maxRunning: 1, maxQueued: 10 } });
await dispatch(window, 'register-provider', { provider });
const result = await dispatch(window, 'enqueue', enqueuePayload({ target: { path: '/Users/example/DLC/Secret.psarc' }, inputs: { token: 'secret', safeFingerprint: 'fingerprint-1' } }));
const job = result.payload.job;
assert.equal(result.status, 'applied');
assert.equal(job.state, 'running');
assert.equal(job.targetRef.startsWith('target-'), true);
assert.equal(job.inputFingerprint, 'fingerprint-1');
assert.equal(calls.length, 1);
assert.doesNotMatch(JSON.stringify(diagnosticsSnapshot(window)), /Secret\.psarc|token|secret/);
});
test('list and inspect are prompt-free and do not invoke provider callbacks', async () => {
const window = loadJobs();
const { calls, provider } = makeProvider();
await dispatch(window, 'register-provider', { provider });
const enqueued = await dispatch(window, 'enqueue', enqueuePayload());
calls.length = 0;
const listed = await dispatch(window, 'list');
const inspected = await dispatch(window, 'inspect', { jobId: enqueued.payload.job.jobId });
assert.equal(listed.status, 'applied');
assert.equal(inspected.status, 'applied');
assert.equal(listed.payload.jobs.length, 1);
assert.equal(inspected.payload.job.jobId, enqueued.payload.job.jobId);
assert.equal(calls.length, 0);
});
+71
View File
@@ -0,0 +1,71 @@
const { test } = require('node:test');
const assert = require('node:assert/strict');
const { loadJobs, dispatch, makeProvider, enqueuePayload, captureEvents, diagnosticsSnapshot } = require('./jobs_test_harness');
test('multiple providers require explicit selection before scheduling', async () => {
const window = loadJobs();
await dispatch(window, 'register-provider', { provider: makeProvider({ providerId: 'provider.a' }).provider });
await dispatch(window, 'register-provider', { provider: makeProvider({ providerId: 'provider.b' }).provider });
const blocked = await dispatch(window, 'enqueue', enqueuePayload());
assert.equal(blocked.status, 'provider-selection-required');
window.slopsmith.jobs.setSelectedProvider('transcode', 'provider.b');
const enqueued = await dispatch(window, 'enqueue', enqueuePayload({ logicalJobKey: 'selected-job' }));
assert.equal(enqueued.status, 'applied');
assert.equal(enqueued.payload.job.providerId, 'provider.b');
});
test('provider capacity starts one job and keeps the rest queued in priority order', async () => {
const window = loadJobs();
const { provider } = makeProvider({ capacity: { maxRunning: 1, maxQueued: 10 } });
await dispatch(window, 'register-provider', { provider });
const first = await dispatch(window, 'enqueue', enqueuePayload({ logicalJobKey: 'first', priority: 'background-maintenance' }));
const second = await dispatch(window, 'enqueue', enqueuePayload({ logicalJobKey: 'second', priority: 'background-maintenance', safeLabel: 'Second' }));
const third = await dispatch(window, 'enqueue', enqueuePayload({ logicalJobKey: 'third', priority: 'user-approved-interactive', safeLabel: 'Third' }));
assert.equal(first.payload.job.state, 'running');
assert.equal(second.payload.job.state, 'queued');
assert.equal(third.payload.job.state, 'queued');
window.slopsmith.jobs.complete(provider.providerId, first.payload.job.jobId, { resultSummary: 'done' });
const snapshot = diagnosticsSnapshot(window);
assert.equal(snapshot.jobs.active.length, 1);
assert.equal(snapshot.jobs.active[0].safeLabel, 'Third');
});
test('progress, completion, and terminal retention are reflected in events and diagnostics', async () => {
const window = loadJobs();
const events = captureEvents(window);
const { provider } = makeProvider();
await dispatch(window, 'register-provider', { provider });
const enqueued = await dispatch(window, 'enqueue', enqueuePayload());
window.slopsmith.jobs.updateProgress(provider.providerId, enqueued.payload.job.jobId, { percent: 33, step: 'convert', message: 'Converting safely' });
window.slopsmith.jobs.complete(provider.providerId, enqueued.payload.job.jobId, { resultSummary: 'Cache ready' });
const snapshot = diagnosticsSnapshot(window);
assert.equal(snapshot.jobs.active.length, 0);
assert.equal(snapshot.jobs.recentTerminal.length, 1);
assert.equal(snapshot.jobs.recentTerminal[0].terminalOutcome.status, 'completed');
assert.ok(snapshot.jobs.recentTerminal[0].history.some(entry => entry.kind === 'progress'));
assert.ok(events.some(event => event.event === 'progress'));
assert.ok(events.some(event => event.event === 'completed'));
});
test('cancel, pause, resume, retry, and stale transitions use canonical outcomes', async () => {
const window = loadJobs();
const { provider } = makeProvider({ capacity: { maxRunning: 1, maxQueued: 10 } });
await dispatch(window, 'register-provider', { provider });
const enqueued = await dispatch(window, 'enqueue', enqueuePayload({ logicalJobKey: 'control-job' }));
const jobId = enqueued.payload.job.jobId;
assert.equal((await dispatch(window, 'pause', { jobId })).status, 'applied');
assert.equal((await dispatch(window, 'resume', { jobId })).status, 'applied');
assert.equal((await dispatch(window, 'pause', { jobId })).status, 'applied');
assert.equal((await dispatch(window, 'cancel', { jobId })).status, 'applied');
window.slopsmith.jobs.fail(provider.providerId, jobId, { category: 'provider-failure', safeReason: 'retryable failure', retryable: true });
assert.equal((await dispatch(window, 'retry', { jobId, authorization: 'user-action' })).status, 'retry-started');
assert.equal((await dispatch(window, 'retry', { jobId, authorization: 'user-action' })).status, 'stale');
});
+159
View File
@@ -0,0 +1,159 @@
const fs = require('node:fs');
const path = require('node:path');
const vm = require('node:vm');
const { createWindow, ROOT } = require('./capabilities_test_harness');
const CAPABILITIES_JS = path.join(ROOT, 'static', 'capabilities.js');
const JOBS_JS = path.join(ROOT, 'static', 'capabilities', 'jobs.js');
const INSPECTOR_JS = path.join(ROOT, 'plugins', 'capability_inspector', 'screen.js');
function loadJobs(options = {}) {
const window = createWindow(options);
const context = vm.createContext(window);
vm.runInContext(fs.readFileSync(CAPABILITIES_JS, 'utf8'), context, { filename: CAPABILITIES_JS });
vm.runInContext(fs.readFileSync(JOBS_JS, 'utf8'), context, { filename: JOBS_JS });
window.__vmContext = context;
return window;
}
function loadInspector(window) {
vm.runInContext(fs.readFileSync(INSPECTOR_JS, 'utf8'), window.__vmContext, { filename: INSPECTOR_JS });
return window;
}
function captureEvents(window, capability = 'jobs') {
const events = [];
window.slopsmith.on('capability:event', event => {
if (event.detail && event.detail.capability === capability) events.push(event.detail);
});
return events;
}
function diagnosticsSnapshot(window) {
return window.slopsmith.jobs.snapshot();
}
function diagnosticsContributions(window) {
return Object.fromEntries(window.__diagnosticsContributions || new Map());
}
function storageEntries(window) {
return Object.fromEntries(window.__storage || new Map());
}
async function dispatch(window, command, payload = {}, requester = 'test') {
return window.slopsmith.capabilities.dispatch({ capability: 'jobs', command, args: payload, requester });
}
function makeProvider(overrides = {}) {
const calls = [];
const providerId = overrides.providerId || 'provider.jobs';
const handlers = overrides.operationHandlers || {};
const provider = {
providerId,
pluginId: overrides.pluginId || providerId,
label: overrides.label || 'Jobs Provider',
jobTypes: overrides.jobTypes || ['transcode'],
actions: overrides.actions || ['enqueue', 'inspect', 'cancel', 'pause', 'resume', 'retry', 'recover'],
availability: overrides.availability || 'available',
capacity: overrides.capacity || { maxRunning: 1, maxQueued: 10 },
recoverySupport: overrides.recoverySupport || { queued: true, running: true, paused: true },
version: overrides.version || 1,
safeReason: overrides.safeReason,
operationHandlers: {
'job.enqueue': request => {
calls.push(['job.enqueue', request]);
return handlers['job.enqueue'] ? handlers['job.enqueue'](request) : { outcome: 'handled' };
},
'job.cancel': request => {
calls.push(['job.cancel', request]);
return handlers['job.cancel'] ? handlers['job.cancel'](request) : { outcome: 'handled' };
},
'job.pause': request => {
calls.push(['job.pause', request]);
return handlers['job.pause'] ? handlers['job.pause'](request) : { outcome: 'handled' };
},
'job.resume': request => {
calls.push(['job.resume', request]);
return handlers['job.resume'] ? handlers['job.resume'](request) : { outcome: 'handled' };
},
'job.retry': request => {
calls.push(['job.retry', request]);
return handlers['job.retry'] ? handlers['job.retry'](request) : { outcome: 'handled' };
},
'job.recover': request => {
calls.push(['job.recover', request]);
return handlers['job.recover'] ? handlers['job.recover'](request) : { outcome: 'handled', state: overrides.recoveredState || 'queued' };
},
...handlers,
},
};
return { calls, provider };
}
function enqueuePayload(overrides = {}) {
return {
jobType: overrides.jobType || 'transcode',
requester: overrides.requester || 'plugin.requester',
authorization: overrides.authorization || 'user-action',
priority: overrides.priority || 'user-approved-interactive',
target: overrides.target || { targetRef: 'song-1' },
inputs: overrides.inputs || { safeFingerprint: 'input-1' },
safeLabel: overrides.safeLabel || 'Build playable cache',
logicalJobKey: overrides.logicalJobKey,
providerId: overrides.providerId,
privileged: overrides.privileged,
approvalScopeKey: overrides.approvalScopeKey,
};
}
function installInspectorDom(window) {
const elements = new Map();
function element(id) {
const item = {
id,
value: '',
textContent: '',
innerHTML: '',
className: '',
dataset: {},
style: {},
classList: { add() {}, remove() {}, toggle() {}, contains() { return false; } },
addEventListener() {},
removeEventListener() {},
querySelectorAll() { return []; },
querySelector() { return null; },
closest() { return null; },
getBoundingClientRect() { return { width: 1000, height: 400, left: 0, top: 0 }; },
appendChild(child) { return child; },
};
elements.set(id, item);
return item;
}
element('capability-inspector-filter');
element('capability-inspector-content');
element('capability-inspector-empty');
element('capability-inspector-summary');
element('capability-inspector-refresh');
window.document.readyState = 'complete';
window.document.getElementById = id => elements.get(id) || null;
window.document.querySelectorAll = () => [];
window.document.addEventListener = () => {};
window.document.createElement = () => element(`created-${elements.size}`);
window.requestAnimationFrame = callback => callback();
return elements;
}
module.exports = {
ROOT,
loadJobs,
loadInspector,
captureEvents,
diagnosticsSnapshot,
diagnosticsContributions,
storageEntries,
dispatch,
makeProvider,
enqueuePayload,
installInspectorDom,
};