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.

517 lines (511 loc) 14.1 kB
// 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(runner, callback, options) { const { immediate, signal } = options ?? {}; let activePromise; const o = () => { if (activePromise === void 0) { const pr = promiseWithResolvers(); runner(() => { 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/support/browser.ts function isArrayEqualIsh(val1, val2) { if (val1 === val2) { return true; } if (!Array.isArray(val1) || !Array.isArray(val2) || val1.length !== val2.length) { return false; } for (let i = 0; i < val1.length; ++i) { if (val1[i] !== val2[i]) { return false; } } return true; } function base64UrlToBytes(s) { if ("fromBase64" in Uint8Array) { return Uint8Array.fromBase64(s, { alphabet: "base64url" }); } const sb = atob(s.replaceAll("-", "+").replaceAll("_", "/")); const out = new Uint8Array(sb.length); for (let i = 0; i < out.length; ++i) { out[i] = sb.charCodeAt(i); } return out; } function base64UrlToString(s) { return new TextDecoder("utf-8").decode(base64UrlToBytes(s)); } function toBase64Url(s) { if (typeof s === "string") { s = new TextEncoder().encode(s); } if ("toBase64" in s) { return s.toBase64({ alphabet: "base64url", omitPadding: true }); } const bs = String.fromCodePoint(...s); return btoa(bs).replace(/=+$/, "").replaceAll("+", "-").replaceAll("/", "_"); } function concatBytes(chunks) { chunks = chunks.filter((chunk) => chunk.length !== 0); if (chunks.length === 0) { return new Uint8Array(); } else if (chunks.length === 1) { return chunks[0]; } let size = 0; chunks.forEach((chunk) => size += chunk.length); const out = new Uint8Array(size); let at = 0; chunks.forEach((chunk) => { out.set(chunk, at); at += chunk.length; }); return out; } // src/support/index.ts var isArrayEqualIsh2 = isArrayEqualIsh; 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 escapeHtmlEntites = (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 = escapeHtmlEntites(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; } export { unresolvedPromise, resolvedPromise, wrapTrigger, timeout, promiseWithResolvers, resolvable, promiseForEvent, spliceNextPromise, buildCallTrain, rafRunner, fastFrameRunner, tickRunner, afterSignal, promiseVoidForSignal, promiseForSignal, abortSignalTimeout, tickAbortSignal, derivedSignal, abortedSignal, neverAbortedSignal, todoSignal, isArrayEqualIsh, base64UrlToBytes, base64UrlToString, toBase64Url, concatBytes, isArrayEqualIsh2, structuredIshClone, HtmlState, indexOfCloserWithinTagLike, escapeStringFor, htmlStateMachine, preprocessHtmlTemplateTag }; //# sourceMappingURL=chunk-FJEQHTCW.js.map