@atcute/bluesky-richtext-builder 2.0.4 → 2.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,19 +1,180 @@
1
1
  # @atcute/bluesky-richtext-builder
2
2
 
3
- builder pattern for Bluesky's rich text facets.
3
+ fluent builder for constructing Bluesky rich text with facets.
4
+
5
+ ```sh
6
+ npm install @atcute/bluesky-richtext-builder
7
+ ```
8
+
9
+ Bluesky posts support rich text with mentions, links, and hashtags. these are represented as
10
+ "facets" - byte ranges with attached features. this package provides a builder that handles the byte
11
+ offset calculations for you.
12
+
13
+ ## why use a builder?
14
+
15
+ Bluesky facets use byte offsets, not character positions. this matters for non-ASCII text:
16
+
17
+ ```ts
18
+ // "café" is 5 bytes in UTF-8 (c=1, a=1, f=1, é=2)
19
+ const text = 'café ☕';
20
+
21
+ // the coffee emoji starts at byte 5, not character 5
22
+ ```
23
+
24
+ the builder handles these calculations automatically, so you don't have to think about byte offsets.
25
+
26
+ ## usage
27
+
28
+ ### basic usage
4
29
 
5
30
  ```ts
6
31
  import RichtextBuilder from '@atcute/bluesky-richtext-builder';
7
32
 
8
- const { text, facets } = new RichtextBuilder()
9
- .addText(`hello, `)
10
- .addMention(`@user`, 'did:plc:ia76kvnndjutgedggx2ibrem')
11
- .addText(`! please visit my`)
12
- .addLink(`website`, 'https://example.com');
33
+ const rt = new RichtextBuilder()
34
+ .addText('hello, ')
35
+ .addMention('@alice', 'did:plc:abc123')
36
+ .addText('! check out ')
37
+ .addLink('my website', 'https://example.com');
38
+
39
+ console.log(rt.text);
40
+ // -> "hello, @alice! check out my website"
41
+
42
+ console.log(rt.facets);
43
+ // -> [
44
+ // { index: { byteStart: 7, byteEnd: 13 }, features: [{ $type: 'app.bsky.richtext.facet#mention', did: '...' }] },
45
+ // { index: { byteStart: 25, byteEnd: 35 }, features: [{ $type: 'app.bsky.richtext.facet#link', uri: '...' }] }
46
+ // ]
47
+ ```
48
+
49
+ ### creating a post
50
+
51
+ use with `@atcute/client` to create a post:
52
+
53
+ ```ts
54
+ import { Client, CredentialManager, ok } from '@atcute/client';
55
+ import RichtextBuilder from '@atcute/bluesky-richtext-builder';
56
+
57
+ import type {} from '@atcute/bluesky';
58
+
59
+ const manager = new CredentialManager({ service: 'https://bsky.social' });
60
+ const rpc = new Client({ handler: manager });
61
+
62
+ await manager.login({ identifier: 'you.bsky.social', password: 'your-app-password' });
63
+
64
+ const rt = new RichtextBuilder()
65
+ .addText('hello ')
66
+ .addMention('@bsky.app', 'did:plc:z72i7hdynmk6r22z27h6tvur')
67
+ .addText('! ')
68
+ .addTag('atproto');
69
+
70
+ await ok(
71
+ rpc.post('com.atproto.repo.createRecord', {
72
+ input: {
73
+ repo: manager.session!.did,
74
+ collection: 'app.bsky.feed.post',
75
+ record: {
76
+ $type: 'app.bsky.feed.post',
77
+ text: rt.text,
78
+ facets: rt.facets,
79
+ createdAt: new Date().toISOString(),
80
+ },
81
+ },
82
+ }),
83
+ );
84
+ ```
85
+
86
+ ### adding links
87
+
88
+ ```ts
89
+ const rt = new RichtextBuilder()
90
+ .addText('read the ')
91
+ .addLink('documentation', 'https://atproto.com/docs')
92
+ .addText(' for more info');
93
+ ```
94
+
95
+ the link text can be anything - it doesn't have to be the URL:
96
+
97
+ ```ts
98
+ const rt = new RichtextBuilder().addLink('click here', 'https://example.com');
99
+ ```
100
+
101
+ ### adding mentions
13
102
 
14
- text;
15
- // ^? `hello, @user! please visit my website`
103
+ mentions require both the display text and the user's DID:
104
+
105
+ ```ts
106
+ const rt = new RichtextBuilder().addMention('@alice.bsky.social', 'did:plc:abc123');
107
+ ```
108
+
109
+ you'll typically resolve the handle to a DID first:
110
+
111
+ ```ts
112
+ import {
113
+ CompositeHandleResolver,
114
+ DohJsonHandleResolver,
115
+ WellKnownHandleResolver,
116
+ } from '@atcute/identity-resolver';
117
+
118
+ const handleResolver = new CompositeHandleResolver({
119
+ methods: {
120
+ dns: new DohJsonHandleResolver({ dohUrl: 'https://mozilla.cloudflare-dns.com/dns-query' }),
121
+ http: new WellKnownHandleResolver(),
122
+ },
123
+ });
124
+
125
+ const handle = 'alice.bsky.social';
126
+ const did = await handleResolver.resolve(handle);
127
+
128
+ const rt = new RichtextBuilder().addMention(`@${handle}`, did);
129
+ ```
130
+
131
+ ### adding hashtags
132
+
133
+ hashtags are added without the `#` prefix - it's added automatically:
134
+
135
+ ```ts
136
+ const rt = new RichtextBuilder().addText('loving ').addTag('atproto').addText(' development!');
137
+
138
+ // text: "loving #atproto development!"
139
+ ```
140
+
141
+ ### custom facet features
142
+
143
+ use `addDecoratedText()` for custom facet features:
144
+
145
+ ```ts
146
+ import type { FacetFeature } from '@atcute/bluesky-richtext-builder';
147
+
148
+ const feature: FacetFeature = {
149
+ $type: 'app.bsky.richtext.facet#link',
150
+ uri: 'https://example.com',
151
+ };
152
+
153
+ const rt = new RichtextBuilder().addDecoratedText('custom link', feature);
154
+ ```
155
+
156
+ ### getting the result
157
+
158
+ there are multiple ways to get the composed rich text:
159
+
160
+ ```ts
161
+ const rt = new RichtextBuilder().addText('hello ').addTag('world');
162
+
163
+ // via getters
164
+ const text = rt.text;
165
+ const facets = rt.facets;
166
+
167
+ // via build() method
168
+ const { text, facets } = rt.build();
169
+ ```
170
+
171
+ ### cloning the builder
172
+
173
+ clone a builder to create variations:
174
+
175
+ ```ts
176
+ const base = new RichtextBuilder().addText('hello ');
16
177
 
17
- facets;
18
- // ^? [{ index: { byteStart: 7, byteEnd: 12 }, ... }, { index: { byteStart: 30, byteEnd: 37 }, ... }];
178
+ const withMention = base.clone().addMention('@alice', 'did:plc:abc');
179
+ const withLink = base.clone().addLink('world', 'https://example.com');
19
180
  ```
package/dist/index.d.ts CHANGED
@@ -51,7 +51,7 @@ declare class RichtextBuilder {
51
51
  /**
52
52
  * Add inline hashtag to the rich text
53
53
  * @param tag The tag, without the pound prefix
54
- * @returns THe builder instance, for chaining
54
+ * @returns The builder instance, for chaining
55
55
  */
56
56
  addTag(tag: string): this;
57
57
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,KAAK,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAExD,KAAK,WAAW,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC;AAExD,uEAAuE;AACvE,MAAM,MAAM,KAAK,GAAG,oBAAoB,CAAC,IAAI,CAAC;AAC9C,gDAAgD;AAChD,MAAM,MAAM,YAAY,GAAG,WAAW,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;AAI1D,0BAA0B;AAC1B,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,KAAK,EAAE,CAAC;CAChB;AAED,kDAAkD;AAClD,cAAM,eAAe;;IAMpB,8BAA8B;IAC9B,IAAI,IAAI,IAAI,MAAM,CASjB;IAED,gCAAgC;IAChC,IAAI,MAAM,IAAI,KAAK,EAAE,CASpB;IAED,sCAAsC;IACtC,KAAK,IAAI,aAAa;IAOtB,uCAAuC;IACvC,KAAK,IAAI,eAAe;IAOxB;;;;OAIG;IACH,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAO7B;;;;;OAKG;IACH,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,IAAI;IAyB7D;;;;;OAKG;IACH,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,GAAG,IAAI;IAI9C;;;;;OAKG;IACH,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,GAAG,IAAI;IAI1C;;;;OAIG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;CAGzB;AAED,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,KAAK,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAGxD,KAAK,WAAW,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC;AAExD,uEAAuE;AACvE,MAAM,MAAM,KAAK,GAAG,oBAAoB,CAAC,IAAI,CAAC;AAC9C,gDAAgD;AAChD,MAAM,MAAM,YAAY,GAAG,WAAW,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;AAE1D,0BAA0B;AAC1B,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,KAAK,EAAE,CAAC;CAChB;AAED,kDAAkD;AAClD,cAAM,eAAe;;IAMpB,8BAA8B;IAC9B,IAAI,IAAI,IAAI,MAAM,CASjB;IAED,gCAAgC;IAChC,IAAI,MAAM,IAAI,KAAK,EAAE,CASpB;IAED,sCAAsC;IACtC,KAAK,IAAI,aAAa,CAKrB;IAED,uCAAuC;IACvC,KAAK,IAAI,eAAe,CAKvB;IAED;;;;OAIG;IACH,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAK5B;IAED;;;;;OAKG;IACH,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,IAAI,CAyB5D;IAED;;;;;OAKG;IACH,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,GAAG,IAAI,CAE7C;IAED;;;;;OAKG;IACH,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,GAAG,IAAI,CAEzC;IAED;;;;OAIG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAExB;CACD;AAED,eAAe,eAAe,CAAC"}
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- const encoder = new TextEncoder();
1
+ import { getUtf8Length } from '@atcute/uint8array';
2
2
  /** Builder for constructing Bluesky rich texts */
3
3
  class RichtextBuilder {
4
4
  // Even-numbered are substrings, odd-numbered are facets
@@ -57,14 +57,15 @@ class RichtextBuilder {
57
57
  const last = segments.length - 1;
58
58
  // Calculate the starting index
59
59
  let start = 0;
60
- start += encoder.encode(segments[last]).byteLength;
60
+ start += getUtf8Length(segments[last]);
61
61
  if (last !== 0) {
62
62
  start += segments[last - 1].index.byteEnd;
63
63
  }
64
+ const byteLength = getUtf8Length(substr);
64
65
  const facet = {
65
66
  index: {
66
67
  byteStart: start,
67
- byteEnd: start + encoder.encode(substr).byteLength,
68
+ byteEnd: start + byteLength,
68
69
  },
69
70
  features: [feature],
70
71
  };
@@ -93,7 +94,7 @@ class RichtextBuilder {
93
94
  /**
94
95
  * Add inline hashtag to the rich text
95
96
  * @param tag The tag, without the pound prefix
96
- * @returns THe builder instance, for chaining
97
+ * @returns The builder instance, for chaining
97
98
  */
98
99
  addTag(tag) {
99
100
  return this.addDecoratedText('#' + tag, { $type: 'app.bsky.richtext.facet#tag', tag: tag });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"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"}
package/lib/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { AppBskyRichtextFacet } from '@atcute/bluesky';
2
2
  import type { Did, GenericUri } from '@atcute/lexicons';
3
+ import { getUtf8Length } from '@atcute/uint8array';
3
4
 
4
5
  type UnwrapArray<T> = T extends (infer V)[] ? V : never;
5
6
 
@@ -8,8 +9,6 @@ export type Facet = AppBskyRichtextFacet.Main;
8
9
  /** Feature union type from Facet['features'] */
9
10
  export type FacetFeature = UnwrapArray<Facet['features']>;
10
11
 
11
- const encoder = new TextEncoder();
12
-
13
12
  /** Resulting rich text */
14
13
  export interface BakedRichtext {
15
14
  text: string;
@@ -88,15 +87,17 @@ class RichtextBuilder {
88
87
  // Calculate the starting index
89
88
  let start = 0;
90
89
 
91
- start += encoder.encode(segments[last] as string).byteLength;
90
+ start += getUtf8Length(segments[last] as string);
92
91
  if (last !== 0) {
93
92
  start += (segments[last - 1] as Facet).index.byteEnd;
94
93
  }
95
94
 
95
+ const byteLength = getUtf8Length(substr);
96
+
96
97
  const facet: Facet = {
97
98
  index: {
98
99
  byteStart: start,
99
- byteEnd: start + encoder.encode(substr).byteLength,
100
+ byteEnd: start + byteLength,
100
101
  },
101
102
  features: [feature],
102
103
  };
@@ -129,7 +130,7 @@ class RichtextBuilder {
129
130
  /**
130
131
  * Add inline hashtag to the rich text
131
132
  * @param tag The tag, without the pound prefix
132
- * @returns THe builder instance, for chaining
133
+ * @returns The builder instance, for chaining
133
134
  */
134
135
  addTag(tag: string): this {
135
136
  return this.addDecoratedText('#' + tag, { $type: 'app.bsky.richtext.facet#tag', tag: tag });
package/package.json CHANGED
@@ -1,7 +1,6 @@
1
1
  {
2
- "type": "module",
3
2
  "name": "@atcute/bluesky-richtext-builder",
4
- "version": "2.0.4",
3
+ "version": "2.0.5",
5
4
  "description": "builder pattern for Bluesky's rich text facets",
6
5
  "license": "0BSD",
7
6
  "repository": {
@@ -14,20 +13,21 @@
14
13
  "!lib/**/*.bench.ts",
15
14
  "!lib/**/*.test.ts"
16
15
  ],
16
+ "type": "module",
17
+ "sideEffects": false,
17
18
  "exports": {
18
19
  ".": "./dist/index.js"
19
20
  },
20
- "sideEffects": false,
21
- "dependencies": {
22
- "@atcute/bluesky": "^3.2.5",
23
- "@atcute/lexicons": "^1.2.2"
21
+ "publishConfig": {
22
+ "access": "public"
24
23
  },
25
- "devDependencies": {
26
- "@types/bun": "^1.2.21"
24
+ "dependencies": {
25
+ "@atcute/bluesky": "^3.2.17",
26
+ "@atcute/lexicons": "^1.2.9",
27
+ "@atcute/uint8array": "^1.1.1"
27
28
  },
28
29
  "scripts": {
29
- "build": "tsc --project tsconfig.build.json",
30
- "test": "bun test --coverage",
30
+ "build": "tsgo --project tsconfig.build.json",
31
31
  "prepublish": "rm -rf dist; pnpm run build"
32
32
  }
33
33
  }