PackageTrack
Sign in Get early access

bluesky_text

Provides the easiest and most powerful way to analyze the text for Bluesky Social.

1.8.0 3.7K downloads/mo #4278 most downloaded on pub.dev myConsciousness/atproto.dart

What this package is like to depend on

Last release 15 days ago

08 Aug 2026

Ships unpredictably

gaps range from 8 days to 8 months

Nearly every release is documented

notes for 74 of 75 stable releases

1 version withdrawn

withdrawn after publishing

3 years old

78 releases · first in 2023

15 releases in the last 12 months

see the full history below

Release timeline

78 releases · May 2023 to Aug 2026
2024 2025 2026
Release Pre-release Withdrawn

Releases

latest 60 of 78
  1. 1.8.0 08 Aug 2026
    Release notes
    • feat: Entity.toFacet, Entities.toFacetsResult and Entities.toFacets accept an optional http.Client? client, forwarded to the built-in handle-resolution call. It is used only when no resolver is supplied and exists so the default (network) mention path can be driven against a mock transport. Existing callers are unaffected — the parameter is optional and additive.

    • test: the mention-facet tests no longer reach the live network. Seven tests resolved real handles against bsky.social and asserted the returned DID, so they broke on any outage, rate limit, or handle change and could not run offline. They now inject a MockClient (via the new client hook) that answers resolveHandle locally, covering the success path, the 4xx "unresolvable handle → no facet" swallow, the 5xx "surface the error" rethrow, and that service reaches the request. One meaningless "does not throw" assertion was replaced with a real output check.

    • security: isLinkFacade no longer passes a display text it cannot parse. It used to return false — "not a facade" — both when the text named no host and when it named one that could not be read, so every shape the parser did not recognize went through unflagged.

      • Display text is now folded the way IDNA (UTS-46) folds it before comparison: the three full-stop variants ( U+3002, U+FF0E, U+FF61) become ., and the fullwidth ASCII block (U+FF01–U+FF5E) becomes plain ASCII. bsky。app and bsky.app are hosts a browser really resolves to bsky.app — the reader sees a domain they trust and the link works — and they were not flagged when pointed at another host. The host of the target is folded the same way, so a link written with a fullwidth host still matches an ASCII display text. This is not homograph detection, which stays out of scope: only the characters IDNA maps onto ASCII are folded, and a Cyrillic look-alike is still a genuinely different host.
      • The authority now ends at a backslash as well as at /, ? and #. The WHATWG URL Standard makes \ a synonym for / in a special scheme, so a browser reads the host of https://bsky.app\@evil.example.com as bsky.app; that went unflagged while its https://bsky.app\evil.example.com sibling was flagged.
      • Wrappers and sentence punctuation are stripped from both ends, and a leading // is read as a protocol-relative reference. "bsky.app", <bsky.app>, (bsky.app), //bsky.app and bsky.app, were all unflagged while bsky.app/ and [bsky.app] were flagged.
    • feat: added checkLinkFacade and LinkFacadeVerdict, which report facade, honest, notAUrl or undetermined instead of the single boolean. undetermined is the case a boolean cannot express — the display text reads as a URL but yields no host, it is host-shaped under an internationalized TLD this package carries no data for, or the link itself has no host to compare against. isLinkFacade is unchanged and is now checkLinkFacade(...) == LinkFacadeVerdict.facade.

    • fix: PostFacet.fromJson refuses malformed JSON instead of leaking a _TypeError out of the render path. A facet with no index, an index that is not an object, a byteStart/byteEnd that is not a whole number, or a features that is not a list now throws a FormatException naming the field; a feature entry that is not an object is skipped, exactly as an unknown $type already was, and a whole-numbered double offset such as 2.0 is accepted. Added PostFacet.tryFromJson, which returns null where fromJson throws, so one bad facet in a fetched post drops out instead of taking the whole render down.

    • perf: extracting entities is no longer quadratic in the length of a run of dotted labels or of a path of balanced parens. A run with no usable TLD was rescanned once per label, with the TLD alternation — thousands of literals wide — as the constant, and a path was matched against a star over an alternation that retried every prefix. 'a.' * 150, a legal 300-character post, took 43 ms and now takes 0.13 ms; 3000 characters took 11 s and now take 1.3 ms; https://a.com/ followed by 3000 (a) groups took 4.9 s and now takes 0.5 ms. Ordinary text is unaffected in either direction. Extraction is unchanged to the entity: the same facets come out of the existing suite and of a 10,344-entry corpus of real and generated posts, and the skip stands down entirely on any text where it could have changed an answer.

    Open source →
  2. 1.7.0 26 Jul 2026

    Nothing published for this version

  3. 1.6.0 26 Jul 2026
    Release notes
    • feat: added isLinkFacade, which reports when a facet's display text reads as a URL or host that does not match the host its link points at — the link-facade phishing shape. Display text that is not URL-looking is never flagged: a bare host is flagged only when this package would itself have linkified that same text. Hosts match on equality or a subdomain relation, ignoring www., trailing root dots, case, ports, paths and punycode encoding. It does not detect homographs, redirects or deceptive non-URL text, and says so.
    • feat: added toDisplayHost, which decodes a punycode (xn--) host into the Unicode it stands for, so a link warning can show what the host actually says.
    • fix: the facet maps returned by Entity.toFacet, Entities.toFacets, Entities.toFacetsResult and BlueskyText.toPostData are now wire-complete. The facet carries "$type": "app.bsky.richtext.facet" and its index carries "$type": "app.bsky.richtext.facet#byteSlice", matching what the lexicon models serialize to — previously only the individual features were typed, and the returned map did not even pass RichtextFacet.validate. Callers going through feed.post.create never noticed, since the generated converter fills them in; callers assembling a record map themselves, for com.atproto.repo.applyWrites or to compute a record CID locally, silently produced a record that differed from the converter's output. The "no facet" results (an unresolvable handle, a raw markdown link) are still an empty map.
    Open source →
  4. 1.5.4 22 Jul 2026
    Release notes
    • chore: bump xrpc to ^1.1.3.
    Open source →
  5. 1.5.3 18 Jul 2026
    Release notes
    • chore: bump dev dependency bluesky to ^2.1.0.
    Open source →
  6. 1.5.2 16 Jul 2026
    Release notes
    • docs: added a runnable inline usage snippet to the README — instantiating BlueskyText, extracting entities (.entities/.handles/.links), and converting with toPostData({service, resolver}) to text/facets/unresolvedHandles.
    • chore: bump xrpc to ^1.1.2.
    Open source →
  7. 1.5.1 16 Jul 2026
    Release notes
    • fix: email addresses are no longer partially linkified (the domain of [email protected] is no longer turned into a link).
    • fix: URL paths containing non-ASCII characters are no longer truncated, so links such as https://ja.wikipedia.org/wiki/日本語 resolve to the full URL.
    Open source →
  8. 1.5.0 13 Jul 2026
    Release notes
    • FEAT: Added BlueskyText.overflow, which returns a TextLengthOverflow describing the range of the text that exceeds the post-length limit (more than 300 graphemes or 3000 UTF-8 bytes), or null when it is within both. The boundary is reported in UTF-16, UTF-8 byte and grapheme coordinates so a UI can, for example, split the value with TextLengthOverflow.utf16Start and render the overflowing tail in red via a Flutter TextSpan.
      • The boundary always lands on a grapheme cluster boundary, so emoji and other multi-code-unit characters are never split.
      • When the boundary would fall inside an entity (handle, link, tag, cashtag or markdown link) it is snapped back to that entity's start, so the entity is treated atomically — wholly within the limit or wholly in the overflow.
      • Because the range is derived from the value, calling .format().overflow reports the overflow of the formatted text (markdown expanded, links shortened), which is what is displayed and posted.
    • FEAT: Added BlueskyText.segments, which partitions the value into non-overlapping, gap-free TextSegments in document order. Each segment carries UTF-16 offsets, the entity it belongs to (if any) and whether it lies in the overflow region, so a Flutter TextEditingController can color links, handles and tags together with the over-limit tail (for example in red) in a single pass — without merging the byte-based entity indices and the overflow range by hand. Concatenating every TextSegment.text reproduces the value, and no segment is ever split across an entity boundary.
    • FEAT: Added renderFacets(text, facets) and PostFacet for displaying a fetched post: it partitions the text into TextSegments using the server-provided facets (authoritative mentions/links/tags, mentions already carrying their DID) instead of re-detecting entities. Each segment exposes a FacetFeature with the resolved DID / URI / tag, so a Flutter client can style received posts with one TextSpan builder shared with the compose path. PostFacet.fromJson parses the app.bsky.richtext.facet API shape, and the byte→UTF-16 Utf16IndexConverter is now public.
    • FEAT: Entities.toFacets / the new Entities.toFacetsResult accept a HandleResolver, so mention DID resolution can be served from a cache or batched instead of the built-in per-handle network call. toFacetsResult additionally returns the handles that failed to resolve, so a client can warn the user rather than silently posting a mention-less message.
    • FEAT: Added BlueskyText.formatted (the memoized, posting-ready form) and BlueskyText.toPostData(...), which formats and resolves facets in one call — the only correct order, since markdown links become link facets only after formatting — returning (text, facets, unresolvedHandles).
    • FIX: split() now budgets each chunk against both post limits (300 graphemes and 3000 UTF-8 bytes). Previously it budgeted graphemes only, so a byte-heavy chunk — e.g. many multi-byte ZWJ emoji — could stay under 300 graphemes yet exceed 3000 bytes and still be rejected by the server.
    • FIX: split() on a format()ted instance now splits the original text instead of the lossy formatted value, so format().split() behaves exactly like split() on the original. Splitting formatted text and re-extracting previously corrupted facets — a shortened link's uri became its truncated display text and a markdown link's facet vanished — because the chunks dropped the position-bound replacements. Each chunk is a raw, independently-formattable piece; format each one after splitting (e.g. via chunk.toPostData()).
    • FIX: split() now breaks on any Unicode whitespace — newlines, tabs and the ideographic (full-width) space U+3000 — not just the ASCII space. Previously a multi-line or CJK post with no ASCII spaces was treated as one giant word and hard-split mid-word (e.g. word44 became wo | rd44). The author's newlines and spacing are now preserved within each chunk, and no chunk starts or ends with whitespace. A markdown link is also kept atomic, so one straddling a chunk boundary is no longer torn open (which would drop its facet).
    • PERF: BlueskyText now lazily memoizes every derived value (length, handles, entities, overflow, segments, format()…), so touching several properties of one instance in a Flutter build costs one analysis instead of one per property (~1.6x faster when touching seven). Note: as a result BlueskyText is no longer constconst BlueskyText(...) must become BlueskyText(...).
    • PERF: The length-limit hot paths (polled on every keystroke in a Flutter editor) avoid the regex-based entity extraction entirely unless it is needed. isLengthLimitExceeded and overflow fall back to a cheap grapheme scan when within the limit, segments resolves the entities only once (instead of extracting them again via overflow), and the grapheme scan counts UTF-8 bytes without allocating an intermediate byte list. For over-limit text this cuts isLengthLimitExceeded ~18x and segments ~2x; a 300-grapheme post segments in well under 0.1 ms.
    Open source →
  9. 1.4.0 13 Jul 2026
    Release notes
    • FIX: Fixed crashes on IDN (internationalized domain) URLs. Text containing URLs such as https://日本語.jp or https://日本.example.com no longer throws from .links / .entities / .format() or markdown-link extraction.
    • BREAKING: Aligned several detectors with Bluesky's official implementation:
      • The mention regex is now case-insensitive, so @Alice.Bsky.Social and @SHINYAKATO.DEV are detected.
      • The hashtag "emoji" character class was rewritten to drop whitespace, U+3000, line separators, CJK punctuation, and lone surrogate ranges, so #タグ こんにちは is one tag and #tag3 #tag4 are both preserved.
      • Tag facet values strip a single leading #, and the tag length limit is now 64 graphemes (excluding #), matching the spec.
      • Mention preceding-character rules follow the official (^|\s|\() boundary (the leftover twitter-text RT: alternative is removed).
      • Full-width is now recognized as a hashtag sign (partial; #tag1#tag2 splitting is still deferred).
    • FIX: The chunk splitter now budgets by grapheme count instead of UTF-16 length, so emoji-heavy text is packed correctly. split() also propagates the active format() replacements / link config to each chunk, so shortened display strings are no longer re-extracted into truncated facet URLs.
    • FIX: http(s) scheme detection is case-insensitive and no longer double-prefixes (HTTPS://EXAMPLE.COM is handled; httpstatus.io is not a scheme).
    • FIX: Overlapping facets are resolved by priority (link > mention > tag > cashtag), so an @handle or #fragment inside a URL no longer produces a duplicate facet.
    • FIX: Enforce the lexicon's 3000 UTF-8 byte limit alongside the 300 grapheme limit; misc fixes to isEmojiOnly, the shorten threshold, and toFacet error propagation.
    • PERF: toUtf8Index is now incremental (no per-call full re-encode).
    • TEST: Added a WS-6 regression suite (IDN input, upper-case TLD/scheme, format()split(), non-BMP splitting, facet overlap) and de-duplicated test names. Where existing tests pinned non-official behavior, they were updated to match the reference implementation.
    Open source →
  10. 1.3.0 09 Jul 2026
    Release notes
    • BREAKING: Aligned cashtag detection with Bluesky's official CASHTAG_REGEX in @atproto/api. Detection is now stricter and consistent with the reference implementation:
      • The ticker symbol is limited to 1–5 ASCII characters ([A-Za-z][A-Za-z0-9]{0,4}); longer candidates like $GOOGLE are rejected.
      • A cashtag must be preceded by a leading boundary — the start of the string, a whitespace character (including U+3000 / U+00A0), or an ASCII ( — and followed by a trailing boundary — whitespace, the end of the string, or one of the ASCII punctuation characters . , ; : ! ? ) " ' or (U+2019). As a result, cashtags glued to Japanese (or other non-delimiting) text such as 日本株$AAPL or $AAPLです are intentionally not detected, matching the official Bluesky behavior. Full-width delimiters like ($AAPL) and $AAPL。 are likewise not treated as boundaries.
      • The ticker is normalized to upper case and the emitted tag facet keeps the leading $ (e.g. $aapl$AAPL), mirroring the official cashtag facet.
    • REGEX: Removed the cashtagBoundary and endCashtag patterns; the validCashtag pattern now embeds the official leading/trailing boundaries directly. cashSigns and validCashtag remain exported from package:bluesky_text/regex.dart.
    • TEST: Updated and expanded the cashtag test suite to pin the official-compliant boundaries, ticker length limit, upper-case normalization, and Japanese-adjacency behavior.
    Open source →
  11. 1.2.1 21 May 2026
    Release notes
    • fix: do not use .substring when creating the cashtag entities.
    Open source →
  12. 1.2.0 21 May 2026
    Release notes
    • FEATURE: Added support for cashtag detection (e.g. $AAPL, $tsla).
      • New BlueskyText.cashtags getter returns all cashtag entities along with their byte indices.
      • New EntityType.cashtag and Entity.isCashtag for type-safe handling.
      • Cashtags are also surfaced from BlueskyText.entities alongside handles, links, and hashtags.
      • Cashtags are converted to app.bsky.richtext.facet#tag features when calling toFacets(), mirroring how Bluesky represents tag-like facets.
      • Symbols must start with an ASCII letter and may contain ASCII letters or digits afterwards. Bare dollar amounts like $1000 are not detected as cashtags.
    • REGEX: Added cashSigns, cashtagBoundary, endCashtag, and validCashtag patterns under package:bluesky_text/regex.dart.
    • TEST: Added 30+ test cases covering cashtag extraction, boundary detection, length limits, byte index accuracy, and interoperability with hashtags, handles, and links.
    Open source →
  13. 1.1.1 30 Sep 2025
    Release notes
    • FIX: Downgraded characters dependency from ^1.4.1 to ^1.4.0 for compatibility
    Open source →
  14. 1.1.0 27 Sep 2025
    Release notes
    • FEATURE: Added support for Unicode space characters as hashtag delimiters. (#1933)
      • Full-width space (U+3000) and other Unicode space characters are now recognized as valid hashtag boundaries
      • Improved compatibility with Bluesky's official client behavior
      • Enhanced hashtag recognition for international users, especially Japanese users
    • ENHANCEMENT: Comprehensive test coverage expansion
      • Added extensive tests for hashtags, handles, and links with real-world scenarios
      • Added security tests to prevent Unicode normalization attacks and ReDoS vulnerabilities
      • Added performance tests with large numbers of entities
      • Added multilingual support tests for various languages
      • Added boundary detection tests for edge cases
    • IMPROVEMENT: Enhanced hashtag boundary detection with support for:
      • Ideographic space (U+3000) - commonly used in Japanese text
      • No-break space (U+00A0) - commonly used in HTML
      • Regular space (U+0020) - standard ASCII space
    • TEST: Added 22+ new comprehensive test cases covering edge cases and real-world usage patterns
    Open source →
  15. 1.0.4 25 Sep 2025
    Release notes
    • DEPENDENCY: Updated xrpc dependency to ^1.0.3 for compatibility with at_primitives consolidation.
    Open source →
  16. 1.0.3 06 Aug 2025
    Release notes
    • chore: update example.
    Open source →
  17. 1.0.2 30 Jul 2025
    Release notes
    • Fix SDK constraint to '">=3.8.0 <4.0.0"'.
    Open source →
  18. 1.0.1 28 Jul 2025
    Release notes
    • chore: optimized docs.
    Open source →
  19. 1.0.1-legacy 28 Jul 2025 pre-release

    Nothing published for this version

  20. 1.0.0 18 Jul 2025
    Release notes
    • Bump SDK constraint to '^3.8.0'.
    Open source →
  21. 1.0.0-legacy 24 Jul 2025 pre-release

    Nothing published for this version

  22. 0.7.2 07 Dec 2024
    Release notes
    • Bump xrpc.
    Open source →
  23. 0.7.1 20 Nov 2024
    Release notes
    • Exposed bluesky_text/regex.dart.
    Open source →
  24. 0.7.0 16 Nov 2024
    Release notes
    • Bump xrpc.
    Open source →
  25. 0.6.10 04 Nov 2024
    Release notes
    • Bump xrpc.
    Open source →
  26. 0.6.9 22 Feb 2024
    Release notes
    • Bump SDK constraint to '^3.3.0'.
    Open source →
  27. 0.6.8 18 Feb 2024
    Release notes
    • Upgraded xrpc.
    Open source →
  28. 0.6.7 27 Jan 2024
    Release notes
    • Upgraded xrpc.
    Open source →
  29. 0.6.6 30 Dec 2023
    Release notes
    • Upgraded xrpc.
    Open source →
  30. 0.6.5 29 Dec 2023
    Release notes
    • Upgraded xrpc.
    Open source →
  31. 0.6.4 21 Dec 2023
    Release notes
    • Upgraded xrpc.
    Open source →
  32. 0.6.3 19 Dec 2023
    Release notes
    Open source →
  33. 0.6.2 15 Nov 2023
    Release notes
    • Upgraded xrpc. (#999)
    Open source →
  34. 0.6.1 13 Nov 2023
    Release notes
    • Improved extraction algo for markdown links.
    Open source →
  35. 0.6.0 09 Nov 2023
    Release notes
    • Upgraded xrpc. (#989)
    Open source →
  36. 0.5.21 01 Nov 2023
    Release notes
    • Exposed .getGraphemeLength.
    Open source →
  37. 0.5.20 31 Oct 2023
    Release notes
    • Upgraded xrpc package.
    Open source →
  38. 0.5.19 28 Oct 2023
    Release notes
    • Exposed .isEmojiOnly as a function.
    Open source →
  39. 0.5.18 28 Oct 2023
    Release notes
    • Added .isEmojiOnly property. It can determine if the text contains only emojis.
    Open source →
  40. 0.5.17 26 Oct 2023
    Release notes
    • Supported hashtag with emoji strings. (#907)
    Open source →
  41. 0.5.16 25 Oct 2023
    Release notes
    • Supported hashtag with - separated strings. (#908)
    Open source →
  42. 0.5.15 21 Oct 2023
    Release notes
    • Improved markdown extraction algo. You can use as a link if the URL contains markdown symbols, such as https://wikipedia.com//track/We_Up_(Album_Version_(Edited)).
    Open source →
  43. 0.5.15-preview.0 17 Oct 2023 pre-release withdrawn

    Nothing published for this version

  44. 0.5.14 12 Oct 2023
    Release notes
    • Hashtag formatted text is not allowed as Markdown.
    • Improved the extraction algo for markdown. (#901)
    Open source →
  45. 0.5.13 11 Oct 2023
    Release notes
    • Improved the extraction algo for hashtags. (#897)
    Open source →
  46. 0.5.12 08 Oct 2023
    Release notes
    • Mentions cannot be set for markdown text. (#894)
    • Invalid url cannot be set for markdown url. (#895)
    Open source →
  47. 0.5.11 07 Oct 2023
    Release notes
    • Improved handle extraction algorithm. From with this version, the use of spaces as well as URLs is no longer required. (#892)
    Open source →
  48. 0.5.10 07 Oct 2023
    Release notes
    • Fixed to add https:// to markdown URLs when it is not given.
    Open source →
  49. 0.5.9 07 Oct 2023
    Release notes
    • The markdown URL must always contain . symbol.
    Open source →
  50. 0.5.8 07 Oct 2023
    Release notes
    • Added enableMarkdown param on BlueskyText. Defaults to true.
    Open source →
  51. 0.5.7 07 Oct 2023
    Release notes
    • Fixed a bug regarding byte calculation when detecting markdowns.
    Open source →
  52. 0.5.6 07 Oct 2023
    Release notes
    • Improved entity extraction for unformatted markdown. For example, [test](https://example.com) extracts entities so that test can be highlighted. Facets of this entity cannot be generated with .toFacets until .format is executed.
      • Also added EntityType.markdownLink. If you want to exclude entities in the markdown without being .format, you can filter by this fixed value.
    Open source →
  53. 0.5.5 04 Oct 2023
    Release notes
    • Added service parameter on .toFacets method. (#882)
    Open source →
  54. 0.5.4 04 Oct 2023
    Release notes
    • Fixed that .format doesn't merge if the URL Path of the detected link is only / when .format is executed. (#876)
    • Fixed markdown URLs to allow only http|https. (#877)
    Open source →
  55. 0.5.3 02 Oct 2023
    Release notes
    • Supported markdown style links. You can set any links to any text such as [test](https://foo.com). Be sure to execute .format() to make the link in markdown format recognized as a facet. (#629)
    Open source →
  56. 0.5.2 01 Oct 2023
    Release notes
    • Fixed safer processing when shortening links.
    Open source →
  57. 0.5.1 30 Sep 2023
    Release notes
    • Improved link detection algorithm.
    Open source →
  58. 0.5.0 28 Sep 2023
    Release notes
    • Improved algorithm for detecting links.
    • Removed deprecated properties.
      • .hasHandle
      • .hasNotHandle
      • .hasLink
      • .hasNotLink
      • .hasEntity
      • .hasNotEntity
    • Changed property from int maxGraphemeLength to bool enableShortening on LinkConfig.
    Open source →
  59. 0.4.5 26 Sep 2023
    Release notes
    • Supported hashtag detection on .entities and .hashtags. (#839)
    Open source →
  60. 0.4.4 25 Sep 2023
    Release notes
    • Improved processing when .format() is executed. Correct if the original text link does not contain the http protocol.
    Open source →

Every package, every release, already written down.

The archive is open and free. Watching your own project is what we are building next.

Browse the archive