UNPKG

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
// 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("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;"); } // 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("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;"); }; // 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('"', "&quot;")}"`; case 17 /* WithinTagAttrDoubleQuote */: return escaped.replaceAll('"', "&quot;"); case 18 /* WithinTagAttrSingleQuote */: return escaped.replaceAll("'", "&apos;"); 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