thorish
Version:
This is a library of useful JS concepts and data structures for Node and the browser. It it, unashamedly, a dumping ground for code needed by [@samthor](https://twitter.com/samthor)'s projects.
2,101 lines (2,082 loc) • 107 kB
JavaScript
// src/aatree.ts
var AANode = class {
constructor(data) {
this.data = data;
}
level = 1;
left = null;
right = null;
};
var AATree = class _AATree {
constructor(compare) {
this.compare = compare;
}
root = null;
_count = 0;
_change = false;
/**
* Clones this {@link AATree} using {@link structuredClone}.
*/
clone() {
const t = new _AATree(this.compare);
t.root = structuredClone(this.root);
t._count = this._count;
return t;
}
clear() {
this.root = null;
this._count = 0;
}
/**
* The count of items in this tree.
*/
count() {
return this._count;
}
/**
* Query for this exact node.
*/
query(data) {
let node = this.root;
while (node !== null) {
const c = this.compare(data, node.data);
if (c < 0) {
node = node.left;
} else if (c > 0) {
node = node.right;
} else {
return node.data;
}
}
return void 0;
}
/**
* Finds the target node or the node closest before the query.
*/
equalBefore(data) {
return this._equalBefore(this.root, data);
}
/**
* Finds the node immediately before the query.
*/
before(data) {
let best;
let node = this.root;
while (node !== null) {
const c = this.compare(data, node.data);
if (c > 0) {
best = node.data;
node = node.right;
} else {
node = node.left;
}
}
return best;
}
/**
* Finds the target node or the node closest after the query.
*/
equalAfter(data) {
return this._equalAfter(this.root, data);
}
/**
* Finds the node immediately after the query.
*/
after(data) {
let best;
let node = this.root;
while (node !== null) {
const c = this.compare(data, node.data);
if (c < 0) {
best = node.data;
node = node.left;
} else {
node = node.right;
}
}
return best;
}
/**
* Inserts the value. Updates the previous value if compare is zero.
*
* @return if there was a change
*/
insert(data) {
this._change = false;
this.root = this._insert(this.root, data);
return this._change;
}
/**
* Removes the value.
*
* @return if there was a change
*/
remove(data) {
this._change = false;
this.root = this._remove(this.root, data);
return this._change;
}
skew(node) {
if (node.left?.level !== node.level) {
return node;
}
const leftNode = node.left;
node.left = leftNode.right;
leftNode.right = node;
return leftNode;
}
split(node) {
if (node.right?.right?.level !== node.level) {
return node;
}
const rightNode = node.right;
node.right = rightNode.left;
rightNode.left = node;
++rightNode.level;
return rightNode;
}
_equalBefore(node, data) {
while (node !== null) {
const c = this.compare(data, node.data);
if (c < 0) {
node = node.left;
continue;
}
if (c === 0) {
return node.data;
}
const within = this._equalBefore(node.right, data);
return within === void 0 ? node.data : within;
}
}
_equalAfter(node, data) {
while (node !== null) {
const c = this.compare(data, node.data);
if (c > 0) {
node = node.right;
continue;
}
if (c === 0) {
return node.data;
}
const within = this._equalAfter(node.left, data);
return within === void 0 ? node.data : within;
}
}
_insert(node, data) {
if (node === null) {
++this._count;
this._change = true;
return new AANode(data);
}
const c = this.compare(data, node.data);
if (c < 0) {
node.left = this._insert(node.left, data);
} else if (c > 0) {
node.right = this._insert(node.right, data);
} else {
if (node.data !== data) {
this._change = true;
node.data = data;
}
return node;
}
node = this.skew(node);
node = this.split(node);
return node;
}
_remove(node, data) {
if (node === null) {
return null;
}
const c = this.compare(data, node.data);
if (c < 0) {
node.left = this._remove(node.left, data);
} else if (c > 0) {
node.right = this._remove(node.right, data);
} else {
--this._count;
this._change = true;
if (node.left === null && node.right === null) {
return null;
} else if (node.left === null) {
return node.right;
} else if (node.right === null) {
return node.left;
} else {
const successor = this._findMin(node.right);
node.data = successor.data;
node.right = this._remove(node.right, successor.data);
}
}
const newLevel = Math.min(node.left?.level ?? 0, node.right?.level ?? 0) + 1;
if (newLevel < node.level) {
node.level = newLevel;
if (node.right && newLevel < node.right.level) {
node.right.level = newLevel;
}
}
node = this.skew(node);
node = this.split(node);
if (node.right) {
node.right = this.skew(node.right);
node.right = this.split(node.right);
if (node.right?.right) {
node.right.right = this.split(node.right.right);
}
}
return node;
}
_findMin(node) {
while (node.left) {
node = node.left;
}
return node;
}
};
// src/array.ts
function findAllIndex(arr, predicate) {
const out = [];
let index = 0;
for (const check of arr) {
if (predicate(check)) {
out.push(index);
}
++index;
}
return out;
}
function arrayContainsSub(arr, sub) {
return findSubArray(arr, sub) !== -1;
}
function findSubArray(arr, sub) {
outer: for (let i = 0; i <= arr.length - sub.length; ++i) {
for (let j = 0; j < sub.length; ++j) {
if (arr[i + j] !== sub[j]) {
continue outer;
}
}
return i;
}
return -1;
}
function arraySwapRemoveAt(arr, at) {
if (at < 0) {
at = arr.length + at;
}
if (at < 0 || at >= arr.length) {
return void 0;
}
const last = arr.pop();
if (arr.length === at) {
return last;
}
const out = arr[at];
arr[at] = last;
return out;
}
function arraySwapInsertAt(arr, at, value) {
if (at < 0) {
at = arr.length + at;
}
if (at >= arr.length) {
arr.push(value);
} else {
at = Math.max(at, 0);
const prev = arr[at];
arr[at] = value;
arr.push(prev);
}
}
// src/bound-proxy.ts
function neverFunctionForProxy() {
throw new Error(`should not get here`);
}
function buildBoundProxy(mod) {
const fakeModule = {};
const functionRef = {};
for (const key of Reflect.ownKeys(mod)) {
if (typeof mod[key] !== "function") {
fakeModule[key] = mod[key];
continue;
}
functionRef[key] = mod[key];
const currentValueReflect = new Proxy(Reflect, {
get(_reflect, reflectKey) {
return (...args) => Reflect[reflectKey](functionRef[key], ...args.slice(1));
}
});
fakeModule[key] = new Proxy(neverFunctionForProxy, currentValueReflect);
}
const setValue = (property, value) => {
if (property in functionRef) {
if (fakeModule[property] === value) {
return true;
} else if (typeof value !== "function") {
throw new Error(`can't redefine function => primitive for ${String(property)}`);
}
functionRef[property] = value;
return true;
} else if (property in fakeModule) {
fakeModule[property] = value;
return true;
}
return false;
};
return new Proxy(
{},
{
ownKeys(_target) {
return Reflect.ownKeys(fakeModule);
},
set(_target, property, value) {
return setValue(property, value);
},
get(_target, property) {
return fakeModule[property];
},
getOwnPropertyDescriptor(_target, property) {
const target = property in functionRef ? functionRef : fakeModule;
return Reflect.getOwnPropertyDescriptor(target, property);
},
defineProperty(_target, property, attributes) {
if (attributes.get || attributes.set) {
throw new Error(`can't defineProperty with get/set on fake module`);
} else if (!("value" in attributes)) {
return false;
}
return setValue(property, attributes.value);
}
}
);
}
// src/cache.ts
var SimpleCache = class {
constructor(gen) {
this.gen = gen;
}
m = /* @__PURE__ */ new Map();
/**
* Copies this as a regular {@link Map}.
*/
copy() {
return new Map(this.m);
}
has(k) {
return this.m.has(k);
}
get(k) {
const prev = this.m.get(k);
if (prev !== void 0) {
return prev;
}
const value = this.gen(k);
if (value !== void 0) {
this.m.set(k, value);
}
return value;
}
get size() {
return this.m.size;
}
clear() {
this.m.clear();
}
keys() {
return this.m.keys();
}
entries() {
return this.m.entries();
}
values() {
return this.m.values();
}
delete(k) {
return this.m.delete(k);
}
};
function once(fn) {
let run = false;
let result;
return () => {
if (!run) {
run = true;
result = fn();
}
return result;
};
}
function lazyWeak(fn) {
const wm = /* @__PURE__ */ new WeakMap();
return (w) => {
let value = wm.get(w);
if (value === void 0) {
value = fn(w);
wm.set(w, value);
}
return value;
};
}
// src/check.ts
async function checkCond(check, ms = defaultCheckCondTimeout) {
const target = performance.now() + ms;
for (; ; ) {
const remain = target - performance.now();
if (remain <= 0) {
return await check();
}
const delay = Math.max(0, Math.pow(remain, 0.65));
await new Promise((r) => setTimeout(r, delay));
try {
return await check();
} catch {
}
}
}
var defaultCheckCondTimeout = 2e3;
// src/cgroup.ts
var CGroup = class {
shutdownCause = void 0;
controller;
active = 0;
tasks = [];
halts = [];
resumeController;
resumeStart;
/**
* Adds a signal to this group.
* Returns `true` if the signal was not already aborted and was added to the active set.
*/
add(signal) {
if (signal.aborted || this.controller?.signal.aborted) {
return false;
}
++this.active;
signal.addEventListener("abort", () => {
--this.active;
if (this.active < 0) {
throw new Error("cgroup active went -ve");
} else if (this.active > 0 || !this.controller) {
return;
}
if (this.resumeController) {
throw new Error("cgroup abort with resumeController");
}
if (this.halts.length === 0) {
this.controller.abort(signal.reason);
return;
}
this.resumeController = new AbortController();
const resumeSignal = this.resumeController.signal;
let haltActive = 0;
this.resumeStart = async (halt) => {
++haltActive;
try {
await halt(this.controller.signal, resumeSignal);
} catch (err) {
this.shutdownCause ??= err;
this.controller.abort(err);
}
--haltActive;
if (haltActive === 0 && this.resumeController?.signal === resumeSignal) {
this.controller.abort("CGroup halt");
}
};
this.halts.forEach(this.resumeStart);
});
if (this.controller && this.resumeController) {
this.resumeController.abort("resume aborted");
this.resumeController = void 0;
this.resumeStart = void 0;
}
return !this.controller?.signal.aborted;
}
/**
* Starts the group and returns its {@link AbortSignal}.
* If no valid signals were added, this will be immediately aborted.
*/
start() {
if (!this.controller) {
this.controller = new AbortController();
if (this.active === 0) {
this.controller.abort("no AbortSignal added to CGroup");
} else {
this.tasks.forEach((fn) => this.runTask(fn));
this.tasks = [];
}
}
return this.controller.signal;
}
/**
* Ensures the group has started, then waits until the group's signal is aborted.
*/
async wait() {
const signal = this.start();
const buildPromise = () => {
if (this.shutdownCause !== void 0) {
return Promise.reject(this.shutdownCause);
} else {
return Promise.resolve();
}
};
if (signal.aborted) {
return buildPromise();
}
return new Promise((resolve) => {
signal.addEventListener("abort", () => resolve(buildPromise()), { once: true });
});
}
/**
* Runs the given function as part of this group.
* It will only start after `start()` has been called.
* Any returned error will cancel the group's signal.
*/
go(fn) {
if (!this.controller) {
this.tasks.push(fn);
return true;
}
if (this.controller.signal.aborted) {
return false;
}
this.runTask(fn);
return true;
}
/**
* Registers a function to run when the group is about to shut down.
* It is passed the group's signal and a "resume" signal which is aborted if the group restarts.
* Any returned error will cancel the group's signal.
*/
halt(fn) {
if (this.controller?.signal.aborted) {
return false;
}
this.resumeStart?.(fn);
this.halts.push(fn);
return true;
}
/**
* Helper to execute a task and handle its potential error.
*/
async runTask(fn) {
try {
await fn(this.controller.signal);
} catch (err) {
this.shutdownCause ??= err;
this.controller.abort(err);
}
}
};
// src/cond.ts
var Condition = class {
listeners = /* @__PURE__ */ new Map();
signal;
_state;
constructor(defaultValue, options) {
options?.signal?.addEventListener("abort", () => {
this.listeners.clear();
});
this._state = defaultValue;
this.signal = options?.signal;
}
get state() {
return this._state;
}
set state(v) {
if (this._state === v) {
return;
}
this._state = v;
for (const [fn, both] of this.listeners.entries()) {
if (both || v) {
fn(v);
}
}
}
/**
* Does this {@link Condition} currently have any listeners.
*/
observed() {
return this.listeners.size !== 0;
}
/**
* For subclasses to override. The actions to take when the first listener is added.
*/
setup() {
}
/**
* For subclasses to override. The actions to take when the last listener is removed, or this
* {@link Condition} is aborted.
*/
teardown() {
}
/**
* Adds a listener to this {@link Condition}.
*/
addListener(fn, options) {
if (this.signal?.aborted || options?.signal?.aborted || this.listeners.has(fn)) {
return false;
}
const first = this.listeners.size === 0;
this.listeners.set(fn, options?.both ?? false);
options?.signal?.addEventListener("abort", () => this.removeListener(fn));
if (first) {
this.setup();
}
return true;
}
/**
* Removes a listener from this {@link Condition}.
*/
removeListener(fn) {
if (this.listeners.size === 0) {
return false;
}
if (!this.listeners.delete(fn)) {
return false;
}
if (this.listeners.size === 0) {
this.teardown();
}
return true;
}
};
// src/limit.ts
function buildLimiter(c) {
const maxTokens = (c?.b ?? 100) * 1;
const rateOfIncrease = (c?.r ?? 10) * 1;
if (maxTokens <= 0 || rateOfIncrease <= 0) {
throw new Error(`invalid LimitConfig, no requests allowed`);
}
let last = performance.now();
let tokens = maxTokens;
return async (signal) => {
for (; ; ) {
signal.throwIfAborted();
const now = performance.now();
const delta = (now - last) / 1e3;
const increase = delta * rateOfIncrease;
tokens = Math.min(maxTokens, tokens + increase);
last = now;
const secondsToWait = (1 - tokens) / rateOfIncrease;
if (secondsToWait <= 0) {
tokens -= 1;
return;
}
await new Promise((r) => {
const timeout2 = setTimeout(r, secondsToWait * 1e3);
signal.addEventListener("abort", () => {
clearTimeout(timeout2);
r();
});
});
}
};
}
var PERSISTENT_DELAY_EXP = 1.65;
var PERSISTENT_DELAY_BASE = 230;
var PERSISTENT_DELAY_MAX = 6e4;
var PERSISTENT_DELAY_RAND = 0.1;
function createBackoff(baseDelay) {
baseDelay ||= 0;
let extraDelay = 0;
let nextDelay = 0;
return {
get delay() {
return nextDelay;
},
async timeout(signal) {
if (signal?.aborted) {
return;
}
return new Promise((r) => {
setTimeout(r, nextDelay);
signal?.addEventListener("abort", () => r(), { once: true });
});
},
success() {
extraDelay = 0;
nextDelay = 0;
},
error() {
extraDelay = (extraDelay + PERSISTENT_DELAY_BASE) * PERSISTENT_DELAY_EXP;
extraDelay = Math.min(extraDelay, PERSISTENT_DELAY_MAX);
const factor = 1 - (Math.random() * PERSISTENT_DELAY_RAND * 2 - PERSISTENT_DELAY_RAND);
const delay = (baseDelay + extraDelay) * factor;
nextDelay = Math.max(0, delay);
}
};
}
// src/promise.ts
var unresolvedPromise = /* @__PURE__ */ new Promise(() => {
});
var resolvedPromise = /* @__PURE__ */ Promise.resolve(void 0);
function wrapTrigger(trigger, ...moreArgs) {
return new Promise((resolve) => {
trigger(resolve, ...moreArgs);
});
}
function timeout(ms, signal) {
if (signal?.aborted) {
return Promise.resolve();
}
return new Promise((resolve) => {
const t = setTimeout(resolve, ms);
signal?.addEventListener("abort", () => {
clearTimeout(t);
resolve();
});
});
}
var promiseWithResolvers = /* @__PURE__ */ (() => Promise.withResolvers ? Promise.withResolvers.bind(Promise) : function localResolvable() {
let resolve, reject;
const promise = new Promise((localResolve, localReject) => {
resolve = localResolve;
reject = localReject;
});
return { resolve, reject, promise };
})();
var resolvable = promiseWithResolvers;
function promiseForEvent(target, eventName, options = {}) {
if (options.signal?.aborted) {
return Promise.reject();
}
return new Promise((resolve, reject) => {
options.signal?.addEventListener("abort", () => reject());
target.addEventListener(eventName, (e) => resolve(e), { ...options, once: true });
});
}
async function spliceNextPromise(arr) {
if (!arr.length) {
return void 0;
}
const internal = arr.map((x, i) => x.then((ret) => ({ ret, i })));
const next = await Promise.race(internal);
arr.splice(next.i, 1);
return next.ret;
}
function buildCallTrain(fn) {
let activePromise;
return () => {
if (!activePromise) {
activePromise = fn().then((ret) => {
activePromise = void 0;
return ret;
});
}
return activePromise;
};
}
function rafRunner(callback, options) {
return internalBuildRunner(requestAnimationFrame, callback, options);
}
function fastFrameRunner(callback, options) {
return internalBuildRunner(
(cb) => {
requestAnimationFrame(cb);
setTimeout(cb, 0);
},
callback,
options
);
}
function tickRunner(callback, options) {
return internalBuildRunner((m) => Promise.resolve().then(m), callback, options);
}
function internalBuildRunner(runner2, callback, options) {
const { immediate, signal } = options ?? {};
let activePromise;
const o = () => {
if (activePromise === void 0) {
const pr = promiseWithResolvers();
runner2(() => {
if (activePromise !== pr.promise) {
return;
}
activePromise = void 0;
if (signal?.aborted) {
pr.reject(signal.reason);
} else {
pr.resolve(callback());
}
});
pr.promise.catch(() => {
});
activePromise = pr.promise;
}
return activePromise;
};
immediate && o();
return o;
}
// src/signal.ts
function afterSignal(signal, fn) {
let shouldRun = true;
if (signal.aborted) {
Promise.resolve().then(() => {
if (!shouldRun) {
return;
}
shouldRun = false;
fn();
});
return () => {
try {
return shouldRun;
} finally {
shouldRun = false;
}
};
}
const wrap = () => {
shouldRun = false;
fn();
};
signal.addEventListener("abort", wrap);
return () => {
if (shouldRun) {
signal.removeEventListener("abort", wrap);
shouldRun = false;
return true;
}
return false;
};
}
function promiseVoidForSignal(signal) {
if (signal.aborted) {
return Promise.resolve();
}
return new Promise((resolve) => {
signal.addEventListener("abort", () => resolve());
});
}
function promiseForSignal(signal, resolveWith) {
if (resolveWith === void 0 && arguments.length < 2) {
if (signal.aborted) {
return Promise.reject(signal.reason);
}
return new Promise((reject) => signal.addEventListener("abort", () => reject(signal.reason)));
}
if (signal.aborted) {
return Promise.resolve(resolveWith);
}
return new Promise((resolve) => {
signal.addEventListener("abort", () => resolve(resolveWith));
});
}
var abortSignalAny = /* @__PURE__ */ (() => AbortSignal.any ? AbortSignal.any : (all) => {
const previouslyAborted = all.find((x) => x.aborted);
if (previouslyAborted !== void 0) {
return previouslyAborted;
}
const c = new AbortController();
all.forEach((p) => p.addEventListener("abort", () => c.abort(p.reason)));
return c.signal;
})();
var buildTimeout = () => new DOMException("The operation was aborted due to timeout", "TimeoutError");
function abortSignalTimeout(timeout2) {
const c = new AbortController();
const s = AbortSignal.timeout(timeout2);
s.addEventListener("abort", () => {
if (s.reason instanceof DOMException && s.reason.name === "TimeoutError") {
c.abort(s.reason);
} else {
c.abort(buildTimeout());
}
});
return c.signal;
}
function tickAbortSignal() {
const c = new AbortController();
Promise.resolve().then(() => c.abort(buildTimeout()));
return c.signal;
}
function derivedSignal(...raw) {
const previous = raw.filter(Boolean);
const c = new AbortController();
previous.push(c.signal);
const signal = abortSignalAny(previous);
const abort = (reason) => c.abort(reason ?? "aborted");
return { signal, abort };
}
var abortedSignal = /* @__PURE__ */ (() => {
const c = new AbortController();
c.abort();
return c.signal;
})();
var neverAbortedSignal = /* @__PURE__ */ (() => new AbortController().signal)();
var todoSignal = neverAbortedSignal;
// src/queue.ts
var WorkQueue = class {
pending = [];
queue = [];
releaseActive = false;
releaseTask = Promise.resolve();
/**
* The {@link WorkQueue} releases waiting tasks one-per-microtask. This maintains the invariant
* that every time `wait` returns or resolves, there's at least one item in the queue: otherwise,
* resolving everything at once might allow other waiters to steal all items.
*/
releasePending() {
if (this.releaseActive) {
return;
}
this.releaseTask = this.releaseTask.then(async () => {
this.releaseActive = true;
try {
while (this.queue.length && this.pending.length) {
const resolve = this.queue.shift();
resolve();
await new Promise((r) => queueMicrotask(r));
}
} finally {
this.releaseActive = false;
}
});
}
/**
* Iterates through the items in this queue _forever_, waiting for more items to appear.
*/
async *[Symbol.asyncIterator]() {
for (; ; ) {
await this.wait();
yield this.pending.shift();
}
}
asyncGenerator() {
return this[Symbol.asyncIterator]();
}
/**
* Iterates through the items in this queue, stopping when no more are available synchronously.
*/
*[Symbol.iterator]() {
while (this.pending.length) {
yield this.shift();
}
}
/**
* Waits until there is something in the queue that your task can process. This does _not_ return
* the item itself. This returns `undefined` if no waiting is required.
*/
wait() {
if (this.pending.length === 0) {
return new Promise((r) => {
this.queue.push(r);
});
}
}
/**
* Takes a token for the next item from the front of the queue. The returned {@link Promise} will
* always receive the next item, so don't throw it away.
*/
async next() {
await this.wait();
return this.pending.shift();
}
/**
* Push items into the queue. Wakes up any pending requests.
*/
push(...items) {
try {
return this.pending.push(...items);
} finally {
if (items.length) {
this.releasePending();
}
}
}
pop() {
return this.pending.pop();
}
/**
* Push items at the start of the queue. Wakes up any pending requests.
*/
unshift(...items) {
try {
return this.pending.unshift(...items);
} finally {
if (items.length) {
this.releasePending();
}
}
}
shift() {
return this.pending.shift();
}
get length() {
return this.pending.length;
}
};
function listenerToAsyncGenerator(l) {
const fn = async function* () {
for (; ; ) {
const value = await l.next();
if (value === void 0) {
return;
}
yield value;
}
};
return fn();
}
var emptyQueueRef = {};
function buildEmptyListener() {
return new EmptyListenerImpl();
}
var ListenerImpl = class {
async batch() {
const next = await this.next();
if (next === void 0) {
return [];
}
const out = [next];
while (this.peek()) {
const next2 = await this.next();
if (next2 === void 0) {
break;
}
out.push(next2);
}
return out;
}
async last() {
const out = await this.batch();
return out.at(-1);
}
};
var EmptyListenerImpl = class extends ListenerImpl {
next() {
return Promise.resolve(void 0);
}
peek() {
return void 0;
}
};
var LinkQueueImpl = class {
head = {};
p;
push(...all) {
if (!all.length) {
return false;
}
for (const each of all) {
const prev = this.head;
this.head = {};
prev.value = each;
prev.next = this.head;
}
if (!this.p) {
return false;
}
const { resolve } = this.p;
this.p = void 0;
resolve();
return true;
}
join(signal) {
let waitNext;
let ref = this.head;
if (!signal) {
waitNext = () => this.p.promise;
} else if (signal.aborted) {
waitNext = () => Promise.resolve();
ref = emptyQueueRef;
} else {
const signalPromise = promiseVoidForSignal(signal);
waitNext = () => Promise.race([signalPromise, this.p.promise]);
}
const outer = this;
return new class extends ListenerImpl {
peek() {
return ref.value;
}
async next() {
if (signal?.aborted) {
return void 0;
}
let { value } = ref;
if (value !== void 0) {
ref = ref.next;
return value;
}
outer.p ??= promiseWithResolvers();
await waitNext();
return this.next();
}
async last() {
let value = await this.next();
if (value === void 0) {
return void 0;
}
while (ref.value !== void 0) {
value = ref.value;
ref = ref.next;
}
return value;
}
}();
}
};
var ArrayQueueImpl = class {
head = 0;
data = [];
subs = /* @__PURE__ */ new Map();
p;
trimTask = false;
push(...all) {
if (all.length === 0) {
return false;
}
this.head += all.length;
if (this.subs.size === 0) {
if (this.data.length) {
this.data = [];
}
return false;
}
this.data.push(...all);
if (!this.p) {
return false;
}
const { resolve } = this.p;
this.p = void 0;
resolve();
return true;
}
join(signal) {
const outer = this;
const signalPromise = promiseVoidForSignal(signal);
const waitFor = async (cb) => {
for (; ; ) {
const last = outer.subs.get(l);
if (last === void 0) {
return [];
} else if (last < outer.head) {
const start = outer.head - outer.data.length;
const skip = last - start;
const avail = outer.data.length - skip;
const toReturn = cb(avail);
const out = outer.data.slice(skip, skip + toReturn);
outer.subs.set(l, last + out.length);
outer.queueTrimEvents();
return out;
}
outer.p ??= promiseWithResolvers();
await Promise.race([signalPromise, outer.p.promise]);
}
};
const l = new class extends ListenerImpl {
peek() {
const last = outer.subs.get(l);
if (last === void 0 || last >= outer.head) {
return void 0;
}
const start = outer.head - outer.data.length;
const skip = last - start;
return outer.data[skip];
}
async next() {
const out = await waitFor(() => 1);
return out[0];
}
async batch() {
return waitFor((avail) => avail);
}
}();
if (!signal.aborted) {
this.subs.set(l, this.head);
signal.addEventListener("abort", () => {
this.subs.delete(l);
this.queueTrimEvents();
});
}
return l;
}
queueTrimEvents() {
if (this.trimTask) {
return;
}
this.trimTask = true;
const timeoutMs = 250;
setTimeout(() => {
this.trimTask = false;
const min = Math.min(this.head, ...this.subs.values());
if (min === this.head) {
if (this.data.length) {
this.data = [];
}
return;
}
const start = this.head - this.data.length;
const strip = min - start;
this.data.splice(0, strip);
}, timeoutMs);
}
};
function buildLinkQueue() {
return new LinkQueueImpl();
}
function buildArrayQueue() {
return new ArrayQueueImpl();
}
// src/socket.ts
function socketQueueSend(socket, transformMessage) {
let p = Promise.resolve(socket);
transformMessage ??= (m) => m;
let resolvedSocket;
const attemptSend = async (raw) => {
if (resolvedSocket === void 0 || resolvedSocket.readyState !== resolvedSocket.OPEN) {
return;
}
let untransformed;
if (typeof raw === "function") {
const fn = raw;
untransformed = await fn();
} else {
untransformed = await raw;
}
const transformed = transformMessage(untransformed);
if (transformed != null) {
resolvedSocket?.send(transformed);
}
};
p = p.then(async (s) => {
resolvedSocket = s;
switch (s.readyState) {
case s.CLOSED:
case s.CLOSING:
case s.OPEN:
return s;
}
await new Promise((resolve) => {
s.addEventListener("open", resolve, { once: true });
s.addEventListener("close", resolve, { once: true });
});
return s;
});
return (message) => {
const maySend = resolvedSocket === void 0 || resolvedSocket.readyState === resolvedSocket.CONNECTING || resolvedSocket.readyState === resolvedSocket.OPEN;
if (maySend) {
p = p.finally(() => attemptSend(message));
}
return maySend;
};
}
async function socketConnect(url, opts) {
if (opts?.delay) {
const p = opts.signal ? promiseVoidForSignal(opts.signal) : void 0;
await Promise.race([p, timeout(opts.delay)]);
}
opts?.signal?.throwIfAborted();
const s = new WebSocket(url, opts?.protocols);
if (opts?.signal) {
afterSignal(opts?.signal, () => s.close());
}
if (opts?.message) {
s.addEventListener("message", opts.message);
}
let wasOpen = false;
return new Promise((resolve, reject) => {
s.addEventListener(
"open",
() => {
wasOpen = true;
resolve(s);
},
{ once: true }
);
s.addEventListener("close", () => reject(new Error("WebSocket close")), { once: true });
s.addEventListener(
"error",
() => {
if (!wasOpen) {
s.close();
}
reject(new Error("WebSocket error"));
},
{ once: true }
);
});
}
// src/call.ts
var startSymbol = /* @__PURE__ */ Symbol("start");
async function startRemoteCall(signal, url) {
const sock = await socketConnect(url, { signal });
const internalController = derivedSignal(signal);
sock.addEventListener("close", internalController.abort);
const helloPromise = new Promise((resolve, reject) => {
sock.addEventListener(
"message",
(e) => {
try {
const p = JSON.parse(e.data);
if (!p.ok) {
throw new Error("no ok");
}
resolve(p);
} catch (e2) {
reject(e2);
}
},
{ once: true }
);
sock.addEventListener("close", reject);
signal.addEventListener("abort", reject);
if (signal.aborted) {
reject();
}
});
sock.send(JSON.stringify({ p: "1" }));
const hello = await helloPromise;
const callLimiter = buildLimiter(hello.l.c);
const packetLimiter = buildLimiter(hello.l.p);
let lastNewCall = 0;
const activeCalls = /* @__PURE__ */ new Map();
const listener = /* @__PURE__ */ (() => {
let activeInCall = 0;
return (e) => {
if (typeof e.data === "string" && e.data[0] === ":") {
const control = safeJSONParse(e.data.substring(1));
if (control?.c !== void 0) {
activeInCall = control.c;
}
if (control?.stop !== void 0) {
const active = activeCalls.get(activeInCall);
active?.({ error: new RemoteCallError(control.stop) });
activeCalls.delete(activeInCall);
}
} else {
const data = safeJSONParse(e.data);
if (data) {
const active = activeCalls.get(activeInCall);
active?.({ data });
}
}
};
})();
sock.addEventListener("message", listener);
const outboundQueue = buildLinkQueue();
const task = (async () => {
const listener2 = outboundQueue.join(internalController.signal);
let activeOutCall = 0;
for (; ; ) {
const next = await listener2.next();
if (!next) {
return;
}
if (next.stop !== void 0) {
const o = { stop: next.stop };
if (activeOutCall !== next.c) {
o.c = next.c;
activeOutCall = next.c;
}
sock.send(`:` + JSON.stringify(o));
continue;
}
if (next.data === startSymbol) {
await callLimiter(internalController.signal);
} else {
await packetLimiter(internalController.signal);
}
if (activeOutCall !== next.c) {
sock.send(`:` + JSON.stringify({ c: next.c }));
activeOutCall = next.c;
}
if (next.data !== startSymbol) {
sock.send(JSON.stringify(next.data));
}
}
})();
task.catch((e) => internalController.abort(e));
sock.addEventListener("error", (e) => internalController.abort(e));
internalController.signal.addEventListener("abort", () => sock.close());
const done = promiseForSignal(internalController.signal);
done.then(() => {
});
return {
call(signal2) {
if (signal2.aborted) {
return {
send(data) {
},
gen: throwAsyncGenerator(signal2.reason)
};
}
const callId = ++lastNewCall;
const q = buildLinkQueue();
activeCalls.set(callId, q.push.bind(q));
const listener2 = q.join(signal2);
const gen = async function* () {
for (; ; ) {
const next = await listener2.next();
if (next?.data) {
yield next.data;
}
if (next?.error || next?.data === void 0) {
return next?.error;
}
}
}();
outboundQueue.push({ data: startSymbol, c: callId });
signal2.addEventListener("abort", () => {
activeCalls.delete(callId);
outboundQueue.push({ stop: "", c: callId });
});
const send = (data) => {
if (!signal2.aborted) {
outboundQueue.push({ data, c: callId });
}
};
return { send, gen };
},
init: hello.i,
done
};
}
var RemoteCallError = class extends Error {
constructor(stop) {
super(stop);
this.stop = stop;
}
};
function throwAsyncGenerator(reason) {
return async function* () {
throw reason instanceof Error ? reason : new Error(reason);
}();
}
function safeJSONParse(raw) {
let out;
try {
out = JSON.parse(raw);
} catch {
return;
}
return out;
}
// src/maps.ts
var CountSet = class {
m = /* @__PURE__ */ new Map();
count = 0;
/**
* The total number of values (aka, the number of calls to {@link CountSetprivate add}).
*/
total() {
return this.count;
}
add(t) {
this.m.set(t, (this.m.get(t) ?? 0) + 1);
++this.count;
return true;
}
entries() {
return this.m.entries();
}
delete(t) {
const prev = this.m.get(t);
if (prev === void 0) {
return false;
}
if (prev === 1) {
this.m.delete(t);
} else {
this.m.set(t, prev - 1);
}
--this.count;
return true;
}
has(t) {
return this.m.has(t);
}
uniques() {
return this.m.keys();
}
*keys() {
for (const [t, count] of this.m.entries()) {
for (let i = 0; i < count; ++i) {
yield t;
}
}
}
};
var PairSet = class {
m = new PairMap();
size() {
return this.m.size();
}
add(a, b) {
return this.m.set(a, b, true);
}
delete(a, b) {
return this.m.delete(a, b);
}
has(a, b) {
return this.m.has(a, b);
}
hasAny(k) {
return this.m.hasAny(k);
}
otherKeys(k) {
return this.m.otherKeys(k);
}
pairsWith(k) {
return this.m.pairsWith(k);
}
keys() {
return this.m.keys();
}
pairs() {
return this.m.pairs();
}
clear() {
this.m.clear();
}
};
var PairMap = class {
m = /* @__PURE__ */ new Map();
implicitGet(k) {
const has = this.m.get(k);
if (has !== void 0) {
return has;
}
const update = /* @__PURE__ */ new Map();
this.m.set(k, update);
return update;
}
set(a, b, v) {
const mapA = this.implicitGet(a);
if (mapA.get(b) === v) {
return false;
}
mapA.set(b, v);
this.implicitGet(b).set(a, v);
return true;
}
size() {
return this.m.size;
}
pairsWith(k) {
return this.m.get(k)?.size ?? 0;
}
otherKeys(k) {
return this.m.get(k)?.keys() ?? [][Symbol.iterator]();
}
otherEntries(k) {
return this.m.get(k)?.entries() ?? [][Symbol.iterator]();
}
*pairs() {
const seen = /* @__PURE__ */ new Set();
for (const e of this.m.entries()) {
const left = e[0];
for (const right of e[1].keys()) {
if (!seen.has(right)) {
yield [left, right];
}
}
seen.add(left);
}
}
*pairsEntries() {
const seen = /* @__PURE__ */ new Set();
for (const e of this.m.entries()) {
const left = e[0];
for (const [right, value] of e[1].entries()) {
if (!seen.has(right)) {
yield [left, right, value];
}
}
seen.add(left);
}
}
delete(a, b) {
const mapA = this.m.get(a);
if (!mapA?.has(b)) {
return false;
}
mapA.delete(b);
if (mapA.size === 0) {
this.m.delete(a);
}
const mapB = this.m.get(b);
mapB.delete(a);
if (mapB.size === 0) {
this.m.delete(b);
}
return true;
}
has(a, b) {
return this.m.get(a)?.has(b) ?? false;
}
hasAny(k) {
return this.m.has(k);
}
get(a, b) {
return this.m.get(a)?.get(b);
}
keys() {
return this.m.keys();
}
clear() {
this.m.clear();
}
};
var MultiMap = class {
m = /* @__PURE__ */ new Map();
_totalSize = 0;
add(k, v) {
let set = this.m.get(k);
if (set === void 0) {
set = /* @__PURE__ */ new Set();
this.m.set(k, set);
}
if (set.has(v)) {
return false;
}
set.add(v);
this._totalSize++;
return true;
}
/**
* Clears all values for this key.
*/
clearKey(k) {
const had = this.m.get(k);
if (had !== void 0) {
this._totalSize -= had.size;
this.m.delete(k);
return true;
}
return false;
}
/**
* Delete a specific key/value combination.
*/
delete(k, v) {
const set = this.m.get(k);
if (set === void 0) {
return false;
}
const deleted = set.delete(v);
if (deleted) {
this._totalSize--;
if (set.size === 0) {
this.m.delete(k);
}
}
return deleted;
}
has(k, v) {
return this.m.get(k)?.has(v) ?? false;
}
get(k) {
return this.m.get(k) ?? [];
}
/**
* Returns the count of values for this key.
*/
count(k) {
return this.m.get(k)?.size ?? 0;
}
/**
* Returns the size of this map; the number of keys with valid values.
*
* This is not the total number of values.
*/
get size() {
return this.m.size;
}
/**
* Returns the total number of values set for all keys.
*/
get totalSize() {
return this._totalSize;
}
/**
* Iterates through all active keys (with any values).
*/
keys() {
return this.m.keys();
}
clear() {
this.m.clear();
}
};
var BiMap = class _BiMap {
constructor(fwd = /* @__PURE__ */ new Map(), rwd = /* @__PURE__ */ new Map()) {
this.fwd = fwd;
this.rwd = rwd;
}
get size() {
return this.fwd.size;
}
clear() {
this.fwd.clear();
this.rwd.clear();
}
keys() {
return this.fwd.keys();
}
values() {
return this.fwd.values();
}
entries() {
return this.fwd.entries();
}
invert() {
return new _BiMap(this.rwd, this.fwd);
}
set(a, b) {
const prevB = this.fwd.get(a);
const prevA = this.rwd.get(b);
if (prevB !== b) {
this.rwd.delete(prevB);
}
this.rwd.set(b, a);
if (prevA !== a) {
this.fwd.delete(prevA);
}
this.fwd.set(a, b);
return this;
}
has(a) {
return this.fwd.has(a);
}
hasFar(b) {
return this.rwd.has(b);
}
get(a) {
return this.fwd.get(a);
}
getFar(b) {
return this.rwd.get(b);
}
delete(a) {
if (!this.fwd.has(a)) {
return false;
}
const prevB = this.fwd.get(a);
this.rwd.delete(prevB);
this.fwd.delete(a);
return true;
}
deleteFar(b) {
if (!this.rwd.has(b)) {
return false;
}
const prevA = this.rwd.get(b);
this.fwd.delete(prevA);
this.rwd.delete(b);
return true;
}
static create() {
return new _BiMap();
}
};
var TransformMap = class {
data = /* @__PURE__ */ new Map();
defaultValue;
transform;
constructor(defaultValue, transform) {
this.defaultValue = defaultValue;
this.transform = transform;
}
/**
* Deletes the given key. Basically reverts it to its default value.
*/
delete(k) {
return this.data.delete(k);
}
/**
* Update the given key.
*/
update(k, update) {
const prev = this.get(k);
const result = this.transform(prev, update);
if (result === this.defaultValue) {
this.data.delete(k);
} else {
this.data.set(k, result);
}
return result;
}
/**
* Return the current value for this key, or the default.
*/
get(k) {
const prev = this.data.get(k);
if (prev === void 0) {
return this.defaultValue;
}
return prev;
}
/**
* Return all keys for non-default values.
*/
keys() {
return this.data.keys();
}
/**
* Whether this key has a non-default value.
*/
has(k) {
return this.data.has(k);
}
clear() {
this.data.clear();
}
};
// src/counter.ts
var StatsCount = class extends TransformMap {
constructor() {
super(0, (v, update) => v + update);
}
inc(k, by) {
return this.update(k, by);
}
};
// src/effect.ts
function prepareEffectTrigger() {
const build = (cb, required) => {
const requiredSet = new Set(required ?? []);
const have = /* @__PURE__ */ new Set();
let abort = (reason) => {
};
const o = {};
const maybeTrigger = () => {
abort("trigger");
if (have.size !== requiredSet.size) {
return;
}
const c = new AbortController();
abort = (reason) => c.abort(reason);
cb(c.signal, { ...o });
};
const p = new Proxy(o, {
deleteProperty(_, key) {
delete o[key];
if (requiredSet.has(key)) {
have.delete(key);
}
maybeTrigger();
return true;
},
set(_, key, value) {
if (requiredSet.has(key)) {
if (value !== void 0) {
have.add(key);
} else {
have.delete(key);
}
}
const prev = o[key];
o[key] = value;
if (prev !== value) {
maybeTrigger();
}
return true;
}
});
return p;
};
return { build };
}
// src/expirable.ts
function buildAsyncExpirable(fn) {
let activePromise;
return () => {
if (activePromise !== void 0) {
return activePromise;
}
activePromise = fn().then((ret) => {
if (ret.signal.aborted) {
activePromise = void 0;
} else {
ret.signal.addEventListener("abort", () => activePromise = void 0);
}
return ret;
});
return activePromise;
};
}
function buildExpirable(fn) {
let active;
return () => {
if (active !== void 0) {
return active;
}
const localActive = fn();
if (!localActive.signal.aborted) {
active = localActive;
localActive.signal.addEventListener("abort", () => active = void 0);
}
return localActive;
};
}
// src/internal.ts
async function promiseForSignal2(signal) {
if (signal === void 0) {
return unresolvedPromise;
} else if (!signal.aborted) {
await new Promise((resolve) => signal.addEventListener("abort", resolve));
}
throw symbolAbortSignal;
}
var symbolAbortSignal = /* @__PURE__ */ Symbol("known");
// src/generator.ts
async function* combineAsyncGenerators(gen) {
gen = gen.slice();
const buildNext = async (index) => {
const p = gen[index].next();
const res = await p;
return { index, res };
};
const nexts = gen.map((_, index) => buildNext(index));
let doneCount = 0;
const doneValues = new Array(gen.length);
while (doneCount !== gen.length) {
const next = await Promise.race(nexts);
if (next.res.done) {
nexts[next.index] = unresolvedPromise;
doneValues[next.index] = next.res.value;
++doneCount;
} else {
nexts[next.index] = buildNext(next.index);
yield { index: next.index, value: next.res.value };
}
}
return doneValues;
}
async function* asyncGeneratorForHandler(handler, args) {
const signalPromise = promiseForSignal2(args?.signal);
for (; ; ) {
let v;
try {
const p = handler();
v = await Promise.race([signalPromise, p]);
} catch (e) {
if (e === symbolAbortSignal) {
return;
}
throw e;
}
yield v;
}
}
var doneSymbol = /* @__PURE__ */ Symbol("done");
function asyncGeneratorQueue() {
let { promise, resolve } = promiseWithResolvers();
let isDone = false;
const pending = [];
const push = (t) => {
if (isDone) {
throw new Error("Can't push into completed asyncGeneratorQueue");
}
pending.push(t);
resolve();
};
let doneValue;
const done = (y) => {
if (isDone) {
throw new Error("Can't complete already completed asyncGeneratorQueue");
}
pending.push(doneSymbol);
isDone = true;
doneValue = y;
resolve();
};
const generator = async function* () {
for (; ; ) {
await promise;
while (pending.length) {
const next = pending.shift();
if (next === doneSymbol) {
return doneValue;
}
yield next;
}
({ promise, resolve } = promiseWithResolvers());
}
}();
return {
generator,
push,
done
};
}
var doneAsyncGenerator = /* @__PURE__ */ async function* () {
}();
var AsyncGeneratorCache = class {
_knownValues = [];
_done = false;
doneValue;
pendingPromise;
gen;
constructor(gen) {
this.gen = gen;
}
waitFor() {
if (this.pendingPromise) {
return this.pendingPromise;
}
return this.pendingPromise = this.gen.next().then((res) => {
if (res.done) {
this._done = true;
this.doneValue = res.value;
this.gen = doneAsyncGenerator;
} else {
this._knownValues.push(res.value);
this.pendingPromise = void 0;
}
});
}
async *read() {
let at = 0;
for (; ; ) {
while (at < this._knownValues.length) {
yield this._knownValues[at];
++at;
}
if (this.done) {
return this.doneValue;
}
await this.waitFor();
}
}
get done() {
return this._done;
}
knownValues() {
return this._knownValues;
}
};
async function forEachAsync(async, cb) {
let result;
let next = [];
while (!(result = await async.next(...next)).done) {
const internalResult = await cb(result.value);
next = [internalResult];
}
return result.value;
}
// src/support/node.ts
import { isDeepStrictEqual } from "node:util";
function base64UrlToBytes(s) {
return Buffer.from(s, "base64url");
}
function base64UrlToString(s) {
return new TextDecoder("utf-8").decode(base64UrlToBytes(s));
}
function toBase64Url(s) {
let b;
if (typeof s === "string") {
b = Buffer.from(s, "utf-8");
} else {
b = Buffer.from(s);
}
return b.toString("base64url");
}
function concatBytes(chunks) {
return Buffer.concat(chunks);
}
var nextTick = /* @__PURE__ */ (() => process.nextTick)();
function escapeHtmlEntites(str) {
return str.replaceAl