UNPKG

@fedify/fedify

Version:

An ActivityPub server framework

1,014 lines (1,013 loc) • 30.2 kB
import "@js-temporal/polyfill"; import "urlpattern-polyfill"; globalThis.addEventListener = () => {}; //#region ../../node_modules/.pnpm/@jsr+std__assert@0.226.0/node_modules/@jsr/std__assert/assertion_error.js /** * Error thrown when an assertion fails. * * @example Usage * ```ts no-eval * import { AssertionError } from "@std/assert/assertion-error"; * * throw new AssertionError("Assertion failed"); * ``` */ var AssertionError = class extends Error { /** Constructs a new instance. * * @example Usage * ```ts no-eval * import { AssertionError } from "@std/assert/assertion-error"; * * throw new AssertionError("Assertion failed"); * ``` * * @param message The error message. */ constructor(message) { super(message); this.name = "AssertionError"; } }; //#endregion //#region ../../node_modules/.pnpm/@jsr+std__assert@0.226.0/node_modules/@jsr/std__assert/equal.js function isKeyedCollection(x) { return [Symbol.iterator, "size"].every((k) => k in x); } function constructorsEqual(a, b) { return a.constructor === b.constructor || a.constructor === Object && !b.constructor || !a.constructor && b.constructor === Object; } /** * Deep equality comparison used in assertions * * @param c The actual value * @param d The expected value * @returns `true` if the values are deeply equal, `false` otherwise * * @example Usage * ```ts * import { equal } from "@std/assert/equal"; * * equal({ foo: "bar" }, { foo: "bar" }); // Returns `true` * equal({ foo: "bar" }, { foo: "baz" }); // Returns `false * ``` */ function equal(c, d) { const seen = /* @__PURE__ */ new Map(); return function compare(a, b) { if (a && b && (a instanceof RegExp && b instanceof RegExp || a instanceof URL && b instanceof URL)) return String(a) === String(b); if (a instanceof Date && b instanceof Date) { const aTime = a.getTime(); const bTime = b.getTime(); if (Number.isNaN(aTime) && Number.isNaN(bTime)) return true; return aTime === bTime; } if (typeof a === "number" && typeof b === "number") return Number.isNaN(a) && Number.isNaN(b) || a === b; if (Object.is(a, b)) return true; if (a && typeof a === "object" && b && typeof b === "object") { if (a && b && !constructorsEqual(a, b)) return false; if (a instanceof WeakMap || b instanceof WeakMap) { if (!(a instanceof WeakMap && b instanceof WeakMap)) return false; throw new TypeError("cannot compare WeakMap instances"); } if (a instanceof WeakSet || b instanceof WeakSet) { if (!(a instanceof WeakSet && b instanceof WeakSet)) return false; throw new TypeError("cannot compare WeakSet instances"); } if (a instanceof WeakRef || b instanceof WeakRef) { if (!(a instanceof WeakRef && b instanceof WeakRef)) return false; return compare(a.deref(), b.deref()); } if (seen.get(a) === b) return true; if (Object.keys(a).length !== Object.keys(b).length) return false; seen.set(a, b); if (isKeyedCollection(a) && isKeyedCollection(b)) { if (a.size !== b.size) return false; let unmatchedEntries = a.size; for (const [aKey, aValue] of a.entries()) for (const [bKey, bValue] of b.entries()) if (aKey === aValue && bKey === bValue && compare(aKey, bKey) || compare(aKey, bKey) && compare(aValue, bValue)) { unmatchedEntries--; break; } return unmatchedEntries === 0; } const merged = { ...a, ...b }; for (const key of [...Object.getOwnPropertyNames(merged), ...Object.getOwnPropertySymbols(merged)]) { if (!compare(a && a[key], b && b[key])) return false; if (key in a && !(key in b) || key in b && !(key in a)) return false; } return true; } return false; }(c, d); } //#endregion //#region ../../node_modules/.pnpm/@jsr+std__internal@1.0.8/node_modules/@jsr/std__internal/format.js /** * Converts the input into a string. Objects, Sets and Maps are sorted so as to * make tests less flaky. * * @param v Value to be formatted * * @returns The formatted string * * @example Usage * ```ts * import { format } from "@std/internal/format"; * import { assertEquals } from "@std/assert"; * * assertEquals(format({ a: 1, b: 2 }), "{\n a: 1,\n b: 2,\n}"); * assertEquals(format(new Set([1, 2])), "Set(2) {\n 1,\n 2,\n}"); * assertEquals(format(new Map([[1, 2]])), "Map(1) {\n 1 => 2,\n}"); * ``` */ function format(v) { const { Deno } = globalThis; return typeof Deno?.inspect === "function" ? Deno.inspect(v, { depth: Infinity, sorted: true, trailingComma: true, compact: false, iterableLimit: Infinity, getters: true, strAbbreviateSize: Infinity }) : `"${String(v).replace(/(?=["\\])/g, "\\")}"`; } //#endregion //#region ../../node_modules/.pnpm/@jsr+std__internal@1.0.8/node_modules/@jsr/std__internal/assertion_state.js /** * Check the test suite internal state * * @example Usage * ```ts ignore * import { AssertionState } from "@std/internal"; * * const assertionState = new AssertionState(); * ``` */ var AssertionState = class { #state; constructor() { this.#state = { assertionCount: void 0, assertionCheck: false, assertionTriggered: false, assertionTriggeredCount: 0 }; if (typeof globalThis?.addEventListener === "function") globalThis.addEventListener("unload", () => { this.#ensureCleanedUp(); }); else if (typeof globalThis?.process?.on === "function") globalThis.process.on("exit", () => { this.#ensureCleanedUp(); }); else console.warn("AssertionCounter cleanup step was not registered"); } #ensureCleanedUp() { if (this.#state.assertionCheck || this.#state.assertionCount !== void 0) throw new Error("AssertionCounter was not cleaned up: If tests are not otherwise failing, ensure `expect.hasAssertion` and `expect.assertions` are only run in bdd tests"); } /** * Get the number that through `expect.assertions` api set. * * @returns the number that through `expect.assertions` api set. * * @example Usage * ```ts ignore * import { AssertionState } from "@std/internal"; * * const assertionState = new AssertionState(); * assertionState.assertionCount; * ``` */ get assertionCount() { return this.#state.assertionCount; } /** * Get a certain number that assertions were called before. * * @returns return a certain number that assertions were called before. * * @example Usage * ```ts ignore * import { AssertionState } from "@std/internal"; * * const assertionState = new AssertionState(); * assertionState.assertionTriggeredCount; * ``` */ get assertionTriggeredCount() { return this.#state.assertionTriggeredCount; } /** * If `expect.hasAssertions` called, then through this method to update #state.assertionCheck value. * * @param val Set #state.assertionCheck's value * * @example Usage * ```ts ignore * import { AssertionState } from "@std/internal"; * * const assertionState = new AssertionState(); * assertionState.setAssertionCheck(true); * ``` */ setAssertionCheck(val) { this.#state.assertionCheck = val; } /** * If any matchers was called, `#state.assertionTriggered` will be set through this method. * * @param val Set #state.assertionTriggered's value * * @example Usage * ```ts ignore * import { AssertionState } from "@std/internal"; * * const assertionState = new AssertionState(); * assertionState.setAssertionTriggered(true); * ``` */ setAssertionTriggered(val) { this.#state.assertionTriggered = val; } /** * If `expect.assertions` called, then through this method to update #state.assertionCheck value. * * @param num Set #state.assertionCount's value, for example if the value is set 2, that means * you must have two assertion matchers call in your test suite. * * @example Usage * ```ts ignore * import { AssertionState } from "@std/internal"; * * const assertionState = new AssertionState(); * assertionState.setAssertionCount(2); * ``` */ setAssertionCount(num) { this.#state.assertionCount = num; } /** * If any matchers was called, `#state.assertionTriggeredCount` value will plus one internally. * * @example Usage * ```ts ignore * import { AssertionState } from "@std/internal"; * * const assertionState = new AssertionState(); * assertionState.updateAssertionTriggerCount(); * ``` */ updateAssertionTriggerCount() { if (this.#state.assertionCount !== void 0) this.#state.assertionTriggeredCount += 1; } /** * Check Assertion internal state, if `#state.assertionCheck` is set true, but * `#state.assertionTriggered` is still false, then should throw an Assertion Error. * * @returns a boolean value, that the test suite is satisfied with the check. If not, * it should throw an AssertionError. * * @example Usage * ```ts ignore * import { AssertionState } from "@std/internal"; * * const assertionState = new AssertionState(); * if (assertionState.checkAssertionErrorState()) { * // throw AssertionError(""); * } * ``` */ checkAssertionErrorState() { return this.#state.assertionCheck && !this.#state.assertionTriggered; } /** * Reset all assertion state when every test suite function ran completely. * * @example Usage * ```ts ignore * import { AssertionState } from "@std/internal"; * * const assertionState = new AssertionState(); * assertionState.resetAssertionState(); * ``` */ resetAssertionState() { this.#state = { assertionCount: void 0, assertionCheck: false, assertionTriggered: false, assertionTriggeredCount: 0 }; } /** * Check Assertion called state, if `#state.assertionCount` is set to a number value, but * `#state.assertionTriggeredCount` is less then it, then should throw an assertion error. * * @returns a boolean value, that the test suite is satisfied with the check. If not, * it should throw an AssertionError. * * @example Usage * ```ts ignore * import { AssertionState } from "@std/internal"; * * const assertionState = new AssertionState(); * if (assertionState.checkAssertionCountSatisfied()) { * // throw AssertionError(""); * } * ``` */ checkAssertionCountSatisfied() { return this.#state.assertionCount !== void 0 && this.#state.assertionCount !== this.#state.assertionTriggeredCount; } }; new AssertionState(); //#endregion //#region ../../node_modules/.pnpm/@jsr+std__internal@1.0.8/node_modules/@jsr/std__internal/styles.js const { Deno } = globalThis; const enabled = !(typeof Deno?.noColor === "boolean" ? Deno.noColor : false); function code(open, close) { return { open: `\x1b[${open.join(";")}m`, close: `\x1b[${close}m`, regexp: new RegExp(`\\x1b\\[${close}m`, "g") }; } function run(str, code) { return enabled ? `${code.open}${str.replace(code.regexp, code.open)}${code.close}` : str; } /** * Sets the style of text to be printed to bold. * * Disable by setting the `NO_COLOR` environmental variable. * * @param str Text to make bold * * @returns Bold text for printing * * @example Usage * ```ts no-assert * import { bold } from "@std/internal/styles"; * * console.log(bold("Hello, world!")); // Prints "Hello, world!" in bold * ``` */ function bold(str) { return run(str, code([1], 22)); } /** * Sets the color of text to be printed to red. * * Disable by setting the `NO_COLOR` environmental variable. * * @param str Text to make red * * @returns Red text for printing * * @example Usage * ```ts no-assert * import { red } from "@std/internal/styles"; * * console.log(red("Hello, world!")); // Prints "Hello, world!" in red * ``` */ function red(str) { return run(str, code([31], 39)); } /** * Sets the color of text to be printed to green. * * Disable by setting the `NO_COLOR` environmental variable. * * @param str Text to make green * * @returns Green text for print * * @example Usage * ```ts no-assert * import { green } from "@std/internal/styles"; * * console.log(green("Hello, world!")); // Prints "Hello, world!" in green * ``` */ function green(str) { return run(str, code([32], 39)); } /** * Sets the color of text to be printed to white. * * @param str Text to make white * * @returns White text for print * * @example Usage * ```ts no-assert * import { white } from "@std/internal/styles"; * * console.log(white("Hello, world!")); // Prints "Hello, world!" in white * ``` */ function white(str) { return run(str, code([37], 39)); } /** * Sets the color of text to be printed to gray. * * @param str Text to make gray * * @returns Gray text for print * * @example Usage * ```ts no-assert * import { gray } from "@std/internal/styles"; * * console.log(gray("Hello, world!")); // Prints "Hello, world!" in gray * ``` */ function gray(str) { return brightBlack(str); } /** * Sets the color of text to be printed to bright-black. * * @param str Text to make bright-black * * @returns Bright-black text for print * * @example Usage * ```ts no-assert * import { brightBlack } from "@std/internal/styles"; * * console.log(brightBlack("Hello, world!")); // Prints "Hello, world!" in bright-black * ``` */ function brightBlack(str) { return run(str, code([90], 39)); } /** * Sets the background color of text to be printed to red. * * @param str Text to make its background red * * @returns Red background text for print * * @example Usage * ```ts no-assert * import { bgRed } from "@std/internal/styles"; * * console.log(bgRed("Hello, world!")); // Prints "Hello, world!" with red background * ``` */ function bgRed(str) { return run(str, code([41], 49)); } /** * Sets the background color of text to be printed to green. * * @param str Text to make its background green * * @returns Green background text for print * * @example Usage * ```ts no-assert * import { bgGreen } from "@std/internal/styles"; * * console.log(bgGreen("Hello, world!")); // Prints "Hello, world!" with green background * ``` */ function bgGreen(str) { return run(str, code([42], 49)); } const ANSI_PATTERN = new RegExp(["[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)", "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TXZcf-nq-uy=><~]))"].join("|"), "g"); /** * Remove ANSI escape codes from the string. * * @param string Text to remove ANSI escape codes from * * @returns Text without ANSI escape codes * * @example Usage * ```ts no-assert * import { red, stripAnsiCode } from "@std/internal/styles"; * * console.log(stripAnsiCode(red("Hello, world!"))); // Prints "Hello, world!" * ``` */ function stripAnsiCode(string) { return string.replace(ANSI_PATTERN, ""); } //#endregion //#region ../../node_modules/.pnpm/@jsr+std__internal@1.0.8/node_modules/@jsr/std__internal/build_message.js /** * Colors the output of assertion diffs. * * @param diffType Difference type, either added or removed. * @param background If true, colors the background instead of the text. * * @returns A function that colors the input string. * * @example Usage * ```ts * import { createColor } from "@std/internal"; * import { assertEquals } from "@std/assert"; * import { bold, green, red, white } from "@std/fmt/colors"; * * assertEquals(createColor("added")("foo"), green(bold("foo"))); * assertEquals(createColor("removed")("foo"), red(bold("foo"))); * assertEquals(createColor("common")("foo"), white("foo")); * ``` */ function createColor(diffType, background = false) { switch (diffType) { case "added": return (s) => background ? bgGreen(white(s)) : green(bold(s)); case "removed": return (s) => background ? bgRed(white(s)) : red(bold(s)); default: return white; } } /** * Prefixes `+` or `-` in diff output. * * @param diffType Difference type, either added or removed * * @returns A string representing the sign. * * @example Usage * ```ts * import { createSign } from "@std/internal"; * import { assertEquals } from "@std/assert"; * * assertEquals(createSign("added"), "+ "); * assertEquals(createSign("removed"), "- "); * assertEquals(createSign("common"), " "); * ``` */ function createSign(diffType) { switch (diffType) { case "added": return "+ "; case "removed": return "- "; default: return " "; } } /** * Builds a message based on the provided diff result. * * @param diffResult The diff result array. * @param options Optional parameters for customizing the message. * * @returns An array of strings representing the built message. * * @example Usage * ```ts no-assert * import { diffStr, buildMessage } from "@std/internal"; * * const diffResult = diffStr("Hello, world!", "Hello, world"); * * console.log(buildMessage(diffResult)); * // [ * // "", * // "", * // " [Diff] Actual / Expected", * // "", * // "", * // "- Hello, world!", * // "+ Hello, world", * // "", * // ] * ``` */ function buildMessage(diffResult, options = {}) { const { stringDiff = false } = options; const messages = [ "", "", ` ${gray(bold("[Diff]"))} ${red(bold("Actual"))} / ${green(bold("Expected"))}`, "", "" ]; const diffMessages = diffResult.map((result) => { const color = createColor(result.type); const line = result.details?.map((detail) => detail.type !== "common" ? createColor(detail.type, true)(detail.value) : detail.value).join("") ?? result.value; return color(`${createSign(result.type)}${line}`); }); messages.push(...stringDiff ? [diffMessages.join("")] : diffMessages, ""); return messages; } //#endregion //#region ../../node_modules/.pnpm/@jsr+std__internal@1.0.8/node_modules/@jsr/std__internal/diff.js const REMOVED = 1; const COMMON = 2; const ADDED = 3; /** * Creates an array of common elements between two arrays. * * @typeParam T The type of elements in the arrays. * * @param A The first array. * @param B The second array. * * @returns An array containing the common elements between the two arrays. * * @example Usage * ```ts * import { createCommon } from "@std/internal/diff"; * import { assertEquals } from "@std/assert"; * * const a = [1, 2, 3]; * const b = [1, 2, 4]; * * assertEquals(createCommon(a, b), [1, 2]); * ``` */ function createCommon(A, B) { const common = []; if (A.length === 0 || B.length === 0) return []; for (let i = 0; i < Math.min(A.length, B.length); i += 1) { const a = A[i]; const b = B[i]; if (a !== void 0 && a === b) common.push(a); else return common; } return common; } /** * Asserts that the value is a {@linkcode FarthestPoint}. * If not, an error is thrown. * * @param value The value to check. * * @returns A void value that returns once the assertion completes. * * @example Usage * ```ts * import { assertFp } from "@std/internal/diff"; * import { assertThrows } from "@std/assert"; * * assertFp({ y: 0, id: 0 }); * assertThrows(() => assertFp({ id: 0 })); * assertThrows(() => assertFp({ y: 0 })); * assertThrows(() => assertFp(undefined)); * ``` */ function assertFp(value) { if (value == null || typeof value !== "object" || typeof value?.y !== "number" || typeof value?.id !== "number") throw new Error(`Unexpected value, expected 'FarthestPoint': received ${typeof value}`); } /** * Creates an array of backtraced differences. * * @typeParam T The type of elements in the arrays. * * @param A The first array. * @param B The second array. * @param current The current {@linkcode FarthestPoint}. * @param swapped Boolean indicating if the arrays are swapped. * @param routes The routes array. * @param diffTypesPtrOffset The offset of the diff types in the routes array. * * @returns An array of backtraced differences. * * @example Usage * ```ts * import { backTrace } from "@std/internal/diff"; * import { assertEquals } from "@std/assert"; * * assertEquals( * backTrace([], [], { y: 0, id: 0 }, false, new Uint32Array(0), 0), * [], * ); * ``` */ function backTrace(A, B, current, swapped, routes, diffTypesPtrOffset) { const M = A.length; const N = B.length; const result = []; let a = M - 1; let b = N - 1; let j = routes[current.id]; let type = routes[current.id + diffTypesPtrOffset]; while (true) { if (!j && !type) break; const prev = j; if (type === REMOVED) { result.unshift({ type: swapped ? "removed" : "added", value: B[b] }); b -= 1; } else if (type === ADDED) { result.unshift({ type: swapped ? "added" : "removed", value: A[a] }); a -= 1; } else { result.unshift({ type: "common", value: A[a] }); a -= 1; b -= 1; } j = routes[prev]; type = routes[prev + diffTypesPtrOffset]; } return result; } /** * Creates a {@linkcode FarthestPoint}. * * @param k The current index. * @param M The length of the first array. * @param routes The routes array. * @param diffTypesPtrOffset The offset of the diff types in the routes array. * @param ptr The current pointer. * @param slide The slide {@linkcode FarthestPoint}. * @param down The down {@linkcode FarthestPoint}. * * @returns A {@linkcode FarthestPoint}. * * @example Usage * ```ts * import { createFp } from "@std/internal/diff"; * import { assertEquals } from "@std/assert"; * * assertEquals( * createFp( * 0, * 0, * new Uint32Array(0), * 0, * 0, * { y: -1, id: 0 }, * { y: 0, id: 0 }, * ), * { y: -1, id: 1 }, * ); * ``` */ function createFp(k, M, routes, diffTypesPtrOffset, ptr, slide, down) { if (slide && slide.y === -1 && down && down.y === -1) return { y: 0, id: 0 }; const isAdding = down?.y === -1 || k === M || (slide?.y ?? 0) > (down?.y ?? 0) + 1; if (slide && isAdding) { const prev = slide.id; ptr++; routes[ptr] = prev; routes[ptr + diffTypesPtrOffset] = ADDED; return { y: slide.y, id: ptr }; } if (down && !isAdding) { const prev = down.id; ptr++; routes[ptr] = prev; routes[ptr + diffTypesPtrOffset] = REMOVED; return { y: down.y + 1, id: ptr }; } throw new Error("Unexpected missing FarthestPoint"); } /** * Renders the differences between the actual and expected values. * * @typeParam T The type of elements in the arrays. * * @param A Actual value * @param B Expected value * * @returns An array of differences between the actual and expected values. * * @example Usage * ```ts * import { diff } from "@std/internal/diff"; * import { assertEquals } from "@std/assert"; * * const a = [1, 2, 3]; * const b = [1, 2, 4]; * * assertEquals(diff(a, b), [ * { type: "common", value: 1 }, * { type: "common", value: 2 }, * { type: "removed", value: 3 }, * { type: "added", value: 4 }, * ]); * ``` */ function diff(A, B) { const prefixCommon = createCommon(A, B); A = A.slice(prefixCommon.length); B = B.slice(prefixCommon.length); const swapped = B.length > A.length; [A, B] = swapped ? [B, A] : [A, B]; const M = A.length; const N = B.length; if (!M && !N && !prefixCommon.length) return []; if (!N) return [...prefixCommon.map((value) => ({ type: "common", value })), ...A.map((value) => ({ type: swapped ? "added" : "removed", value }))]; const offset = N; const delta = M - N; const length = M + N + 1; const fp = Array.from({ length }, () => ({ y: -1, id: -1 })); /** * Note: this buffer is used to save memory and improve performance. The first * half is used to save route and the last half is used to save diff type. */ const routes = new Uint32Array((M * N + length + 1) * 2); const diffTypesPtrOffset = routes.length / 2; let ptr = 0; function snake(k, A, B, slide, down) { const M = A.length; const N = B.length; const fp = createFp(k, M, routes, diffTypesPtrOffset, ptr, slide, down); ptr = fp.id; while (fp.y + k < M && fp.y < N && A[fp.y + k] === B[fp.y]) { const prev = fp.id; ptr++; fp.id = ptr; fp.y += 1; routes[ptr] = prev; routes[ptr + diffTypesPtrOffset] = COMMON; } return fp; } let currentFp = fp[delta + offset]; assertFp(currentFp); let p = -1; while (currentFp.y < N) { p = p + 1; for (let k = -p; k < delta; ++k) { const index = k + offset; fp[index] = snake(k, A, B, fp[index - 1], fp[index + 1]); } for (let k = delta + p; k > delta; --k) { const index = k + offset; fp[index] = snake(k, A, B, fp[index - 1], fp[index + 1]); } const index = delta + offset; fp[delta + offset] = snake(delta, A, B, fp[index - 1], fp[index + 1]); currentFp = fp[delta + offset]; assertFp(currentFp); } return [...prefixCommon.map((value) => ({ type: "common", value })), ...backTrace(A, B, currentFp, swapped, routes, diffTypesPtrOffset)]; } //#endregion //#region ../../node_modules/.pnpm/@jsr+std__internal@1.0.8/node_modules/@jsr/std__internal/diff_str.js /** * Unescape invisible characters. * * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String#escape_sequences} * * @param string String to unescape. * * @returns Unescaped string. * * @example Usage * ```ts * import { unescape } from "@std/internal/diff-str"; * import { assertEquals } from "@std/assert"; * * assertEquals(unescape("Hello\nWorld"), "Hello\\n\nWorld"); * ``` */ function unescape(string) { return string.replaceAll("\\", "\\\\").replaceAll("\b", "\\b").replaceAll("\f", "\\f").replaceAll(" ", "\\t").replaceAll("\v", "\\v").replaceAll(/\r\n|\r|\n/g, (str) => str === "\r" ? "\\r" : str === "\n" ? "\\n\n" : "\\r\\n\r\n"); } const WHITESPACE_SYMBOLS = /((?:\\[bftv]|[^\S\r\n])+|\\[rn\\]|[()[\]{}'"\r\n]|\b)/; /** * Tokenizes a string into an array of tokens. * * @param string The string to tokenize. * @param wordDiff If true, performs word-based tokenization. Default is false. * * @returns An array of tokens. * * @example Usage * ```ts * import { tokenize } from "@std/internal/diff-str"; * import { assertEquals } from "@std/assert"; * * assertEquals(tokenize("Hello\nWorld"), ["Hello\n", "World"]); * ``` */ function tokenize(string, wordDiff = false) { if (wordDiff) return string.split(WHITESPACE_SYMBOLS).filter((token) => token); const tokens = []; const lines = string.split(/(\n|\r\n)/).filter((line) => line); for (const [i, line] of lines.entries()) if (i % 2) tokens[tokens.length - 1] += line; else tokens.push(line); return tokens; } /** * Create details by filtering relevant word-diff for current line and merge * "space-diff" if surrounded by word-diff for cleaner displays. * * @param line Current line * @param tokens Word-diff tokens * * @returns Array of diff results. * * @example Usage * ```ts * import { createDetails } from "@std/internal/diff-str"; * import { assertEquals } from "@std/assert"; * * const tokens = [ * { type: "added", value: "a" }, * { type: "removed", value: "b" }, * { type: "common", value: "c" }, * ] as const; * assertEquals( * createDetails({ type: "added", value: "a" }, [...tokens]), * [{ type: "added", value: "a" }, { type: "common", value: "c" }] * ); * ``` */ function createDetails(line, tokens) { return tokens.filter(({ type }) => type === line.type || type === "common").map((result, i, t) => { const token = t[i - 1]; if (result.type === "common" && token && token.type === t[i + 1]?.type && /\s+/.test(result.value)) return { ...result, type: token.type }; return result; }); } const NON_WHITESPACE_REGEXP = /\S/; /** * Renders the differences between the actual and expected strings. Partially * inspired from {@link https://github.com/kpdecker/jsdiff}. * * @param A Actual string * @param B Expected string * * @returns Array of diff results. * * @example Usage * ```ts * import { diffStr } from "@std/internal/diff-str"; * import { assertEquals } from "@std/assert"; * * assertEquals(diffStr("Hello!", "Hello"), [ * { * type: "removed", * value: "Hello!\n", * details: [ * { type: "common", value: "Hello" }, * { type: "removed", value: "!" }, * { type: "common", value: "\n" } * ] * }, * { * type: "added", * value: "Hello\n", * details: [ * { type: "common", value: "Hello" }, * { type: "common", value: "\n" } * ] * } * ]); * ``` */ function diffStr(A, B) { const diffResult = diff(tokenize(`${unescape(A)}\n`), tokenize(`${unescape(B)}\n`)); const added = []; const removed = []; for (const result of diffResult) { if (result.type === "added") added.push(result); if (result.type === "removed") removed.push(result); } const hasMoreRemovedLines = added.length < removed.length; const aLines = hasMoreRemovedLines ? added : removed; const bLines = hasMoreRemovedLines ? removed : added; for (const a of aLines) { let tokens = []; let b; while (bLines.length) { b = bLines.shift(); const tokenized = [tokenize(a.value, true), tokenize(b.value, true)]; if (hasMoreRemovedLines) tokenized.reverse(); tokens = diff(tokenized[0], tokenized[1]); if (tokens.some(({ type, value }) => type === "common" && NON_WHITESPACE_REGEXP.test(value))) break; } a.details = createDetails(a, tokens); if (b) b.details = createDetails(b, tokens); } return diffResult; } //#endregion //#region ../../node_modules/.pnpm/@jsr+std__assert@0.226.0/node_modules/@jsr/std__assert/assert_equals.js /** * Make an assertion that `actual` and `expected` are equal, deeply. If not * deeply equal, then throw. * * Type parameter can be specified to ensure values under comparison have the * same type. * * @example Usage * ```ts no-eval * import { assertEquals } from "@std/assert/assert-equals"; * * assertEquals("world", "world"); // Doesn't throw * assertEquals("hello", "world"); // Throws * ``` * * Note: formatter option is experimental and may be removed in the future. * * @typeParam T The type of the values to compare. This is usually inferred. * @param actual The actual value to compare. * @param expected The expected value to compare. * @param msg The optional message to display if the assertion fails. * @param options The optional object for the assertion. */ function assertEquals(actual, expected, msg) { if (equal(actual, expected)) return; let message = `Values are not equal${msg ? `: ${msg}` : "."}`; const actualString = format(actual); const expectedString = format(expected); const stringDiff = typeof actual === "string" && typeof expected === "string"; const diffMsg = buildMessage(stringDiff ? diffStr(actual, expected) : diff(actualString.split("\n"), expectedString.split("\n")), { stringDiff }).join("\n"); message = `${message}\n${diffMsg}`; throw new AssertionError(message); } //#endregion export { red as a, equal as c, buildMessage as i, AssertionError as l, diffStr as n, stripAnsiCode as o, diff as r, format as s, assertEquals as t };