From e680d55ca2d7f6b213e2a8693eba6be39163ba41 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Thu, 31 Oct 2024 18:09:49 +0000 Subject: [PATCH] Filter out invalid facets in RichText (#2933) * add failing test * simplify test * check facet was removed * filter out invalid facets * changeset --- .changeset/swift-sheep-confess.md | 5 +++ packages/api/src/rich-text/rich-text.ts | 8 +++-- packages/api/tests/rich-text.test.ts | 44 +++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 .changeset/swift-sheep-confess.md diff --git a/.changeset/swift-sheep-confess.md b/.changeset/swift-sheep-confess.md new file mode 100644 index 00000000000..e4723e584ca --- /dev/null +++ b/.changeset/swift-sheep-confess.md @@ -0,0 +1,5 @@ +--- +"@atproto/api": patch +--- + +Fix handling of invalid facets in RichText diff --git a/packages/api/src/rich-text/rich-text.ts b/packages/api/src/rich-text/rich-text.ts index 261ea7e4e68..a9c037b33bf 100644 --- a/packages/api/src/rich-text/rich-text.ts +++ b/packages/api/src/rich-text/rich-text.ts @@ -169,7 +169,7 @@ export class RichText { this.facets = entitiesToFacets(this.unicodeText, props.entities) } if (this.facets) { - this.facets.sort(facetSort) + this.facets = this.facets.filter(facetFilter).sort(facetSort) } if (opts?.cleanNewlines) { sanitizeRichText(this, { cleanNewlines: true }).copyInto(this) @@ -378,7 +378,11 @@ export class RichText { } } -const facetSort = (a, b) => a.index.byteStart - b.index.byteStart +const facetSort = (a: Facet, b: Facet) => a.index.byteStart - b.index.byteStart + +const facetFilter = (facet: Facet) => + // discard negative-length facets. zero-length facets are valid + facet.index.byteStart <= facet.index.byteEnd function entitiesToFacets(text: UnicodeString, entities: Entity[]): Facet[] { const facets: Facet[] = [] diff --git a/packages/api/tests/rich-text.test.ts b/packages/api/tests/rich-text.test.ts index 5c30edcf94f..7683babb3aa 100644 --- a/packages/api/tests/rich-text.test.ts +++ b/packages/api/tests/rich-text.test.ts @@ -658,4 +658,48 @@ describe('RichText#segments', () => { }, ]) }) + + it("doesn't duplicate text when negative-length facets are present", () => { + const input = { + text: 'hello world', + facets: [ + // invalid zero-length + { + features: [], + index: { + byteStart: 6, + byteEnd: 0, + }, + }, + // valid normal facet + { + features: [], + index: { + byteEnd: 11, + byteStart: 6, + }, + }, + // valid zero-length + { + features: [], + index: { + byteEnd: 0, + byteStart: 0, + }, + }, + ], + } + + const rt = new RichText(input) + + let output = '' + for (const segment of rt.segments()) { + output += segment.text + } + + expect(output).toEqual(input.text) + + // invalid one should have been removed + expect(rt.facets?.length).toEqual(2) + }) })