UNPKG

rvx

Version:

A signal based rendering library

152 lines (142 loc) 3.62 kB
/*! This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. */ import { View, ENV, leak, captureSelf, viewNodes } from './rvx.js'; import { Queue, AsyncContext, ASYNC } from './rvx.async.js'; function assertViewState(view) { if (!(view instanceof View)) { throw new Error("view is not a View"); } const env = ENV.current; if (!(view.first instanceof env.Node)) { throw new Error("view.first is not a Node"); } if (!(view.last instanceof env.Node)) { throw new Error("view.last is not a Node"); } if (view.first !== view.last) { const parent = view.first.parentNode; if (!(parent instanceof env.Node)) { throw new Error("view.first.parent is not a Node"); } let node = view.first.nextSibling; nodes: for (;;) { if (node === null) { throw new Error("view is unterminated"); } if (!(node instanceof env.Node)) { throw new Error("view contains non-Node"); } if (node === view.last) { break nodes; } node = node.nextSibling; } } } class PollTimeoutError extends Error { } async function poll(fn, timeout) { const ac = new AbortController(); let timer; if (timeout !== undefined) { timer = setTimeout(() => ac.abort(new PollTimeoutError()), timeout); } try { for (;;) { const value = await fn(ac.signal); if (value) { return value; } ac.signal.throwIfAborted(); await new Promise(r => setTimeout(r, 0)); } } finally { clearTimeout(timer); } } const KEY = Symbol.for("rvx:test:queues"); const QUEUES = globalThis[KEY] ?? (globalThis[KEY] = new Map()); function exclusive(key, action) { let queue = QUEUES.get(key); if (queue === undefined) { queue = leak(() => new Queue()); QUEUES.set(key, queue); } return queue.block(action); } async function runAsyncTest(fn) { const teardown = []; const asyncCtx = new AsyncContext(); async function cleanup() { for (let i = teardown.length - 1; i >= 0; i--) { teardown[i](); } return asyncCtx.complete(); } try { const result = await fn({ asyncCtx, use: fn => captureSelf(dispose => { teardown.push(dispose); return ASYNC.provide(asyncCtx, fn); }), }); await cleanup(); return result; } catch (error) { try { await cleanup(); } catch { } throw error; } } function runTest(fn) { return captureSelf(dispose => { try { return fn(); } finally { dispose(); } }); } function querySelector(view, selector) { for (const node of viewNodes(view)) { if (node.nodeType === 1) { if (node.matches(selector)) { return node; } const elem = node.querySelector(selector); if (elem !== null) { return elem; } } } return null; } function querySelectorAll(view, selector) { const elems = []; for (const node of viewNodes(view)) { if (node.nodeType === 1) { if (node.matches(selector)) { elems.push(node); } elems.push(...node.querySelectorAll(selector)); } } return elems; } export { PollTimeoutError, assertViewState, exclusive, poll, querySelector, querySelectorAll, runAsyncTest, runTest };