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,067 lines (2,047 loc) • 82.5 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;
};
}
// 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/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);
});
}
var timeout = (duration) => wrapTrigger(setTimeout, duration);
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);
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/conn.ts
function connForSocket(ws) {
const c = new AbortController();
ws.addEventListener("close", () => c.abort());
if (ws.readyState === ws.CLOSED || ws.readyState === ws.CLOSING) {
c.abort();
return {
listener: buildEmptyListener(),
send() {
},
close() {
},
signal: c.signal,
done: Promise.resolve()
};
}
const {
promise: donePromise,
resolve: doneResolve,
reject: doneReject
} = promiseWithResolvers();
donePromise.finally(() => c.abort());
let sendQueue;
if (ws.readyState !== ws.OPEN) {
const localSendQueue = [];
sendQueue = localSendQueue;
ws.addEventListener("open", () => {
localSendQueue.forEach((q2) => ws.send(q2));
localSendQueue.splice(0, localSendQueue.length);
sendQueue = void 0;
});
}
const q = buildLinkQueue();
const conn = {
listener: q.join(c.signal),
send(raw) {
if (sendQueue) {
sendQueue.push(JSON.stringify(raw));
} else {
ws.send(JSON.stringify(raw));
}
},
close(cause) {
ws.close(cause ? 4e3 : 1e3, String(cause));
cause && doneReject(cause);
},
signal: c.signal,
done: donePromise
};
const messageHandler = (data) => {
let j;
try {
j = JSON.parse(data);
} catch {
return;
}
q.push(j);
};
if ("on" in ws) {
ws.on("message", messageHandler);
ws.on("error", (err) => doneReject(err));
ws.on("close", () => doneResolve());
} else {
ws.addEventListener("message", (e) => messageHandler(e.data));
ws.addEventListener("error", (e) => doneReject(e));
ws.addEventListener("close", () => doneResolve());
}
return conn;
}
// 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();
}
};
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();
}
};
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();
}
};
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);
}
};
// 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 = () => {
};
const o = {};
const maybeTrigger = () => {
abort();
if (have.size !== requiredSet.size) {
return;
}
const c = new AbortController();
abort = () => c.abort();
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;
}
};
// 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.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
}
// src/support/index.ts
var isArrayEqualIsh = isDeepStrictEqual;
var fauxStructuredClone = (o) => {
if (typeof o !== "object") {
return o;
}
const out = { ...o };
for (const k in out) {
out[k] = fauxStructuredClone(out[k]);
}
return out;
};
var structuredIshClone = typeof structuredClone === "function" ? structuredClone : fauxStructuredClone;
var escapeHtmlEntites2 = (str) => {
return str.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
};
// src/html-state.ts
var HtmlState = /* @__PURE__ */ ((HtmlState2) => {
HtmlState2[HtmlState2["Normal"] = 0] = "Normal";
HtmlState2[HtmlState2["WithinTag"] = 2] = "WithinTag";
HtmlState2[HtmlState2["TagAttr"] = 3] = "TagAttr";
HtmlState2[HtmlState2["WithinComment"] = 9] = "WithinComment";
HtmlState2[HtmlState2["WithinScriptTag"] = 10] = "WithinScriptTag";
HtmlState2[HtmlState2["WithinStyleTag"] = 11] = "WithinStyleTag";
HtmlState2[HtmlState2["WithinTextAreaTag"] = 12] = "WithinTextAreaTag";
HtmlState2[HtmlState2["WithinTagAttrDoubleQuote"] = 17] = "WithinTagAttrDoubleQuote";
HtmlState2[HtmlState2["WithinTagAttrSingleQuote"] = 18] = "WithinTagAttrSingleQuote";
return HtmlState2;
})(HtmlState || {});
var nextRe = /<(\!--|\w+)/;
var withinTagNext = /(=?\s*\'|=?\s*\"|=\s*|>|\/\>)/;
var closedByRe = /^\s*>/;
function indexOfCloserWithinTagLike(state, check) {
let find;
switch (state) {
case 9 /* WithinComment */: {
const index = check.indexOf("-->");
if (index === -1) {
return -1;
}
return index + 3;
}
case 10 /* WithinScriptTag */:
find = "</script";
break;
case 11 /* WithinStyleTag */:
find = "</style";
break;
case 12 /* WithinTextAreaTag */:
find = "</textarea";
break;
default:
return -1;
}
let out = check.indexOf(find);
if (out === -1) {
return -1;
}
out += find.length;
const rest = check.substring(out);
if (!closedByRe.test(rest)) {
return -1;
}
const indexOfEnd = rest.indexOf(">");
if (indexOfEnd === -1) {
throw new Error(`should never happen`);
}
return out + indexOfEnd + 1;
}
function escapeStringFor(state, s) {
s = String(s);
if (indexOfCloserWithinTagLike(state, s) !== -1) {
let msg = "?";
switch (state) {
case 9 /* WithinComment */:
msg = "-->";
break;
case 10 /* WithinScriptTag */:
msg = "</script>";
break;
case 11 /* WithinStyleTag */:
msg = "</style>";
break;
case 12 /* WithinTextAreaTag */:
msg = "</textarea>";
break;
}
throw new Error(`can't inline text: dangerously contains closer "${msg}"`);
}
switch (state) {
case 9 /* WithinComment */:
case 10 /* WithinScriptTag */:
case 11 /* WithinStyleTag */:
case 12 /* WithinTextAreaTag */:
return s;
}
const escaped = escapeHtmlEntites2(s);
switch (state) {
case 2 /* WithinTag */:
throw new Error(`unsupported interpolation within <tag>`);
case 3 /* TagAttr */:
return `"${escaped.replaceAll('"', """)}"`;
case 17 /* WithinTagAttrDoubleQuote */:
return escaped.replaceAll('"', """);
case 18 /* WithinTagAttrSingleQuote */:
return escaped.replaceAll("'", "'");
case 0 /* Normal */:
return escaped;
}
}
function htmlStateMachine() {
let state = 0 /* Normal */;
let upcomingWithinTag = "";
const internalConsume = (next) => {
if (!next) {
return;
}
switch (state) {
case 3 /* TagAttr */: {
state = 2 /* WithinTag */;
return internalConsume(next);
}
case 17 /* WithinTagAttrDoubleQuote */:
case 18 /* WithinTagAttrSingleQuote */: {
const search = state === 17 /* WithinTagAttrDoubleQuote */ ? '"' : "'";
const escapeIndex = next.indexOf(search);
if (escapeIndex === -1) {
return;
}
state = 2 /* WithinTag */;
return internalConsume(next.substring(escapeIndex + 1));
}
case 2 /* WithinTag */: {
const m = withinTagNext.exec(next);
if (!m) {
return;
}
const inner = m[1];
const last = inner[inner.length - 1];
if (last === '"') {
state = 17 /* WithinTagAttrDoubleQuote */;
} else if (last === "'") {
state = 18 /* WithinTagAttrSingleQuote */;
} else if (inner[0] === "=") {
state = 3 /* TagAttr */;
} else {
switch (upcomingWithinTag) {
case "script":
state = 10 /* WithinScriptTag */;
break;
case "style":
state = 11 /* WithinStyleTag */;
break;
case "textarea":
state = 12 /* WithinTextAreaTag */;
break;
default:
state = 0 /* Normal */;
}
upcomingWithinTag = "";
}
return internalConsume(next.substring(m.index + m[1].length));
}
case 0 /* Normal */: {
const m = nextRe.exec(next);
if (!m) {
return;
}
if (m[1] === "!--") {
state = 9 /* WithinComment */;
return internalConsume(next.substring(m.index));
}
upcomingWithinTag = m[1];
state = 2 /* WithinTag */;
return internalConsume(next.substring(m.index + m[1].length));
}
case 9 /* WithinComment */:
case 10 /* WithinScriptTag */:
case 11 /* WithinStyleTag */:
case 12 /* WithinTextAreaTag */: {
const index = indexOfCloserWithinTagLike(state, next);
if (index === -1) {
return;
}
state = 0 /* Normal */;
next = next.substring(index);
return internalConsume(next);
}
default: {
const x = state;
throw new Error(`unhandled state: ${state}`);
}
}
};
return {
consume(next) {
internalConsume(next);
return state;
}
};
}
var preprocessCache = /* @__PURE__ */ new WeakMap();
function preprocessHtmlTemplateTag(arr) {
const prev = preprocessCache.get(arr);
if (prev) {
return prev;
}
const sm = htmlStateMachine();
const states = [];
for (let i = 0; ; ++i) {
if (i + 1 === arr.length) {
break;
}
const state = sm.consume(arr[i]);
states.push(state);
}
const f = Object.freeze(states);
preprocessCache.set(arr, f);
return f;
}
// src/intermediate.ts
function buildAsyncIntermediate() {
let wakeup = () => {
};
const pending = [];
let done = false;
const gen = async function* () {
for (; ; ) {
const next = pending.shift();
if (next === void 0) {
await new Promise((r) => wakeup = r);
continue;
}
if (next.done) {
next.resolve();
return next.value;
}
yield next.value;
next.resolve();
}
}();
const send = (value) => {
if (done) {
throw new Error(`Cannot send to stopped AsyncIntermediate`);
}
return new Promise((resolve) => {
pending.push({ done: false, value, resolve });
wakeup();
});
};
const stop = (value) => {
if (done) {
throw new Error(`Cannot stop already stopped AsyncIntermediate`);
}
done = true;
return new Promise((resolve) => {
pending.push({ done: true, value, resolve });
wakeup();
});
};
return { gen, send, stop };
}
// src/listener.ts
function namedListeners() {
const listeners = /* @__PURE__ */ new Map();
const ensureListener = (type) => {
let s = listeners.get(type);
if (s === void 0) {
s = {
listeners: /* @__PURE__ */ new Set(),
any: /* @__PURE__ */ new Set(),
activeSignal: abortedSignal,
abort: () => {
}
};
listeners.set(type, s);
}
return s;
};
return {
addListener(name, listener, signal) {
if (signal.aborted) {
return;
}
const state = ensureListener(name);
if (state.listeners.has(listener)) {
const original = listener;
listener = (arg) => original(arg);
} else if (state.listeners.size === 0) {
const c = new AbortController();
state.activeSignal = c.signal;
state.abort = () => c.abort();
state.any.forEach((l) => l(state.activeSignal));
}
signal.addEventListener("abort", () => {
state.listeners.delete(listener);
if (state.listeners.size === 0) {
state.abort();
}
});
state.listeners.add(listener);
},
any(name, callback, signal) {
if (signal?.aborted) {
return;
}
const state = ensureListener(name);
if (state.any.has(callback)) {
const original = callback;
callback = (arg) => original(arg);
}
state.any.add(callback);
signal?.addEventListener("abort", () => state.any.delete(callback));
if (state.listeners.size) {
callback(derivedSignal(state.activeSignal, signal).signal);
}
},
hasAny(name) {
return listeners.has(name);
},
// @ts-expect-error TS is confused because it thinks we should use `...arg`
dispatch(name, arg) {
const s = listeners.get(name);
s?.listeners.forEach((l) => l(arg));
return Boolean(s?.listeners.size);
},
eventTarget(arg) {
return namedListenersToEventTarget(this, arg);
}
};
}
function soloListenerFrom(name, nl) {
return {
addListener(listener, signal) {
nl.addListener(name, listener, signal);
},
dispatch(v) {
return nl.dispatch(name, v);
},
any(callback, signal) {
nl.any(name, callback, signal);
},
hasAny() {
return nl.hasAny(name);
}
};
}
function soloListener() {
const nl = namedListeners();
return soloListenerFrom("", nl);
}
function coeerceOptions(options) {
if (typeof options === "object") {
return { once: options.once, signal: options.signal };
}
return { once: false, signal: void 0 };
}
function buildForRef(overallSignal, callback, options) {
let { once: once2, signal } = coeerceOptions(options);
let fn;
if ("handleEvent" in callback) {
fn = (e) => callback.handleEvent(e);
} else {
fn = callback;
}
let abort;
if (once2) {
const c = new AbortController();
const originalFn = fn;
fn = (e) => {
c.abort();
originalFn(e);
};
({ signal, abort } = derivedSignal(c.signal, overallSignal, signal));
} else if (!signal && !overallSignal) {
signal = neverAbortedSignal;
abort = () => {
};
} else {
({ signal, abort } = derivedSignal(signal, overallSignal));
}
return { ref: callback, signal, fn, abort };
}
function namedListenersToEventTarget(nl, opts) {
const addedByRef = /* @__PURE__ */ new Map();
const addByRef = (type, ref, signal, abort) => {
if (signal.aborted) {
return false;
}
let forType = addedByRef.get(type);
if (forType === void 0) {
forType = /* @__PURE__ */ new Map();
addedByRef.set(type, forType);
}
if (forType.has(ref)) {
return false;
}
forType.set(ref, abort);
signal.addEventListener("abort", () => forType.delete(ref));
return true;
};
const { signal: overallSignal, buildEvent } = opts ?? {};
return {
addEventListener(type, callback, options) {
if (!callback) {
return;
}
const { ref, signal, abort, fn } = buildForRef(overallSignal, callback, options);
if (!addByRef(type, ref, signal, abort)) {
return;
}
const boundFn = (raw) => {
const e = buildEvent?.(type, raw) || new CustomEvent(type, { detail: raw });
if (!(e instanceof Event) || e.type !== type) {
throw new Error(
`buildEvent must return Event with type=${type}, ${e instanceof Event ? e.type : "?"}`
);
}
fn.call(this, e);
};
nl.addListener(type, boundFn, signal);
},
dispatchEvent(event) {
throw new Error("dispatchEvent unsupported");
},
removeEventListener(type, callback, options) {
if (!callback) {
return;
}
const prev = addedByRef.get(type)?.get(callback);
prev?.();
}
};
}
// src/object-utils.ts
var matchAny = /* @__PURE__ */ Symbol("matchAny");
function matchPartial(filter, object) {
if (object === void 0) {
return false;
} else if (object === filter || filter === matchAny) {
return true;
} else if (typeof object === "object" && object) {
for (const key in filter) {
if (!matchPartial(filter[key], object[key])) {
return false;
}
}
return true;
} else {
return false;
}
}
function readMatchAny(filter, object) {
if (filter === matchAny) {
return [object];
} else if (typeof filter === "object" && filter) {
let agg;
const traverseInto = typeof object === "object" || object === void 0 ? object : void 0;
for (const key in filte