diff --git a/.well-known/funding-manifest-urls b/.well-known/funding-manifest-urls index 42647b8..8cd6afd 100644 --- a/.well-known/funding-manifest-urls +++ b/.well-known/funding-manifest-urls @@ -1 +1,2 @@ https://oss.codercops.com/funding.json +https://raw.githubusercontent.com/codercops/.github/main/funding.json diff --git a/app/(tools)/markdown-preview/MarkdownPreviewClient.test.tsx b/app/(tools)/markdown-preview/MarkdownPreviewClient.test.tsx index 98dc1d6..33e7b87 100644 --- a/app/(tools)/markdown-preview/MarkdownPreviewClient.test.tsx +++ b/app/(tools)/markdown-preview/MarkdownPreviewClient.test.tsx @@ -68,4 +68,18 @@ describe("MarkdownPreviewClient", () => { expect(preview?.querySelector("[onerror]")).toBeNull(); expect(preview?.querySelector('a[href^="javascript:"]')).toBeNull(); }); + + it.each([ + ["H2", "My title", "## My title"], + ["Task", "Shopping list", "- [ ] Shopping list"], + ])("%s turns the line under the cursor into its block", (label, text, expected) => { + render(); + const editor = screen.getByLabelText("Markdown input") as HTMLTextAreaElement; + fireEvent.change(editor, { target: { value: text } }); + editor.setSelectionRange(text.length, text.length); + + fireEvent.click(screen.getByRole("button", { name: label })); + + expect(editor).toHaveValue(expected); + }); }); diff --git a/app/(tools)/markdown-preview/MarkdownPreviewClient.tsx b/app/(tools)/markdown-preview/MarkdownPreviewClient.tsx index fb11b17..d17eaf6 100644 --- a/app/(tools)/markdown-preview/MarkdownPreviewClient.tsx +++ b/app/(tools)/markdown-preview/MarkdownPreviewClient.tsx @@ -3,7 +3,7 @@ import { useMemo, useRef, useState, useSyncExternalStore } from "react"; import DOMPurify from "dompurify"; import { CopyButton } from "@/components/shared/CopyButton"; -import { createStandaloneHtml, getMarkdownStats, insertMarkdown, insertTableMarkdown, renderMarkdown } from "@/lib/markdown-preview"; +import { createStandaloneHtml, getMarkdownStats, insertLinePrefix, insertMarkdown, insertTableMarkdown, renderMarkdown } from "@/lib/markdown-preview"; const starter = `# Hello, Markdown!\n\nWrite on the left and see **GitHub-flavored Markdown** rendered here.\n\n- [x] Live preview\n- [ ] Copy sanitized HTML\n\n| Feature | Status |\n| --- | --- |\n| Tables | Ready |\n| Code | \`const answer = 42\` |\n\n~~~ts\nconst greeting = "Hello, world!";\n~~~`; const MAX_IMPORT_BYTES = 1024 * 1024; @@ -42,11 +42,17 @@ export function MarkdownPreviewClient() { const { words, chars, lines, readingMinutes } = getMarkdownStats(markdown); function insert(before: string, after = before, placeholder = "text") { + apply((start, end) => insertMarkdown(markdown, start, end, before, after, placeholder)); + } + + function insertLine(prefix: string, placeholder = "text") { + apply((start, end) => insertLinePrefix(markdown, start, end, prefix, placeholder)); + } + + function apply(edit: (start: number, end: number) => ReturnType) { const input = textareaRef.current; if (!input) return; - const start = input.selectionStart; - const end = input.selectionEnd; - const result = insertMarkdown(markdown, start, end, before, after, placeholder); + const result = edit(input.selectionStart, input.selectionEnd); setMarkdown(result.value); requestAnimationFrame(() => { input.focus(); input.setSelectionRange(result.selectionStart, result.selectionEnd); }); } @@ -101,10 +107,11 @@ export function MarkdownPreviewClient() { return
- {[['Bold','**','**'],['Italic','*','*'],['Code','`','`'],['Link','[','](https://example.com)'],['H2','## ','']].map(([label, before, after]) => )} + {[['Bold','**','**'],['Italic','*','*'],['Code','`','`'],['Link','[','](https://example.com)']].map(([label, before, after]) => )} + - + diff --git a/app/guides/make-a-table-in-markdown/page.tsx b/app/guides/make-a-table-in-markdown/page.tsx index 282ec13..b8a9ea5 100644 --- a/app/guides/make-a-table-in-markdown/page.tsx +++ b/app/guides/make-a-table-in-markdown/page.tsx @@ -186,7 +186,7 @@ export default function MakeATableInMarkdownGuidePage() {
  • A single line break becomes a <br>. The preview is set to turn every newline inside a paragraph into a line break. GitHub does the same in issues, pull requests, comments and discussions, but not in a .md file such as a README, where a single newline does not break the line. So text that looks right in the preview can run together in a README. To force a break in a .md file, end the line with two spaces or a backslash, or use <br>.
  • The HTML is sanitized. Scripts and inline event handlers such as onerror are removed before the preview is shown or the HTML is copied.
  • -
  • There is a toolbar. Bold, Italic, Code, Link, H2, Image, Table and Task add the syntax at the cursor or around the selected text.
  • +
  • There is a toolbar. Bold, Italic, Code, Link, Image and Table add the syntax at the cursor or around the selected text, and H2 and Task turn the current line into a heading or a task.
  • You can take the result with you. Copy HTML copies the sanitized HTML, Copy Markdown copies the source, and Download .md saves it. A switch changes the preview between light and dark, and the bar below shows the word, character and line counts and the reading time.
  • Everything runs in your browser. Nothing you type is uploaded.
diff --git a/lib/__tests__/csv-utils.test.ts b/lib/__tests__/csv-utils.test.ts index 997e63b..8b10dc2 100644 --- a/lib/__tests__/csv-utils.test.ts +++ b/lib/__tests__/csv-utils.test.ts @@ -1,3 +1,4 @@ +import ts from "typescript"; import { describe, expect, it } from "vitest"; import { csvToJson, @@ -148,6 +149,12 @@ describe("uniqueHeaders", () => { it("suffixes duplicates and fills empty names", () => { expect(uniqueHeaders(["a", "a", "", "b", "a"])).toEqual(["a", "a_2", "column_3", "b", "a_3"]); }); + + it("skips a suffix that another column already uses", () => { + expect(uniqueHeaders(["id", "id", "id_2"])).toEqual(["id", "id_3", "id_2"]); + expect(uniqueHeaders(["a", "", "column_2"])).toEqual(["a", "column_2_2", "column_2"]); + expect(csvToJson(["id", "id", "id_2"], [["1", "2", "3"]])).toEqual([{ id: "1", id_3: "2", id_2: "3" }]); + }); }); describe("formatCsv", () => { @@ -206,6 +213,77 @@ describe("csvToTypeScript", () => { it("handles no columns", () => { expect(csvToTypeScript([])).toBe("export interface Row {}"); }); + + describe("the type name", () => { + // Words TypeScript will not take as the name of an interface: reserved words, the ones reserved in strict mode + // (which a module always is), `await`, and the names of the built-in types. + const reserved = [ + "break", "case", "catch", "class", "const", "continue", "debugger", "default", "delete", "do", "else", "enum", + "export", "extends", "false", "finally", "for", "function", "if", "import", "in", "instanceof", "new", "null", + "return", "super", "switch", "this", "throw", "true", "try", "typeof", "var", "void", "while", "with", + "implements", "interface", "let", "package", "private", "protected", "public", "static", "yield", "await", + "any", "unknown", "never", "number", "bigint", "boolean", "string", "symbol", "object", "undefined", + ]; + // Words that look like they might be, and are fine. + const allowed = [ + "Class", "Default", "String", "Number", "Object", "Array", "Date", "Row", "Person", "type", "of", "as", "async", + "from", "get", "set", "constructor", "declare", "module", "namespace", "readonly", "keyof", "abstract", "is", + "global", "require", "infer", "asserts", "arguments", "eval", "_", "$", "class_", "$class", "classy", + ]; + + /** The diagnostics the TypeScript compiler gives each source text, all checked in one program. */ + function diagnose(texts: string[]): string[][] { + const files = new Map(texts.map((text, index) => [`/probe/f${index}.ts`, text])); + const options: ts.CompilerOptions = { noEmit: true, strict: true, skipLibCheck: true, target: ts.ScriptTarget.ES2020, module: ts.ModuleKind.ESNext }; + const host = ts.createCompilerHost(options); + const getSourceFile = host.getSourceFile.bind(host); + host.getSourceFile = (name, languageVersion, ...rest) => + files.has(name) ? ts.createSourceFile(name, files.get(name) as string, languageVersion) : getSourceFile(name, languageVersion, ...rest); + host.fileExists = (name) => files.has(name) || ts.sys.fileExists(name); + host.readFile = (name) => files.get(name) ?? ts.sys.readFile(name); + const program = ts.createProgram([...files.keys()], options, host); + return [...files.keys()].map((name) => { + const file = program.getSourceFile(name) as ts.SourceFile; + return [...program.getSyntacticDiagnostics(file), ...program.getSemanticDiagnostics(file)].map((d) => ts.flattenDiagnosticMessageText(d.messageText, " ")); + }); + } + + it.each(reserved)("falls back to Row for the reserved name %s", (name) => { + expect(csvToTypeScript(["a"], name)).toBe("export interface Row {\n a: string;\n}"); + }); + + it("keeps a name that only looks reserved, or differs in case", () => { + for (const name of allowed) expect(csvToTypeScript(["a"], name), name).toBe(`export interface ${name} {\n a: string;\n}`); + }); + + it("still falls back for a name that is not an identifier", () => { + for (const name of ["", " ", "my type", "1st", "a-b", "a.b", "é", "class Row"]) { + expect(csvToTypeScript(["a"], name), JSON.stringify(name)).toBe("export interface Row {\n a: string;\n}"); + } + }); + + it("uses the default name for no name at all", () => { + expect(csvToTypeScript(["a"])).toBe("export interface Row {\n a: string;\n}"); + }); + + it("writes TypeScript that compiles, for every name here, and the list is what the compiler rejects", () => { + const names = [...reserved, ...allowed]; + const outputs = names.map((name) => csvToTypeScript(["a", "class", "first name"], name)); + const written = diagnose(outputs); + expect(written.map((found, index) => (found.length ? `${names[index]}: ${found[0]}` : null)).filter(Boolean)).toEqual([]); + + // The same names used as they were typed: the compiler rejects exactly the reserved ones. + const raw = diagnose(names.map((name) => `export interface ${name} {\n a: string;\n}\n`)); + const rejected = names.filter((_, index) => raw[index].length > 0); + expect(rejected.sort()).toEqual([...reserved].sort()); + }); + + it("leaves property names alone, since a reserved word is fine as a property", () => { + expect(csvToTypeScript(["class", "default", "string"], "Row")).toBe( + "export interface Row {\n class: string;\n default: string;\n string: string;\n}", + ); + }); + }); }); describe("csvToYaml", () => { @@ -217,6 +295,55 @@ describe("csvToYaml", () => { expect(csvToYaml(["note"], [['a "b" \\ c\nd']])).toBe('- note: "a \\"b\\" \\\\ c\\nd"'); }); + // A raw control character makes the document invalid YAML, and js-yaml refuses to load it. Escaped the way the + // JSON formatter's YAML conversion does (#234). + describe("control characters", () => { + const yamlOf = (value: string) => csvToYaml(["note"], [[value]]); + + it.each([ + ["NUL", "\u0000", "\\x00"], + ["BEL", "\u0007", "\\x07"], + ["backspace", "\u0008", "\\x08"], + ["vertical tab", "\u000b", "\\x0B"], + ["form feed", "\u000c", "\\x0C"], + ["shift out", "\u000e", "\\x0E"], + ["ESC", "\u001b", "\\x1B"], + ["unit separator", "\u001f", "\\x1F"], + ["DEL", "\u007f", "\\x7F"], + ["next line", "\u0085", "\\x85"], + ["C1 control", "\u009f", "\\x9F"], + ["line separator", "\u2028", "\\u2028"], + ["paragraph separator", "\u2029", "\\u2029"], + ["noncharacter FFFE", "\ufffe", "\\uFFFE"], + ["noncharacter FFFF", "\uffff", "\\uFFFF"], + ])("writes %s as an escape", (_name, character, escape) => { + expect(yamlOf(`a${character}b`)).toBe(`- note: "a${escape}b"`); + }); + + it("writes a tab as \\t", () => { + expect(yamlOf("a\tb")).toBe('- note: "a\\tb"'); + }); + + it("keeps the escapes it already had", () => { + expect(yamlOf('q"\\\n\r')).toBe('- note: "q\\"\\\\\\n\\r"'); + }); + + it("leaves no raw control character anywhere in the output", () => { + const every = Array.from({ length: 0xa0 }, (_, code) => String.fromCharCode(code)).join(""); + const output = csvToYaml([`k${every}`], [[every, `x${every}y`]]); + // Only the line breaks between the lines of the document are left. + expect(output.replace(/\n/g, "")).not.toMatch(/[\u0000-\u001f\u007f-\u009f\u2028\u2029]/); + }); + + it("escapes them in a column name too", () => { + expect(csvToYaml(["a\u0007b"], [["x"]])).toBe('- "a\\x07b": "x"'); + }); + + it("leaves other text, accents and emoji as they were", () => { + expect(yamlOf("café \u00e9 \u{1f600} \u00a0")).toBe('- note: "café \u00e9 \u{1f600} \u00a0"'); + }); + }); + it("quotes non-identifier keys", () => { expect(csvToYaml(["first name"], [["Ada"]])).toBe('- "first name": "Ada"'); }); diff --git a/lib/__tests__/invoice-types.test.ts b/lib/__tests__/invoice-types.test.ts index 89f75ff..f046794 100644 --- a/lib/__tests__/invoice-types.test.ts +++ b/lib/__tests__/invoice-types.test.ts @@ -152,6 +152,32 @@ describe("nextInvoiceNumber", () => { expect(nextInvoiceNumber([base("1001 "), base("draft"), base("")])).toBe("1002"); }); + // A JavaScript number holds 15 digits exactly, so these used to come back lower than the number they follow. + it.each([ + ["20261031143012345", "20261031143012346"], + ["INV-9007199254740993", "INV-9007199254740994"], + ["INV-9007199254740992", "INV-9007199254740993"], + ["INV-99999999999999999999", "INV-100000000000000000000"], + ["INV-9999999999999999999999", "INV-10000000000000000000000"], + ["INV-0000000000000000000007", "INV-0000000000000000000008"], + ["INV-12345678901234567890123456789", "INV-12345678901234567890123456790"], + ["2026-00000000000000000099", "2026-00000000000000000100"], + ])("counts on from %s without losing digits, giving %s", (highest, expected) => { + expect(nextInvoiceNumber([base(highest)])).toBe(expected); + }); + + it("picks the highest of long numbers that a JavaScript number cannot tell apart", () => { + expect(nextInvoiceNumber([base("INV-9007199254740993"), base("INV-9007199254740992")])).toBe("INV-9007199254740994"); + expect(nextInvoiceNumber([base("INV-9007199254740992"), base("INV-9007199254740993")])).toBe("INV-9007199254740994"); + expect(nextInvoiceNumber([base("INV-100000000000000000001"), base("INV-100000000000000000002"), base("INV-099999999999999999999")])).toBe( + "INV-100000000000000000003", + ); + }); + + it("never gives a number that is not made of digits", () => { + expect(nextInvoiceNumber([base(`INV-${"9".repeat(40)}`)])).toBe(`INV-1${"0".repeat(40)}`); + }); + it("starts at the fallback prefix when no invoice has a number", () => { expect(nextInvoiceNumber([base("draft"), base("")])).toBe("INV0001"); expect(nextInvoiceNumber([], "BILL", 6)).toBe("BILL000001"); diff --git a/lib/__tests__/json-utils.test.ts b/lib/__tests__/json-utils.test.ts index c16604f..1f889c4 100644 --- a/lib/__tests__/json-utils.test.ts +++ b/lib/__tests__/json-utils.test.ts @@ -12,6 +12,7 @@ import { parseJson5Compat, diffLines, validate, + singularize, SAMPLE_JSON, } from "../json-utils"; @@ -282,6 +283,100 @@ describe("toXML", () => { expect(result.value).toContain(" x\n y"); }); + it("names the items of common plurals the way JSON to TypeScript does", () => { + const result = toXML('{"categories":["books"],"addresses":["Paris"],"address":["x"]}'); + expect(result.ok).toBe(true); + if (!result.ok) return; + + expect(result.value).toContain(" books"); + expect(result.value).toContain("
Paris
"); + expect(result.value).toContain(" x"); + }); + + it("singularizes sibilant plurals (-ches, -shes, -xes, -zes)", () => { + const result = toXML( + '{"matches":["a"],"branches":["b"],"hashes":["c"],"boxes":["d"],"fixes":["e"],"quizzes":["f"]}', + ); + expect(result.ok).toBe(true); + if (!result.ok) return; + + expect(result.value).toContain("a"); + expect(result.value).toContain("b"); + expect(result.value).toContain("c"); + expect(result.value).toContain("d"); + expect(result.value).toContain("e"); + expect(result.value).toContain("f"); + }); + + it("singularizes ambiguous -ses cases correctly", () => { + const result = toXML('{"cases":["x"],"addresses":["y"],"statuses":["z"]}'); + expect(result.ok).toBe(true); + if (!result.ok) return; + + expect(result.value).toContain("x"); + expect(result.value).toContain("
y
"); + expect(result.value).toContain("z"); + }); + + it("singularizes ambiguous -ses words based on stem endings", () => { + const stems = toXML( + '{"addresses":["a"],"classes":["b"],"statuses":["c"],"viruses":["d"],"bonuses":["e"],"campuses":["f"],"buses":["g"]}', + ); + expect(stems.ok).toBe(true); + if (stems.ok) { + expect(stems.value).toContain("
a
"); + expect(stems.value).toContain("b"); + expect(stems.value).toContain("c"); + expect(stems.value).toContain("d"); + expect(stems.value).toContain("e"); + expect(stems.value).toContain("f"); + expect(stems.value).toContain("g"); + } + + const nonStems = toXML( + '{"cases":["a"],"causes":["b"],"houses":["c"],"responses":["d"],"courses":["e"],"releases":["f"],"licenses":["g"],"databases":["h"]}', + ); + expect(nonStems.ok).toBe(true); + if (nonStems.ok) { + expect(nonStems.value).toContain("a"); + expect(nonStems.value).toContain("b"); + expect(nonStems.value).toContain("c"); + expect(nonStems.value).toContain("d"); + expect(nonStems.value).toContain("e"); + expect(nonStems.value).toContain("f"); + expect(nonStems.value).toContain("g"); + expect(nonStems.value).toContain("h"); + } + }); + + it("handles -zes plurals and caches appropriately in XML", () => { + const result = toXML( + '{"sizes":["s"],"prizes":["p"],"mazes":["m"],"quizzes":["q"],"caches":["c"]}', + ); + expect(result.ok).toBe(true); + if (!result.ok) return; + + expect(result.value).toContain("s"); + expect(result.value).toContain("p"); + expect(result.value).toContain("m"); + expect(result.value).toContain("q"); + expect(result.value).toContain("c"); + }); + + it("preserves existing singularization for -ies, simple -s, and non-plural keys", () => { + const result = toXML( + '{"categories":["a"],"users":["b"],"boss":["c"],"root":["d"],"tags":["e"]}', + ); + expect(result.ok).toBe(true); + if (!result.ok) return; + + expect(result.value).toContain("a"); + expect(result.value).toContain("b"); + expect(result.value).toContain("c"); + expect(result.value).toContain("d"); + expect(result.value).toContain("e"); + }); + it("wraps a top-level array of arrays, each sub-array in its own element", () => { const result = toXML("[[1,2],[3]]"); expect(result.ok).toBe(true); @@ -755,6 +850,145 @@ describe("toTypeScript array items", () => { expect(transpile.diagnostics ?? []).toHaveLength(0); } }); + + it("singularizes sibilant plural array names in interfaces", () => { + const result = toTypeScript('{"matches":[{"x":1}],"branches":[{"y":2}],"boxes":[{"z":3}]}'); + expect(result.ok).toBe(true); + if (!result.ok) return; + + expect(result.value).toContain("export interface Match {"); + expect(result.value).toContain("matches: Match[];"); + expect(result.value).toContain("export interface Branch {"); + expect(result.value).toContain("branches: Branch[];"); + expect(result.value).toContain("export interface Box {"); + expect(result.value).toContain("boxes: Box[];"); + }); + + it("singularizes ambiguous -ses array names in interfaces", () => { + const result = toTypeScript('{"cases":[{"a":1}],"addresses":[{"b":2}],"statuses":[{"c":3}]}'); + expect(result.ok).toBe(true); + if (!result.ok) return; + + expect(result.value).toContain("export interface Case {"); + expect(result.value).toContain("cases: Case[];"); + expect(result.value).toContain("export interface Address {"); + expect(result.value).toContain("addresses: Address[];"); + expect(result.value).toContain("export interface Status {"); + expect(result.value).toContain("statuses: Status[];"); + }); + + it("singularizes -ses and -zes array names in interfaces", () => { + const result = toTypeScript( + JSON.stringify({ + classes: [{ id: 1 }], + viruses: [{ id: 2 }], + bonuses: [{ id: 3 }], + campuses: [{ id: 4 }], + buses: [{ id: 5 }], + causes: [{ id: 6 }], + houses: [{ id: 7 }], + responses: [{ id: 8 }], + courses: [{ id: 9 }], + releases: [{ id: 10 }], + licenses: [{ id: 11 }], + databases: [{ id: 12 }], + sizes: [{ id: 13 }], + prizes: [{ id: 14 }], + mazes: [{ id: 15 }], + quizzes: [{ id: 16 }], + }), + ); + expect(result.ok).toBe(true); + if (!result.ok) return; + + expect(result.value).toContain("export interface Class {"); + expect(result.value).toContain("classes: Class[];"); + expect(result.value).toContain("export interface Virus {"); + expect(result.value).toContain("viruses: Virus[];"); + expect(result.value).toContain("export interface Bonus {"); + expect(result.value).toContain("bonuses: Bonus[];"); + expect(result.value).toContain("export interface Campus {"); + expect(result.value).toContain("campuses: Campus[];"); + expect(result.value).toContain("export interface Bus {"); + expect(result.value).toContain("buses: Bus[];"); + expect(result.value).toContain("export interface Cause {"); + expect(result.value).toContain("causes: Cause[];"); + expect(result.value).toContain("export interface House {"); + expect(result.value).toContain("houses: House[];"); + expect(result.value).toContain("export interface Response {"); + expect(result.value).toContain("responses: Response[];"); + expect(result.value).toContain("export interface Course {"); + expect(result.value).toContain("courses: Course[];"); + expect(result.value).toContain("export interface Release {"); + expect(result.value).toContain("releases: Release[];"); + expect(result.value).toContain("export interface License {"); + expect(result.value).toContain("licenses: License[];"); + expect(result.value).toContain("export interface Database {"); + expect(result.value).toContain("databases: Database[];"); + expect(result.value).toContain("export interface Size {"); + expect(result.value).toContain("sizes: Size[];"); + expect(result.value).toContain("export interface Prize {"); + expect(result.value).toContain("prizes: Prize[];"); + expect(result.value).toContain("export interface Maze {"); + expect(result.value).toContain("mazes: Maze[];"); + expect(result.value).toContain("export interface Quiz {"); + expect(result.value).toContain("quizzes: Quiz[];"); + }); +}); + +describe("singularize", () => { + it("singularizes all required maintainer corrections and preserves existing rules", () => { + // Sibilant plurals: -ches, -shes, -xes + expect(singularize("matches")).toBe("match"); + expect(singularize("branches")).toBe("branch"); + expect(singularize("hashes")).toBe("hash"); + expect(singularize("boxes")).toBe("box"); + expect(singularize("fixes")).toBe("fix"); + expect(singularize("caches")).toBe("cach"); + + // -zes handling: -zzes vs non-doubled + expect(singularize("quizzes")).toBe("quiz"); + expect(singularize("sizes")).toBe("size"); + expect(singularize("prizes")).toBe("prize"); + expect(singularize("mazes")).toBe("maze"); + + // Ambiguous -ses: stem ends in ss, tus, rus, nus, pus, bus -> remove es + expect(singularize("addresses")).toBe("address"); + expect(singularize("classes")).toBe("class"); + expect(singularize("statuses")).toBe("status"); + expect(singularize("viruses")).toBe("virus"); + expect(singularize("bonuses")).toBe("bonus"); + expect(singularize("campuses")).toBe("campus"); + expect(singularize("buses")).toBe("bus"); + + // Ambiguous -ses: stem does not end in those endings -> remove trailing s + expect(singularize("cases")).toBe("case"); + expect(singularize("causes")).toBe("cause"); + expect(singularize("houses")).toBe("house"); + expect(singularize("responses")).toBe("response"); + expect(singularize("courses")).toBe("course"); + expect(singularize("releases")).toBe("release"); + expect(singularize("licenses")).toBe("license"); + expect(singularize("databases")).toBe("database"); + + // Preserved rules + expect(singularize("categories")).toBe("category"); + expect(singularize("users")).toBe("user"); + expect(singularize("boss")).toBe("bossItem"); + expect(singularize("root")).toBe("rootItem"); + + // PascalCase support for TypeScript interface naming + expect(singularize("Addresses")).toBe("Address"); + expect(singularize("Classes")).toBe("Class"); + expect(singularize("Statuses")).toBe("Status"); + expect(singularize("Viruses")).toBe("Virus"); + expect(singularize("Bonuses")).toBe("Bonus"); + expect(singularize("Campuses")).toBe("Campus"); + expect(singularize("Buses")).toBe("Bus"); + expect(singularize("Cases")).toBe("Case"); + expect(singularize("Sizes")).toBe("Size"); + expect(singularize("Quizzes")).toBe("Quiz"); + }); }); /* ----------------------------------------------------------------------- diff --git a/lib/__tests__/markdown-preview.test.ts b/lib/__tests__/markdown-preview.test.ts index 95f3454..76cee5c 100644 --- a/lib/__tests__/markdown-preview.test.ts +++ b/lib/__tests__/markdown-preview.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { createStandaloneHtml, getMarkdownStats, insertMarkdown, insertTableMarkdown, renderMarkdown } from "@/lib/markdown-preview"; +import { createStandaloneHtml, getMarkdownStats, insertLinePrefix, insertMarkdown, insertTableMarkdown, renderMarkdown } from "@/lib/markdown-preview"; describe("markdown preview", () => { it("wraps provided content in a complete light HTML document", () => { @@ -65,6 +65,24 @@ describe("markdown preview", () => { it("inserts formatting around the selection", () => expect(insertMarkdown("hello", 0, 5, "**")).toEqual({ value: "**hello**", selectionStart: 2, selectionEnd: 7 })); }); +describe("insertLinePrefix", () => { + it("turns a line with text into a heading or task wherever the cursor is", () => { + expect(insertLinePrefix("My title", 8, 8, "## ")).toEqual({ value: "## My title", selectionStart: 11, selectionEnd: 11 }); + expect(insertLinePrefix("My title", 3, 8, "## ")).toEqual({ value: "## My title", selectionStart: 6, selectionEnd: 11 }); + expect(insertLinePrefix("Intro\nShopping list\nEnd", 10, 10, "- [ ] ", "task").value).toBe("Intro\n- [ ] Shopping list\nEnd"); + expect(renderMarkdown(insertLinePrefix("My title", 8, 8, "## ").value)).toContain(" { + expect(insertLinePrefix("", 0, 0, "## ")).toEqual({ value: "## text", selectionStart: 3, selectionEnd: 7 }); + expect(insertLinePrefix("Intro\n\nEnd", 6, 6, "- [ ] ", "task").value).toBe("Intro\n- [ ] task\nEnd"); + expect(insertLinePrefix("\nEnd", 0, 0, "## ").value).toBe("## text\nEnd"); + expect(insertLinePrefix("A\n\n ", 7, 7, "## ").value).toBe("A\n\n## text"); + expect(insertLinePrefix("A\n\n\t", 4, 4, "## ").value).toBe("A\n\n## text"); + expect(renderMarkdown(insertLinePrefix("A\n\n ", 7, 7, "## ").value)).toContain(" { const starter = "| Header | Header |\n| --- | --- |\n| Cell | Cell |\n"; const count = (text: string, part: string) => text.split(part).length - 1; diff --git a/lib/__tests__/unicode-inspector.test.ts b/lib/__tests__/unicode-inspector.test.ts index cae4029..9186bfa 100644 --- a/lib/__tests__/unicode-inspector.test.ts +++ b/lib/__tests__/unicode-inspector.test.ts @@ -110,6 +110,49 @@ describe("inspectUnicode", () => { expect(inspectUnicode("\ufeff").rows[0].invisibleLabel).toBe("BYTE ORDER MARK (BOM)"); }); + it("labels DELETE as a control character, like the other controls", () => { + expect(inspectUnicode("\u007f").rows[0]).toMatchObject({ + invisibleLabel: "CONTROL (U+007F)", + intentional: false, + }); + expect(inspectUnicode("a\u007fb").rows.map((row) => row.invisibleLabel)).toEqual([null, "CONTROL (U+007F)", null]); + }); + + it("labels the Hangul filler and the blank Braille pattern, which show nothing on screen", () => { + expect(inspectUnicode("\u3164").rows[0]).toMatchObject({ + invisibleLabel: "HANGUL FILLER", + intentional: true, + }); + expect(inspectUnicode("\u2800").rows[0]).toMatchObject({ + invisibleLabel: "BRAILLE PATTERN BLANK", + intentional: false, + }); + }); + + it("keeps the other Hangul fillers as they were", () => { + expect(inspectUnicode("\u115f").rows[0]).toMatchObject({ invisibleLabel: "HANGUL CHOSEONG FILLER", intentional: true }); + expect(inspectUnicode("\u1160").rows[0]).toMatchObject({ invisibleLabel: "HANGUL JUNGSEONG FILLER", intentional: true }); + expect(inspectUnicode("\uffa0").rows[0]).toMatchObject({ invisibleLabel: "HALFWIDTH HANGUL FILLER", intentional: true }); + }); + + it("does not label the characters next to them, which are visible", () => { + for (const visible of ["~", "\u00a1", "\u2801", "\u28ff", "\u3163", "\u3165", "\u3001", "\u4e2d", "a", "0"]) { + expect(inspectUnicode(visible).rows[0].invisibleLabel, visible.codePointAt(0)?.toString(16)).toBeNull(); + } + }); + + it("labels every control, space separator and format character in the BMP", () => { + const unlabelled: string[] = []; + for (let code = 0; code <= 0xffff; code++) { + if (code >= 0xd800 && code <= 0xdfff) continue; + const char = String.fromCharCode(code); + if (/[\p{Cc}\p{Zs}\p{Cf}]/u.test(char) && inspectUnicode(char).rows[0].invisibleLabel === null) { + unlabelled.push(`U+${code.toString(16).toUpperCase().padStart(4, "0")}`); + } + } + expect(unlabelled).toEqual([]); + }); + it("falls back to category labels for unlisted Zs or Cf characters", () => { expect(inspectUnicode("\u0600").rows[0]).toMatchObject({ invisibleLabel: "FORMAT CHARACTER", diff --git a/lib/__tests__/uuid.test.ts b/lib/__tests__/uuid.test.ts index 1a537d6..16637c3 100644 --- a/lib/__tests__/uuid.test.ts +++ b/lib/__tests__/uuid.test.ts @@ -105,6 +105,8 @@ describe("inspectUuid", () => { ["a urn:uuid: prefix", "urn:uuid:3f2504e0-4f89-41d3-9a0c-0305e82c3301"], ["an upper case URN:UUID: prefix", "URN:UUID:3F2504E0-4F89-41D3-9A0C-0305E82C3301"], ["surrounding whitespace", " {3f2504e0-4f89-41d3-9a0c-0305e82c3301}\n"], + ["double quotes", "\"3f2504e0-4f89-41d3-9a0c-0305e82c3301\""], + ["single quotes", "'3f2504e0-4f89-41d3-9a0c-0305e82c3301'"], ])("inspects a UUID with %s the same as the bare value", (_label, text) => { expect(inspectUuid(text)).toEqual(inspectUuid("3f2504e0-4f89-41d3-9a0c-0305e82c3301")); expect(inspectUuid(text)).toMatchObject({ ok: true, version: 4, variant: "rfc" }); @@ -116,6 +118,7 @@ describe("inspectUuid", () => { "{{3f2504e0-4f89-41d3-9a0c-0305e82c3301}}", "urn:uuid:{3f2504e0-4f89-41d3-9a0c-0305e82c3301}", "uuid:3f2504e0-4f89-41d3-9a0c-0305e82c3301", + "\"3f2504e0-4f89-41d3-9a0c-0305e82c3301'", ])("rejects %s", (text) => { expect(inspectUuid(text)).toEqual({ ok: false }); }); diff --git a/lib/csv-utils.ts b/lib/csv-utils.ts index 2f60eae..4acb9cc 100644 --- a/lib/csv-utils.ts +++ b/lib/csv-utils.ts @@ -70,6 +70,16 @@ export const MAX_REPORTED_ROW_ERRORS = 25; const BOM = "\uFEFF"; const PLAIN_NUMBER_RE = /^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$/; const IDENTIFIER_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/; +// Words TypeScript will not take as an interface name: reserved words, those reserved in strict mode (a module is +// strict), `await`, and the names of the built-in types. A property can still be called any of them. +const RESERVED_TYPE_NAMES = new Set( + ( + "break case catch class const continue debugger default delete do else enum export extends false finally for " + + "function if import in instanceof new null return super switch this throw true try typeof var void while with " + + "implements interface let package private protected public static yield await " + + "any unknown never number bigint boolean string symbol object undefined" + ).split(" "), +); const YAML_KEY_RE = /^[A-Za-z_][A-Za-z0-9_-]*$/; // Plain YAML keys that a 1.1 parser reads as null or a boolean. They must be // quoted, or `null: x` becomes `{None: "x"}` and `on: y` becomes `{True: "y"}`. @@ -298,12 +308,18 @@ function generatedHeader(columns: number): string[] { /** Make column names unique without changing the first occurrence. */ export function uniqueHeaders(header: string[]): string[] { - const seen = new Map(); + const taken = new Set(header.filter((name) => name.trim() !== "")); + const kept = new Set(); return header.map((name, index) => { + if (name.trim() !== "" && !kept.has(name)) { + kept.add(name); + return name; + } const base = name.trim() === "" ? `column_${index + 1}` : name; - const count = seen.get(base) ?? 0; - seen.set(base, count + 1); - return count === 0 ? base : `${base}_${count + 1}`; + let unique = base; + for (let n = 2; taken.has(unique); n += 1) unique = `${base}_${n}`; + taken.add(unique); + return unique; }); } @@ -542,7 +558,7 @@ function tsProperty(name: string): string { */ export function csvToTypeScript(header: string[], typeName = "Row"): string { const keys = uniqueHeaders(header); - const name = IDENTIFIER_RE.test(typeName) ? typeName : "Row"; + const name = IDENTIFIER_RE.test(typeName) && !RESERVED_TYPE_NAMES.has(typeName) ? typeName : "Row"; if (keys.length === 0) return `export interface ${name} {}`; @@ -551,8 +567,24 @@ export function csvToTypeScript(header: string[], typeName = "Row"): string { return `export interface ${name} {\n${fields.join("\n")}\n}`; } +// Control characters, the C1 range, the Unicode line and paragraph separators and the noncharacters FFFE and FFFF +// (tab, \n and \r have their own escapes). A raw one in a double-quoted scalar is invalid YAML: js-yaml won't load it. +const YAML_CONTROL_CHARS = /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x9f\u2028\u2029\ufffe\uffff]/g; + function yamlString(value: string): string { - return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r")}"`; + const escaped = value + .replace(/\\/g, "\\\\") + .replace(/"/g, '\\"') + .replace(/\n/g, "\\n") + .replace(/\r/g, "\\r") + .replace(/\t/g, "\\t") + .replace(YAML_CONTROL_CHARS, (char) => { + const code = char.charCodeAt(0); + return code < 0x100 + ? `\\x${code.toString(16).toUpperCase().padStart(2, "0")}` + : `\\u${code.toString(16).toUpperCase().padStart(4, "0")}`; + }); + return `"${escaped}"`; } function yamlKey(key: string): string { diff --git a/lib/invoice-types.ts b/lib/invoice-types.ts index 039563f..90d9f06 100644 --- a/lib/invoice-types.ts +++ b/lib/invoice-types.ts @@ -161,17 +161,18 @@ export function todayISO(): string { * "INV0001". */ export function nextInvoiceNumber(invoices: Invoice[], fallbackPrefix = "INV", pad = 4): string { - let highest: { prefix: string; digits: string; value: number } | undefined; + // BigInt, because a number of more than 15 digits (a timestamp with milliseconds, say) is not exact as a JS number. + let highest: { prefix: string; digits: string; value: bigint } | undefined; for (const inv of invoices) { const match = (inv.number || "").match(/^(.*?)(\d+)\s*$/); if (!match) continue; - const value = parseInt(match[2], 10); + const value = BigInt(match[2]); if (!highest || value >= highest.value) { highest = { prefix: match[1], digits: match[2], value }; } } if (!highest) return `${fallbackPrefix}${String(1).padStart(pad, "0")}`; - return `${highest.prefix}${String(highest.value + 1).padStart(highest.digits.length, "0")}`; + return `${highest.prefix}${String(highest.value + BigInt(1)).padStart(highest.digits.length, "0")}`; } export const DEFAULT_SENDER: SenderProfile = { diff --git a/lib/json-utils.ts b/lib/json-utils.ts index fd4d598..83ffa3f 100644 --- a/lib/json-utils.ts +++ b/lib/json-utils.ts @@ -237,10 +237,20 @@ function pascalCase(s: string): string { return /^[0-9]/.test(result) ? `_${result}` : result; } -function singularize(name: string): string { - if (/ies$/.test(name)) return name.replace(/ies$/, "y"); - if (/ses$/.test(name)) return name.replace(/es$/, ""); - if (/s$/.test(name) && !/ss$/.test(name)) return name.replace(/s$/, ""); +export function singularize(name: string): string { + if (/ies$/i.test(name)) return name.replace(/ies$/i, "y"); + // Sibilant plurals with a doubled consonant: quizzes → quiz + if (/zzes$/i.test(name)) return name.replace(/zes$/i, ""); + // Sibilant plurals: matches → match, branches → branch, hashes → hash, boxes → box, fixes → fix + if (/(ches|shes|xes)$/i.test(name)) return name.replace(/es$/i, ""); + // Ambiguous -ses: remove "es" only when the stem ends in ss, tus, rus, nus, pus, or bus; + // otherwise remove only the trailing "s". + if (/ses$/i.test(name)) { + const stem = name.slice(0, -2); + if (/(ss|tus|rus|nus|pus|bus)$/i.test(stem)) return stem; + return name.slice(0, -1); + } + if (/s$/i.test(name) && !/ss$/i.test(name)) return name.replace(/s$/i, ""); return name + "Item"; } @@ -684,9 +694,7 @@ export function toXML(raw: string, rootTag = "root"): Result { if (node === null) return `${pad}<${tag} nil="true"/>`; if (isLosslessNumber(node)) return `${pad}<${tag}>${node.toString()}`; if (Array.isArray(node)) { - const itemTag = sanitizeTag( - tag.endsWith("s") && tag.length > 1 ? tag.slice(0, -1) : `${tag}Item` - ); + const itemTag = sanitizeTag(singularize(tag)); if (node.length === 0) return `${pad}<${tag}/>`; if (parent === "object") { return node diff --git a/lib/markdown-preview.ts b/lib/markdown-preview.ts index 6bd679c..cfd45bb 100644 --- a/lib/markdown-preview.ts +++ b/lib/markdown-preview.ts @@ -66,6 +66,19 @@ export function insertMarkdown(markdown: string, start: number, end: number, bef return { value: markdown.slice(0, start) + before + selected + after + markdown.slice(end), selectionStart: start + before.length, selectionEnd: start + before.length + selected.length }; } +/** Puts a line marker such as `## ` at the start of the cursor's line, so a line that has text becomes a heading or task. */ +export function insertLinePrefix(markdown: string, start: number, end: number, prefix: string, placeholder = "text") { + const lineStart = markdown.slice(0, start).lastIndexOf("\n") + 1; + const newline = markdown.indexOf("\n", start); + const lineEnd = newline === -1 ? markdown.length : newline; + if (markdown.slice(lineStart, lineEnd).trim() === "") { + // A blank line: drop its white space, since four spaces or a tab before the marker can make a code block. + const at = lineStart + prefix.length; + return { value: markdown.slice(0, lineStart) + prefix + placeholder + markdown.slice(lineEnd), selectionStart: at, selectionEnd: at + placeholder.length }; + } + return { value: markdown.slice(0, lineStart) + prefix + markdown.slice(lineStart), selectionStart: start + prefix.length, selectionEnd: end + prefix.length }; +} + const STARTER_TABLE = "| Header | Header |\n| --- | --- |\n| Cell | Cell |\n"; /** diff --git a/lib/unicode-inspector.ts b/lib/unicode-inspector.ts index 4e54ef3..39afdd7 100644 --- a/lib/unicode-inspector.ts +++ b/lib/unicode-inspector.ts @@ -62,7 +62,9 @@ const INVISIBLE: Record = { 0x206d: { label: "ACTIVATE ARABIC FORM SHAPING", intentional: true }, 0x206e: { label: "NATIONAL DIGIT SHAPES", intentional: true }, 0x206f: { label: "NOMINAL DIGIT SHAPES", intentional: true }, + 0x2800: { label: "BRAILLE PATTERN BLANK" }, 0x3000: { label: "IDEOGRAPHIC SPACE" }, + 0x3164: { label: "HANGUL FILLER", intentional: true }, 0xfeff: { label: "BYTE ORDER MARK (BOM)" }, 0xffa0: { label: "HALFWIDTH HANGUL FILLER", intentional: true }, 0x1d159: { label: "MUSICAL SYMBOL NULL NOTEHEAD" }, @@ -199,7 +201,7 @@ function getInvisibleInfo(cp: number, char: string): { label: string | null; int } const isControl = (cp <= 0x1f && cp !== 0x09 && cp !== 0x0a && cp !== 0x0d) || - (cp >= 0x80 && cp <= 0x9f); + (cp >= 0x7f && cp <= 0x9f); if (isControl) { return { label: `CONTROL (U+${hex4(cp)})`, intentional: false }; } diff --git a/lib/uuid.ts b/lib/uuid.ts index a942dc3..f6a36fe 100644 --- a/lib/uuid.ts +++ b/lib/uuid.ts @@ -75,7 +75,7 @@ export function formatUuid( } function unwrapUuid(text: string): string { - const trimmed = text.trim(); + const trimmed = text.trim().replace(/^(["'])([\s\S]*)\1$/, "$2").trim(); if (URN_PREFIX.test(trimmed)) return trimmed.replace(URN_PREFIX, ""); return trimmed.startsWith("{") && trimmed.endsWith("}") ? trimmed.slice(1, -1) : trimmed; } diff --git a/package-lock.json b/package-lock.json index 748dcd1..2cd9e1d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,16 @@ { "name": "toolbelt", - "version": "0.11.0", + "version": "0.11.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "toolbelt", - "version": "0.11.0", + "version": "0.11.1", "license": "MIT", "dependencies": { - "@cfworker/json-schema": "^4.1.1", - "@opennextjs/cloudflare": "^1.20.13", + "@cfworker/json-schema": "^4.1.4", + "@opennextjs/cloudflare": "^1.20.15", "clsx": "^2.1.1", "dompurify": "^3.4.17", "json5": "^2.2.3", @@ -18,7 +18,7 @@ "jspdf": "^4.2.1", "lossless-json": "^4.3.3", "lucide-react": "^0.468.0", - "marked": "^18.1.1", + "marked": "^18.1.2", "next": "^16.4.3", "prismjs": "^1.30.0", "react": "^19.3.1", @@ -41,7 +41,7 @@ "tailwindcss": "^3.4.1", "typescript": "^5", "vitest": "^4.1.12", - "wrangler": "^4.152.0" + "wrangler": "^4.156.0" }, "engines": { "node": ">=20" @@ -346,14 +346,14 @@ } }, "node_modules/@aws-sdk/checksums": { - "version": "3.1001.5", - "resolved": "https://registry.npmjs.org/@aws-sdk/checksums/-/checksums-3.1001.5.tgz", - "integrity": "sha512-6erto9nK2OZO/woEgmFIaPh5gChgdNd7nL6jgAf8TAXz3/QHC8Pan2TFe9GYp3IxOIKCCGWPylSjtOHKfNzQlw==", + "version": "3.1002.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/checksums/-/checksums-3.1002.1.tgz", + "integrity": "sha512-CXNESwkQC9jdrUG6cKXBfudVt1frGcYQykcCYbNclVrsewuGMbo3PlFjRteMe4OftNAqhyc3xC2jTz7SUqLasQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.5", + "@aws-sdk/core": "^3.979.1", "@aws-sdk/types": "^3.975.0", - "@smithy/core": "^3.36.1", + "@smithy/core": "^3.38.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, @@ -640,16 +640,16 @@ } }, "node_modules/@aws-sdk/core": { - "version": "3.978.5", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.978.5.tgz", - "integrity": "sha512-ruI0AWqcrfL20XDS6SwZIJFPOBZ6N0ADa9nLzWlBhxk1aoUjbUX3JkTR9vEmk7o35XpAHi68ahZ4T6RE2U9XyA==", + "version": "3.979.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.979.1.tgz", + "integrity": "sha512-ayOlV1VhLNMiq8PW9ZkVobG4gkldnUoHjG69yy2b8j+VYFlhz/uAL7ZdYQAoQ+eShtGUdNDnnG5rxDg6yUJrOg==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.975.0", - "@aws-sdk/xml-builder": "^3.972.41", + "@aws-sdk/xml-builder": "^3.972.42", "@aws/lambda-invoke-store": "^0.3.0", - "@smithy/core": "^3.36.1", - "@smithy/signature-v4": "^5.7.4", + "@smithy/core": "^3.38.0", + "@smithy/signature-v4": "^5.7.5", "@smithy/types": "^4.19.0", "bowser": "^2.11.0", "tslib": "^2.6.2" @@ -659,14 +659,14 @@ } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.972.76", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.76.tgz", - "integrity": "sha512-YxmL1Ar7bV9ZkJvtv+lj+MF6le76NnZnbmgySyZuxpZaCv0/1lZ3KxKy6RIj1h9ui2hWnVjZxbmTMeO0y/NrQA==", + "version": "3.972.80", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.80.tgz", + "integrity": "sha512-uO5TiSvb6j1w9RTe5z28OaOmsrOxVQUPaC5+wZl50RfOXcrYD/KgnS8I9lfyGXc7sGbkCopK6VnhNIAoLqfewQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.5", + "@aws-sdk/core": "^3.979.1", "@aws-sdk/types": "^3.975.0", - "@smithy/core": "^3.36.1", + "@smithy/core": "^3.38.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, @@ -675,16 +675,16 @@ } }, "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.972.78", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.78.tgz", - "integrity": "sha512-3VHknDRngoebsaqyT1iR75b5cBT2zfEtODK6yYmYltljkK1HEAQLs2FEd1IpWqAeOYdMGaG9wikngWLTufY7RA==", + "version": "3.972.82", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.82.tgz", + "integrity": "sha512-vQzlDHlC24Yz42p+rT4NBoP/5VdetkBoujbOLq9h6QYWC8THuZSPYnXIR9OP3XcOrdMgPBBipOrdssfxnIiWUg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.5", + "@aws-sdk/core": "^3.979.1", "@aws-sdk/types": "^3.975.0", - "@smithy/core": "^3.36.1", + "@smithy/core": "^3.38.0", "@smithy/fetch-http-handler": "^5.8.0", - "@smithy/node-http-handler": "^4.12.1", + "@smithy/node-http-handler": "^4.14.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, @@ -693,21 +693,21 @@ } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.973.21", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.973.21.tgz", - "integrity": "sha512-aLx9hbCX6mGAyv4ZZd8UagJ4RASHVJw6BFDSIyuKmLkqNL6H47EjBPK6qaJ4B5TDV6mc+vMa0SJFYyo4mkvVQg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.978.5", - "@aws-sdk/credential-provider-env": "^3.972.76", - "@aws-sdk/credential-provider-http": "^3.972.78", - "@aws-sdk/credential-provider-login": "^3.972.83", - "@aws-sdk/credential-provider-process": "^3.972.76", - "@aws-sdk/credential-provider-sso": "^3.973.20", - "@aws-sdk/credential-provider-web-identity": "^3.972.82", - "@aws-sdk/nested-clients": "^3.997.50", + "version": "3.973.25", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.973.25.tgz", + "integrity": "sha512-b5xl71uCRdWsu7P1VeGpiGuCf6zfYETsSNOv8AmYVTrsgbvTJRXdmmOPVfxkvGGVNsiKkGctClQW3jAn0SZLug==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.979.1", + "@aws-sdk/credential-provider-env": "^3.972.80", + "@aws-sdk/credential-provider-http": "^3.972.82", + "@aws-sdk/credential-provider-login": "^3.972.87", + "@aws-sdk/credential-provider-process": "^3.972.80", + "@aws-sdk/credential-provider-sso": "^3.973.24", + "@aws-sdk/credential-provider-web-identity": "^3.972.86", + "@aws-sdk/nested-clients": "^3.997.54", "@aws-sdk/types": "^3.975.0", - "@smithy/core": "^3.36.1", + "@smithy/core": "^3.38.0", "@smithy/credential-provider-imds": "^4.5.2", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" @@ -717,15 +717,15 @@ } }, "node_modules/@aws-sdk/credential-provider-login": { - "version": "3.972.83", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.83.tgz", - "integrity": "sha512-9bJU6FjRacF4FK/QHf8Dyxhur7B1grRj8C1Yh4IkpoFW2exQflPdCv0VrbemLW9ZctLNle+AE7TETs9KNvuLYg==", + "version": "3.972.87", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.87.tgz", + "integrity": "sha512-C4ZFmcwM+Zj14Z8huVUk54Gv4uzT1ZdzG9cY3Gdh8FePakq7Bss1OFsgZjAhWFOkFOGpBvFBeu0OWjWf8HNKRQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.5", - "@aws-sdk/nested-clients": "^3.997.50", + "@aws-sdk/core": "^3.979.1", + "@aws-sdk/nested-clients": "^3.997.54", "@aws-sdk/types": "^3.975.0", - "@smithy/core": "^3.36.1", + "@smithy/core": "^3.38.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, @@ -734,19 +734,19 @@ } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.972.88", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.88.tgz", - "integrity": "sha512-AG0KID5qhQPzrFkh5b0Cws1Amytvzo0L0Xj6hMgJXEwD6ZXAqninDhSNAWJiN5zU4YVVZnuidnqwPwYJ04IamA==", + "version": "3.972.92", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.92.tgz", + "integrity": "sha512-jC3zmOo7Qxlk+oBivvSsS1Ph7sFGm0QfuhqDGbqgRXi4cxUYMG+dWtBwS2XYYXgc8TIVXJiwgQ65sbNpbYupTA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/credential-provider-env": "^3.972.76", - "@aws-sdk/credential-provider-http": "^3.972.78", - "@aws-sdk/credential-provider-ini": "^3.973.21", - "@aws-sdk/credential-provider-process": "^3.972.76", - "@aws-sdk/credential-provider-sso": "^3.973.20", - "@aws-sdk/credential-provider-web-identity": "^3.972.82", + "@aws-sdk/credential-provider-env": "^3.972.80", + "@aws-sdk/credential-provider-http": "^3.972.82", + "@aws-sdk/credential-provider-ini": "^3.973.25", + "@aws-sdk/credential-provider-process": "^3.972.80", + "@aws-sdk/credential-provider-sso": "^3.973.24", + "@aws-sdk/credential-provider-web-identity": "^3.972.86", "@aws-sdk/types": "^3.975.0", - "@smithy/core": "^3.36.1", + "@smithy/core": "^3.38.0", "@smithy/credential-provider-imds": "^4.5.2", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" @@ -756,14 +756,14 @@ } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.972.76", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.76.tgz", - "integrity": "sha512-pdhKFTXv1zPYhh92MTlp0jVbLWvUEV9v+rziuEkoskGGQ2iJskxHTO38nA8UAbT/N4IM3n8OZJ9rIY5IBO15/A==", + "version": "3.972.80", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.80.tgz", + "integrity": "sha512-xqfixO5aclje7tbQJDias+iNND5wp/3QZixCdZcVQpgWPTZ0V5DfTW9A804A5Z7ezu7sH3WRO+vzUjtiJBA8yg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.5", + "@aws-sdk/core": "^3.979.1", "@aws-sdk/types": "^3.975.0", - "@smithy/core": "^3.36.1", + "@smithy/core": "^3.38.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, @@ -772,16 +772,16 @@ } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.973.20", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.973.20.tgz", - "integrity": "sha512-DlxrAB3MnjNLx/da7b89W+3DyJQYso/q7Ns15MsST2IV2nkAYQOYTUCaffNSjgrpMP99Dm2mV2jxzcbyF94ZfA==", + "version": "3.973.24", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.973.24.tgz", + "integrity": "sha512-nlg3BKG03Cgzvv/dMnhxvDJPhAhQSpaN0RUdwgU7ziwkasoev7iI8el8Xy4Rd5sqzgaDUbV269fOXck5SargqA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.5", - "@aws-sdk/nested-clients": "^3.997.50", - "@aws-sdk/token-providers": "3.1155.0", + "@aws-sdk/core": "^3.979.1", + "@aws-sdk/nested-clients": "^3.997.54", + "@aws-sdk/token-providers": "3.1161.0", "@aws-sdk/types": "^3.975.0", - "@smithy/core": "^3.36.1", + "@smithy/core": "^3.38.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, @@ -790,15 +790,15 @@ } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.972.82", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.82.tgz", - "integrity": "sha512-LUo46IqPTJ4xKBsrLuoaxiv/D2GTBCcFDMD1y3jKIqPR/p7Rw14RJFZ0iRWftqZtyLPQX9JMGnSi3mBu4y/pNA==", + "version": "3.972.86", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.86.tgz", + "integrity": "sha512-zKTyLJBEyfQBVlYqT6Jt+mYV4r8kGbWM7dn6v4Y4MRARizhYafjDXP/iMu76UMRhToSPNmIKX1XAb24gtpBFIA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.5", - "@aws-sdk/nested-clients": "^3.997.50", + "@aws-sdk/core": "^3.979.1", + "@aws-sdk/nested-clients": "^3.997.54", "@aws-sdk/types": "^3.975.0", - "@smithy/core": "^3.36.1", + "@smithy/core": "^3.38.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, @@ -807,13 +807,13 @@ } }, "node_modules/@aws-sdk/dynamodb-codec": { - "version": "3.973.50", - "resolved": "https://registry.npmjs.org/@aws-sdk/dynamodb-codec/-/dynamodb-codec-3.973.50.tgz", - "integrity": "sha512-1jn3q2Ik/qUjPw1su5Abu6nzeDutzYbNUYKt+AqUh+45rb+MVWhgP1QWarTuxrm4MJKcSLVHLCrvi0rY5jmJZw==", + "version": "3.973.54", + "resolved": "https://registry.npmjs.org/@aws-sdk/dynamodb-codec/-/dynamodb-codec-3.973.54.tgz", + "integrity": "sha512-+Z4tyUabT/ATvXw7Bpo2j8Sh5ot5XqhvJYwNqHaKEib1KIgrVByZKSw7ndeAMOHjMAhygZnH09N9D1ygD/uTRg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.5", - "@smithy/core": "^3.36.1", + "@aws-sdk/core": "^3.979.1", + "@smithy/core": "^3.38.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, @@ -835,12 +835,12 @@ } }, "node_modules/@aws-sdk/middleware-bucket-endpoint": { - "version": "3.972.54", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.972.54.tgz", - "integrity": "sha512-/865W4kY+ZENpo4ZELvNNVGjjrDW5YFtY7+QdvtoKpzRWmC0LFNuXJb9BHpPVsqeUosZ7QZOLZx1WkElPK4QIA==", + "version": "3.972.58", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.972.58.tgz", + "integrity": "sha512-pwD0JKJncGoOLAxHKAGFd6LrUSqodhN0CGUfHvQKpVFklylDcA27BkoGO+A/n8UTMbcDMRg1K/E003W49LdGHQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-sdk-s3": "^3.972.81", + "@aws-sdk/middleware-sdk-s3": "^3.972.85", "tslib": "^2.6.2" }, "engines": { @@ -848,14 +848,14 @@ } }, "node_modules/@aws-sdk/middleware-endpoint-discovery": { - "version": "3.972.34", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint-discovery/-/middleware-endpoint-discovery-3.972.34.tgz", - "integrity": "sha512-hwPWSvttg+bEpcM9e8HdVJF/oz79xInjnou23+GdsbDv75DVDLGhn2XhIbuNghp5SqyjtngXEI+K22Vq3VvSxw==", + "version": "3.972.37", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint-discovery/-/middleware-endpoint-discovery-3.972.37.tgz", + "integrity": "sha512-+KAK020JY95Q7Swwo5b9pl9S11VPuNLqecCW5bzkksWiU5pmI85lAloOD4qlIKIA/P8FlrRJ60XJ6+lnDQK/DQ==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/endpoint-cache": "^3.972.11", "@aws-sdk/types": "^3.975.0", - "@smithy/core": "^3.36.1", + "@smithy/core": "^3.38.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, @@ -864,12 +864,12 @@ } }, "node_modules/@aws-sdk/middleware-expect-continue": { - "version": "3.972.50", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.972.50.tgz", - "integrity": "sha512-Z7qRC0/wzsUdkZh7nGJahnX558jL/jG45fZb1Mql/w/10qcDY2AxnEoCC78l6n5aQbMC1x7j4TF0fd5OwpZIDQ==", + "version": "3.972.54", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.972.54.tgz", + "integrity": "sha512-zj8L/3ukRBMI9+49BZ53shhVc9L47GIGBZlZu4zwmDc0mGBmtOom/ewIkzHHNgSLxW5q5KEOfpSf7HDKn8yXXA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-sdk-s3": "^3.972.81", + "@aws-sdk/middleware-sdk-s3": "^3.972.85", "tslib": "^2.6.2" }, "engines": { @@ -877,12 +877,12 @@ } }, "node_modules/@aws-sdk/middleware-flexible-checksums": { - "version": "3.974.60", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.974.60.tgz", - "integrity": "sha512-V9rcPj4Qh8OYfO3RwNTfJVfAlxlnFxE71GkWxl++8yEMS7dRViKQaQm56P79Z97el2xmFzet78E+r9XxyuyD0A==", + "version": "3.974.64", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.974.64.tgz", + "integrity": "sha512-DjDQSetkeXiRyOB6RaR/FFTElcIIG6v45tiY6pd4gGLDem4c44EGKWYFc1SQ7Xq4c2xPuuAv3lPzz2zrOoXlYg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/checksums": "^3.1001.5", + "@aws-sdk/checksums": "^3.1002.1", "tslib": "^2.6.2" }, "engines": { @@ -890,12 +890,12 @@ } }, "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.972.51", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.972.51.tgz", - "integrity": "sha512-s1Orgn9zi4H9hKO6iLmF9vOd3rJZ013aCcp5pfhOpqNoX6PdD0Z2GMIKQLcRzqjynpi64PI0UQXmQDiqWtK4Nw==", + "version": "3.972.55", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.972.55.tgz", + "integrity": "sha512-/zR8YmRi9OO6voQ1sxs4t5hkmi28m8wHvQ6dYJY1c6f1cEACvpoGE3+xIwnjZVjuiy3i7vPilXFleqA46llCaQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.5", + "@aws-sdk/core": "^3.979.1", "tslib": "^2.6.2" }, "engines": { @@ -903,12 +903,12 @@ } }, "node_modules/@aws-sdk/middleware-location-constraint": { - "version": "3.972.47", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.972.47.tgz", - "integrity": "sha512-CYep3qdXorbWNtblXPt6hGuRjrn2ZQVAOdgjyw43/jgiD0nVGB5lPs/HsCf2Ex1XexXtFfrXqDELomFb75k1fA==", + "version": "3.972.51", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.972.51.tgz", + "integrity": "sha512-3mJ9F4ttZvrPoGVeT9NvwQ//TRcX3iCB0caZZ0M72OD1/owT6LlCiRdqppNIWN7a/jS5RcNY57wDbQg43hY+Yw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-sdk-s3": "^3.972.81", + "@aws-sdk/middleware-sdk-s3": "^3.972.85", "tslib": "^2.6.2" }, "engines": { @@ -916,12 +916,12 @@ } }, "node_modules/@aws-sdk/middleware-logger": { - "version": "3.972.50", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.972.50.tgz", - "integrity": "sha512-CBF3yNK3aKSU606aM83P5DsiTiNcVwnA1ujV6OP17BOHrwO6/P2rJZjV3hNDbqMCqVTHtBakGkJfL2JSMwbpDQ==", + "version": "3.972.54", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.972.54.tgz", + "integrity": "sha512-23Ptwj16CUYpJElGYh+svuV3q8h8ho9spejhjrgK+oQgb4yvqH/W/QxAejR1omBELb3zzx3QY9yEbR/7B/kYTg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.5", + "@aws-sdk/core": "^3.979.1", "tslib": "^2.6.2" }, "engines": { @@ -929,12 +929,12 @@ } }, "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.972.52", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.52.tgz", - "integrity": "sha512-PL8AcO1mqmPiq4XYcqcZ9fN49AMLU8a72YOtmyXLOMb4B1zFqAifNH8qjO6IJdY6rF3l6dbAT/Wa84LS2ISvlA==", + "version": "3.972.56", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.56.tgz", + "integrity": "sha512-yd3G90KqbzFkoaST0KtGU2uMPUNReGUrLGxfwWVKOUdC5R+vyp17DX7h1B6sI9cIgzpnjDsWU2mStkTJKI7jTA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.5", + "@aws-sdk/core": "^3.979.1", "tslib": "^2.6.2" }, "engines": { @@ -942,15 +942,15 @@ } }, "node_modules/@aws-sdk/middleware-sdk-s3": { - "version": "3.972.81", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.81.tgz", - "integrity": "sha512-jfanv1pV0sJFZw6/DPiNjvK7B478god0zOum5yIWo+/+1Gy+CSXWpGBWx+3nBJyDvjcu/2kUMr4ADUp9BHH34Q==", + "version": "3.972.85", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.85.tgz", + "integrity": "sha512-Kyl2G6zhIS9qFoeLdlxvXFrWyc6g9D9ZCIRJnGkkfX6JZhxCZB5ebXBc7GKEeetrnhXcail6ALZT0lKbojcaNA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.5", - "@aws-sdk/signature-v4-multi-region": "^3.996.49", + "@aws-sdk/core": "^3.979.1", + "@aws-sdk/signature-v4-multi-region": "^3.996.51", "@aws-sdk/types": "^3.975.0", - "@smithy/core": "^3.36.1", + "@smithy/core": "^3.38.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, @@ -959,13 +959,13 @@ } }, "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.996.49", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.49.tgz", - "integrity": "sha512-nvuAZNRrRRVCiARRKsYlQ0VnXiEQiq7vXPH+UA+LSo+kN3Djl9eo/V2r0udkw85PnsNshmv2Em1clld5M3j67w==", + "version": "3.996.51", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.51.tgz", + "integrity": "sha512-+aHvUugVBuMPXxgkPfWnTMyGdE/yWS8GHi5+qQ37HowS0YqzrbuaDLznj+Dtmtu5jgCdqz36gXmujOToUhNNWw==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.975.0", - "@smithy/signature-v4": "^5.7.4", + "@smithy/signature-v4": "^5.7.5", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, @@ -974,13 +974,13 @@ } }, "node_modules/@aws-sdk/middleware-sdk-sqs": { - "version": "3.972.46", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sqs/-/middleware-sdk-sqs-3.972.46.tgz", - "integrity": "sha512-xXUZj3pl5rc2F2cf/n00WZRF4eI19gL5xr1cfqN882CJ6j53RAOt3GRLGOuFWz1iRgGm2OOVgpHqBDz/MXAP8Q==", + "version": "3.972.48", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sqs/-/middleware-sdk-sqs-3.972.48.tgz", + "integrity": "sha512-GNzuBLwvUUV++svohxeAj1sf4AgA2gOevKTYHRKIDPpVD30YoBiNBb+X0L28b9KazqHhG6WilyiOE4XDmZeHeg==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.975.0", - "@smithy/core": "^3.36.1", + "@smithy/core": "^3.38.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, @@ -989,12 +989,12 @@ } }, "node_modules/@aws-sdk/middleware-ssec": { - "version": "3.972.47", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.972.47.tgz", - "integrity": "sha512-RwYHFMX63Nmuot+tuIEqf6AQVC5w0l9c6eznLI6MO31zXYWmvvqv/rRX/1nUn0m6Ip4OX5qRCa1ZHxyHwlm04g==", + "version": "3.972.51", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.972.51.tgz", + "integrity": "sha512-/W0UjJQDI1XF2k0Z8hkdds7/WoGRfcjFVVCKjSXosf9nBLfREAX8zlmpEM/8AAJeTvvvAi3SHeueMejbbDAhhA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-sdk-s3": "^3.972.81", + "@aws-sdk/middleware-sdk-s3": "^3.972.85", "tslib": "^2.6.2" }, "engines": { @@ -1002,12 +1002,12 @@ } }, "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.972.80", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.80.tgz", - "integrity": "sha512-PvETLhb+u7hWRTZOt1ZyRnwyzGU0v6abk+69uFbcZ8wpqsoAtudd/wXbUg/8Yu09fD8kS6VqWPHFiZy3ai3cuA==", + "version": "3.972.84", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.84.tgz", + "integrity": "sha512-XvF9GVEEOAHyo8zlDQBt1fyfX61W3daprMS1kJrFpuri4Lpvd6WDlE7wK3LUrUCS85hIu83sTC8RRx/2TpdjwA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.5", + "@aws-sdk/core": "^3.979.1", "tslib": "^2.6.2" }, "engines": { @@ -1015,17 +1015,17 @@ } }, "node_modules/@aws-sdk/nested-clients": { - "version": "3.997.50", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.50.tgz", - "integrity": "sha512-nKb8qu4QsnpZTq/6JIXc72mzuzfh7BK4sUQkpQznPec2z4W7Ob2xYkcM4i5e1yGTBnbJVeFv/0sf3jcz85hclA==", + "version": "3.997.54", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.54.tgz", + "integrity": "sha512-/OczlavsS7U5sOodRO10Ug4R/AKFVZiXTstKipWe68EfjPkmdDFNXNl9FCJyXWkg1DG6RBm3qAuVB8FNW9O8KQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.5", - "@aws-sdk/signature-v4-multi-region": "^3.996.49", + "@aws-sdk/core": "^3.979.1", + "@aws-sdk/signature-v4-multi-region": "^3.996.51", "@aws-sdk/types": "^3.975.0", - "@smithy/core": "^3.36.1", + "@smithy/core": "^3.38.0", "@smithy/fetch-http-handler": "^5.8.0", - "@smithy/node-http-handler": "^4.12.1", + "@smithy/node-http-handler": "^4.14.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, @@ -1034,13 +1034,13 @@ } }, "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.996.49", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.49.tgz", - "integrity": "sha512-nvuAZNRrRRVCiARRKsYlQ0VnXiEQiq7vXPH+UA+LSo+kN3Djl9eo/V2r0udkw85PnsNshmv2Em1clld5M3j67w==", + "version": "3.996.51", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.51.tgz", + "integrity": "sha512-+aHvUugVBuMPXxgkPfWnTMyGdE/yWS8GHi5+qQ37HowS0YqzrbuaDLznj+Dtmtu5jgCdqz36gXmujOToUhNNWw==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.975.0", - "@smithy/signature-v4": "^5.7.4", + "@smithy/signature-v4": "^5.7.5", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, @@ -1049,12 +1049,12 @@ } }, "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.972.54", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.54.tgz", - "integrity": "sha512-W1fP3XL+Wu9Cd3hBxFiin9x1ufPOzBMIk+ggd1uNLVm7IguJS+SE6gkm53JgF5o4c/APtk9wv4QgS70r8O5ELA==", + "version": "3.972.58", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.58.tgz", + "integrity": "sha512-J6sdO8NybNf+mziF4PNskFOnMPY8dy4SA0hofHb8p3hOIrmyiwXtXHFjHkCKhIBuQPeFA9zAsfcNGLoITQJrxA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.5", + "@aws-sdk/core": "^3.979.1", "tslib": "^2.6.2" }, "engines": { @@ -1079,15 +1079,15 @@ } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.1155.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1155.0.tgz", - "integrity": "sha512-iIcQnYLhWuq9qOLQbIWFrxF3PF0svVeZvHnUS4DUnO4zUFmFHIyGyjqrntXG+e/5PUgfavV0pYfDIKHxTzN2LA==", + "version": "3.1161.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1161.0.tgz", + "integrity": "sha512-ypbXSofNcSHwf/KA40bFCXJb0MLaAKrfT7sot4dFaA+2lsuXNah4zLRJIAU7Qmvs9PFJ6rnY0bZFEC+mE7attA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.5", - "@aws-sdk/nested-clients": "^3.997.50", + "@aws-sdk/core": "^3.979.1", + "@aws-sdk/nested-clients": "^3.997.54", "@aws-sdk/types": "^3.975.0", - "@smithy/core": "^3.36.1", + "@smithy/core": "^3.38.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, @@ -1137,22 +1137,22 @@ } }, "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.972.51", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.972.51.tgz", - "integrity": "sha512-TJCBtOq0sUXWOCzxk8NAI5T2GIzZ8I2juAVvNi1MT8kAAGAXQyyCbvl0kAehHG0gS+P44wTn/ajxfFmf3QQ+3g==", + "version": "3.972.55", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.972.55.tgz", + "integrity": "sha512-S/h10N8J7nVEA2g5YNbc0F/sAQV7tKFkELqR9pLu6HAe9VcvkSlbzZxJwoMM5fSvoMhox+LYpHB+Hw5DbODE+g==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.5", + "@aws-sdk/core": "^3.979.1", "tslib": "^2.6.2" } }, "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.973.66", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.973.66.tgz", - "integrity": "sha512-mVHJnyJsyqVLI7iq3d5+uux27RtH/auRwVDb+26XRkN/k/TW3wfbxDi2i8904Gnvf7YZlg9yljM1kFVFqx2CWw==", + "version": "3.973.70", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.973.70.tgz", + "integrity": "sha512-z6YNhIWKxySFzVv0mvtUTZ9cMbrJ16PpHupNUYMCcqU4oEoGKoK4j+hHPEVmQS84xngKbbD8JiI6kjIXJuiCnw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.5", + "@aws-sdk/core": "^3.979.1", "tslib": "^2.6.2" }, "engines": { @@ -1160,9 +1160,9 @@ } }, "node_modules/@aws-sdk/xml-builder": { - "version": "3.972.41", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.41.tgz", - "integrity": "sha512-ctjVSyCMegrWfXlx6VqzSBFI6UqmQ5ZlnfMhdLIiWmhoH8UAQxSCP5N3OpG7X3k4LnS7ou74C4mt20+bfTW2aQ==", + "version": "3.972.42", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.42.tgz", + "integrity": "sha512-smX8/gNP8Kjq+CWTBMZQLQ912vOQjiWExzwzvMO+eujPB5RY8IbtF0AGdUUXHKN7KoDWK9dIcuW0WG269kiP6w==", "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.19.0", @@ -1451,9 +1451,9 @@ } }, "node_modules/@cfworker/json-schema": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@cfworker/json-schema/-/json-schema-4.1.1.tgz", - "integrity": "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@cfworker/json-schema/-/json-schema-4.1.4.tgz", + "integrity": "sha512-20rPE8EX0dSg6JGOVe31HpqsLYZTDSlcFJZA5mpZqQxcZ71vh/SXCMX+UuHO1mMcZBDKeb752z+3L8IWa+0Svg==", "license": "MIT" }, "node_modules/@cloudflare/kv-asset-handler": { @@ -1481,9 +1481,9 @@ } }, "node_modules/@cloudflare/workerd-darwin-64": { - "version": "1.20261019.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20261019.1.tgz", - "integrity": "sha512-5ZILbAta/fBQvhmAYeCE7dKMMYIr7N9wJEX50fEGLKSIwGtvT0HzSfHNIK/O7cBQ4gHQ2WfIiPClvwcye6E0fA==", + "version": "1.20261023.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20261023.1.tgz", + "integrity": "sha512-lsEx0i7nR0PNG/erUFFHmQmiidEhLfDE5uQmAL9421/y4/ycuxDDOSaxu3VGGYFFPK5sduyUeMmjIlX4CwOBFg==", "cpu": [ "x64" ], @@ -1497,9 +1497,9 @@ } }, "node_modules/@cloudflare/workerd-darwin-arm64": { - "version": "1.20261019.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20261019.1.tgz", - "integrity": "sha512-cww2Ab6lVMxpTiEEjKmPI58p5EMo0vYTZSH+3WkqXJdQgc3ES66GuL247+U2B2YWajhhu2tqj7jySrXneU749g==", + "version": "1.20261023.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20261023.1.tgz", + "integrity": "sha512-5e79fHgabdDD8EgmjOhJ1S+xMsf6DbC7fL7IgTBizMnzOp0P2isDn7pYmUxI9AJZE9FVQ4RUilH/I36YNg+GQQ==", "cpu": [ "arm64" ], @@ -1513,9 +1513,9 @@ } }, "node_modules/@cloudflare/workerd-linux-64": { - "version": "1.20261019.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20261019.1.tgz", - "integrity": "sha512-4++VRi9vkQROYqrpZYOixIJuVDmujkwrT4kszsxNFlvcmsX6Mr2GumvxIN3gIixS3vJUh3Umdtkthqj5X0S3Xw==", + "version": "1.20261023.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20261023.1.tgz", + "integrity": "sha512-VHQ6jKFSihjR2+x1buby8XTdBLLJztVnYiB7OLOTYO8aDXB+c1Rd2410PdgFuFYc4AgFEk6PCJLyCLL5L1KBnw==", "cpu": [ "x64" ], @@ -1529,9 +1529,9 @@ } }, "node_modules/@cloudflare/workerd-linux-arm64": { - "version": "1.20261019.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20261019.1.tgz", - "integrity": "sha512-75PVtJOwfjScQ53sbLtQXI7D8yP1c21t+WEUPaYbqkzaz4ZqfR5wAd+zuWs8G5Q7kOj2SQIvEfY5Sn8Y+UVrnA==", + "version": "1.20261023.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20261023.1.tgz", + "integrity": "sha512-ilpRHzfDN8YFRM5CLRKuFw40x/LJBS89yVdhdQmxehlVDuBqjKpJB++zPFbTgKRRFaNupgnXc8RHktXAIhgCmQ==", "cpu": [ "arm64" ], @@ -1545,9 +1545,9 @@ } }, "node_modules/@cloudflare/workerd-windows-64": { - "version": "1.20261019.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20261019.1.tgz", - "integrity": "sha512-z49fvKTETMCnG6wnMcQwxx9CpryFceiKz04FRjq26EkhCyzRNJ6c3UXhyZz3r5f1CXnnBZc5DcU8ZouPkUJCqw==", + "version": "1.20261023.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20261023.1.tgz", + "integrity": "sha512-279WLVjNsMycAwr0EZNiNEf0l2SY6NUtsN80NwZzbTHzakmiN+WSx4w38muxPm+PAU0oSPPAPXKVD+H6Hya8xw==", "cpu": [ "x64" ], @@ -2791,9 +2791,9 @@ } }, "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", - "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.5.0.tgz", + "integrity": "sha512-zhvbbbdt1kbw7EJ9OQoxmQcuaNR9P5kTj7e/YdmOSU10aHWbycofJxA6jKfoEy3vlLdctOF9ZHaxLtmSm998wQ==", "license": "MIT", "engines": { "node": ">=12" @@ -3304,9 +3304,9 @@ } }, "node_modules/@opennextjs/aws": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@opennextjs/aws/-/aws-4.1.12.tgz", - "integrity": "sha512-tZm1rBTRY6MVOOPCxvx0oZIg3b2Z5u0TzDm9D4Wy1cZpMp8Rjfex20gRQeEn0o6dsXpVQCE8q65TjsCFJA5Fpw==", + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@opennextjs/aws/-/aws-4.1.14.tgz", + "integrity": "sha512-jV+rxEi5dNxnQbKBftWuIppVvemMrbfaUj0tXINJMEzX7crk1pkmsMwudgk2kgyak1T03/GSMSEpE8nzOKrkDA==", "license": "MIT", "dependencies": { "@ast-grep/napi": "^0.45.3", @@ -3331,7 +3331,13 @@ "open-next": "dist/index.js" }, "peerDependencies": { + "diff": "^9.0.0", "next": ">=15.5.28 <16 || >=16.4.1" + }, + "peerDependenciesMeta": { + "diff": { + "optional": true + } } }, "node_modules/@opennextjs/aws/node_modules/chalk": { @@ -3347,14 +3353,14 @@ } }, "node_modules/@opennextjs/cloudflare": { - "version": "1.20.13", - "resolved": "https://registry.npmjs.org/@opennextjs/cloudflare/-/cloudflare-1.20.13.tgz", - "integrity": "sha512-Rhcvsx/PlQzOslleCBttG+r2o16lOFazKbULxiN33CYexm8S1Fd00mn3tU2w76PIaqO9evzgVKInOpR/fTMc0Q==", + "version": "1.20.15", + "resolved": "https://registry.npmjs.org/@opennextjs/cloudflare/-/cloudflare-1.20.15.tgz", + "integrity": "sha512-Ft7GdS3oDnZ5cZpAowSqpFsjHuZCZ9zBcTjJwYeJGa7Uq32JG3DOlLXh8GW+gsMVWXTz8YTL20sMxG1u+k4NwQ==", "license": "MIT", "dependencies": { "@ast-grep/napi": "^0.45.3", "@dotenvx/dotenvx": "^2.33.0", - "@opennextjs/aws": "4.1.12", + "@opennextjs/aws": "4.1.14", "ci-info": "^4.2.0", "cloudflare": "^4.4.1", "comment-json": "^4.5.1", @@ -3730,9 +3736,9 @@ } }, "node_modules/@smithy/core": { - "version": "3.36.2", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.36.2.tgz", - "integrity": "sha512-oRw0rB9QSUiCZfEs23G5F4Af+b2rQt1lItIICWu5IejFNd+uPM8SWqiKLMpQ6bX1Q7slsLbjuoE2niErFtkVPQ==", + "version": "3.38.1", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.38.1.tgz", + "integrity": "sha512-U/mB2XiwTLaf+NSgLSWwJfptsMT/H8aBi0aTAA/E5e2/TagFeGCqAyhKGG0w0A9IWTkm4qYEmLoD/HB6aYfnRA==", "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.19.0", @@ -3965,13 +3971,13 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.12.1.tgz", - "integrity": "sha512-ThMkboGeONWXAelq9FvGsuJC4rOi+qyC4/zhUF58xYpxUg5sQKx2VXZYJmtNjr4dSuBJ1HeJXETQILCz3wOHvw==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.14.1.tgz", + "integrity": "sha512-/T6E1jzCL0fErS9uq8JqES/5yYQQ4ppVmmYHwXw2nlov8KFzLb+3QqHR5iAun1BsuAB50IPxZliV8YMgghLwhA==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.33.3", - "@smithy/types": "^4.18.0", + "@smithy/core": "^3.38.1", + "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, "engines": { @@ -3992,12 +3998,12 @@ } }, "node_modules/@smithy/signature-v4": { - "version": "5.7.4", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.7.4.tgz", - "integrity": "sha512-tHy0K0VtqNd5Y7Y41h0a0Lhh0L1GzC08dTWg0F7vRJWFtTENg7IZikf3wQkanYIRdb7ngoIPMTmqgUi401fEeQ==", + "version": "5.7.5", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.7.5.tgz", + "integrity": "sha512-WPjdr5oWOk1LArpI4pavu+fSo+/evi0Re1olxGvqwuZopd4gYR1UGCi+Soyn/IjmafqbWs+ZC+3AKxJS3ZpwUg==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.35.0", + "@smithy/core": "^3.38.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, @@ -5682,9 +5688,9 @@ } }, "node_modules/bowser": { - "version": "2.14.1", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz", - "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==", + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.15.0.tgz", + "integrity": "sha512-xic6KSZglg/I4ymp45fdM4GWfYpBK79Fxd/TOvJktlaPgQqqDxvmGwOPZl2V0dyeS3Lj5c180bLhXF43iyAjZQ==", "license": "MIT" }, "node_modules/brace-expansion": { @@ -6482,12 +6488,16 @@ "license": "MIT" }, "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.1.tgz", + "integrity": "sha512-Ev/hIyePLZtpL98x562lvPtenrUfGr0lfaf44HFZ+hDEwso7ccV9QAZrEVLg0hxNAzygaAFxy2dYaAKKM0G06A==", "license": "MIT", "engines": { "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/enquirer": { @@ -9258,9 +9268,9 @@ } }, "node_modules/marked": { - "version": "18.1.1", - "resolved": "https://registry.npmjs.org/marked/-/marked-18.1.1.tgz", - "integrity": "sha512-zEGgrtUG8crk+TrMacJSVABU4DYDDkS8uqgU3ohrn/2fW9C2AEfUrO3m6IhpIpQpl7YcVpiXXC9cSMWDANT3dg==", + "version": "18.1.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-18.1.2.tgz", + "integrity": "sha512-DfOnvLAQ6WYeWxWO1u4RyFOeqyHup6dks5PPqnoFiooU7ipRvidO/2j8plhAHIiZg574E9ZlzSjUiPMLAzFkgg==", "license": "MIT", "bin": { "marked": "bin/marked.js" @@ -9363,15 +9373,15 @@ } }, "node_modules/miniflare": { - "version": "5.20261019.2-beta", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-5.20261019.2-beta.tgz", - "integrity": "sha512-QNgCA/axizZ7kv7RV3nwbRoz+w4watBlwIsyXjwgYHw4eSw5BrR1F2152s3Kz+Bzy/YxjKFHrZl3LzBRbPgYoQ==", + "version": "5.20261023.3-beta", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-5.20261023.3-beta.tgz", + "integrity": "sha512-thdXzlktu5GE1o/BlmMAPeJ0Cu6Tu5LpMbRlyfRrpVvMRrhd6p52TMqcITCZZLVzt1UO92VGBkLDhLOpryUAyQ==", "license": "MIT", "dependencies": { "@cspotcode/source-map-support": "0.8.1", "sharp": "0.35.5", - "undici": "7.29.1", - "workerd": "1.20261019.1", + "undici": "7.30.1", + "workerd": "1.20261023.1", "ws": "8.21.0", "youch": "4.1.0-beta.10" }, @@ -10098,9 +10108,9 @@ } }, "node_modules/postcss": { - "version": "8.5.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.31.tgz", - "integrity": "sha512-YUbWYCgErwnMAVkjUnYa+KlMBzGpHwiIt/DE7f0G33AuX2LH9UcTk6KgWxd3zRU+pN3u/C+Xl3EnMLM4Hea7Sg==", + "version": "8.5.32", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.32.tgz", + "integrity": "sha512-LJlKCCAUFKOuZxipUdYw1NzIITEsPcgyOG9LkvRJvuKj/bRzbuRY0ARXvL0pWGpsZvn8jku+LMep2eU0seHp8g==", "dev": true, "funding": [ { @@ -10376,9 +10386,9 @@ } }, "node_modules/qs": { - "version": "6.16.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.1.tgz", - "integrity": "sha512-5Px6J7rfHBAGUhNQ3es4YzcOdBBw0SDcVtThJml4RdnKw4tKs2epA0kPZ+K8X+WiHktT93RHVcQ/3ym5aTCgtg==", + "version": "6.17.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.17.1.tgz", + "integrity": "sha512-jd3COkaKVgGUW+ZHyEHCG9bH7RoaaiWuMgzsIBjUMVjLAaWGWpoUAVwljVvBHxRu/2qEHnadLGZrkutYUBPQJA==", "license": "BSD-3-Clause", "dependencies": { "es-define-property": "^1.0.1", @@ -12417,9 +12427,9 @@ } }, "node_modules/workerd": { - "version": "1.20261019.1", - "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20261019.1.tgz", - "integrity": "sha512-UUYRUq/7DmeCl11YriNbcVbjUZVLtt8uuCXFu+nz77hGFf8d4DEVXoCYtnA+MlBatE4+wxoj47DVqzK3xGBAiw==", + "version": "1.20261023.1", + "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20261023.1.tgz", + "integrity": "sha512-NeofJFyaYNpqKpFRJW3ApuonaerPd6/JWlfTtLDYf2A6ygqrDEFnYgEXmc0hhOxqnJsSM+kXJN6VebPojzRrsg==", "hasInstallScript": true, "license": "Apache-2.0", "bin": { @@ -12429,27 +12439,27 @@ "node": ">=16" }, "optionalDependencies": { - "@cloudflare/workerd-darwin-64": "1.20261019.1", - "@cloudflare/workerd-darwin-arm64": "1.20261019.1", - "@cloudflare/workerd-linux-64": "1.20261019.1", - "@cloudflare/workerd-linux-arm64": "1.20261019.1", - "@cloudflare/workerd-windows-64": "1.20261019.1" + "@cloudflare/workerd-darwin-64": "1.20261023.1", + "@cloudflare/workerd-darwin-arm64": "1.20261023.1", + "@cloudflare/workerd-linux-64": "1.20261023.1", + "@cloudflare/workerd-linux-arm64": "1.20261023.1", + "@cloudflare/workerd-windows-64": "1.20261023.1" } }, "node_modules/wrangler": { - "version": "4.152.0", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.152.0.tgz", - "integrity": "sha512-lmXTF1sCDEqBaEMpreGczvg/x3AIh1d/qxc4BN7utQ4jEDwdA4YG0L6kr3ev0TtVLHRb6QLySNAOAunwn+dHeA==", + "version": "4.156.0", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.156.0.tgz", + "integrity": "sha512-kVLAShuLiNgE3jcrmXcymDf/iPKDPrSxACe5ueugW/33HeNB9E9CkKYchudCeBwgmsnl4k+1zt4NO7vqb/fRuQ==", "license": "MIT OR Apache-2.0", "dependencies": { "@cloudflare/kv-asset-handler": "0.5.0", "@cloudflare/unenv-preset": "2.16.2", "blake3-wasm": "2.1.5", "esbuild": "0.28.2", - "miniflare": "5.20261019.2-beta", + "miniflare": "5.20261023.3-beta", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", - "workerd": "1.20261019.1" + "workerd": "1.20261023.1" }, "bin": { "cf-wrangler": "bin/cf-wrangler.js", @@ -12463,7 +12473,7 @@ "fsevents": "2.3.3" }, "peerDependencies": { - "@cloudflare/workers-types": "^5.20261019.1" + "@cloudflare/workers-types": "^5.20261023.1" }, "peerDependenciesMeta": { "@cloudflare/workers-types": { diff --git a/package.json b/package.json index 70a6755..8c1828f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "toolbelt", - "version": "0.11.0", + "version": "0.11.1", "private": true, "description": "A developer's toolbelt: fast, privacy-first browser tools — JSON formatter, JWT decoder, Base64, URL encoder, invoice generator, unicode inspector. By CODERCOPS.", "license": "MIT", @@ -53,8 +53,8 @@ "lighthouse": "npx --yes @lhci/cli@0.14.x collect && (npx --yes @lhci/cli@0.14.x assert || true) && node scripts/lighthouse-summary.mjs" }, "dependencies": { - "@cfworker/json-schema": "^4.1.1", - "@opennextjs/cloudflare": "^1.20.13", + "@cfworker/json-schema": "^4.1.4", + "@opennextjs/cloudflare": "^1.20.15", "clsx": "^2.1.1", "dompurify": "^3.4.17", "json5": "^2.2.3", @@ -62,7 +62,7 @@ "jspdf": "^4.2.1", "lossless-json": "^4.3.3", "lucide-react": "^0.468.0", - "marked": "^18.1.1", + "marked": "^18.1.2", "next": "^16.4.3", "prismjs": "^1.30.0", "react": "^19.3.1", @@ -85,7 +85,7 @@ "tailwindcss": "^3.4.1", "typescript": "^5", "vitest": "^4.1.12", - "wrangler": "^4.152.0" + "wrangler": "^4.156.0" }, "overrides": { "@node-minify/core": { diff --git a/public/.well-known/funding-manifest-urls b/public/.well-known/funding-manifest-urls index 42647b8..8cd6afd 100644 --- a/public/.well-known/funding-manifest-urls +++ b/public/.well-known/funding-manifest-urls @@ -1 +1,2 @@ https://oss.codercops.com/funding.json +https://raw.githubusercontent.com/codercops/.github/main/funding.json