@sunafterrainwm/telegram-entities-builder 0.1.4 → 0.2.0

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/dist/index.mjs CHANGED
@@ -1,356 +1,336 @@
1
- // src/index.ts
2
- var EntityBuilder = class _EntityBuilder {
3
- #text = "";
4
- #entities = [];
5
- #parent;
6
- addText(text) {
7
- this.#text += text;
8
- return this;
9
- }
10
- addTextEntity(text, entity) {
11
- text = String(text);
12
- if (text.length > 0) {
13
- this.#entities.push({
14
- ...entity,
15
- offset: this.#text.length,
16
- length: text.length
17
- });
18
- this.#text += text;
19
- }
20
- return this;
21
- }
22
- addTextEntities(text, entities) {
23
- for (const entity of entities) {
24
- this.#entities.push({
25
- ...entity,
26
- offset: this.#text.length,
27
- length: text.length
28
- });
29
- }
30
- this.#text += text;
31
- return this;
32
- }
33
- addTextSegmentList(segments) {
34
- for (const segment of segments) {
35
- if (segment.entity) {
36
- this.addTextEntity(segment.text, segment.entity);
37
- } else if (segment.entities) {
38
- this.addTextEntities(segment.text, segment.entities);
39
- } else {
40
- this.#text += segment.text;
41
- }
42
- }
43
- return this;
44
- }
45
- buildTextPayload() {
46
- return {
47
- text: this.#text,
48
- entities: this.#entities
49
- };
50
- }
51
- buildCaptionPayload() {
52
- return {
53
- caption: this.#text,
54
- caption_entities: this.#entities
55
- };
56
- }
57
- buildInlinePayload() {
58
- return {
59
- message_text: this.#text,
60
- entities: this.#entities
61
- };
62
- }
63
- sortEntities() {
64
- this.#entities.sort((a, b) => a.offset - b.offset || a.length - b.length);
65
- return this;
66
- }
67
- sliceInplace(start, end) {
68
- if (!start && !end) {
69
- return this;
70
- }
71
- const len = this.#text.length;
72
- const s = start === void 0 ? 0 : start < 0 ? Math.max(len + start, 0) : Math.min(start, len);
73
- const e = end === void 0 ? len : end < 0 ? Math.max(len + end, 0) : Math.min(end, len);
74
- if (s >= e) {
75
- this.#text = "";
76
- this.#entities = [];
77
- return this;
78
- }
79
- this.#text = this.#text.slice(s, e);
80
- this.#entities = this.#entities.flatMap((entity) => {
81
- const overlapStart = Math.max(s, entity.offset);
82
- const overlapEnd = Math.min(e, entity.offset + entity.length);
83
- if (overlapStart < overlapEnd) {
84
- return [
85
- {
86
- ...entity,
87
- offset: overlapStart - s,
88
- length: overlapEnd - overlapStart
89
- }
90
- ];
91
- }
92
- return [];
93
- });
94
- return this;
95
- }
96
- slice(start, end) {
97
- return this.clone().sliceInplace(start, end);
98
- }
99
- trimStart() {
100
- const trimmedLength = this.#text.trimStart().length;
101
- const diff = this.#text.length - trimmedLength;
102
- if (diff > 0) {
103
- this.sliceInplace(diff);
104
- }
105
- return this;
106
- }
107
- trimEnd() {
108
- const trimmedLength = this.#text.trimEnd().length;
109
- if (trimmedLength < this.#text.length) {
110
- this.sliceInplace(0, trimmedLength);
111
- }
112
- return this;
113
- }
114
- trim() {
115
- const originalLength = this.#text.length;
116
- const startOffset = originalLength - this.#text.trimStart().length;
117
- const endOffset = this.#text.trimEnd().length;
118
- if (startOffset > 0 || endOffset < originalLength) {
119
- this.sliceInplace(startOffset, endOffset);
120
- }
121
- return this;
122
- }
123
- clone() {
124
- const instance = new _EntityBuilder();
125
- instance.#text = this.#text;
126
- instance.#entities = structuredClone(this.#entities);
127
- return instance;
128
- }
129
- fork() {
130
- const instance = new _EntityBuilder();
131
- instance.#parent = this;
132
- return instance;
133
- }
134
- /**
135
- * Merges a payload text and its entities into this builder, optionally wrapping them with additional entities.
136
- */
137
- mergePayload(text, entities, wrappers = []) {
138
- const startOffset = this.#text.length;
139
- this.#text += text;
140
- this.#entities.push(
141
- ...wrappers.map(
142
- (w) => ({
143
- ...w,
144
- offset: startOffset,
145
- length: text.length
146
- })
147
- ),
148
- ...entities.map((e) => ({
149
- ...e,
150
- offset: e.offset + startOffset
151
- }))
152
- );
153
- }
154
- merge(wrapperEntities = []) {
155
- if (!this.#parent) {
156
- throw new Error("Cannot merge: This builder is not a fork or has already been merged.");
157
- }
158
- this.#parent.mergePayload(this.#text, this.#entities, wrapperEntities);
159
- this.#parent = void 0;
160
- }
1
+ //#region src/index.ts
2
+ /**
3
+ * Main implementation of IEntityBuilder that eagerly evaluates and stores entities.
4
+ */
5
+ var EntityBuilder = class EntityBuilder {
6
+ #text = "";
7
+ #entities = [];
8
+ #parent;
9
+ addText(text) {
10
+ this.#text += text;
11
+ return this;
12
+ }
13
+ addTextEntity(text, entity) {
14
+ text = String(text);
15
+ if (text.length > 0) {
16
+ this.#entities.push({
17
+ ...entity,
18
+ offset: this.#text.length,
19
+ length: text.length
20
+ });
21
+ this.#text += text;
22
+ }
23
+ return this;
24
+ }
25
+ addTextEntities(text, entities) {
26
+ for (const entity of entities) this.#entities.push({
27
+ ...entity,
28
+ offset: this.#text.length,
29
+ length: text.length
30
+ });
31
+ this.#text += text;
32
+ return this;
33
+ }
34
+ addTextSegmentList(segments) {
35
+ for (const segment of segments) if (segment.entity) this.addTextEntity(segment.text, segment.entity);
36
+ else if (segment.entities) this.addTextEntities(segment.text, segment.entities);
37
+ else this.#text += segment.text;
38
+ return this;
39
+ }
40
+ buildTextPayload() {
41
+ return {
42
+ text: this.#text,
43
+ entities: this.#entities
44
+ };
45
+ }
46
+ buildCaptionPayload() {
47
+ return {
48
+ caption: this.#text,
49
+ caption_entities: this.#entities
50
+ };
51
+ }
52
+ buildInlinePayload() {
53
+ return {
54
+ message_text: this.#text,
55
+ entities: this.#entities
56
+ };
57
+ }
58
+ sortEntities() {
59
+ this.#entities.sort((a, b) => a.offset - b.offset || a.length - b.length);
60
+ return this;
61
+ }
62
+ sliceInplace(start, end) {
63
+ if (!start && !end) return this;
64
+ const len = this.#text.length;
65
+ const s = start === void 0 ? 0 : start < 0 ? Math.max(len + start, 0) : Math.min(start, len);
66
+ const e = end === void 0 ? len : end < 0 ? Math.max(len + end, 0) : Math.min(end, len);
67
+ if (s >= e) {
68
+ this.#text = "";
69
+ this.#entities = [];
70
+ return this;
71
+ }
72
+ this.#text = this.#text.slice(s, e);
73
+ this.#entities = this.#entities.flatMap((entity) => {
74
+ const overlapStart = Math.max(s, entity.offset);
75
+ const overlapEnd = Math.min(e, entity.offset + entity.length);
76
+ if (overlapStart < overlapEnd) return [{
77
+ ...entity,
78
+ offset: overlapStart - s,
79
+ length: overlapEnd - overlapStart
80
+ }];
81
+ return [];
82
+ });
83
+ return this;
84
+ }
85
+ slice(start, end) {
86
+ return this.clone().sliceInplace(start, end);
87
+ }
88
+ trimStart() {
89
+ const trimmedLength = this.#text.trimStart().length;
90
+ const diff = this.#text.length - trimmedLength;
91
+ if (diff > 0) this.sliceInplace(diff);
92
+ return this;
93
+ }
94
+ trimEnd() {
95
+ const trimmedLength = this.#text.trimEnd().length;
96
+ if (trimmedLength < this.#text.length) this.sliceInplace(0, trimmedLength);
97
+ return this;
98
+ }
99
+ trim() {
100
+ const originalLength = this.#text.length;
101
+ const startOffset = originalLength - this.#text.trimStart().length;
102
+ const endOffset = this.#text.trimEnd().length;
103
+ if (startOffset > 0 || endOffset < originalLength) this.sliceInplace(startOffset, endOffset);
104
+ return this;
105
+ }
106
+ clone() {
107
+ const instance = new EntityBuilder();
108
+ instance.#text = this.#text;
109
+ instance.#entities = structuredClone(this.#entities);
110
+ return instance;
111
+ }
112
+ fork() {
113
+ const instance = new EntityBuilder();
114
+ instance.#parent = this;
115
+ return instance;
116
+ }
117
+ /**
118
+ * Merges a payload text and its entities into this builder, optionally wrapping them with additional entities.
119
+ */
120
+ mergePayload(text, entities, wrappers = []) {
121
+ const startOffset = this.#text.length;
122
+ this.#text += text;
123
+ this.#entities.push(...wrappers.map((w) => ({
124
+ ...w,
125
+ offset: startOffset,
126
+ length: text.length
127
+ })), ...entities.map((e) => ({
128
+ ...e,
129
+ offset: e.offset + startOffset
130
+ })));
131
+ }
132
+ merge(wrapperEntities = []) {
133
+ if (!this.#parent) throw new Error("Cannot merge: This builder is not a fork or has already been merged.");
134
+ this.#parent.mergePayload(this.#text, this.#entities, wrapperEntities);
135
+ this.#parent = void 0;
136
+ }
161
137
  };
162
- var LazyEntityBuilder = class _LazyEntityBuilder {
163
- #segments = [];
164
- #parent;
165
- /**
166
- * Flattens the lazy segments into a new EntityBuilder instance.
167
- */
168
- flatten() {
169
- const builder = new EntityBuilder();
170
- builder.addTextSegmentList(this.#segments);
171
- return builder;
172
- }
173
- addText(text) {
174
- this.#segments.push({ text });
175
- return this;
176
- }
177
- addTextEntity(text, entity) {
178
- this.#segments.push({ text, entity });
179
- return this;
180
- }
181
- addTextEntities(text, entities) {
182
- this.#segments.push({ text, entities });
183
- return this;
184
- }
185
- addTextSegmentList(segments) {
186
- this.#segments.push(...segments);
187
- return this;
188
- }
189
- clone() {
190
- const instance = new _LazyEntityBuilder();
191
- instance.#segments = structuredClone(this.#segments);
192
- return instance;
193
- }
194
- fork() {
195
- const instance = new _LazyEntityBuilder();
196
- instance.#parent = this;
197
- return instance;
198
- }
199
- merge(wrapperEntities = []) {
200
- if (!this.#parent) {
201
- throw new Error("Cannot merge: This builder is not a fork or has already been merged.");
202
- }
203
- const wrappedSegments = this.#segments.map((seg) => {
204
- if (seg.text.length === 0) {
205
- return seg;
206
- }
207
- const existing = seg.entities ? [...seg.entities] : seg.entity ? [seg.entity] : [];
208
- return {
209
- text: seg.text,
210
- entities: [...existing, ...wrapperEntities]
211
- };
212
- });
213
- this.#parent.addTextSegmentList(wrappedSegments);
214
- this.#parent = void 0;
215
- }
138
+ /**
139
+ * A lazy implementation of IEntityBuilderBase that stores segments and flattens them into an EntityBuilder only when needed.
140
+ */
141
+ var LazyEntityBuilder = class LazyEntityBuilder {
142
+ #segments = [];
143
+ #parent;
144
+ /**
145
+ * Flattens the lazy segments into a new EntityBuilder instance.
146
+ */
147
+ flatten() {
148
+ const builder = new EntityBuilder();
149
+ builder.addTextSegmentList(this.#segments);
150
+ return builder;
151
+ }
152
+ addText(text) {
153
+ this.#segments.push({ text });
154
+ return this;
155
+ }
156
+ addTextEntity(text, entity) {
157
+ this.#segments.push({
158
+ text,
159
+ entity
160
+ });
161
+ return this;
162
+ }
163
+ addTextEntities(text, entities) {
164
+ this.#segments.push({
165
+ text,
166
+ entities
167
+ });
168
+ return this;
169
+ }
170
+ addTextSegmentList(segments) {
171
+ this.#segments.push(...segments);
172
+ return this;
173
+ }
174
+ clone() {
175
+ const instance = new LazyEntityBuilder();
176
+ instance.#segments = structuredClone(this.#segments);
177
+ return instance;
178
+ }
179
+ fork() {
180
+ const instance = new LazyEntityBuilder();
181
+ instance.#parent = this;
182
+ return instance;
183
+ }
184
+ merge(wrapperEntities = []) {
185
+ if (!this.#parent) throw new Error("Cannot merge: This builder is not a fork or has already been merged.");
186
+ const wrappedSegments = this.#segments.map((seg) => {
187
+ if (seg.text.length === 0) return seg;
188
+ const existing = seg.entities ? [...seg.entities] : seg.entity ? [seg.entity] : [];
189
+ return {
190
+ text: seg.text,
191
+ entities: [...existing, ...wrapperEntities]
192
+ };
193
+ });
194
+ this.#parent.addTextSegmentList(wrappedSegments);
195
+ this.#parent = void 0;
196
+ }
216
197
  };
198
+ /**
199
+ * Abstract proxy class that delegates all IEntityBuilder operations to an underlying builder instance.
200
+ */
217
201
  var EntityBuilderProxy = class {
218
- constructor(_entities) {
219
- this._entities = _entities;
220
- }
221
- _entities;
222
- get entities() {
223
- return this._entities;
224
- }
225
- addText(...args) {
226
- this._entities.addText(...args);
227
- return this;
228
- }
229
- addTextEntity(...args) {
230
- this._entities.addTextEntity(...args);
231
- return this;
232
- }
233
- addTextEntities(...args) {
234
- this._entities.addTextEntities(...args);
235
- return this;
236
- }
237
- addTextSegmentList(...args) {
238
- this._entities.addTextSegmentList(...args);
239
- return this;
240
- }
241
- sliceInplace(...args) {
242
- this._entities.sliceInplace(...args);
243
- return this;
244
- }
245
- slice(...args) {
246
- return this.fork().sliceInplace(...args);
247
- }
248
- trim() {
249
- this._entities.trim();
250
- return this;
251
- }
252
- trimStart() {
253
- this._entities.trimStart();
254
- return this;
255
- }
256
- trimEnd() {
257
- this._entities.trimEnd();
258
- return this;
259
- }
260
- sortEntities() {
261
- this._entities.sortEntities();
262
- return this;
263
- }
264
- clone() {
265
- const newInstance = Object.create(this.constructor.prototype);
266
- newInstance._entities.clone();
267
- return newInstance;
268
- }
269
- fork() {
270
- return this._entities.fork();
271
- }
272
- merge(...args) {
273
- this._entities.merge(...args);
274
- }
275
- buildTextPayload() {
276
- return this._entities.buildTextPayload();
277
- }
278
- buildCaptionPayload() {
279
- const { text, entities } = this.buildTextPayload();
280
- return {
281
- caption: text,
282
- caption_entities: entities
283
- };
284
- }
285
- buildInlinePayload() {
286
- const { text, entities, link_preview_options } = this.buildTextPayload();
287
- return {
288
- message_text: text,
289
- entities,
290
- link_preview_options
291
- };
292
- }
202
+ _entities;
203
+ constructor(_entities) {
204
+ this._entities = _entities;
205
+ }
206
+ get entities() {
207
+ return this._entities;
208
+ }
209
+ addText(...args) {
210
+ this._entities.addText(...args);
211
+ return this;
212
+ }
213
+ addTextEntity(...args) {
214
+ this._entities.addTextEntity(...args);
215
+ return this;
216
+ }
217
+ addTextEntities(...args) {
218
+ this._entities.addTextEntities(...args);
219
+ return this;
220
+ }
221
+ addTextSegmentList(...args) {
222
+ this._entities.addTextSegmentList(...args);
223
+ return this;
224
+ }
225
+ sliceInplace(...args) {
226
+ this._entities.sliceInplace(...args);
227
+ return this;
228
+ }
229
+ slice(...args) {
230
+ return this.fork().sliceInplace(...args);
231
+ }
232
+ trim() {
233
+ this._entities.trim();
234
+ return this;
235
+ }
236
+ trimStart() {
237
+ this._entities.trimStart();
238
+ return this;
239
+ }
240
+ trimEnd() {
241
+ this._entities.trimEnd();
242
+ return this;
243
+ }
244
+ sortEntities() {
245
+ this._entities.sortEntities();
246
+ return this;
247
+ }
248
+ clone() {
249
+ const newInstance = Object.create(this.constructor.prototype);
250
+ newInstance._entities.clone();
251
+ return newInstance;
252
+ }
253
+ fork() {
254
+ return this._entities.fork();
255
+ }
256
+ merge(...args) {
257
+ this._entities.merge(...args);
258
+ }
259
+ buildTextPayload() {
260
+ return this._entities.buildTextPayload();
261
+ }
262
+ buildCaptionPayload() {
263
+ const { text, entities } = this.buildTextPayload();
264
+ return {
265
+ caption: text,
266
+ caption_entities: entities
267
+ };
268
+ }
269
+ buildInlinePayload() {
270
+ const { text, entities, link_preview_options } = this.buildTextPayload();
271
+ return {
272
+ message_text: text,
273
+ entities,
274
+ link_preview_options
275
+ };
276
+ }
293
277
  };
294
278
  function upperFirst(input) {
295
- return input.slice(0, 1).toUpperCase() + input.slice(1);
279
+ return input.slice(0, 1).toUpperCase() + input.slice(1);
296
280
  }
281
+ /**
282
+ * Escapes a string to be used as a valid tag by removing or replacing invalid characters.
283
+ */
297
284
  function escapeTag(input) {
298
- return input.replaceAll(/["'′]/g, "").split(/[^\p{L}\p{N}_]/gu).map((s) => upperFirst(s)).join("");
285
+ return input.replaceAll(/["'′]/g, "").split(/[^\p{L}\p{N}_]/gu).map((s) => upperFirst(s)).join("");
299
286
  }
300
- var SymbolMessageComposer = /* @__PURE__ */ Symbol("MessageComposer");
287
+ const SymbolMessageComposer = Symbol("MessageComposer");
288
+ /**
289
+ * A high-level composer that wraps an IEntityBuilder and provides additional features like tags and link preview options.
290
+ */
301
291
  var MessageComposer = class extends EntityBuilderProxy {
302
- [SymbolMessageComposer] = true;
303
- #upperTags = /* @__PURE__ */ new Set();
304
- #tags = [];
305
- /**
306
- * Options for link preview generation.
307
- */
308
- linkPreviewOptions;
309
- constructor(entities) {
310
- if (entities && SymbolMessageComposer in entities) {
311
- throw new Error("Cannot nest MessageComposer inside MessageComposer");
312
- }
313
- super(entities ?? new EntityBuilder());
314
- }
315
- /**
316
- * Gets the current list of tags.
317
- */
318
- get tags() {
319
- return [...this.#tags];
320
- }
321
- /**
322
- * Adds one or more tags, escaping them and avoiding duplicates.
323
- */
324
- addTags(...tags) {
325
- for (let tag of tags) {
326
- tag = escapeTag(tag);
327
- const upperTag = tag.toUpperCase();
328
- if (this.#upperTags.has(upperTag)) {
329
- continue;
330
- }
331
- this.#upperTags.add(upperTag);
332
- this.#tags.push(tag);
333
- }
334
- return this;
335
- }
336
- buildTextPayload() {
337
- const entities = this.entities.clone();
338
- entities.trimEnd().addText("\n\n");
339
- for (const tag of this.tags) {
340
- entities.addTextEntity(`#${tag}`, { type: "hashtag" }).addText(" ");
341
- }
342
- entities.trimEnd();
343
- return {
344
- ...entities.buildTextPayload(),
345
- link_preview_options: this.linkPreviewOptions
346
- };
347
- }
348
- };
349
- export {
350
- EntityBuilder,
351
- EntityBuilderProxy,
352
- LazyEntityBuilder,
353
- MessageComposer,
354
- escapeTag
292
+ [SymbolMessageComposer] = true;
293
+ #upperTags = /* @__PURE__ */ new Set();
294
+ #tags = [];
295
+ /**
296
+ * Options for link preview generation.
297
+ */
298
+ linkPreviewOptions;
299
+ constructor(entities) {
300
+ if (entities && SymbolMessageComposer in entities) throw new TypeError("Cannot nest MessageComposer inside MessageComposer");
301
+ super(entities ?? new EntityBuilder());
302
+ }
303
+ /**
304
+ * Gets the current list of tags.
305
+ */
306
+ get tags() {
307
+ return [...this.#tags];
308
+ }
309
+ /**
310
+ * Adds one or more tags, escaping them and avoiding duplicates.
311
+ */
312
+ addTags(...tags) {
313
+ for (let tag of tags) {
314
+ tag = escapeTag(tag);
315
+ const upperTag = tag.toUpperCase();
316
+ if (this.#upperTags.has(upperTag)) continue;
317
+ this.#upperTags.add(upperTag);
318
+ this.#tags.push(tag);
319
+ }
320
+ return this;
321
+ }
322
+ buildTextPayload() {
323
+ const entities = this.entities.clone();
324
+ entities.trimEnd().addText("\n\n");
325
+ for (const tag of this.tags) entities.addTextEntity(`#${tag}`, { type: "hashtag" }).addText(" ");
326
+ entities.trimEnd();
327
+ return {
328
+ ...entities.buildTextPayload(),
329
+ link_preview_options: this.linkPreviewOptions
330
+ };
331
+ }
355
332
  };
333
+
334
+ //#endregion
335
+ export { EntityBuilder, EntityBuilderProxy, LazyEntityBuilder, MessageComposer, MessageComposer as default, escapeTag };
356
336
  //# sourceMappingURL=index.mjs.map