-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Contributor-reported fixes: paths with spaces, redactor gaps, safe Slack #476
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 1 commit
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| /** | ||
| * Cut points that never split what a reader (or Slack's parser) treats as one | ||
| * unit: a surrogate pair (emoji), a `<url|label>` entity, or an inline code / | ||
| * bold / strike span. Backing off never goes past `floor` characters (a | ||
| * pathological single entity longer than the whole budget is cut anyway — | ||
| * better a broken link than an empty message). | ||
| */ | ||
| const FLOOR_FRACTION = 0.25; | ||
|
|
||
| function isLowSurrogate(code: number): boolean { | ||
| return code >= 0xdc00 && code <= 0xdfff; | ||
| } | ||
|
|
||
| /** The largest index <= max that is safe to cut at. */ | ||
| export function safeCutIndex(text: string, max: number): number { | ||
| if (text.length <= max) return text.length; | ||
| let cut = max; | ||
| // never split a surrogate pair | ||
| if (isLowSurrogate(text.charCodeAt(cut))) cut--; | ||
| const floor = Math.max(1, Math.floor(max * FLOOR_FRACTION)); | ||
| // never split a <...> entity (links, mentions): back off to before the "<" | ||
| const lastOpen = text.lastIndexOf("<", cut - 1); | ||
| if (lastOpen >= 0) { | ||
| const lastClose = text.lastIndexOf(">", cut - 1); | ||
| if (lastOpen > lastClose && lastOpen >= floor) cut = lastOpen; | ||
| } | ||
| // never leave an unbalanced formatting run open right at the cut | ||
| for (const marker of ["`", "*", "~"]) { | ||
| let count = 0; | ||
| for (let i = 0; i < cut; i++) if (text[i] === marker) count++; | ||
| if (count % 2 === 1) { | ||
| const back = text.lastIndexOf(marker, cut - 1); | ||
| if (back >= floor) cut = Math.min(cut, back); | ||
| } | ||
| } | ||
| if (isLowSurrogate(text.charCodeAt(cut))) cut--; | ||
| return Math.max(cut, 1); | ||
| } | ||
|
|
||
| /** Split text into chunks of at most `max`, cutting only at safe points. */ | ||
| export function safeChunks(text: string, max: number): string[] { | ||
| const chunks: string[] = []; | ||
| let rest = text; | ||
| while (rest.length > 0) { | ||
| const cut = safeCutIndex(rest, max); | ||
| chunks.push(rest.slice(0, cut)); | ||
| rest = rest.slice(cut); | ||
| } | ||
| return chunks.length ? chunks : [""]; | ||
| } | ||
|
|
||
| /** Truncate to at most `max` characters (plus ellipsis) at a safe point. */ | ||
| export function safeClip(text: string, max: number): string { | ||
| if (text.length <= max) return text; | ||
| return `${text.slice(0, safeCutIndex(text, max))}…`; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| import { test } from "node:test"; | ||
| import assert from "node:assert/strict"; | ||
| import { jsonbStringify } from "../src/persistence/durable-map.ts"; | ||
|
|
||
| test("NUL characters are dropped for jsonb", () => { | ||
| const out = jsonbStringify({ name: "a\u0000b" }); | ||
| assert.equal(out, '{"name":"ab"}'); | ||
| }); | ||
|
|
||
| test("a lone high surrogate (emoji cut in half by slice) becomes U+FFFD", () => { | ||
| const cut = "prefix 😀".slice(0, 8); // strands the high surrogate | ||
| const out = JSON.parse(jsonbStringify({ title: cut })) as { title: string }; | ||
| assert.ok(!/[\uD800-\uDBFF](?![\uDC00-\uDFFF])/.test(out.title), "no stranded surrogate survives"); | ||
| assert.ok(out.title.includes("\uFFFD")); | ||
| }); | ||
|
|
||
| test("well-formed strings, keys, arrays, and nesting pass through untouched", () => { | ||
| const value = { a: "hello 😀", list: ["x", { deep: "ok" }], n: 3, b: true, z: null }; | ||
| assert.equal(jsonbStringify(value), JSON.stringify(value)); | ||
| }); | ||
|
|
||
| test("keys are sanitized too", () => { | ||
| const out = jsonbStringify({ ["k\u0000ey"]: 1 }); | ||
| assert.equal(out, '{"key":1}'); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.