spectrum-ts 4.2.0 → 5.0.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.
Files changed (46) hide show
  1. package/README.md +29 -67
  2. package/dist/authoring.d.ts +1 -6
  3. package/dist/authoring.js +2 -36
  4. package/dist/elysia.d.ts +1 -94
  5. package/dist/elysia.js +2 -15
  6. package/dist/express.d.ts +1 -62
  7. package/dist/express.js +2 -19
  8. package/dist/hono.d.ts +1 -64
  9. package/dist/hono.js +2 -11
  10. package/dist/index.d.ts +1 -2851
  11. package/dist/index.js +2 -3763
  12. package/dist/manifest.json +5 -5
  13. package/dist/providers/imessage/index.d.ts +1 -222
  14. package/dist/providers/imessage/index.js +2 -25
  15. package/dist/providers/index.d.ts +6 -19
  16. package/dist/providers/index.js +6 -34
  17. package/dist/providers/slack/index.d.ts +1 -46
  18. package/dist/providers/slack/index.js +2 -11
  19. package/dist/providers/telegram/index.d.ts +1 -45
  20. package/dist/providers/telegram/index.js +2 -13
  21. package/dist/providers/terminal/index.d.ts +1 -119
  22. package/dist/providers/terminal/index.js +2 -13
  23. package/dist/providers/whatsapp-business/index.d.ts +1 -27
  24. package/dist/providers/whatsapp-business/index.js +2 -14
  25. package/package.json +11 -38
  26. package/dist/attachment-CnivEhr6.d.ts +0 -29
  27. package/dist/authoring-b9AhXgPI.d.ts +0 -304
  28. package/dist/chunk-2D27WW5B.js +0 -63
  29. package/dist/chunk-34FQGGD7.js +0 -34
  30. package/dist/chunk-3GEJYGZK.js +0 -84
  31. package/dist/chunk-5XEFJBN2.js +0 -197
  32. package/dist/chunk-6UZFVXQF.js +0 -374
  33. package/dist/chunk-A37PM5N2.js +0 -91
  34. package/dist/chunk-ARL2NOBO.js +0 -887
  35. package/dist/chunk-B52VPQO3.js +0 -1379
  36. package/dist/chunk-DMPDLSFU.js +0 -864
  37. package/dist/chunk-FAIFTUV2.js +0 -139
  38. package/dist/chunk-LZXPLXZF.js +0 -35
  39. package/dist/chunk-N6THJDZV.js +0 -929
  40. package/dist/chunk-NLMQ75LH.js +0 -2980
  41. package/dist/chunk-UXAKIXVM.js +0 -409
  42. package/dist/chunk-WXLQNANA.js +0 -539
  43. package/dist/chunk-ZR3TKZMT.js +0 -129
  44. package/dist/read-C4uvozGX.d.ts +0 -53
  45. package/dist/types-CyfLJXgu.d.ts +0 -1530
  46. package/dist/types-ZgFTj5hJ.d.ts +0 -87
package/dist/index.js CHANGED
@@ -1,3763 +1,2 @@
1
- import { createRequire as __spectrumCreateRequire } from "node:module"; const require = __spectrumCreateRequire(import.meta.url);
2
- import {
3
- FUSOR_MESSAGES_CHANNEL,
4
- fusor,
5
- fusorEvent,
6
- isFusorClient,
7
- isFusorEvent
8
- } from "./chunk-34FQGGD7.js";
9
- import {
10
- voice
11
- } from "./chunk-FAIFTUV2.js";
12
- import {
13
- asRichlink,
14
- richlink
15
- } from "./chunk-ZR3TKZMT.js";
16
- import {
17
- group
18
- } from "./chunk-LZXPLXZF.js";
19
- import {
20
- option,
21
- poll
22
- } from "./chunk-2D27WW5B.js";
23
- import {
24
- asContact,
25
- contact
26
- } from "./chunk-A37PM5N2.js";
27
- import {
28
- fromVCard,
29
- toVCard
30
- } from "./chunk-6UZFVXQF.js";
31
- import {
32
- SpectrumCloudError,
33
- cloud
34
- } from "./chunk-3GEJYGZK.js";
35
- import {
36
- broadcast,
37
- createAsyncQueue,
38
- mergeStreams,
39
- stream
40
- } from "./chunk-5XEFJBN2.js";
41
- import {
42
- UnsupportedError,
43
- avatar,
44
- buildSpace,
45
- contentAttrs,
46
- definePlatform,
47
- edit,
48
- markdown,
49
- read,
50
- rename,
51
- reply,
52
- senderAttrs,
53
- typing,
54
- unsend,
55
- wrapProviderMessage
56
- } from "./chunk-B52VPQO3.js";
57
- import {
58
- asAttachment,
59
- asCustom,
60
- attachment,
61
- custom,
62
- reaction,
63
- resolveContents,
64
- text
65
- } from "./chunk-UXAKIXVM.js";
66
-
67
- // src/emoji/generated.ts
68
- var GeneratedEmoji = {
69
- _1stPlaceMedal: "\u{1F947}",
70
- _2ndPlaceMedal: "\u{1F948}",
71
- _3rdPlaceMedal: "\u{1F949}",
72
- abacus: "\u{1F9EE}",
73
- abButton: "\u{1F18E}",
74
- aButton: "\u{1F170}\uFE0F",
75
- accordion: "\u{1FA97}",
76
- adhesiveBandage: "\u{1FA79}",
77
- admissionTickets: "\u{1F39F}\uFE0F",
78
- aerialTramway: "\u{1F6A1}",
79
- airplane: "\u2708\uFE0F",
80
- airplaneArrival: "\u{1F6EC}",
81
- airplaneDeparture: "\u{1F6EB}",
82
- alarmClock: "\u23F0",
83
- alembic: "\u2697\uFE0F",
84
- alien: "\u{1F47D}",
85
- alienMonster: "\u{1F47E}",
86
- ambulance: "\u{1F691}",
87
- americanFootball: "\u{1F3C8}",
88
- amphora: "\u{1F3FA}",
89
- anatomicalHeart: "\u{1FAC0}",
90
- anchor: "\u2693",
91
- angerSymbol: "\u{1F4A2}",
92
- angryFace: "\u{1F620}",
93
- angryFaceWithHorns: "\u{1F47F}",
94
- anguishedFace: "\u{1F627}",
95
- ant: "\u{1F41C}",
96
- antennaBars: "\u{1F4F6}",
97
- anxiousFaceWithSweat: "\u{1F630}",
98
- aquarius: "\u2652",
99
- aries: "\u2648",
100
- articulatedLorry: "\u{1F69B}",
101
- artist: "\u{1F9D1}\u200D\u{1F3A8}",
102
- artistPalette: "\u{1F3A8}",
103
- astonishedFace: "\u{1F632}",
104
- astronaut: "\u{1F9D1}\u200D\u{1F680}",
105
- atmSign: "\u{1F3E7}",
106
- atomSymbol: "\u269B\uFE0F",
107
- automobile: "\u{1F697}",
108
- autoRickshaw: "\u{1F6FA}",
109
- avocado: "\u{1F951}",
110
- axe: "\u{1FA93}",
111
- baby: "\u{1F476}",
112
- babyAngel: "\u{1F47C}",
113
- babyBottle: "\u{1F37C}",
114
- babyChick: "\u{1F424}",
115
- babySymbol: "\u{1F6BC}",
116
- backArrow: "\u{1F519}",
117
- backhandIndexPointingDown: "\u{1F447}",
118
- backhandIndexPointingLeft: "\u{1F448}",
119
- backhandIndexPointingRight: "\u{1F449}",
120
- backhandIndexPointingUp: "\u{1F446}",
121
- backpack: "\u{1F392}",
122
- bacon: "\u{1F953}",
123
- badger: "\u{1F9A1}",
124
- badminton: "\u{1F3F8}",
125
- bagel: "\u{1F96F}",
126
- baggageClaim: "\u{1F6C4}",
127
- baguetteBread: "\u{1F956}",
128
- balanceScale: "\u2696\uFE0F",
129
- balletDancer: "\u{1F9D1}\u200D\u{1FA70}",
130
- balletShoes: "\u{1FA70}",
131
- balloon: "\u{1F388}",
132
- ballotBoxWithBallot: "\u{1F5F3}\uFE0F",
133
- banana: "\u{1F34C}",
134
- banjo: "\u{1FA95}",
135
- bank: "\u{1F3E6}",
136
- barberPole: "\u{1F488}",
137
- barChart: "\u{1F4CA}",
138
- baseball: "\u26BE",
139
- basket: "\u{1F9FA}",
140
- basketball: "\u{1F3C0}",
141
- bat: "\u{1F987}",
142
- bathtub: "\u{1F6C1}",
143
- battery: "\u{1F50B}",
144
- bButton: "\u{1F171}\uFE0F",
145
- beachWithUmbrella: "\u{1F3D6}\uFE0F",
146
- beamingFaceWithSmilingEyes: "\u{1F601}",
147
- beans: "\u{1FAD8}",
148
- bear: "\u{1F43B}",
149
- beatingHeart: "\u{1F493}",
150
- beaver: "\u{1F9AB}",
151
- bed: "\u{1F6CF}\uFE0F",
152
- beerMug: "\u{1F37A}",
153
- beetle: "\u{1FAB2}",
154
- bell: "\u{1F514}",
155
- bellhopBell: "\u{1F6CE}\uFE0F",
156
- bellPepper: "\u{1FAD1}",
157
- bellWithSlash: "\u{1F515}",
158
- bentoBox: "\u{1F371}",
159
- beverageBox: "\u{1F9C3}",
160
- bicycle: "\u{1F6B2}",
161
- bikini: "\u{1F459}",
162
- billedCap: "\u{1F9E2}",
163
- biohazard: "\u2623\uFE0F",
164
- bird: "\u{1F426}",
165
- birthdayCake: "\u{1F382}",
166
- bison: "\u{1F9AC}",
167
- bitingLip: "\u{1FAE6}",
168
- blackBird: "\u{1F426}\u200D\u2B1B",
169
- blackCat: "\u{1F408}\u200D\u2B1B",
170
- blackCircle: "\u26AB",
171
- blackFlag: "\u{1F3F4}",
172
- blackHeart: "\u{1F5A4}",
173
- blackLargeSquare: "\u2B1B",
174
- blackMediumSmallSquare: "\u25FE",
175
- blackMediumSquare: "\u25FC\uFE0F",
176
- blackNib: "\u2712\uFE0F",
177
- blackSmallSquare: "\u25AA\uFE0F",
178
- blackSquareButton: "\u{1F532}",
179
- blossom: "\u{1F33C}",
180
- blowfish: "\u{1F421}",
181
- blueberries: "\u{1FAD0}",
182
- blueBook: "\u{1F4D8}",
183
- blueCircle: "\u{1F535}",
184
- blueHeart: "\u{1F499}",
185
- blueSquare: "\u{1F7E6}",
186
- boar: "\u{1F417}",
187
- bomb: "\u{1F4A3}",
188
- bone: "\u{1F9B4}",
189
- bookmark: "\u{1F516}",
190
- bookmarkTabs: "\u{1F4D1}",
191
- books: "\u{1F4DA}",
192
- boomerang: "\u{1FA83}",
193
- bottleWithPoppingCork: "\u{1F37E}",
194
- bouquet: "\u{1F490}",
195
- bowAndArrow: "\u{1F3F9}",
196
- bowling: "\u{1F3B3}",
197
- bowlWithSpoon: "\u{1F963}",
198
- boxingGlove: "\u{1F94A}",
199
- boy: "\u{1F466}",
200
- brain: "\u{1F9E0}",
201
- bread: "\u{1F35E}",
202
- breastFeeding: "\u{1F931}",
203
- brick: "\u{1F9F1}",
204
- bridgeAtNight: "\u{1F309}",
205
- briefcase: "\u{1F4BC}",
206
- briefs: "\u{1FA72}",
207
- brightButton: "\u{1F506}",
208
- broccoli: "\u{1F966}",
209
- brokenChain: "\u26D3\uFE0F\u200D\u{1F4A5}",
210
- brokenHeart: "\u{1F494}",
211
- broom: "\u{1F9F9}",
212
- brownCircle: "\u{1F7E4}",
213
- brownHeart: "\u{1F90E}",
214
- brownMushroom: "\u{1F344}\u200D\u{1F7EB}",
215
- brownSquare: "\u{1F7EB}",
216
- bubbles: "\u{1FAE7}",
217
- bubbleTea: "\u{1F9CB}",
218
- bucket: "\u{1FAA3}",
219
- bug: "\u{1F41B}",
220
- buildingConstruction: "\u{1F3D7}\uFE0F",
221
- bulletTrain: "\u{1F685}",
222
- bullseye: "\u{1F3AF}",
223
- burrito: "\u{1F32F}",
224
- bus: "\u{1F68C}",
225
- busStop: "\u{1F68F}",
226
- bustInSilhouette: "\u{1F464}",
227
- bustsInSilhouette: "\u{1F465}",
228
- butter: "\u{1F9C8}",
229
- butterfly: "\u{1F98B}",
230
- cactus: "\u{1F335}",
231
- calendar: "\u{1F4C5}",
232
- callMeHand: "\u{1F919}",
233
- camel: "\u{1F42A}",
234
- camera: "\u{1F4F7}",
235
- cameraWithFlash: "\u{1F4F8}",
236
- camping: "\u{1F3D5}\uFE0F",
237
- cancer: "\u264B",
238
- candle: "\u{1F56F}\uFE0F",
239
- candy: "\u{1F36C}",
240
- cannedFood: "\u{1F96B}",
241
- canoe: "\u{1F6F6}",
242
- capricorn: "\u2651",
243
- cardFileBox: "\u{1F5C3}\uFE0F",
244
- cardIndex: "\u{1F4C7}",
245
- cardIndexDividers: "\u{1F5C2}\uFE0F",
246
- carouselHorse: "\u{1F3A0}",
247
- carpentrySaw: "\u{1FA9A}",
248
- carpStreamer: "\u{1F38F}",
249
- carrot: "\u{1F955}",
250
- castle: "\u{1F3F0}",
251
- cat: "\u{1F408}",
252
- catFace: "\u{1F431}",
253
- catWithTearsOfJoy: "\u{1F639}",
254
- catWithWrySmile: "\u{1F63C}",
255
- chains: "\u26D3\uFE0F",
256
- chair: "\u{1FA91}",
257
- chartDecreasing: "\u{1F4C9}",
258
- chartIncreasing: "\u{1F4C8}",
259
- chartIncreasingWithYen: "\u{1F4B9}",
260
- checkBoxWithCheck: "\u2611\uFE0F",
261
- checkMark: "\u2714\uFE0F",
262
- checkMarkButton: "\u2705",
263
- cheeseWedge: "\u{1F9C0}",
264
- chequeredFlag: "\u{1F3C1}",
265
- cherries: "\u{1F352}",
266
- cherryBlossom: "\u{1F338}",
267
- chessPawn: "\u265F\uFE0F",
268
- chestnut: "\u{1F330}",
269
- chicken: "\u{1F414}",
270
- child: "\u{1F9D2}",
271
- childrenCrossing: "\u{1F6B8}",
272
- chipmunk: "\u{1F43F}\uFE0F",
273
- chocolateBar: "\u{1F36B}",
274
- chopsticks: "\u{1F962}",
275
- christmasTree: "\u{1F384}",
276
- church: "\u26EA",
277
- cigarette: "\u{1F6AC}",
278
- cinema: "\u{1F3A6}",
279
- circledM: "\u24C2\uFE0F",
280
- circusTent: "\u{1F3AA}",
281
- cityscape: "\u{1F3D9}\uFE0F",
282
- cityscapeAtDusk: "\u{1F306}",
283
- clamp: "\u{1F5DC}\uFE0F",
284
- clapperBoard: "\u{1F3AC}",
285
- clappingHands: "\u{1F44F}",
286
- classicalBuilding: "\u{1F3DB}\uFE0F",
287
- clButton: "\u{1F191}",
288
- clinkingBeerMugs: "\u{1F37B}",
289
- clinkingGlasses: "\u{1F942}",
290
- clipboard: "\u{1F4CB}",
291
- clockwiseVerticalArrows: "\u{1F503}",
292
- closedBook: "\u{1F4D5}",
293
- closedMailboxWithLoweredFlag: "\u{1F4EA}",
294
- closedMailboxWithRaisedFlag: "\u{1F4EB}",
295
- closedUmbrella: "\u{1F302}",
296
- cloud: "\u2601\uFE0F",
297
- cloudWithLightning: "\u{1F329}\uFE0F",
298
- cloudWithLightningAndRain: "\u26C8\uFE0F",
299
- cloudWithRain: "\u{1F327}\uFE0F",
300
- cloudWithSnow: "\u{1F328}\uFE0F",
301
- clownFace: "\u{1F921}",
302
- clubSuit: "\u2663\uFE0F",
303
- clutchBag: "\u{1F45D}",
304
- coat: "\u{1F9E5}",
305
- cockroach: "\u{1FAB3}",
306
- cocktailGlass: "\u{1F378}",
307
- coconut: "\u{1F965}",
308
- coffin: "\u26B0\uFE0F",
309
- coin: "\u{1FA99}",
310
- coldFace: "\u{1F976}",
311
- collision: "\u{1F4A5}",
312
- comet: "\u2604\uFE0F",
313
- compass: "\u{1F9ED}",
314
- computerDisk: "\u{1F4BD}",
315
- computerMouse: "\u{1F5B1}\uFE0F",
316
- confettiBall: "\u{1F38A}",
317
- confoundedFace: "\u{1F616}",
318
- confusedFace: "\u{1F615}",
319
- construction: "\u{1F6A7}",
320
- constructionWorker: "\u{1F477}",
321
- controlKnobs: "\u{1F39B}\uFE0F",
322
- convenienceStore: "\u{1F3EA}",
323
- cook: "\u{1F9D1}\u200D\u{1F373}",
324
- cookedRice: "\u{1F35A}",
325
- cookie: "\u{1F36A}",
326
- cooking: "\u{1F373}",
327
- coolButton: "\u{1F192}",
328
- copyright: "\xA9\uFE0F",
329
- coral: "\u{1FAB8}",
330
- couchAndLamp: "\u{1F6CB}\uFE0F",
331
- counterclockwiseArrowsButton: "\u{1F504}",
332
- coupleWithHeart: "\u{1F491}",
333
- coupleWithHeartManMan: "\u{1F468}\u200D\u2764\uFE0F\u200D\u{1F468}",
334
- coupleWithHeartWomanMan: "\u{1F469}\u200D\u2764\uFE0F\u200D\u{1F468}",
335
- coupleWithHeartWomanWoman: "\u{1F469}\u200D\u2764\uFE0F\u200D\u{1F469}",
336
- cow: "\u{1F404}",
337
- cowboyHatFace: "\u{1F920}",
338
- cowFace: "\u{1F42E}",
339
- crab: "\u{1F980}",
340
- crayon: "\u{1F58D}\uFE0F",
341
- creditCard: "\u{1F4B3}",
342
- crescentMoon: "\u{1F319}",
343
- cricket: "\u{1F997}",
344
- cricketGame: "\u{1F3CF}",
345
- crocodile: "\u{1F40A}",
346
- croissant: "\u{1F950}",
347
- crossedFingers: "\u{1F91E}",
348
- crossedFlags: "\u{1F38C}",
349
- crossedSwords: "\u2694\uFE0F",
350
- crossMark: "\u274C",
351
- crossMarkButton: "\u274E",
352
- crown: "\u{1F451}",
353
- crutch: "\u{1FA7C}",
354
- cryingCat: "\u{1F63F}",
355
- cryingFace: "\u{1F622}",
356
- crystalBall: "\u{1F52E}",
357
- cucumber: "\u{1F952}",
358
- cupcake: "\u{1F9C1}",
359
- cupWithStraw: "\u{1F964}",
360
- curlingStone: "\u{1F94C}",
361
- curlyLoop: "\u27B0",
362
- currencyExchange: "\u{1F4B1}",
363
- curryRice: "\u{1F35B}",
364
- custard: "\u{1F36E}",
365
- customs: "\u{1F6C3}",
366
- cutOfMeat: "\u{1F969}",
367
- cyclone: "\u{1F300}",
368
- dagger: "\u{1F5E1}\uFE0F",
369
- dango: "\u{1F361}",
370
- dashingAway: "\u{1F4A8}",
371
- deafMan: "\u{1F9CF}\u200D\u2642\uFE0F",
372
- deafPerson: "\u{1F9CF}",
373
- deafWoman: "\u{1F9CF}\u200D\u2640\uFE0F",
374
- deciduousTree: "\u{1F333}",
375
- deer: "\u{1F98C}",
376
- deliveryTruck: "\u{1F69A}",
377
- departmentStore: "\u{1F3EC}",
378
- derelictHouse: "\u{1F3DA}\uFE0F",
379
- desert: "\u{1F3DC}\uFE0F",
380
- desertIsland: "\u{1F3DD}\uFE0F",
381
- desktopComputer: "\u{1F5A5}\uFE0F",
382
- detective: "\u{1F575}\uFE0F",
383
- diamondSuit: "\u2666\uFE0F",
384
- diamondWithADot: "\u{1F4A0}",
385
- dimButton: "\u{1F505}",
386
- disappointedFace: "\u{1F61E}",
387
- disguisedFace: "\u{1F978}",
388
- distortedFace: "\u{1FAEA}",
389
- divide: "\u2797",
390
- divingMask: "\u{1F93F}",
391
- diyaLamp: "\u{1FA94}",
392
- dizzy: "\u{1F4AB}",
393
- dna: "\u{1F9EC}",
394
- dodo: "\u{1F9A4}",
395
- dog: "\u{1F415}",
396
- dogFace: "\u{1F436}",
397
- dollarBanknote: "\u{1F4B5}",
398
- dolphin: "\u{1F42C}",
399
- donkey: "\u{1FACF}",
400
- door: "\u{1F6AA}",
401
- dottedLineFace: "\u{1FAE5}",
402
- dottedSixPointedStar: "\u{1F52F}",
403
- doubleCurlyLoop: "\u27BF",
404
- doubleExclamationMark: "\u203C\uFE0F",
405
- doughnut: "\u{1F369}",
406
- dove: "\u{1F54A}\uFE0F",
407
- downArrow: "\u2B07\uFE0F",
408
- downcastFaceWithSweat: "\u{1F613}",
409
- downLeftArrow: "\u2199\uFE0F",
410
- downRightArrow: "\u2198\uFE0F",
411
- downwardsButton: "\u{1F53D}",
412
- dragon: "\u{1F409}",
413
- dragonFace: "\u{1F432}",
414
- dress: "\u{1F457}",
415
- droolingFace: "\u{1F924}",
416
- droplet: "\u{1F4A7}",
417
- dropOfBlood: "\u{1FA78}",
418
- drum: "\u{1F941}",
419
- duck: "\u{1F986}",
420
- dumpling: "\u{1F95F}",
421
- dvd: "\u{1F4C0}",
422
- eagle: "\u{1F985}",
423
- ear: "\u{1F442}",
424
- earOfCorn: "\u{1F33D}",
425
- earWithHearingAid: "\u{1F9BB}",
426
- egg: "\u{1F95A}",
427
- eggplant: "\u{1F346}",
428
- eightOClock: "\u{1F557}",
429
- eightPointedStar: "\u2734\uFE0F",
430
- eightSpokedAsterisk: "\u2733\uFE0F",
431
- eightThirty: "\u{1F563}",
432
- ejectButton: "\u23CF\uFE0F",
433
- electricPlug: "\u{1F50C}",
434
- elephant: "\u{1F418}",
435
- elevator: "\u{1F6D7}",
436
- elevenOClock: "\u{1F55A}",
437
- elevenThirty: "\u{1F566}",
438
- elf: "\u{1F9DD}",
439
- eMail: "\u{1F4E7}",
440
- emptyNest: "\u{1FAB9}",
441
- endArrow: "\u{1F51A}",
442
- enragedFace: "\u{1F621}",
443
- envelope: "\u2709\uFE0F",
444
- envelopeWithArrow: "\u{1F4E9}",
445
- euroBanknote: "\u{1F4B6}",
446
- evergreenTree: "\u{1F332}",
447
- ewe: "\u{1F411}",
448
- exclamationQuestionMark: "\u2049\uFE0F",
449
- explodingHead: "\u{1F92F}",
450
- expressionlessFace: "\u{1F611}",
451
- eye: "\u{1F441}\uFE0F",
452
- eyeInSpeechBubble: "\u{1F441}\uFE0F\u200D\u{1F5E8}\uFE0F",
453
- eyes: "\u{1F440}",
454
- faceBlowingAKiss: "\u{1F618}",
455
- faceExhaling: "\u{1F62E}\u200D\u{1F4A8}",
456
- faceHoldingBackTears: "\u{1F979}",
457
- faceInClouds: "\u{1F636}\u200D\u{1F32B}\uFE0F",
458
- faceSavoringFood: "\u{1F60B}",
459
- faceScreamingInFear: "\u{1F631}",
460
- faceVomiting: "\u{1F92E}",
461
- faceWithBagsUnderEyes: "\u{1FAE9}",
462
- faceWithCrossedOutEyes: "\u{1F635}",
463
- faceWithDiagonalMouth: "\u{1FAE4}",
464
- faceWithHandOverMouth: "\u{1F92D}",
465
- faceWithHeadBandage: "\u{1F915}",
466
- faceWithMedicalMask: "\u{1F637}",
467
- faceWithMonocle: "\u{1F9D0}",
468
- faceWithOpenEyesAndHandOverMouth: "\u{1FAE2}",
469
- faceWithOpenMouth: "\u{1F62E}",
470
- faceWithoutMouth: "\u{1F636}",
471
- faceWithPeekingEye: "\u{1FAE3}",
472
- faceWithRaisedEyebrow: "\u{1F928}",
473
- faceWithRollingEyes: "\u{1F644}",
474
- faceWithSpiralEyes: "\u{1F635}\u200D\u{1F4AB}",
475
- faceWithSteamFromNose: "\u{1F624}",
476
- faceWithSymbolsOnMouth: "\u{1F92C}",
477
- faceWithTearsOfJoy: "\u{1F602}",
478
- faceWithThermometer: "\u{1F912}",
479
- faceWithTongue: "\u{1F61B}",
480
- factory: "\u{1F3ED}",
481
- factoryWorker: "\u{1F9D1}\u200D\u{1F3ED}",
482
- fairy: "\u{1F9DA}",
483
- falafel: "\u{1F9C6}",
484
- fallenLeaf: "\u{1F342}",
485
- family: "\u{1F46A}",
486
- familyAdultAdultChild: "\u{1F9D1}\u200D\u{1F9D1}\u200D\u{1F9D2}",
487
- familyAdultAdultChildChild: "\u{1F9D1}\u200D\u{1F9D1}\u200D\u{1F9D2}\u200D\u{1F9D2}",
488
- familyAdultChild: "\u{1F9D1}\u200D\u{1F9D2}",
489
- familyAdultChildChild: "\u{1F9D1}\u200D\u{1F9D2}\u200D\u{1F9D2}",
490
- familyManBoy: "\u{1F468}\u200D\u{1F466}",
491
- familyManBoyBoy: "\u{1F468}\u200D\u{1F466}\u200D\u{1F466}",
492
- familyManGirl: "\u{1F468}\u200D\u{1F467}",
493
- familyManGirlBoy: "\u{1F468}\u200D\u{1F467}\u200D\u{1F466}",
494
- familyManGirlGirl: "\u{1F468}\u200D\u{1F467}\u200D\u{1F467}",
495
- familyManManBoy: "\u{1F468}\u200D\u{1F468}\u200D\u{1F466}",
496
- familyManManBoyBoy: "\u{1F468}\u200D\u{1F468}\u200D\u{1F466}\u200D\u{1F466}",
497
- familyManManGirl: "\u{1F468}\u200D\u{1F468}\u200D\u{1F467}",
498
- familyManManGirlBoy: "\u{1F468}\u200D\u{1F468}\u200D\u{1F467}\u200D\u{1F466}",
499
- familyManManGirlGirl: "\u{1F468}\u200D\u{1F468}\u200D\u{1F467}\u200D\u{1F467}",
500
- familyManWomanBoy: "\u{1F468}\u200D\u{1F469}\u200D\u{1F466}",
501
- familyManWomanBoyBoy: "\u{1F468}\u200D\u{1F469}\u200D\u{1F466}\u200D\u{1F466}",
502
- familyManWomanGirl: "\u{1F468}\u200D\u{1F469}\u200D\u{1F467}",
503
- familyManWomanGirlBoy: "\u{1F468}\u200D\u{1F469}\u200D\u{1F467}\u200D\u{1F466}",
504
- familyManWomanGirlGirl: "\u{1F468}\u200D\u{1F469}\u200D\u{1F467}\u200D\u{1F467}",
505
- familyWomanBoy: "\u{1F469}\u200D\u{1F466}",
506
- familyWomanBoyBoy: "\u{1F469}\u200D\u{1F466}\u200D\u{1F466}",
507
- familyWomanGirl: "\u{1F469}\u200D\u{1F467}",
508
- familyWomanGirlBoy: "\u{1F469}\u200D\u{1F467}\u200D\u{1F466}",
509
- familyWomanGirlGirl: "\u{1F469}\u200D\u{1F467}\u200D\u{1F467}",
510
- familyWomanWomanBoy: "\u{1F469}\u200D\u{1F469}\u200D\u{1F466}",
511
- familyWomanWomanBoyBoy: "\u{1F469}\u200D\u{1F469}\u200D\u{1F466}\u200D\u{1F466}",
512
- familyWomanWomanGirl: "\u{1F469}\u200D\u{1F469}\u200D\u{1F467}",
513
- familyWomanWomanGirlBoy: "\u{1F469}\u200D\u{1F469}\u200D\u{1F467}\u200D\u{1F466}",
514
- familyWomanWomanGirlGirl: "\u{1F469}\u200D\u{1F469}\u200D\u{1F467}\u200D\u{1F467}",
515
- farmer: "\u{1F9D1}\u200D\u{1F33E}",
516
- fastDownButton: "\u23EC",
517
- fastForwardButton: "\u23E9",
518
- fastReverseButton: "\u23EA",
519
- fastUpButton: "\u23EB",
520
- faxMachine: "\u{1F4E0}",
521
- fearfulFace: "\u{1F628}",
522
- feather: "\u{1FAB6}",
523
- femaleSign: "\u2640\uFE0F",
524
- ferrisWheel: "\u{1F3A1}",
525
- ferry: "\u26F4\uFE0F",
526
- fieldHockey: "\u{1F3D1}",
527
- fightCloud: "\u{1FAEF}",
528
- fileCabinet: "\u{1F5C4}\uFE0F",
529
- fileFolder: "\u{1F4C1}",
530
- filmFrames: "\u{1F39E}\uFE0F",
531
- filmProjector: "\u{1F4FD}\uFE0F",
532
- fingerprint: "\u{1FAC6}",
533
- fire: "\u{1F525}",
534
- firecracker: "\u{1F9E8}",
535
- fireEngine: "\u{1F692}",
536
- fireExtinguisher: "\u{1F9EF}",
537
- firefighter: "\u{1F9D1}\u200D\u{1F692}",
538
- fireworks: "\u{1F386}",
539
- firstQuarterMoon: "\u{1F313}",
540
- firstQuarterMoonFace: "\u{1F31B}",
541
- fish: "\u{1F41F}",
542
- fishCakeWithSwirl: "\u{1F365}",
543
- fishingPole: "\u{1F3A3}",
544
- fiveOClock: "\u{1F554}",
545
- fiveThirty: "\u{1F560}",
546
- flagAfghanistan: "\u{1F1E6}\u{1F1EB}",
547
- flagAlandIslands: "\u{1F1E6}\u{1F1FD}",
548
- flagAlbania: "\u{1F1E6}\u{1F1F1}",
549
- flagAlgeria: "\u{1F1E9}\u{1F1FF}",
550
- flagAmericanSamoa: "\u{1F1E6}\u{1F1F8}",
551
- flagAndorra: "\u{1F1E6}\u{1F1E9}",
552
- flagAngola: "\u{1F1E6}\u{1F1F4}",
553
- flagAnguilla: "\u{1F1E6}\u{1F1EE}",
554
- flagAntarctica: "\u{1F1E6}\u{1F1F6}",
555
- flagAntiguaBarbuda: "\u{1F1E6}\u{1F1EC}",
556
- flagArgentina: "\u{1F1E6}\u{1F1F7}",
557
- flagArmenia: "\u{1F1E6}\u{1F1F2}",
558
- flagAruba: "\u{1F1E6}\u{1F1FC}",
559
- flagAscensionIsland: "\u{1F1E6}\u{1F1E8}",
560
- flagAustralia: "\u{1F1E6}\u{1F1FA}",
561
- flagAustria: "\u{1F1E6}\u{1F1F9}",
562
- flagAzerbaijan: "\u{1F1E6}\u{1F1FF}",
563
- flagBahamas: "\u{1F1E7}\u{1F1F8}",
564
- flagBahrain: "\u{1F1E7}\u{1F1ED}",
565
- flagBangladesh: "\u{1F1E7}\u{1F1E9}",
566
- flagBarbados: "\u{1F1E7}\u{1F1E7}",
567
- flagBelarus: "\u{1F1E7}\u{1F1FE}",
568
- flagBelgium: "\u{1F1E7}\u{1F1EA}",
569
- flagBelize: "\u{1F1E7}\u{1F1FF}",
570
- flagBenin: "\u{1F1E7}\u{1F1EF}",
571
- flagBermuda: "\u{1F1E7}\u{1F1F2}",
572
- flagBhutan: "\u{1F1E7}\u{1F1F9}",
573
- flagBolivia: "\u{1F1E7}\u{1F1F4}",
574
- flagBosniaHerzegovina: "\u{1F1E7}\u{1F1E6}",
575
- flagBotswana: "\u{1F1E7}\u{1F1FC}",
576
- flagBouvetIsland: "\u{1F1E7}\u{1F1FB}",
577
- flagBrazil: "\u{1F1E7}\u{1F1F7}",
578
- flagBritishIndianOceanTerritory: "\u{1F1EE}\u{1F1F4}",
579
- flagBritishVirginIslands: "\u{1F1FB}\u{1F1EC}",
580
- flagBrunei: "\u{1F1E7}\u{1F1F3}",
581
- flagBulgaria: "\u{1F1E7}\u{1F1EC}",
582
- flagBurkinaFaso: "\u{1F1E7}\u{1F1EB}",
583
- flagBurundi: "\u{1F1E7}\u{1F1EE}",
584
- flagCambodia: "\u{1F1F0}\u{1F1ED}",
585
- flagCameroon: "\u{1F1E8}\u{1F1F2}",
586
- flagCanada: "\u{1F1E8}\u{1F1E6}",
587
- flagCanaryIslands: "\u{1F1EE}\u{1F1E8}",
588
- flagCapeVerde: "\u{1F1E8}\u{1F1FB}",
589
- flagCaribbeanNetherlands: "\u{1F1E7}\u{1F1F6}",
590
- flagCaymanIslands: "\u{1F1F0}\u{1F1FE}",
591
- flagCentralAfricanRepublic: "\u{1F1E8}\u{1F1EB}",
592
- flagCeutaMelilla: "\u{1F1EA}\u{1F1E6}",
593
- flagChad: "\u{1F1F9}\u{1F1E9}",
594
- flagChile: "\u{1F1E8}\u{1F1F1}",
595
- flagChina: "\u{1F1E8}\u{1F1F3}",
596
- flagChristmasIsland: "\u{1F1E8}\u{1F1FD}",
597
- flagClippertonIsland: "\u{1F1E8}\u{1F1F5}",
598
- flagCocosIslands: "\u{1F1E8}\u{1F1E8}",
599
- flagColombia: "\u{1F1E8}\u{1F1F4}",
600
- flagComoros: "\u{1F1F0}\u{1F1F2}",
601
- flagCongoBrazzaville: "\u{1F1E8}\u{1F1EC}",
602
- flagCongoKinshasa: "\u{1F1E8}\u{1F1E9}",
603
- flagCookIslands: "\u{1F1E8}\u{1F1F0}",
604
- flagCostaRica: "\u{1F1E8}\u{1F1F7}",
605
- flagCoteDIvoire: "\u{1F1E8}\u{1F1EE}",
606
- flagCroatia: "\u{1F1ED}\u{1F1F7}",
607
- flagCuba: "\u{1F1E8}\u{1F1FA}",
608
- flagCuracao: "\u{1F1E8}\u{1F1FC}",
609
- flagCyprus: "\u{1F1E8}\u{1F1FE}",
610
- flagCzechia: "\u{1F1E8}\u{1F1FF}",
611
- flagDenmark: "\u{1F1E9}\u{1F1F0}",
612
- flagDiegoGarcia: "\u{1F1E9}\u{1F1EC}",
613
- flagDjibouti: "\u{1F1E9}\u{1F1EF}",
614
- flagDominica: "\u{1F1E9}\u{1F1F2}",
615
- flagDominicanRepublic: "\u{1F1E9}\u{1F1F4}",
616
- flagEcuador: "\u{1F1EA}\u{1F1E8}",
617
- flagEgypt: "\u{1F1EA}\u{1F1EC}",
618
- flagElSalvador: "\u{1F1F8}\u{1F1FB}",
619
- flagEngland: "\u{1F3F4}\u{E0067}\u{E0062}\u{E0065}\u{E006E}\u{E0067}\u{E007F}",
620
- flagEquatorialGuinea: "\u{1F1EC}\u{1F1F6}",
621
- flagEritrea: "\u{1F1EA}\u{1F1F7}",
622
- flagEstonia: "\u{1F1EA}\u{1F1EA}",
623
- flagEswatini: "\u{1F1F8}\u{1F1FF}",
624
- flagEthiopia: "\u{1F1EA}\u{1F1F9}",
625
- flagEuropeanUnion: "\u{1F1EA}\u{1F1FA}",
626
- flagFalklandIslands: "\u{1F1EB}\u{1F1F0}",
627
- flagFaroeIslands: "\u{1F1EB}\u{1F1F4}",
628
- flagFiji: "\u{1F1EB}\u{1F1EF}",
629
- flagFinland: "\u{1F1EB}\u{1F1EE}",
630
- flagFrance: "\u{1F1EB}\u{1F1F7}",
631
- flagFrenchGuiana: "\u{1F1EC}\u{1F1EB}",
632
- flagFrenchPolynesia: "\u{1F1F5}\u{1F1EB}",
633
- flagFrenchSouthernTerritories: "\u{1F1F9}\u{1F1EB}",
634
- flagGabon: "\u{1F1EC}\u{1F1E6}",
635
- flagGambia: "\u{1F1EC}\u{1F1F2}",
636
- flagGeorgia: "\u{1F1EC}\u{1F1EA}",
637
- flagGermany: "\u{1F1E9}\u{1F1EA}",
638
- flagGhana: "\u{1F1EC}\u{1F1ED}",
639
- flagGibraltar: "\u{1F1EC}\u{1F1EE}",
640
- flagGreece: "\u{1F1EC}\u{1F1F7}",
641
- flagGreenland: "\u{1F1EC}\u{1F1F1}",
642
- flagGrenada: "\u{1F1EC}\u{1F1E9}",
643
- flagGuadeloupe: "\u{1F1EC}\u{1F1F5}",
644
- flagGuam: "\u{1F1EC}\u{1F1FA}",
645
- flagGuatemala: "\u{1F1EC}\u{1F1F9}",
646
- flagGuernsey: "\u{1F1EC}\u{1F1EC}",
647
- flagGuinea: "\u{1F1EC}\u{1F1F3}",
648
- flagGuineaBissau: "\u{1F1EC}\u{1F1FC}",
649
- flagGuyana: "\u{1F1EC}\u{1F1FE}",
650
- flagHaiti: "\u{1F1ED}\u{1F1F9}",
651
- flagHeardMcdonaldIslands: "\u{1F1ED}\u{1F1F2}",
652
- flagHonduras: "\u{1F1ED}\u{1F1F3}",
653
- flagHongKongSarChina: "\u{1F1ED}\u{1F1F0}",
654
- flagHungary: "\u{1F1ED}\u{1F1FA}",
655
- flagIceland: "\u{1F1EE}\u{1F1F8}",
656
- flagIndia: "\u{1F1EE}\u{1F1F3}",
657
- flagIndonesia: "\u{1F1EE}\u{1F1E9}",
658
- flagInHole: "\u26F3",
659
- flagIran: "\u{1F1EE}\u{1F1F7}",
660
- flagIraq: "\u{1F1EE}\u{1F1F6}",
661
- flagIreland: "\u{1F1EE}\u{1F1EA}",
662
- flagIsleOfMan: "\u{1F1EE}\u{1F1F2}",
663
- flagIsrael: "\u{1F1EE}\u{1F1F1}",
664
- flagItaly: "\u{1F1EE}\u{1F1F9}",
665
- flagJamaica: "\u{1F1EF}\u{1F1F2}",
666
- flagJapan: "\u{1F1EF}\u{1F1F5}",
667
- flagJersey: "\u{1F1EF}\u{1F1EA}",
668
- flagJordan: "\u{1F1EF}\u{1F1F4}",
669
- flagKazakhstan: "\u{1F1F0}\u{1F1FF}",
670
- flagKenya: "\u{1F1F0}\u{1F1EA}",
671
- flagKiribati: "\u{1F1F0}\u{1F1EE}",
672
- flagKosovo: "\u{1F1FD}\u{1F1F0}",
673
- flagKuwait: "\u{1F1F0}\u{1F1FC}",
674
- flagKyrgyzstan: "\u{1F1F0}\u{1F1EC}",
675
- flagLaos: "\u{1F1F1}\u{1F1E6}",
676
- flagLatvia: "\u{1F1F1}\u{1F1FB}",
677
- flagLebanon: "\u{1F1F1}\u{1F1E7}",
678
- flagLesotho: "\u{1F1F1}\u{1F1F8}",
679
- flagLiberia: "\u{1F1F1}\u{1F1F7}",
680
- flagLibya: "\u{1F1F1}\u{1F1FE}",
681
- flagLiechtenstein: "\u{1F1F1}\u{1F1EE}",
682
- flagLithuania: "\u{1F1F1}\u{1F1F9}",
683
- flagLuxembourg: "\u{1F1F1}\u{1F1FA}",
684
- flagMacaoSarChina: "\u{1F1F2}\u{1F1F4}",
685
- flagMadagascar: "\u{1F1F2}\u{1F1EC}",
686
- flagMalawi: "\u{1F1F2}\u{1F1FC}",
687
- flagMalaysia: "\u{1F1F2}\u{1F1FE}",
688
- flagMaldives: "\u{1F1F2}\u{1F1FB}",
689
- flagMali: "\u{1F1F2}\u{1F1F1}",
690
- flagMalta: "\u{1F1F2}\u{1F1F9}",
691
- flagMarshallIslands: "\u{1F1F2}\u{1F1ED}",
692
- flagMartinique: "\u{1F1F2}\u{1F1F6}",
693
- flagMauritania: "\u{1F1F2}\u{1F1F7}",
694
- flagMauritius: "\u{1F1F2}\u{1F1FA}",
695
- flagMayotte: "\u{1F1FE}\u{1F1F9}",
696
- flagMexico: "\u{1F1F2}\u{1F1FD}",
697
- flagMicronesia: "\u{1F1EB}\u{1F1F2}",
698
- flagMoldova: "\u{1F1F2}\u{1F1E9}",
699
- flagMonaco: "\u{1F1F2}\u{1F1E8}",
700
- flagMongolia: "\u{1F1F2}\u{1F1F3}",
701
- flagMontenegro: "\u{1F1F2}\u{1F1EA}",
702
- flagMontserrat: "\u{1F1F2}\u{1F1F8}",
703
- flagMorocco: "\u{1F1F2}\u{1F1E6}",
704
- flagMozambique: "\u{1F1F2}\u{1F1FF}",
705
- flagMyanmar: "\u{1F1F2}\u{1F1F2}",
706
- flagNamibia: "\u{1F1F3}\u{1F1E6}",
707
- flagNauru: "\u{1F1F3}\u{1F1F7}",
708
- flagNepal: "\u{1F1F3}\u{1F1F5}",
709
- flagNetherlands: "\u{1F1F3}\u{1F1F1}",
710
- flagNewCaledonia: "\u{1F1F3}\u{1F1E8}",
711
- flagNewZealand: "\u{1F1F3}\u{1F1FF}",
712
- flagNicaragua: "\u{1F1F3}\u{1F1EE}",
713
- flagNiger: "\u{1F1F3}\u{1F1EA}",
714
- flagNigeria: "\u{1F1F3}\u{1F1EC}",
715
- flagNiue: "\u{1F1F3}\u{1F1FA}",
716
- flagNorfolkIsland: "\u{1F1F3}\u{1F1EB}",
717
- flagNorthernMarianaIslands: "\u{1F1F2}\u{1F1F5}",
718
- flagNorthKorea: "\u{1F1F0}\u{1F1F5}",
719
- flagNorthMacedonia: "\u{1F1F2}\u{1F1F0}",
720
- flagNorway: "\u{1F1F3}\u{1F1F4}",
721
- flagOman: "\u{1F1F4}\u{1F1F2}",
722
- flagPakistan: "\u{1F1F5}\u{1F1F0}",
723
- flagPalau: "\u{1F1F5}\u{1F1FC}",
724
- flagPalestinianTerritories: "\u{1F1F5}\u{1F1F8}",
725
- flagPanama: "\u{1F1F5}\u{1F1E6}",
726
- flagPapuaNewGuinea: "\u{1F1F5}\u{1F1EC}",
727
- flagParaguay: "\u{1F1F5}\u{1F1FE}",
728
- flagPeru: "\u{1F1F5}\u{1F1EA}",
729
- flagPhilippines: "\u{1F1F5}\u{1F1ED}",
730
- flagPitcairnIslands: "\u{1F1F5}\u{1F1F3}",
731
- flagPoland: "\u{1F1F5}\u{1F1F1}",
732
- flagPortugal: "\u{1F1F5}\u{1F1F9}",
733
- flagPuertoRico: "\u{1F1F5}\u{1F1F7}",
734
- flagQatar: "\u{1F1F6}\u{1F1E6}",
735
- flagReunion: "\u{1F1F7}\u{1F1EA}",
736
- flagRomania: "\u{1F1F7}\u{1F1F4}",
737
- flagRussia: "\u{1F1F7}\u{1F1FA}",
738
- flagRwanda: "\u{1F1F7}\u{1F1FC}",
739
- flagSamoa: "\u{1F1FC}\u{1F1F8}",
740
- flagSanMarino: "\u{1F1F8}\u{1F1F2}",
741
- flagSaoTomePrincipe: "\u{1F1F8}\u{1F1F9}",
742
- flagSark: "\u{1F1E8}\u{1F1F6}",
743
- flagSaudiArabia: "\u{1F1F8}\u{1F1E6}",
744
- flagScotland: "\u{1F3F4}\u{E0067}\u{E0062}\u{E0073}\u{E0063}\u{E0074}\u{E007F}",
745
- flagSenegal: "\u{1F1F8}\u{1F1F3}",
746
- flagSerbia: "\u{1F1F7}\u{1F1F8}",
747
- flagSeychelles: "\u{1F1F8}\u{1F1E8}",
748
- flagSierraLeone: "\u{1F1F8}\u{1F1F1}",
749
- flagSingapore: "\u{1F1F8}\u{1F1EC}",
750
- flagSintMaarten: "\u{1F1F8}\u{1F1FD}",
751
- flagSlovakia: "\u{1F1F8}\u{1F1F0}",
752
- flagSlovenia: "\u{1F1F8}\u{1F1EE}",
753
- flagSolomonIslands: "\u{1F1F8}\u{1F1E7}",
754
- flagSomalia: "\u{1F1F8}\u{1F1F4}",
755
- flagSouthAfrica: "\u{1F1FF}\u{1F1E6}",
756
- flagSouthGeorgiaSouthSandwichIslands: "\u{1F1EC}\u{1F1F8}",
757
- flagSouthKorea: "\u{1F1F0}\u{1F1F7}",
758
- flagSouthSudan: "\u{1F1F8}\u{1F1F8}",
759
- flagSpain: "\u{1F1EA}\u{1F1F8}",
760
- flagSriLanka: "\u{1F1F1}\u{1F1F0}",
761
- flagStBarthelemy: "\u{1F1E7}\u{1F1F1}",
762
- flagStHelena: "\u{1F1F8}\u{1F1ED}",
763
- flagStKittsNevis: "\u{1F1F0}\u{1F1F3}",
764
- flagStLucia: "\u{1F1F1}\u{1F1E8}",
765
- flagStMartin: "\u{1F1F2}\u{1F1EB}",
766
- flagStPierreMiquelon: "\u{1F1F5}\u{1F1F2}",
767
- flagStVincentGrenadines: "\u{1F1FB}\u{1F1E8}",
768
- flagSudan: "\u{1F1F8}\u{1F1E9}",
769
- flagSuriname: "\u{1F1F8}\u{1F1F7}",
770
- flagSvalbardJanMayen: "\u{1F1F8}\u{1F1EF}",
771
- flagSweden: "\u{1F1F8}\u{1F1EA}",
772
- flagSwitzerland: "\u{1F1E8}\u{1F1ED}",
773
- flagSyria: "\u{1F1F8}\u{1F1FE}",
774
- flagTaiwan: "\u{1F1F9}\u{1F1FC}",
775
- flagTajikistan: "\u{1F1F9}\u{1F1EF}",
776
- flagTanzania: "\u{1F1F9}\u{1F1FF}",
777
- flagThailand: "\u{1F1F9}\u{1F1ED}",
778
- flagTimorLeste: "\u{1F1F9}\u{1F1F1}",
779
- flagTogo: "\u{1F1F9}\u{1F1EC}",
780
- flagTokelau: "\u{1F1F9}\u{1F1F0}",
781
- flagTonga: "\u{1F1F9}\u{1F1F4}",
782
- flagTrinidadTobago: "\u{1F1F9}\u{1F1F9}",
783
- flagTristanDaCunha: "\u{1F1F9}\u{1F1E6}",
784
- flagTunisia: "\u{1F1F9}\u{1F1F3}",
785
- flagTurkiye: "\u{1F1F9}\u{1F1F7}",
786
- flagTurkmenistan: "\u{1F1F9}\u{1F1F2}",
787
- flagTurksCaicosIslands: "\u{1F1F9}\u{1F1E8}",
788
- flagTuvalu: "\u{1F1F9}\u{1F1FB}",
789
- flagUganda: "\u{1F1FA}\u{1F1EC}",
790
- flagUkraine: "\u{1F1FA}\u{1F1E6}",
791
- flagUnitedArabEmirates: "\u{1F1E6}\u{1F1EA}",
792
- flagUnitedKingdom: "\u{1F1EC}\u{1F1E7}",
793
- flagUnitedNations: "\u{1F1FA}\u{1F1F3}",
794
- flagUnitedStates: "\u{1F1FA}\u{1F1F8}",
795
- flagUruguay: "\u{1F1FA}\u{1F1FE}",
796
- flagUSOutlyingIslands: "\u{1F1FA}\u{1F1F2}",
797
- flagUSVirginIslands: "\u{1F1FB}\u{1F1EE}",
798
- flagUzbekistan: "\u{1F1FA}\u{1F1FF}",
799
- flagVanuatu: "\u{1F1FB}\u{1F1FA}",
800
- flagVaticanCity: "\u{1F1FB}\u{1F1E6}",
801
- flagVenezuela: "\u{1F1FB}\u{1F1EA}",
802
- flagVietnam: "\u{1F1FB}\u{1F1F3}",
803
- flagWales: "\u{1F3F4}\u{E0067}\u{E0062}\u{E0077}\u{E006C}\u{E0073}\u{E007F}",
804
- flagWallisFutuna: "\u{1F1FC}\u{1F1EB}",
805
- flagWesternSahara: "\u{1F1EA}\u{1F1ED}",
806
- flagYemen: "\u{1F1FE}\u{1F1EA}",
807
- flagZambia: "\u{1F1FF}\u{1F1F2}",
808
- flagZimbabwe: "\u{1F1FF}\u{1F1FC}",
809
- flamingo: "\u{1F9A9}",
810
- flashlight: "\u{1F526}",
811
- flatbread: "\u{1FAD3}",
812
- flatShoe: "\u{1F97F}",
813
- fleurDeLis: "\u269C\uFE0F",
814
- flexedBiceps: "\u{1F4AA}",
815
- floppyDisk: "\u{1F4BE}",
816
- flowerPlayingCards: "\u{1F3B4}",
817
- flushedFace: "\u{1F633}",
818
- flute: "\u{1FA88}",
819
- fly: "\u{1FAB0}",
820
- flyingDisc: "\u{1F94F}",
821
- flyingSaucer: "\u{1F6F8}",
822
- fog: "\u{1F32B}\uFE0F",
823
- foggy: "\u{1F301}",
824
- foldedHands: "\u{1F64F}",
825
- foldingHandFan: "\u{1FAAD}",
826
- fondue: "\u{1FAD5}",
827
- foot: "\u{1F9B6}",
828
- footprints: "\u{1F463}",
829
- forkAndKnife: "\u{1F374}",
830
- forkAndKnifeWithPlate: "\u{1F37D}\uFE0F",
831
- fortuneCookie: "\u{1F960}",
832
- fountain: "\u26F2",
833
- fountainPen: "\u{1F58B}\uFE0F",
834
- fourLeafClover: "\u{1F340}",
835
- fourOClock: "\u{1F553}",
836
- fourThirty: "\u{1F55F}",
837
- fox: "\u{1F98A}",
838
- framedPicture: "\u{1F5BC}\uFE0F",
839
- freeButton: "\u{1F193}",
840
- frenchFries: "\u{1F35F}",
841
- friedShrimp: "\u{1F364}",
842
- frog: "\u{1F438}",
843
- frontFacingBabyChick: "\u{1F425}",
844
- frowningFace: "\u2639\uFE0F",
845
- frowningFaceWithOpenMouth: "\u{1F626}",
846
- fuelPump: "\u26FD",
847
- fullMoon: "\u{1F315}",
848
- fullMoonFace: "\u{1F31D}",
849
- funeralUrn: "\u26B1\uFE0F",
850
- gameDie: "\u{1F3B2}",
851
- garlic: "\u{1F9C4}",
852
- gear: "\u2699\uFE0F",
853
- gemini: "\u264A",
854
- gemStone: "\u{1F48E}",
855
- genie: "\u{1F9DE}",
856
- ghost: "\u{1F47B}",
857
- gingerRoot: "\u{1FADA}",
858
- giraffe: "\u{1F992}",
859
- girl: "\u{1F467}",
860
- glasses: "\u{1F453}",
861
- glassOfMilk: "\u{1F95B}",
862
- globeShowingAmericas: "\u{1F30E}",
863
- globeShowingAsiaAustralia: "\u{1F30F}",
864
- globeShowingEuropeAfrica: "\u{1F30D}",
865
- globeWithMeridians: "\u{1F310}",
866
- gloves: "\u{1F9E4}",
867
- glowingStar: "\u{1F31F}",
868
- goalNet: "\u{1F945}",
869
- goat: "\u{1F410}",
870
- goblin: "\u{1F47A}",
871
- goggles: "\u{1F97D}",
872
- goose: "\u{1FABF}",
873
- gorilla: "\u{1F98D}",
874
- graduationCap: "\u{1F393}",
875
- grapes: "\u{1F347}",
876
- greenApple: "\u{1F34F}",
877
- greenBook: "\u{1F4D7}",
878
- greenCircle: "\u{1F7E2}",
879
- greenHeart: "\u{1F49A}",
880
- greenSalad: "\u{1F957}",
881
- greenSquare: "\u{1F7E9}",
882
- greyHeart: "\u{1FA76}",
883
- grimacingFace: "\u{1F62C}",
884
- grinningCat: "\u{1F63A}",
885
- grinningCatWithSmilingEyes: "\u{1F638}",
886
- grinningFace: "\u{1F600}",
887
- grinningFaceWithBigEyes: "\u{1F603}",
888
- grinningFaceWithSmilingEyes: "\u{1F604}",
889
- grinningFaceWithSweat: "\u{1F605}",
890
- grinningSquintingFace: "\u{1F606}",
891
- growingHeart: "\u{1F497}",
892
- guard: "\u{1F482}",
893
- guideDog: "\u{1F9AE}",
894
- guitar: "\u{1F3B8}",
895
- hairPick: "\u{1FAAE}",
896
- hairyCreature: "\u{1FAC8}",
897
- hamburger: "\u{1F354}",
898
- hammer: "\u{1F528}",
899
- hammerAndPick: "\u2692\uFE0F",
900
- hammerAndWrench: "\u{1F6E0}\uFE0F",
901
- hamsa: "\u{1FAAC}",
902
- hamster: "\u{1F439}",
903
- handbag: "\u{1F45C}",
904
- handshake: "\u{1F91D}",
905
- handWithFingersSplayed: "\u{1F590}\uFE0F",
906
- handWithIndexFingerAndThumbCrossed: "\u{1FAF0}",
907
- harp: "\u{1FA89}",
908
- hatchingChick: "\u{1F423}",
909
- headphone: "\u{1F3A7}",
910
- headShakingHorizontally: "\u{1F642}\u200D\u2194\uFE0F",
911
- headShakingVertically: "\u{1F642}\u200D\u2195\uFE0F",
912
- headstone: "\u{1FAA6}",
913
- healthWorker: "\u{1F9D1}\u200D\u2695\uFE0F",
914
- hearNoEvilMonkey: "\u{1F649}",
915
- heartDecoration: "\u{1F49F}",
916
- heartExclamation: "\u2763\uFE0F",
917
- heartHands: "\u{1FAF6}",
918
- heartOnFire: "\u2764\uFE0F\u200D\u{1F525}",
919
- heartSuit: "\u2665\uFE0F",
920
- heartWithArrow: "\u{1F498}",
921
- heartWithRibbon: "\u{1F49D}",
922
- heavyDollarSign: "\u{1F4B2}",
923
- heavyEqualsSign: "\u{1F7F0}",
924
- hedgehog: "\u{1F994}",
925
- helicopter: "\u{1F681}",
926
- herb: "\u{1F33F}",
927
- hibiscus: "\u{1F33A}",
928
- highHeeledShoe: "\u{1F460}",
929
- highSpeedTrain: "\u{1F684}",
930
- highVoltage: "\u26A1",
931
- hikingBoot: "\u{1F97E}",
932
- hinduTemple: "\u{1F6D5}",
933
- hippopotamus: "\u{1F99B}",
934
- hole: "\u{1F573}\uFE0F",
935
- hollowRedCircle: "\u2B55",
936
- honeybee: "\u{1F41D}",
937
- honeyPot: "\u{1F36F}",
938
- hook: "\u{1FA9D}",
939
- horizontalTrafficLight: "\u{1F6A5}",
940
- horse: "\u{1F40E}",
941
- horseFace: "\u{1F434}",
942
- horseRacing: "\u{1F3C7}",
943
- hospital: "\u{1F3E5}",
944
- hotBeverage: "\u2615",
945
- hotDog: "\u{1F32D}",
946
- hotel: "\u{1F3E8}",
947
- hotFace: "\u{1F975}",
948
- hotPepper: "\u{1F336}\uFE0F",
949
- hotSprings: "\u2668\uFE0F",
950
- hourglassDone: "\u231B",
951
- hourglassNotDone: "\u23F3",
952
- house: "\u{1F3E0}",
953
- houses: "\u{1F3D8}\uFE0F",
954
- houseWithGarden: "\u{1F3E1}",
955
- hundredPoints: "\u{1F4AF}",
956
- hushedFace: "\u{1F62F}",
957
- hut: "\u{1F6D6}",
958
- hyacinth: "\u{1FABB}",
959
- ice: "\u{1F9CA}",
960
- iceCream: "\u{1F368}",
961
- iceHockey: "\u{1F3D2}",
962
- iceSkate: "\u26F8\uFE0F",
963
- idButton: "\u{1F194}",
964
- identificationCard: "\u{1FAAA}",
965
- inboxTray: "\u{1F4E5}",
966
- incomingEnvelope: "\u{1F4E8}",
967
- indexPointingAtTheViewer: "\u{1FAF5}",
968
- indexPointingUp: "\u261D\uFE0F",
969
- infinity: "\u267E\uFE0F",
970
- information: "\u2139\uFE0F",
971
- inputLatinLetters: "\u{1F524}",
972
- inputLatinLowercase: "\u{1F521}",
973
- inputLatinUppercase: "\u{1F520}",
974
- inputNumbers: "\u{1F522}",
975
- inputSymbols: "\u{1F523}",
976
- jackOLantern: "\u{1F383}",
977
- japaneseAcceptableButton: "\u{1F251}",
978
- japaneseApplicationButton: "\u{1F238}",
979
- japaneseBargainButton: "\u{1F250}",
980
- japaneseCastle: "\u{1F3EF}",
981
- japaneseCongratulationsButton: "\u3297\uFE0F",
982
- japaneseDiscountButton: "\u{1F239}",
983
- japaneseDolls: "\u{1F38E}",
984
- japaneseFreeOfChargeButton: "\u{1F21A}",
985
- japaneseHereButton: "\u{1F201}",
986
- japaneseMonthlyAmountButton: "\u{1F237}\uFE0F",
987
- japaneseNotFreeOfChargeButton: "\u{1F236}",
988
- japaneseNoVacancyButton: "\u{1F235}",
989
- japaneseOpenForBusinessButton: "\u{1F23A}",
990
- japanesePassingGradeButton: "\u{1F234}",
991
- japanesePostOffice: "\u{1F3E3}",
992
- japaneseProhibitedButton: "\u{1F232}",
993
- japaneseReservedButton: "\u{1F22F}",
994
- japaneseSecretButton: "\u3299\uFE0F",
995
- japaneseServiceChargeButton: "\u{1F202}\uFE0F",
996
- japaneseSymbolForBeginner: "\u{1F530}",
997
- japaneseVacancyButton: "\u{1F233}",
998
- jar: "\u{1FAD9}",
999
- jeans: "\u{1F456}",
1000
- jellyfish: "\u{1FABC}",
1001
- joker: "\u{1F0CF}",
1002
- joystick: "\u{1F579}\uFE0F",
1003
- judge: "\u{1F9D1}\u200D\u2696\uFE0F",
1004
- kaaba: "\u{1F54B}",
1005
- kangaroo: "\u{1F998}",
1006
- key: "\u{1F511}",
1007
- keyboard: "\u2328\uFE0F",
1008
- keycap0: "0\uFE0F\u20E3",
1009
- keycap1: "1\uFE0F\u20E3",
1010
- keycap10: "\u{1F51F}",
1011
- keycap2: "2\uFE0F\u20E3",
1012
- keycap3: "3\uFE0F\u20E3",
1013
- keycap4: "4\uFE0F\u20E3",
1014
- keycap5: "5\uFE0F\u20E3",
1015
- keycap6: "6\uFE0F\u20E3",
1016
- keycap7: "7\uFE0F\u20E3",
1017
- keycap8: "8\uFE0F\u20E3",
1018
- keycap9: "9\uFE0F\u20E3",
1019
- keycapAsterisk: "*\uFE0F\u20E3",
1020
- keycapNumberSign: "#\uFE0F\u20E3",
1021
- khanda: "\u{1FAAF}",
1022
- kickScooter: "\u{1F6F4}",
1023
- kimono: "\u{1F458}",
1024
- kiss: "\u{1F48F}",
1025
- kissingCat: "\u{1F63D}",
1026
- kissingFace: "\u{1F617}",
1027
- kissingFaceWithClosedEyes: "\u{1F61A}",
1028
- kissingFaceWithSmilingEyes: "\u{1F619}",
1029
- kissManMan: "\u{1F468}\u200D\u2764\uFE0F\u200D\u{1F48B}\u200D\u{1F468}",
1030
- kissMark: "\u{1F48B}",
1031
- kissWomanMan: "\u{1F469}\u200D\u2764\uFE0F\u200D\u{1F48B}\u200D\u{1F468}",
1032
- kissWomanWoman: "\u{1F469}\u200D\u2764\uFE0F\u200D\u{1F48B}\u200D\u{1F469}",
1033
- kitchenKnife: "\u{1F52A}",
1034
- kite: "\u{1FA81}",
1035
- kiwiFruit: "\u{1F95D}",
1036
- knot: "\u{1FAA2}",
1037
- koala: "\u{1F428}",
1038
- labCoat: "\u{1F97C}",
1039
- label: "\u{1F3F7}\uFE0F",
1040
- lacrosse: "\u{1F94D}",
1041
- ladder: "\u{1FA9C}",
1042
- ladyBeetle: "\u{1F41E}",
1043
- landslide: "\u{1F6D8}",
1044
- laptop: "\u{1F4BB}",
1045
- largeBlueDiamond: "\u{1F537}",
1046
- largeOrangeDiamond: "\u{1F536}",
1047
- lastQuarterMoon: "\u{1F317}",
1048
- lastQuarterMoonFace: "\u{1F31C}",
1049
- lastTrackButton: "\u23EE\uFE0F",
1050
- latinCross: "\u271D\uFE0F",
1051
- leafFlutteringInWind: "\u{1F343}",
1052
- leaflessTree: "\u{1FABE}",
1053
- leafyGreen: "\u{1F96C}",
1054
- ledger: "\u{1F4D2}",
1055
- leftArrow: "\u2B05\uFE0F",
1056
- leftArrowCurvingRight: "\u21AA\uFE0F",
1057
- leftFacingFist: "\u{1F91B}",
1058
- leftLuggage: "\u{1F6C5}",
1059
- leftRightArrow: "\u2194\uFE0F",
1060
- leftSpeechBubble: "\u{1F5E8}\uFE0F",
1061
- leftwardsHand: "\u{1FAF2}",
1062
- leftwardsPushingHand: "\u{1FAF7}",
1063
- leg: "\u{1F9B5}",
1064
- lemon: "\u{1F34B}",
1065
- leo: "\u264C",
1066
- leopard: "\u{1F406}",
1067
- levelSlider: "\u{1F39A}\uFE0F",
1068
- libra: "\u264E",
1069
- lightBlueHeart: "\u{1FA75}",
1070
- lightBulb: "\u{1F4A1}",
1071
- lightRail: "\u{1F688}",
1072
- lime: "\u{1F34B}\u200D\u{1F7E9}",
1073
- link: "\u{1F517}",
1074
- linkedPaperclips: "\u{1F587}\uFE0F",
1075
- lion: "\u{1F981}",
1076
- lipstick: "\u{1F484}",
1077
- litterInBinSign: "\u{1F6AE}",
1078
- lizard: "\u{1F98E}",
1079
- llama: "\u{1F999}",
1080
- lobster: "\u{1F99E}",
1081
- locked: "\u{1F512}",
1082
- lockedWithKey: "\u{1F510}",
1083
- lockedWithPen: "\u{1F50F}",
1084
- locomotive: "\u{1F682}",
1085
- lollipop: "\u{1F36D}",
1086
- longDrum: "\u{1FA98}",
1087
- lotionBottle: "\u{1F9F4}",
1088
- lotus: "\u{1FAB7}",
1089
- loudlyCryingFace: "\u{1F62D}",
1090
- loudspeaker: "\u{1F4E2}",
1091
- loveHotel: "\u{1F3E9}",
1092
- loveLetter: "\u{1F48C}",
1093
- loveYouGesture: "\u{1F91F}",
1094
- lowBattery: "\u{1FAAB}",
1095
- luggage: "\u{1F9F3}",
1096
- lungs: "\u{1FAC1}",
1097
- lyingFace: "\u{1F925}",
1098
- mage: "\u{1F9D9}",
1099
- magicWand: "\u{1FA84}",
1100
- magnet: "\u{1F9F2}",
1101
- magnifyingGlassTiltedLeft: "\u{1F50D}",
1102
- magnifyingGlassTiltedRight: "\u{1F50E}",
1103
- mahjongRedDragon: "\u{1F004}",
1104
- maleSign: "\u2642\uFE0F",
1105
- mammoth: "\u{1F9A3}",
1106
- man: "\u{1F468}",
1107
- manArtist: "\u{1F468}\u200D\u{1F3A8}",
1108
- manAstronaut: "\u{1F468}\u200D\u{1F680}",
1109
- manBald: "\u{1F468}\u200D\u{1F9B2}",
1110
- manBeard: "\u{1F9D4}\u200D\u2642\uFE0F",
1111
- manBiking: "\u{1F6B4}\u200D\u2642\uFE0F",
1112
- manBlondHair: "\u{1F471}\u200D\u2642\uFE0F",
1113
- manBouncingBall: "\u26F9\uFE0F\u200D\u2642\uFE0F",
1114
- manBowing: "\u{1F647}\u200D\u2642\uFE0F",
1115
- manCartwheeling: "\u{1F938}\u200D\u2642\uFE0F",
1116
- manClimbing: "\u{1F9D7}\u200D\u2642\uFE0F",
1117
- manConstructionWorker: "\u{1F477}\u200D\u2642\uFE0F",
1118
- manCook: "\u{1F468}\u200D\u{1F373}",
1119
- manCurlyHair: "\u{1F468}\u200D\u{1F9B1}",
1120
- manDancing: "\u{1F57A}",
1121
- manDetective: "\u{1F575}\uFE0F\u200D\u2642\uFE0F",
1122
- manElf: "\u{1F9DD}\u200D\u2642\uFE0F",
1123
- manFacepalming: "\u{1F926}\u200D\u2642\uFE0F",
1124
- manFactoryWorker: "\u{1F468}\u200D\u{1F3ED}",
1125
- manFairy: "\u{1F9DA}\u200D\u2642\uFE0F",
1126
- manFarmer: "\u{1F468}\u200D\u{1F33E}",
1127
- manFeedingBaby: "\u{1F468}\u200D\u{1F37C}",
1128
- manFirefighter: "\u{1F468}\u200D\u{1F692}",
1129
- manFrowning: "\u{1F64D}\u200D\u2642\uFE0F",
1130
- manGenie: "\u{1F9DE}\u200D\u2642\uFE0F",
1131
- manGesturingNo: "\u{1F645}\u200D\u2642\uFE0F",
1132
- manGesturingOk: "\u{1F646}\u200D\u2642\uFE0F",
1133
- manGettingHaircut: "\u{1F487}\u200D\u2642\uFE0F",
1134
- manGettingMassage: "\u{1F486}\u200D\u2642\uFE0F",
1135
- mango: "\u{1F96D}",
1136
- manGolfing: "\u{1F3CC}\uFE0F\u200D\u2642\uFE0F",
1137
- manGuard: "\u{1F482}\u200D\u2642\uFE0F",
1138
- manHealthWorker: "\u{1F468}\u200D\u2695\uFE0F",
1139
- manInLotusPosition: "\u{1F9D8}\u200D\u2642\uFE0F",
1140
- manInManualWheelchair: "\u{1F468}\u200D\u{1F9BD}",
1141
- manInManualWheelchairFacingRight: "\u{1F468}\u200D\u{1F9BD}\u200D\u27A1\uFE0F",
1142
- manInMotorizedWheelchair: "\u{1F468}\u200D\u{1F9BC}",
1143
- manInMotorizedWheelchairFacingRight: "\u{1F468}\u200D\u{1F9BC}\u200D\u27A1\uFE0F",
1144
- manInSteamyRoom: "\u{1F9D6}\u200D\u2642\uFE0F",
1145
- manInTuxedo: "\u{1F935}\u200D\u2642\uFE0F",
1146
- manJudge: "\u{1F468}\u200D\u2696\uFE0F",
1147
- manJuggling: "\u{1F939}\u200D\u2642\uFE0F",
1148
- manKneeling: "\u{1F9CE}\u200D\u2642\uFE0F",
1149
- manKneelingFacingRight: "\u{1F9CE}\u200D\u2642\uFE0F\u200D\u27A1\uFE0F",
1150
- manLiftingWeights: "\u{1F3CB}\uFE0F\u200D\u2642\uFE0F",
1151
- manMage: "\u{1F9D9}\u200D\u2642\uFE0F",
1152
- manMechanic: "\u{1F468}\u200D\u{1F527}",
1153
- manMountainBiking: "\u{1F6B5}\u200D\u2642\uFE0F",
1154
- manOfficeWorker: "\u{1F468}\u200D\u{1F4BC}",
1155
- manPilot: "\u{1F468}\u200D\u2708\uFE0F",
1156
- manPlayingHandball: "\u{1F93E}\u200D\u2642\uFE0F",
1157
- manPlayingWaterPolo: "\u{1F93D}\u200D\u2642\uFE0F",
1158
- manPoliceOfficer: "\u{1F46E}\u200D\u2642\uFE0F",
1159
- manPouting: "\u{1F64E}\u200D\u2642\uFE0F",
1160
- manRaisingHand: "\u{1F64B}\u200D\u2642\uFE0F",
1161
- manRedHair: "\u{1F468}\u200D\u{1F9B0}",
1162
- manRowingBoat: "\u{1F6A3}\u200D\u2642\uFE0F",
1163
- manRunning: "\u{1F3C3}\u200D\u2642\uFE0F",
1164
- manRunningFacingRight: "\u{1F3C3}\u200D\u2642\uFE0F\u200D\u27A1\uFE0F",
1165
- manScientist: "\u{1F468}\u200D\u{1F52C}",
1166
- manShrugging: "\u{1F937}\u200D\u2642\uFE0F",
1167
- manSinger: "\u{1F468}\u200D\u{1F3A4}",
1168
- manSShoe: "\u{1F45E}",
1169
- manStanding: "\u{1F9CD}\u200D\u2642\uFE0F",
1170
- manStudent: "\u{1F468}\u200D\u{1F393}",
1171
- manSuperhero: "\u{1F9B8}\u200D\u2642\uFE0F",
1172
- manSupervillain: "\u{1F9B9}\u200D\u2642\uFE0F",
1173
- manSurfing: "\u{1F3C4}\u200D\u2642\uFE0F",
1174
- manSwimming: "\u{1F3CA}\u200D\u2642\uFE0F",
1175
- manTeacher: "\u{1F468}\u200D\u{1F3EB}",
1176
- manTechnologist: "\u{1F468}\u200D\u{1F4BB}",
1177
- mantelpieceClock: "\u{1F570}\uFE0F",
1178
- manTippingHand: "\u{1F481}\u200D\u2642\uFE0F",
1179
- manualWheelchair: "\u{1F9BD}",
1180
- manVampire: "\u{1F9DB}\u200D\u2642\uFE0F",
1181
- manWalking: "\u{1F6B6}\u200D\u2642\uFE0F",
1182
- manWalkingFacingRight: "\u{1F6B6}\u200D\u2642\uFE0F\u200D\u27A1\uFE0F",
1183
- manWearingTurban: "\u{1F473}\u200D\u2642\uFE0F",
1184
- manWhiteHair: "\u{1F468}\u200D\u{1F9B3}",
1185
- manWithVeil: "\u{1F470}\u200D\u2642\uFE0F",
1186
- manWithWhiteCane: "\u{1F468}\u200D\u{1F9AF}",
1187
- manWithWhiteCaneFacingRight: "\u{1F468}\u200D\u{1F9AF}\u200D\u27A1\uFE0F",
1188
- manZombie: "\u{1F9DF}\u200D\u2642\uFE0F",
1189
- mapleLeaf: "\u{1F341}",
1190
- mapOfJapan: "\u{1F5FE}",
1191
- maracas: "\u{1FA87}",
1192
- martialArtsUniform: "\u{1F94B}",
1193
- mate: "\u{1F9C9}",
1194
- meatOnBone: "\u{1F356}",
1195
- mechanic: "\u{1F9D1}\u200D\u{1F527}",
1196
- mechanicalArm: "\u{1F9BE}",
1197
- mechanicalLeg: "\u{1F9BF}",
1198
- medicalSymbol: "\u2695\uFE0F",
1199
- megaphone: "\u{1F4E3}",
1200
- melon: "\u{1F348}",
1201
- meltingFace: "\u{1FAE0}",
1202
- memo: "\u{1F4DD}",
1203
- mendingHeart: "\u2764\uFE0F\u200D\u{1FA79}",
1204
- menHoldingHands: "\u{1F46C}",
1205
- menorah: "\u{1F54E}",
1206
- menSRoom: "\u{1F6B9}",
1207
- menWithBunnyEars: "\u{1F46F}\u200D\u2642\uFE0F",
1208
- menWrestling: "\u{1F93C}\u200D\u2642\uFE0F",
1209
- mermaid: "\u{1F9DC}\u200D\u2640\uFE0F",
1210
- merman: "\u{1F9DC}\u200D\u2642\uFE0F",
1211
- merperson: "\u{1F9DC}",
1212
- metro: "\u{1F687}",
1213
- microbe: "\u{1F9A0}",
1214
- microphone: "\u{1F3A4}",
1215
- microscope: "\u{1F52C}",
1216
- middleFinger: "\u{1F595}",
1217
- militaryHelmet: "\u{1FA96}",
1218
- militaryMedal: "\u{1F396}\uFE0F",
1219
- milkyWay: "\u{1F30C}",
1220
- minibus: "\u{1F690}",
1221
- minus: "\u2796",
1222
- mirror: "\u{1FA9E}",
1223
- mirrorBall: "\u{1FAA9}",
1224
- moai: "\u{1F5FF}",
1225
- mobilePhone: "\u{1F4F1}",
1226
- mobilePhoneOff: "\u{1F4F4}",
1227
- mobilePhoneWithArrow: "\u{1F4F2}",
1228
- moneyBag: "\u{1F4B0}",
1229
- moneyMouthFace: "\u{1F911}",
1230
- moneyWithWings: "\u{1F4B8}",
1231
- monkey: "\u{1F412}",
1232
- monkeyFace: "\u{1F435}",
1233
- monorail: "\u{1F69D}",
1234
- moonCake: "\u{1F96E}",
1235
- moonViewingCeremony: "\u{1F391}",
1236
- moose: "\u{1FACE}",
1237
- mosque: "\u{1F54C}",
1238
- mosquito: "\u{1F99F}",
1239
- motorBoat: "\u{1F6E5}\uFE0F",
1240
- motorcycle: "\u{1F3CD}\uFE0F",
1241
- motorizedWheelchair: "\u{1F9BC}",
1242
- motorScooter: "\u{1F6F5}",
1243
- motorway: "\u{1F6E3}\uFE0F",
1244
- mountain: "\u26F0\uFE0F",
1245
- mountainCableway: "\u{1F6A0}",
1246
- mountainRailway: "\u{1F69E}",
1247
- mountFuji: "\u{1F5FB}",
1248
- mouse: "\u{1F401}",
1249
- mouseFace: "\u{1F42D}",
1250
- mouseTrap: "\u{1FAA4}",
1251
- mouth: "\u{1F444}",
1252
- movieCamera: "\u{1F3A5}",
1253
- mrsClaus: "\u{1F936}",
1254
- multiply: "\u2716\uFE0F",
1255
- mushroom: "\u{1F344}",
1256
- musicalKeyboard: "\u{1F3B9}",
1257
- musicalNote: "\u{1F3B5}",
1258
- musicalNotes: "\u{1F3B6}",
1259
- musicalScore: "\u{1F3BC}",
1260
- mutedSpeaker: "\u{1F507}",
1261
- mxClaus: "\u{1F9D1}\u200D\u{1F384}",
1262
- nailPolish: "\u{1F485}",
1263
- nameBadge: "\u{1F4DB}",
1264
- nationalPark: "\u{1F3DE}\uFE0F",
1265
- nauseatedFace: "\u{1F922}",
1266
- nazarAmulet: "\u{1F9FF}",
1267
- necktie: "\u{1F454}",
1268
- nerdFace: "\u{1F913}",
1269
- nestingDolls: "\u{1FA86}",
1270
- nestWithEggs: "\u{1FABA}",
1271
- neutralFace: "\u{1F610}",
1272
- newButton: "\u{1F195}",
1273
- newMoon: "\u{1F311}",
1274
- newMoonFace: "\u{1F31A}",
1275
- newspaper: "\u{1F4F0}",
1276
- nextTrackButton: "\u23ED\uFE0F",
1277
- ngButton: "\u{1F196}",
1278
- nightWithStars: "\u{1F303}",
1279
- nineOClock: "\u{1F558}",
1280
- nineThirty: "\u{1F564}",
1281
- ninja: "\u{1F977}",
1282
- noBicycles: "\u{1F6B3}",
1283
- noEntry: "\u26D4",
1284
- noLittering: "\u{1F6AF}",
1285
- noMobilePhones: "\u{1F4F5}",
1286
- nonPotableWater: "\u{1F6B1}",
1287
- noOneUnderEighteen: "\u{1F51E}",
1288
- noPedestrians: "\u{1F6B7}",
1289
- nose: "\u{1F443}",
1290
- noSmoking: "\u{1F6AD}",
1291
- notebook: "\u{1F4D3}",
1292
- notebookWithDecorativeCover: "\u{1F4D4}",
1293
- nutAndBolt: "\u{1F529}",
1294
- oButton: "\u{1F17E}\uFE0F",
1295
- octopus: "\u{1F419}",
1296
- oden: "\u{1F362}",
1297
- officeBuilding: "\u{1F3E2}",
1298
- officeWorker: "\u{1F9D1}\u200D\u{1F4BC}",
1299
- ogre: "\u{1F479}",
1300
- oilDrum: "\u{1F6E2}\uFE0F",
1301
- okButton: "\u{1F197}",
1302
- okHand: "\u{1F44C}",
1303
- olderPerson: "\u{1F9D3}",
1304
- oldKey: "\u{1F5DD}\uFE0F",
1305
- oldMan: "\u{1F474}",
1306
- oldWoman: "\u{1F475}",
1307
- olive: "\u{1FAD2}",
1308
- om: "\u{1F549}\uFE0F",
1309
- onArrow: "\u{1F51B}",
1310
- oncomingAutomobile: "\u{1F698}",
1311
- oncomingBus: "\u{1F68D}",
1312
- oncomingFist: "\u{1F44A}",
1313
- oncomingPoliceCar: "\u{1F694}",
1314
- oncomingTaxi: "\u{1F696}",
1315
- oneOClock: "\u{1F550}",
1316
- onePieceSwimsuit: "\u{1FA71}",
1317
- oneThirty: "\u{1F55C}",
1318
- onion: "\u{1F9C5}",
1319
- openBook: "\u{1F4D6}",
1320
- openFileFolder: "\u{1F4C2}",
1321
- openHands: "\u{1F450}",
1322
- openMailboxWithLoweredFlag: "\u{1F4ED}",
1323
- openMailboxWithRaisedFlag: "\u{1F4EC}",
1324
- ophiuchus: "\u26CE",
1325
- opticalDisk: "\u{1F4BF}",
1326
- orangeBook: "\u{1F4D9}",
1327
- orangeCircle: "\u{1F7E0}",
1328
- orangeHeart: "\u{1F9E1}",
1329
- orangeSquare: "\u{1F7E7}",
1330
- orangutan: "\u{1F9A7}",
1331
- orca: "\u{1FACD}",
1332
- orthodoxCross: "\u2626\uFE0F",
1333
- otter: "\u{1F9A6}",
1334
- outboxTray: "\u{1F4E4}",
1335
- owl: "\u{1F989}",
1336
- ox: "\u{1F402}",
1337
- oyster: "\u{1F9AA}",
1338
- package_: "\u{1F4E6}",
1339
- pageFacingUp: "\u{1F4C4}",
1340
- pager: "\u{1F4DF}",
1341
- pageWithCurl: "\u{1F4C3}",
1342
- paintbrush: "\u{1F58C}\uFE0F",
1343
- palmDownHand: "\u{1FAF3}",
1344
- palmsUpTogether: "\u{1F932}",
1345
- palmTree: "\u{1F334}",
1346
- palmUpHand: "\u{1FAF4}",
1347
- pancakes: "\u{1F95E}",
1348
- panda: "\u{1F43C}",
1349
- paperclip: "\u{1F4CE}",
1350
- parachute: "\u{1FA82}",
1351
- parrot: "\u{1F99C}",
1352
- partAlternationMark: "\u303D\uFE0F",
1353
- partyingFace: "\u{1F973}",
1354
- partyPopper: "\u{1F389}",
1355
- passengerShip: "\u{1F6F3}\uFE0F",
1356
- passportControl: "\u{1F6C2}",
1357
- pauseButton: "\u23F8\uFE0F",
1358
- pawPrints: "\u{1F43E}",
1359
- pButton: "\u{1F17F}\uFE0F",
1360
- peaceSymbol: "\u262E\uFE0F",
1361
- peach: "\u{1F351}",
1362
- peacock: "\u{1F99A}",
1363
- peanuts: "\u{1F95C}",
1364
- peaPod: "\u{1FADB}",
1365
- pear: "\u{1F350}",
1366
- pen: "\u{1F58A}\uFE0F",
1367
- pencil: "\u270F\uFE0F",
1368
- penguin: "\u{1F427}",
1369
- pensiveFace: "\u{1F614}",
1370
- peopleHoldingHands: "\u{1F9D1}\u200D\u{1F91D}\u200D\u{1F9D1}",
1371
- peopleHugging: "\u{1FAC2}",
1372
- peopleWithBunnyEars: "\u{1F46F}",
1373
- peopleWrestling: "\u{1F93C}",
1374
- performingArts: "\u{1F3AD}",
1375
- perseveringFace: "\u{1F623}",
1376
- person: "\u{1F9D1}",
1377
- personBald: "\u{1F9D1}\u200D\u{1F9B2}",
1378
- personBeard: "\u{1F9D4}",
1379
- personBiking: "\u{1F6B4}",
1380
- personBlondHair: "\u{1F471}",
1381
- personBouncingBall: "\u26F9\uFE0F",
1382
- personBowing: "\u{1F647}",
1383
- personCartwheeling: "\u{1F938}",
1384
- personClimbing: "\u{1F9D7}",
1385
- personCurlyHair: "\u{1F9D1}\u200D\u{1F9B1}",
1386
- personFacepalming: "\u{1F926}",
1387
- personFeedingBaby: "\u{1F9D1}\u200D\u{1F37C}",
1388
- personFencing: "\u{1F93A}",
1389
- personFrowning: "\u{1F64D}",
1390
- personGesturingNo: "\u{1F645}",
1391
- personGesturingOk: "\u{1F646}",
1392
- personGettingHaircut: "\u{1F487}",
1393
- personGettingMassage: "\u{1F486}",
1394
- personGolfing: "\u{1F3CC}\uFE0F",
1395
- personInBed: "\u{1F6CC}",
1396
- personInLotusPosition: "\u{1F9D8}",
1397
- personInManualWheelchair: "\u{1F9D1}\u200D\u{1F9BD}",
1398
- personInManualWheelchairFacingRight: "\u{1F9D1}\u200D\u{1F9BD}\u200D\u27A1\uFE0F",
1399
- personInMotorizedWheelchair: "\u{1F9D1}\u200D\u{1F9BC}",
1400
- personInMotorizedWheelchairFacingRight: "\u{1F9D1}\u200D\u{1F9BC}\u200D\u27A1\uFE0F",
1401
- personInSteamyRoom: "\u{1F9D6}",
1402
- personInSuitLevitating: "\u{1F574}\uFE0F",
1403
- personInTuxedo: "\u{1F935}",
1404
- personJuggling: "\u{1F939}",
1405
- personKneeling: "\u{1F9CE}",
1406
- personKneelingFacingRight: "\u{1F9CE}\u200D\u27A1\uFE0F",
1407
- personLiftingWeights: "\u{1F3CB}\uFE0F",
1408
- personMountainBiking: "\u{1F6B5}",
1409
- personPlayingHandball: "\u{1F93E}",
1410
- personPlayingWaterPolo: "\u{1F93D}",
1411
- personPouting: "\u{1F64E}",
1412
- personRaisingHand: "\u{1F64B}",
1413
- personRedHair: "\u{1F9D1}\u200D\u{1F9B0}",
1414
- personRowingBoat: "\u{1F6A3}",
1415
- personRunning: "\u{1F3C3}",
1416
- personRunningFacingRight: "\u{1F3C3}\u200D\u27A1\uFE0F",
1417
- personShrugging: "\u{1F937}",
1418
- personStanding: "\u{1F9CD}",
1419
- personSurfing: "\u{1F3C4}",
1420
- personSwimming: "\u{1F3CA}",
1421
- personTakingBath: "\u{1F6C0}",
1422
- personTippingHand: "\u{1F481}",
1423
- personWalking: "\u{1F6B6}",
1424
- personWalkingFacingRight: "\u{1F6B6}\u200D\u27A1\uFE0F",
1425
- personWearingTurban: "\u{1F473}",
1426
- personWhiteHair: "\u{1F9D1}\u200D\u{1F9B3}",
1427
- personWithCrown: "\u{1FAC5}",
1428
- personWithSkullcap: "\u{1F472}",
1429
- personWithVeil: "\u{1F470}",
1430
- personWithWhiteCane: "\u{1F9D1}\u200D\u{1F9AF}",
1431
- personWithWhiteCaneFacingRight: "\u{1F9D1}\u200D\u{1F9AF}\u200D\u27A1\uFE0F",
1432
- petriDish: "\u{1F9EB}",
1433
- phoenix: "\u{1F426}\u200D\u{1F525}",
1434
- pick: "\u26CF\uFE0F",
1435
- pickupTruck: "\u{1F6FB}",
1436
- pie: "\u{1F967}",
1437
- pig: "\u{1F416}",
1438
- pigFace: "\u{1F437}",
1439
- pigNose: "\u{1F43D}",
1440
- pileOfPoo: "\u{1F4A9}",
1441
- pill: "\u{1F48A}",
1442
- pilot: "\u{1F9D1}\u200D\u2708\uFE0F",
1443
- pinata: "\u{1FA85}",
1444
- pinchedFingers: "\u{1F90C}",
1445
- pinchingHand: "\u{1F90F}",
1446
- pineapple: "\u{1F34D}",
1447
- pineDecoration: "\u{1F38D}",
1448
- pingPong: "\u{1F3D3}",
1449
- pinkHeart: "\u{1FA77}",
1450
- pirateFlag: "\u{1F3F4}\u200D\u2620\uFE0F",
1451
- pisces: "\u2653",
1452
- pizza: "\u{1F355}",
1453
- placard: "\u{1FAA7}",
1454
- placeOfWorship: "\u{1F6D0}",
1455
- playButton: "\u25B6\uFE0F",
1456
- playgroundSlide: "\u{1F6DD}",
1457
- playOrPauseButton: "\u23EF\uFE0F",
1458
- pleadingFace: "\u{1F97A}",
1459
- plunger: "\u{1FAA0}",
1460
- plus: "\u2795",
1461
- polarBear: "\u{1F43B}\u200D\u2744\uFE0F",
1462
- policeCar: "\u{1F693}",
1463
- policeCarLight: "\u{1F6A8}",
1464
- policeOfficer: "\u{1F46E}",
1465
- poodle: "\u{1F429}",
1466
- pool8Ball: "\u{1F3B1}",
1467
- popcorn: "\u{1F37F}",
1468
- postalHorn: "\u{1F4EF}",
1469
- postbox: "\u{1F4EE}",
1470
- postOffice: "\u{1F3E4}",
1471
- potableWater: "\u{1F6B0}",
1472
- potato: "\u{1F954}",
1473
- potOfFood: "\u{1F372}",
1474
- pottedPlant: "\u{1FAB4}",
1475
- poultryLeg: "\u{1F357}",
1476
- poundBanknote: "\u{1F4B7}",
1477
- pouringLiquid: "\u{1FAD7}",
1478
- poutingCat: "\u{1F63E}",
1479
- prayerBeads: "\u{1F4FF}",
1480
- pregnantMan: "\u{1FAC3}",
1481
- pregnantPerson: "\u{1FAC4}",
1482
- pregnantWoman: "\u{1F930}",
1483
- pretzel: "\u{1F968}",
1484
- prince: "\u{1F934}",
1485
- princess: "\u{1F478}",
1486
- printer: "\u{1F5A8}\uFE0F",
1487
- prohibited: "\u{1F6AB}",
1488
- purpleCircle: "\u{1F7E3}",
1489
- purpleHeart: "\u{1F49C}",
1490
- purpleSquare: "\u{1F7EA}",
1491
- purse: "\u{1F45B}",
1492
- pushpin: "\u{1F4CC}",
1493
- puzzlePiece: "\u{1F9E9}",
1494
- rabbit: "\u{1F407}",
1495
- rabbitFace: "\u{1F430}",
1496
- raccoon: "\u{1F99D}",
1497
- racingCar: "\u{1F3CE}\uFE0F",
1498
- radio: "\u{1F4FB}",
1499
- radioactive: "\u2622\uFE0F",
1500
- radioButton: "\u{1F518}",
1501
- railwayCar: "\u{1F683}",
1502
- railwayTrack: "\u{1F6E4}\uFE0F",
1503
- rainbow: "\u{1F308}",
1504
- rainbowFlag: "\u{1F3F3}\uFE0F\u200D\u{1F308}",
1505
- raisedBackOfHand: "\u{1F91A}",
1506
- raisedFist: "\u270A",
1507
- raisedHand: "\u270B",
1508
- raisingHands: "\u{1F64C}",
1509
- ram: "\u{1F40F}",
1510
- rat: "\u{1F400}",
1511
- razor: "\u{1FA92}",
1512
- receipt: "\u{1F9FE}",
1513
- recordButton: "\u23FA\uFE0F",
1514
- recyclingSymbol: "\u267B\uFE0F",
1515
- redApple: "\u{1F34E}",
1516
- redCircle: "\u{1F534}",
1517
- redEnvelope: "\u{1F9E7}",
1518
- redExclamationMark: "\u2757",
1519
- redHeart: "\u2764\uFE0F",
1520
- redPaperLantern: "\u{1F3EE}",
1521
- redQuestionMark: "\u2753",
1522
- redSquare: "\u{1F7E5}",
1523
- redTrianglePointedDown: "\u{1F53B}",
1524
- redTrianglePointedUp: "\u{1F53A}",
1525
- registered: "\xAE\uFE0F",
1526
- relievedFace: "\u{1F60C}",
1527
- reminderRibbon: "\u{1F397}\uFE0F",
1528
- repeatButton: "\u{1F501}",
1529
- repeatSingleButton: "\u{1F502}",
1530
- rescueWorkerSHelmet: "\u26D1\uFE0F",
1531
- restroom: "\u{1F6BB}",
1532
- reverseButton: "\u25C0\uFE0F",
1533
- revolvingHearts: "\u{1F49E}",
1534
- rhinoceros: "\u{1F98F}",
1535
- ribbon: "\u{1F380}",
1536
- riceBall: "\u{1F359}",
1537
- riceCracker: "\u{1F358}",
1538
- rightAngerBubble: "\u{1F5EF}\uFE0F",
1539
- rightArrow: "\u27A1\uFE0F",
1540
- rightArrowCurvingDown: "\u2935\uFE0F",
1541
- rightArrowCurvingLeft: "\u21A9\uFE0F",
1542
- rightArrowCurvingUp: "\u2934\uFE0F",
1543
- rightFacingFist: "\u{1F91C}",
1544
- rightwardsHand: "\u{1FAF1}",
1545
- rightwardsPushingHand: "\u{1FAF8}",
1546
- ring: "\u{1F48D}",
1547
- ringBuoy: "\u{1F6DF}",
1548
- ringedPlanet: "\u{1FA90}",
1549
- roastedSweetPotato: "\u{1F360}",
1550
- robot: "\u{1F916}",
1551
- rock: "\u{1FAA8}",
1552
- rocket: "\u{1F680}",
1553
- rolledUpNewspaper: "\u{1F5DE}\uFE0F",
1554
- rollerCoaster: "\u{1F3A2}",
1555
- rollerSkate: "\u{1F6FC}",
1556
- rollingOnTheFloorLaughing: "\u{1F923}",
1557
- rollOfPaper: "\u{1F9FB}",
1558
- rooster: "\u{1F413}",
1559
- rootVegetable: "\u{1FADC}",
1560
- rose: "\u{1F339}",
1561
- rosette: "\u{1F3F5}\uFE0F",
1562
- roundPushpin: "\u{1F4CD}",
1563
- rugbyFootball: "\u{1F3C9}",
1564
- runningShirt: "\u{1F3BD}",
1565
- runningShoe: "\u{1F45F}",
1566
- sadButRelievedFace: "\u{1F625}",
1567
- safetyPin: "\u{1F9F7}",
1568
- safetyVest: "\u{1F9BA}",
1569
- sagittarius: "\u2650",
1570
- sailboat: "\u26F5",
1571
- sake: "\u{1F376}",
1572
- salt: "\u{1F9C2}",
1573
- salutingFace: "\u{1FAE1}",
1574
- sandwich: "\u{1F96A}",
1575
- santaClaus: "\u{1F385}",
1576
- sari: "\u{1F97B}",
1577
- satellite: "\u{1F6F0}\uFE0F",
1578
- satelliteAntenna: "\u{1F4E1}",
1579
- sauropod: "\u{1F995}",
1580
- saxophone: "\u{1F3B7}",
1581
- scarf: "\u{1F9E3}",
1582
- school: "\u{1F3EB}",
1583
- scientist: "\u{1F9D1}\u200D\u{1F52C}",
1584
- scissors: "\u2702\uFE0F",
1585
- scorpio: "\u264F",
1586
- scorpion: "\u{1F982}",
1587
- screwdriver: "\u{1FA9B}",
1588
- scroll: "\u{1F4DC}",
1589
- seal: "\u{1F9AD}",
1590
- seat: "\u{1F4BA}",
1591
- seedling: "\u{1F331}",
1592
- seeNoEvilMonkey: "\u{1F648}",
1593
- selfie: "\u{1F933}",
1594
- serviceDog: "\u{1F415}\u200D\u{1F9BA}",
1595
- sevenOClock: "\u{1F556}",
1596
- sevenThirty: "\u{1F562}",
1597
- sewingNeedle: "\u{1FAA1}",
1598
- shakingFace: "\u{1FAE8}",
1599
- shallowPanOfFood: "\u{1F958}",
1600
- shamrock: "\u2618\uFE0F",
1601
- shark: "\u{1F988}",
1602
- shavedIce: "\u{1F367}",
1603
- sheafOfRice: "\u{1F33E}",
1604
- shield: "\u{1F6E1}\uFE0F",
1605
- shintoShrine: "\u26E9\uFE0F",
1606
- ship: "\u{1F6A2}",
1607
- shootingStar: "\u{1F320}",
1608
- shoppingBags: "\u{1F6CD}\uFE0F",
1609
- shoppingCart: "\u{1F6D2}",
1610
- shortcake: "\u{1F370}",
1611
- shorts: "\u{1FA73}",
1612
- shovel: "\u{1FA8F}",
1613
- shower: "\u{1F6BF}",
1614
- shrimp: "\u{1F990}",
1615
- shuffleTracksButton: "\u{1F500}",
1616
- shushingFace: "\u{1F92B}",
1617
- signOfTheHorns: "\u{1F918}",
1618
- singer: "\u{1F9D1}\u200D\u{1F3A4}",
1619
- sixOClock: "\u{1F555}",
1620
- sixThirty: "\u{1F561}",
1621
- skateboard: "\u{1F6F9}",
1622
- skier: "\u26F7\uFE0F",
1623
- skis: "\u{1F3BF}",
1624
- skull: "\u{1F480}",
1625
- skullAndCrossbones: "\u2620\uFE0F",
1626
- skunk: "\u{1F9A8}",
1627
- sled: "\u{1F6F7}",
1628
- sleepingFace: "\u{1F634}",
1629
- sleepyFace: "\u{1F62A}",
1630
- slightlyFrowningFace: "\u{1F641}",
1631
- slightlySmilingFace: "\u{1F642}",
1632
- sloth: "\u{1F9A5}",
1633
- slotMachine: "\u{1F3B0}",
1634
- smallAirplane: "\u{1F6E9}\uFE0F",
1635
- smallBlueDiamond: "\u{1F539}",
1636
- smallOrangeDiamond: "\u{1F538}",
1637
- smilingCatWithHeartEyes: "\u{1F63B}",
1638
- smilingFace: "\u263A\uFE0F",
1639
- smilingFaceWithHalo: "\u{1F607}",
1640
- smilingFaceWithHeartEyes: "\u{1F60D}",
1641
- smilingFaceWithHearts: "\u{1F970}",
1642
- smilingFaceWithHorns: "\u{1F608}",
1643
- smilingFaceWithOpenHands: "\u{1F917}",
1644
- smilingFaceWithSmilingEyes: "\u{1F60A}",
1645
- smilingFaceWithSunglasses: "\u{1F60E}",
1646
- smilingFaceWithTear: "\u{1F972}",
1647
- smirkingFace: "\u{1F60F}",
1648
- snail: "\u{1F40C}",
1649
- snake: "\u{1F40D}",
1650
- sneezingFace: "\u{1F927}",
1651
- snowboarder: "\u{1F3C2}",
1652
- snowCappedMountain: "\u{1F3D4}\uFE0F",
1653
- snowflake: "\u2744\uFE0F",
1654
- snowman: "\u2603\uFE0F",
1655
- snowmanWithoutSnow: "\u26C4",
1656
- soap: "\u{1F9FC}",
1657
- soccerBall: "\u26BD",
1658
- socks: "\u{1F9E6}",
1659
- softball: "\u{1F94E}",
1660
- softIceCream: "\u{1F366}",
1661
- soonArrow: "\u{1F51C}",
1662
- sosButton: "\u{1F198}",
1663
- spadeSuit: "\u2660\uFE0F",
1664
- spaghetti: "\u{1F35D}",
1665
- sparkle: "\u2747\uFE0F",
1666
- sparkler: "\u{1F387}",
1667
- sparkles: "\u2728",
1668
- sparklingHeart: "\u{1F496}",
1669
- speakerHighVolume: "\u{1F50A}",
1670
- speakerLowVolume: "\u{1F508}",
1671
- speakerMediumVolume: "\u{1F509}",
1672
- speakingHead: "\u{1F5E3}\uFE0F",
1673
- speakNoEvilMonkey: "\u{1F64A}",
1674
- speechBalloon: "\u{1F4AC}",
1675
- speedboat: "\u{1F6A4}",
1676
- spider: "\u{1F577}\uFE0F",
1677
- spiderWeb: "\u{1F578}\uFE0F",
1678
- spiralCalendar: "\u{1F5D3}\uFE0F",
1679
- spiralNotepad: "\u{1F5D2}\uFE0F",
1680
- spiralShell: "\u{1F41A}",
1681
- splatter: "\u{1FADF}",
1682
- sponge: "\u{1F9FD}",
1683
- spoon: "\u{1F944}",
1684
- sportsMedal: "\u{1F3C5}",
1685
- sportUtilityVehicle: "\u{1F699}",
1686
- spoutingWhale: "\u{1F433}",
1687
- squid: "\u{1F991}",
1688
- squintingFaceWithTongue: "\u{1F61D}",
1689
- stadium: "\u{1F3DF}\uFE0F",
1690
- star: "\u2B50",
1691
- starAndCrescent: "\u262A\uFE0F",
1692
- starOfDavid: "\u2721\uFE0F",
1693
- starStruck: "\u{1F929}",
1694
- station: "\u{1F689}",
1695
- statueOfLiberty: "\u{1F5FD}",
1696
- steamingBowl: "\u{1F35C}",
1697
- stethoscope: "\u{1FA7A}",
1698
- stopButton: "\u23F9\uFE0F",
1699
- stopSign: "\u{1F6D1}",
1700
- stopwatch: "\u23F1\uFE0F",
1701
- straightRuler: "\u{1F4CF}",
1702
- strawberry: "\u{1F353}",
1703
- student: "\u{1F9D1}\u200D\u{1F393}",
1704
- studioMicrophone: "\u{1F399}\uFE0F",
1705
- stuffedFlatbread: "\u{1F959}",
1706
- sun: "\u2600\uFE0F",
1707
- sunBehindCloud: "\u26C5",
1708
- sunBehindLargeCloud: "\u{1F325}\uFE0F",
1709
- sunBehindRainCloud: "\u{1F326}\uFE0F",
1710
- sunBehindSmallCloud: "\u{1F324}\uFE0F",
1711
- sunflower: "\u{1F33B}",
1712
- sunglasses: "\u{1F576}\uFE0F",
1713
- sunrise: "\u{1F305}",
1714
- sunriseOverMountains: "\u{1F304}",
1715
- sunset: "\u{1F307}",
1716
- sunWithFace: "\u{1F31E}",
1717
- superhero: "\u{1F9B8}",
1718
- supervillain: "\u{1F9B9}",
1719
- sushi: "\u{1F363}",
1720
- suspensionRailway: "\u{1F69F}",
1721
- swan: "\u{1F9A2}",
1722
- sweatDroplets: "\u{1F4A6}",
1723
- synagogue: "\u{1F54D}",
1724
- syringe: "\u{1F489}",
1725
- taco: "\u{1F32E}",
1726
- takeoutBox: "\u{1F961}",
1727
- tamale: "\u{1FAD4}",
1728
- tanabataTree: "\u{1F38B}",
1729
- tangerine: "\u{1F34A}",
1730
- taurus: "\u2649",
1731
- taxi: "\u{1F695}",
1732
- teacher: "\u{1F9D1}\u200D\u{1F3EB}",
1733
- teacupWithoutHandle: "\u{1F375}",
1734
- teapot: "\u{1FAD6}",
1735
- tearOffCalendar: "\u{1F4C6}",
1736
- technologist: "\u{1F9D1}\u200D\u{1F4BB}",
1737
- teddyBear: "\u{1F9F8}",
1738
- telephone: "\u260E\uFE0F",
1739
- telephoneReceiver: "\u{1F4DE}",
1740
- telescope: "\u{1F52D}",
1741
- television: "\u{1F4FA}",
1742
- tennis: "\u{1F3BE}",
1743
- tenOClock: "\u{1F559}",
1744
- tent: "\u26FA",
1745
- tenThirty: "\u{1F565}",
1746
- testTube: "\u{1F9EA}",
1747
- thermometer: "\u{1F321}\uFE0F",
1748
- thinkingFace: "\u{1F914}",
1749
- thongSandal: "\u{1FA74}",
1750
- thoughtBalloon: "\u{1F4AD}",
1751
- thread: "\u{1F9F5}",
1752
- threeOClock: "\u{1F552}",
1753
- threeThirty: "\u{1F55E}",
1754
- thumbsDown: "\u{1F44E}",
1755
- thumbsUp: "\u{1F44D}",
1756
- ticket: "\u{1F3AB}",
1757
- tiger: "\u{1F405}",
1758
- tigerFace: "\u{1F42F}",
1759
- timerClock: "\u23F2\uFE0F",
1760
- tiredFace: "\u{1F62B}",
1761
- toilet: "\u{1F6BD}",
1762
- tokyoTower: "\u{1F5FC}",
1763
- tomato: "\u{1F345}",
1764
- tongue: "\u{1F445}",
1765
- toolbox: "\u{1F9F0}",
1766
- tooth: "\u{1F9B7}",
1767
- toothbrush: "\u{1FAA5}",
1768
- topArrow: "\u{1F51D}",
1769
- topHat: "\u{1F3A9}",
1770
- tornado: "\u{1F32A}\uFE0F",
1771
- trackball: "\u{1F5B2}\uFE0F",
1772
- tractor: "\u{1F69C}",
1773
- tradeMark: "\u2122\uFE0F",
1774
- train: "\u{1F686}",
1775
- tram: "\u{1F68A}",
1776
- tramCar: "\u{1F68B}",
1777
- transgenderFlag: "\u{1F3F3}\uFE0F\u200D\u26A7\uFE0F",
1778
- transgenderSymbol: "\u26A7\uFE0F",
1779
- treasureChest: "\u{1FA8E}",
1780
- tRex: "\u{1F996}",
1781
- triangularFlag: "\u{1F6A9}",
1782
- triangularRuler: "\u{1F4D0}",
1783
- tridentEmblem: "\u{1F531}",
1784
- troll: "\u{1F9CC}",
1785
- trolleybus: "\u{1F68E}",
1786
- trombone: "\u{1FA8A}",
1787
- trophy: "\u{1F3C6}",
1788
- tropicalDrink: "\u{1F379}",
1789
- tropicalFish: "\u{1F420}",
1790
- trumpet: "\u{1F3BA}",
1791
- tShirt: "\u{1F455}",
1792
- tulip: "\u{1F337}",
1793
- tumblerGlass: "\u{1F943}",
1794
- turkey: "\u{1F983}",
1795
- turtle: "\u{1F422}",
1796
- twelveOClock: "\u{1F55B}",
1797
- twelveThirty: "\u{1F567}",
1798
- twoHearts: "\u{1F495}",
1799
- twoHumpCamel: "\u{1F42B}",
1800
- twoOClock: "\u{1F551}",
1801
- twoThirty: "\u{1F55D}",
1802
- umbrella: "\u2602\uFE0F",
1803
- umbrellaOnGround: "\u26F1\uFE0F",
1804
- umbrellaWithRainDrops: "\u2614",
1805
- unamusedFace: "\u{1F612}",
1806
- unicorn: "\u{1F984}",
1807
- unlocked: "\u{1F513}",
1808
- upArrow: "\u2B06\uFE0F",
1809
- upButton: "\u{1F199}",
1810
- upDownArrow: "\u2195\uFE0F",
1811
- upLeftArrow: "\u2196\uFE0F",
1812
- upRightArrow: "\u2197\uFE0F",
1813
- upsideDownFace: "\u{1F643}",
1814
- upwardsButton: "\u{1F53C}",
1815
- vampire: "\u{1F9DB}",
1816
- verticalTrafficLight: "\u{1F6A6}",
1817
- vibrationMode: "\u{1F4F3}",
1818
- victoryHand: "\u270C\uFE0F",
1819
- videoCamera: "\u{1F4F9}",
1820
- videocassette: "\u{1F4FC}",
1821
- videoGame: "\u{1F3AE}",
1822
- violin: "\u{1F3BB}",
1823
- virgo: "\u264D",
1824
- volcano: "\u{1F30B}",
1825
- volleyball: "\u{1F3D0}",
1826
- vsButton: "\u{1F19A}",
1827
- vulcanSalute: "\u{1F596}",
1828
- waffle: "\u{1F9C7}",
1829
- waningCrescentMoon: "\u{1F318}",
1830
- waningGibbousMoon: "\u{1F316}",
1831
- warning: "\u26A0\uFE0F",
1832
- wastebasket: "\u{1F5D1}\uFE0F",
1833
- watch: "\u231A",
1834
- waterBuffalo: "\u{1F403}",
1835
- waterCloset: "\u{1F6BE}",
1836
- watermelon: "\u{1F349}",
1837
- waterPistol: "\u{1F52B}",
1838
- waterWave: "\u{1F30A}",
1839
- wavingHand: "\u{1F44B}",
1840
- wavyDash: "\u3030\uFE0F",
1841
- waxingCrescentMoon: "\u{1F312}",
1842
- waxingGibbousMoon: "\u{1F314}",
1843
- wearyCat: "\u{1F640}",
1844
- wearyFace: "\u{1F629}",
1845
- wedding: "\u{1F492}",
1846
- whale: "\u{1F40B}",
1847
- wheel: "\u{1F6DE}",
1848
- wheelchairSymbol: "\u267F",
1849
- wheelOfDharma: "\u2638\uFE0F",
1850
- whiteCane: "\u{1F9AF}",
1851
- whiteCircle: "\u26AA",
1852
- whiteExclamationMark: "\u2755",
1853
- whiteFlag: "\u{1F3F3}\uFE0F",
1854
- whiteFlower: "\u{1F4AE}",
1855
- whiteHeart: "\u{1F90D}",
1856
- whiteLargeSquare: "\u2B1C",
1857
- whiteMediumSmallSquare: "\u25FD",
1858
- whiteMediumSquare: "\u25FB\uFE0F",
1859
- whiteQuestionMark: "\u2754",
1860
- whiteSmallSquare: "\u25AB\uFE0F",
1861
- whiteSquareButton: "\u{1F533}",
1862
- wiltedFlower: "\u{1F940}",
1863
- windChime: "\u{1F390}",
1864
- windFace: "\u{1F32C}\uFE0F",
1865
- window: "\u{1FA9F}",
1866
- wineGlass: "\u{1F377}",
1867
- wing: "\u{1FABD}",
1868
- winkingFace: "\u{1F609}",
1869
- winkingFaceWithTongue: "\u{1F61C}",
1870
- wireless: "\u{1F6DC}",
1871
- wolf: "\u{1F43A}",
1872
- woman: "\u{1F469}",
1873
- womanAndManHoldingHands: "\u{1F46B}",
1874
- womanArtist: "\u{1F469}\u200D\u{1F3A8}",
1875
- womanAstronaut: "\u{1F469}\u200D\u{1F680}",
1876
- womanBald: "\u{1F469}\u200D\u{1F9B2}",
1877
- womanBeard: "\u{1F9D4}\u200D\u2640\uFE0F",
1878
- womanBiking: "\u{1F6B4}\u200D\u2640\uFE0F",
1879
- womanBlondHair: "\u{1F471}\u200D\u2640\uFE0F",
1880
- womanBouncingBall: "\u26F9\uFE0F\u200D\u2640\uFE0F",
1881
- womanBowing: "\u{1F647}\u200D\u2640\uFE0F",
1882
- womanCartwheeling: "\u{1F938}\u200D\u2640\uFE0F",
1883
- womanClimbing: "\u{1F9D7}\u200D\u2640\uFE0F",
1884
- womanConstructionWorker: "\u{1F477}\u200D\u2640\uFE0F",
1885
- womanCook: "\u{1F469}\u200D\u{1F373}",
1886
- womanCurlyHair: "\u{1F469}\u200D\u{1F9B1}",
1887
- womanDancing: "\u{1F483}",
1888
- womanDetective: "\u{1F575}\uFE0F\u200D\u2640\uFE0F",
1889
- womanElf: "\u{1F9DD}\u200D\u2640\uFE0F",
1890
- womanFacepalming: "\u{1F926}\u200D\u2640\uFE0F",
1891
- womanFactoryWorker: "\u{1F469}\u200D\u{1F3ED}",
1892
- womanFairy: "\u{1F9DA}\u200D\u2640\uFE0F",
1893
- womanFarmer: "\u{1F469}\u200D\u{1F33E}",
1894
- womanFeedingBaby: "\u{1F469}\u200D\u{1F37C}",
1895
- womanFirefighter: "\u{1F469}\u200D\u{1F692}",
1896
- womanFrowning: "\u{1F64D}\u200D\u2640\uFE0F",
1897
- womanGenie: "\u{1F9DE}\u200D\u2640\uFE0F",
1898
- womanGesturingNo: "\u{1F645}\u200D\u2640\uFE0F",
1899
- womanGesturingOk: "\u{1F646}\u200D\u2640\uFE0F",
1900
- womanGettingHaircut: "\u{1F487}\u200D\u2640\uFE0F",
1901
- womanGettingMassage: "\u{1F486}\u200D\u2640\uFE0F",
1902
- womanGolfing: "\u{1F3CC}\uFE0F\u200D\u2640\uFE0F",
1903
- womanGuard: "\u{1F482}\u200D\u2640\uFE0F",
1904
- womanHealthWorker: "\u{1F469}\u200D\u2695\uFE0F",
1905
- womanInLotusPosition: "\u{1F9D8}\u200D\u2640\uFE0F",
1906
- womanInManualWheelchair: "\u{1F469}\u200D\u{1F9BD}",
1907
- womanInManualWheelchairFacingRight: "\u{1F469}\u200D\u{1F9BD}\u200D\u27A1\uFE0F",
1908
- womanInMotorizedWheelchair: "\u{1F469}\u200D\u{1F9BC}",
1909
- womanInMotorizedWheelchairFacingRight: "\u{1F469}\u200D\u{1F9BC}\u200D\u27A1\uFE0F",
1910
- womanInSteamyRoom: "\u{1F9D6}\u200D\u2640\uFE0F",
1911
- womanInTuxedo: "\u{1F935}\u200D\u2640\uFE0F",
1912
- womanJudge: "\u{1F469}\u200D\u2696\uFE0F",
1913
- womanJuggling: "\u{1F939}\u200D\u2640\uFE0F",
1914
- womanKneeling: "\u{1F9CE}\u200D\u2640\uFE0F",
1915
- womanKneelingFacingRight: "\u{1F9CE}\u200D\u2640\uFE0F\u200D\u27A1\uFE0F",
1916
- womanLiftingWeights: "\u{1F3CB}\uFE0F\u200D\u2640\uFE0F",
1917
- womanMage: "\u{1F9D9}\u200D\u2640\uFE0F",
1918
- womanMechanic: "\u{1F469}\u200D\u{1F527}",
1919
- womanMountainBiking: "\u{1F6B5}\u200D\u2640\uFE0F",
1920
- womanOfficeWorker: "\u{1F469}\u200D\u{1F4BC}",
1921
- womanPilot: "\u{1F469}\u200D\u2708\uFE0F",
1922
- womanPlayingHandball: "\u{1F93E}\u200D\u2640\uFE0F",
1923
- womanPlayingWaterPolo: "\u{1F93D}\u200D\u2640\uFE0F",
1924
- womanPoliceOfficer: "\u{1F46E}\u200D\u2640\uFE0F",
1925
- womanPouting: "\u{1F64E}\u200D\u2640\uFE0F",
1926
- womanRaisingHand: "\u{1F64B}\u200D\u2640\uFE0F",
1927
- womanRedHair: "\u{1F469}\u200D\u{1F9B0}",
1928
- womanRowingBoat: "\u{1F6A3}\u200D\u2640\uFE0F",
1929
- womanRunning: "\u{1F3C3}\u200D\u2640\uFE0F",
1930
- womanRunningFacingRight: "\u{1F3C3}\u200D\u2640\uFE0F\u200D\u27A1\uFE0F",
1931
- womanSBoot: "\u{1F462}",
1932
- womanScientist: "\u{1F469}\u200D\u{1F52C}",
1933
- womanSClothes: "\u{1F45A}",
1934
- womanSHat: "\u{1F452}",
1935
- womanShrugging: "\u{1F937}\u200D\u2640\uFE0F",
1936
- womanSinger: "\u{1F469}\u200D\u{1F3A4}",
1937
- womanSSandal: "\u{1F461}",
1938
- womanStanding: "\u{1F9CD}\u200D\u2640\uFE0F",
1939
- womanStudent: "\u{1F469}\u200D\u{1F393}",
1940
- womanSuperhero: "\u{1F9B8}\u200D\u2640\uFE0F",
1941
- womanSupervillain: "\u{1F9B9}\u200D\u2640\uFE0F",
1942
- womanSurfing: "\u{1F3C4}\u200D\u2640\uFE0F",
1943
- womanSwimming: "\u{1F3CA}\u200D\u2640\uFE0F",
1944
- womanTeacher: "\u{1F469}\u200D\u{1F3EB}",
1945
- womanTechnologist: "\u{1F469}\u200D\u{1F4BB}",
1946
- womanTippingHand: "\u{1F481}\u200D\u2640\uFE0F",
1947
- womanVampire: "\u{1F9DB}\u200D\u2640\uFE0F",
1948
- womanWalking: "\u{1F6B6}\u200D\u2640\uFE0F",
1949
- womanWalkingFacingRight: "\u{1F6B6}\u200D\u2640\uFE0F\u200D\u27A1\uFE0F",
1950
- womanWearingTurban: "\u{1F473}\u200D\u2640\uFE0F",
1951
- womanWhiteHair: "\u{1F469}\u200D\u{1F9B3}",
1952
- womanWithHeadscarf: "\u{1F9D5}",
1953
- womanWithVeil: "\u{1F470}\u200D\u2640\uFE0F",
1954
- womanWithWhiteCane: "\u{1F469}\u200D\u{1F9AF}",
1955
- womanWithWhiteCaneFacingRight: "\u{1F469}\u200D\u{1F9AF}\u200D\u27A1\uFE0F",
1956
- womanZombie: "\u{1F9DF}\u200D\u2640\uFE0F",
1957
- womenHoldingHands: "\u{1F46D}",
1958
- womenSRoom: "\u{1F6BA}",
1959
- womenWithBunnyEars: "\u{1F46F}\u200D\u2640\uFE0F",
1960
- womenWrestling: "\u{1F93C}\u200D\u2640\uFE0F",
1961
- wood: "\u{1FAB5}",
1962
- woozyFace: "\u{1F974}",
1963
- worldMap: "\u{1F5FA}\uFE0F",
1964
- worm: "\u{1FAB1}",
1965
- worriedFace: "\u{1F61F}",
1966
- wrappedGift: "\u{1F381}",
1967
- wrench: "\u{1F527}",
1968
- writingHand: "\u270D\uFE0F",
1969
- xRay: "\u{1FA7B}",
1970
- yarn: "\u{1F9F6}",
1971
- yawningFace: "\u{1F971}",
1972
- yellowCircle: "\u{1F7E1}",
1973
- yellowHeart: "\u{1F49B}",
1974
- yellowSquare: "\u{1F7E8}",
1975
- yenBanknote: "\u{1F4B4}",
1976
- yinYang: "\u262F\uFE0F",
1977
- yoYo: "\u{1FA80}",
1978
- zanyFace: "\u{1F92A}",
1979
- zebra: "\u{1F993}",
1980
- zipperMouthFace: "\u{1F910}",
1981
- zombie: "\u{1F9DF}",
1982
- zzz: "\u{1F4A4}"
1983
- };
1984
-
1985
- // src/emoji/index.ts
1986
- var aliases = {
1987
- love: GeneratedEmoji.redHeart,
1988
- like: GeneratedEmoji.thumbsUp,
1989
- dislike: GeneratedEmoji.thumbsDown,
1990
- laugh: GeneratedEmoji.faceWithTearsOfJoy,
1991
- emphasize: GeneratedEmoji.doubleExclamationMark,
1992
- question: GeneratedEmoji.redQuestionMark
1993
- };
1994
- var Emoji = { ...GeneratedEmoji, ...aliases };
1995
-
1996
- // src/spectrum.ts
1997
- import {
1998
- createLogger as createLogger3,
1999
- setupOtel,
2000
- withSpan
2001
- } from "@photon-ai/otel";
2002
- import { RawInboundEvent } from "@photon-ai/proto/photon/fusor/v1/inbound";
2003
- import z2 from "zod";
2004
-
2005
- // src/build-env.ts
2006
- var SPECTRUM_SDK_VERSION = "local";
2007
- var SPECTRUM_BUILD_ENV = "development";
2008
-
2009
- // src/fusor/core.ts
2010
- import { createLogger as createLogger2 } from "@photon-ai/otel";
2011
-
2012
- // src/fusor/auth.ts
2013
- var RENEWAL_RATIO = 0.8;
2014
- var EXPIRY_BUFFER_MS = 3e4;
2015
- var RETRY_DELAY_MS = 3e4;
2016
- function createFusorTokenProvider(projectId, projectSecret) {
2017
- return (async () => {
2018
- let tokenData = await cloud.issueFusorToken(projectId, projectSecret);
2019
- let tokenExpiresAt = Date.now() + tokenData.expiresIn * 1e3;
2020
- let disposed = false;
2021
- let renewalTimer;
2022
- const clearRenewalTimer = () => {
2023
- if (renewalTimer !== void 0) {
2024
- clearTimeout(renewalTimer);
2025
- renewalTimer = void 0;
2026
- }
2027
- };
2028
- const refresh = async () => {
2029
- tokenData = await cloud.issueFusorToken(projectId, projectSecret);
2030
- tokenExpiresAt = Date.now() + tokenData.expiresIn * 1e3;
2031
- };
2032
- const scheduleRetry = () => {
2033
- if (disposed) {
2034
- return;
2035
- }
2036
- clearRenewalTimer();
2037
- renewalTimer = setTimeout(async () => {
2038
- if (disposed) {
2039
- return;
2040
- }
2041
- try {
2042
- await refresh();
2043
- scheduleRenewal();
2044
- } catch (retryErr) {
2045
- console.warn(
2046
- `[spectrum-ts] Fusor token refresh failed; retrying in ${RETRY_DELAY_MS}ms.`,
2047
- retryErr
2048
- );
2049
- scheduleRetry();
2050
- }
2051
- }, RETRY_DELAY_MS);
2052
- renewalTimer?.unref?.();
2053
- };
2054
- const scheduleRenewal = () => {
2055
- if (disposed) {
2056
- return;
2057
- }
2058
- clearRenewalTimer();
2059
- const ttlMs = tokenData.expiresIn * 1e3;
2060
- const renewInMs = Math.max(ttlMs * RENEWAL_RATIO, 5e3);
2061
- renewalTimer = setTimeout(async () => {
2062
- try {
2063
- await refresh();
2064
- scheduleRenewal();
2065
- } catch (err) {
2066
- console.warn(
2067
- `[spectrum-ts] Fusor token refresh failed; retrying in ${RETRY_DELAY_MS}ms.`,
2068
- err
2069
- );
2070
- scheduleRetry();
2071
- }
2072
- }, renewInMs);
2073
- renewalTimer?.unref?.();
2074
- };
2075
- scheduleRenewal();
2076
- return {
2077
- async getToken() {
2078
- if (Date.now() >= tokenExpiresAt - EXPIRY_BUFFER_MS) {
2079
- await refresh();
2080
- scheduleRenewal();
2081
- }
2082
- return tokenData.token;
2083
- },
2084
- invalidate() {
2085
- tokenExpiresAt = 0;
2086
- },
2087
- async dispose() {
2088
- disposed = true;
2089
- clearRenewalTimer();
2090
- }
2091
- };
2092
- })();
2093
- }
2094
-
2095
- // src/fusor/parse.ts
2096
- var CR = 13;
2097
- var LF = 10;
2098
- function findHeaderEnd(bytes) {
2099
- for (let i = 0; i + 3 < bytes.length; i++) {
2100
- if (bytes[i] === CR && bytes[i + 1] === LF && bytes[i + 2] === CR && bytes[i + 3] === LF) {
2101
- return i;
2102
- }
2103
- }
2104
- return -1;
2105
- }
2106
- function parseHttpRequest(bytes) {
2107
- const headerEnd = findHeaderEnd(bytes);
2108
- if (headerEnd < 0) {
2109
- throw new Error("fusor: raw_request missing CRLFCRLF header terminator");
2110
- }
2111
- const headerText = new TextDecoder("utf-8").decode(
2112
- bytes.subarray(0, headerEnd)
2113
- );
2114
- const rawBody = bytes.subarray(headerEnd + 4);
2115
- const lines = headerText.split("\r\n");
2116
- const requestLine = lines[0];
2117
- if (!requestLine) {
2118
- throw new Error("fusor: raw_request missing request line");
2119
- }
2120
- const firstSpace = requestLine.indexOf(" ");
2121
- const lastSpace = requestLine.lastIndexOf(" ");
2122
- if (firstSpace < 0 || lastSpace <= firstSpace) {
2123
- throw new Error(`fusor: malformed request line: ${requestLine}`);
2124
- }
2125
- const method = requestLine.slice(0, firstSpace);
2126
- const path = requestLine.slice(firstSpace + 1, lastSpace);
2127
- const headers = {};
2128
- for (let i = 1; i < lines.length; i++) {
2129
- const line = lines[i];
2130
- if (!line) {
2131
- continue;
2132
- }
2133
- const colon = line.indexOf(":");
2134
- if (colon < 0) {
2135
- continue;
2136
- }
2137
- const key = line.slice(0, colon).trim().toLowerCase();
2138
- const value = line.slice(colon + 1).trim();
2139
- if (!key) {
2140
- continue;
2141
- }
2142
- const existing = headers[key];
2143
- headers[key] = existing ? `${existing}, ${value}` : value;
2144
- }
2145
- return { method, path, headers, rawBody };
2146
- }
2147
-
2148
- // src/fusor/websocket.ts
2149
- import { createLogger } from "@photon-ai/otel";
2150
- var log = createLogger("spectrum.fusor.ws");
2151
- var FUSOR_WS_SUBPROTOCOL = "fusor.v1.json";
2152
- var DEFAULT_HEARTBEAT_INTERVAL_MS = 3e4;
2153
- var STALENESS_GRACE_MS = 5e3;
2154
- var FusorWsError = class extends Error {
2155
- closeCode;
2156
- errorCode;
2157
- constructor(message, closeCode, errorCode) {
2158
- super(message);
2159
- this.name = "FusorWsError";
2160
- this.closeCode = closeCode;
2161
- this.errorCode = errorCode;
2162
- }
2163
- };
2164
- function isWsAuthError(error) {
2165
- return error instanceof FusorWsError && (error.closeCode === 4401 || error.errorCode === "unauthenticated");
2166
- }
2167
- function decodeBase64(value) {
2168
- if (typeof Buffer !== "undefined") {
2169
- return new Uint8Array(Buffer.from(value, "base64"));
2170
- }
2171
- return Uint8Array.from(atob(value), (c) => c.charCodeAt(0));
2172
- }
2173
- function encodeBase64(bytes) {
2174
- if (typeof Buffer !== "undefined") {
2175
- return Buffer.from(bytes).toString("base64");
2176
- }
2177
- const chunkSize = 32768;
2178
- const parts = [];
2179
- for (let i = 0; i < bytes.length; i += chunkSize) {
2180
- parts.push(String.fromCharCode(...bytes.subarray(i, i + chunkSize)));
2181
- }
2182
- return btoa(parts.join(""));
2183
- }
2184
- function toRawInboundEvent(frame) {
2185
- const e = frame.event;
2186
- return {
2187
- eventId: e.eventId,
2188
- projectId: e.projectId,
2189
- platform: e.platform,
2190
- receivedAt: e.receivedAt ? new Date(e.receivedAt) : void 0,
2191
- sourceId: e.sourceId ?? "",
2192
- prevSubjectSeq: e.prevSubjectSeq ?? 0,
2193
- rawRequest: decodeBase64(e.rawRequest)
2194
- };
2195
- }
2196
- function runFusorWsSession(options) {
2197
- const WebSocketCtor = globalThis.WebSocket;
2198
- if (typeof WebSocketCtor !== "function") {
2199
- throw new FusorWsError(
2200
- "global WebSocket is not available in this runtime \u2014 the fusor websocket transport needs Bun, Node >= 22, or a browser/worker environment"
2201
- );
2202
- }
2203
- const wsOpen = WebSocketCtor.OPEN;
2204
- const ws = new WebSocketCtor(options.url, [FUSOR_WS_SUBPROTOCOL]);
2205
- let settled = false;
2206
- let closedByUs = false;
2207
- let pendingError = null;
2208
- let stalenessBudgetMs = 2 * DEFAULT_HEARTBEAT_INTERVAL_MS + STALENESS_GRACE_MS;
2209
- let watchdog;
2210
- let tail = Promise.resolve();
2211
- let resolveDone;
2212
- let rejectDone;
2213
- const done = new Promise((resolve, reject) => {
2214
- resolveDone = resolve;
2215
- rejectDone = reject;
2216
- });
2217
- const settle = (error) => {
2218
- if (settled) {
2219
- return;
2220
- }
2221
- settled = true;
2222
- if (watchdog) {
2223
- clearTimeout(watchdog);
2224
- watchdog = void 0;
2225
- }
2226
- if (error) {
2227
- rejectDone(error);
2228
- } else {
2229
- resolveDone();
2230
- }
2231
- };
2232
- const armWatchdog = () => {
2233
- if (settled) {
2234
- return;
2235
- }
2236
- if (watchdog) {
2237
- clearTimeout(watchdog);
2238
- }
2239
- watchdog = setTimeout(() => {
2240
- log.warn("fusor ws: no frame within staleness budget; closing", {
2241
- budgetMs: stalenessBudgetMs
2242
- });
2243
- settle(new FusorWsError("websocket heartbeat timeout"));
2244
- try {
2245
- ws.close();
2246
- } catch {
2247
- }
2248
- }, stalenessBudgetMs);
2249
- watchdog.unref?.();
2250
- };
2251
- const sendReplyFor = (eventId) => (reply2) => {
2252
- if (ws.readyState !== wsOpen) {
2253
- return;
2254
- }
2255
- ws.send(
2256
- JSON.stringify({
2257
- type: "reply",
2258
- eventId,
2259
- status: reply2.status,
2260
- headers: reply2.headers,
2261
- ...reply2.body.length > 0 && { body: encodeBase64(reply2.body) },
2262
- ...reply2.errorReason && { errorReason: reply2.errorReason }
2263
- })
2264
- );
2265
- };
2266
- const handleReadyFrame = (frame) => {
2267
- const interval = frame.heartbeatIntervalMs;
2268
- if (typeof interval === "number" && interval > 0) {
2269
- stalenessBudgetMs = 2 * interval + STALENESS_GRACE_MS;
2270
- }
2271
- log.info("fusor ws stream ready", {
2272
- projectId: typeof frame.projectId === "string" ? frame.projectId : "",
2273
- heartbeatIntervalMs: typeof interval === "number" ? interval : 0
2274
- });
2275
- };
2276
- const handleEventFrame = (frame) => {
2277
- const eventFrame = frame;
2278
- let event;
2279
- try {
2280
- event = toRawInboundEvent(eventFrame);
2281
- } catch (error) {
2282
- log.warn("fusor ws: undecodable event frame; skipping", {
2283
- error: error instanceof Error ? error.message : String(error)
2284
- });
2285
- return;
2286
- }
2287
- const sendReply = eventFrame.replyExpected ? sendReplyFor(event.eventId) : void 0;
2288
- tail = tail.then(() => options.onEvent(event, sendReply)).catch((error) => {
2289
- log.warn("fusor ws: event handler failed", {
2290
- eventId: event.eventId,
2291
- error: error instanceof Error ? error.message : String(error)
2292
- });
2293
- });
2294
- };
2295
- const handleErrorFrame = (frame) => {
2296
- const code = typeof frame.code === "string" ? frame.code : "unknown";
2297
- const message = typeof frame.message === "string" ? frame.message : "server error";
2298
- const reason = typeof frame.reason === "string" ? frame.reason : void 0;
2299
- if (frame.fatal === true) {
2300
- pendingError = { code, message, reason };
2301
- } else {
2302
- log.warn("fusor ws: server notice", { code, message, reason });
2303
- }
2304
- };
2305
- const handleFrame = (raw) => {
2306
- if (typeof raw !== "string") {
2307
- return;
2308
- }
2309
- let frame;
2310
- try {
2311
- frame = JSON.parse(raw);
2312
- } catch {
2313
- log.warn("fusor ws: unparseable server frame; ignoring");
2314
- return;
2315
- }
2316
- switch (frame.type) {
2317
- case "ready":
2318
- handleReadyFrame(frame);
2319
- return;
2320
- case "event":
2321
- handleEventFrame(frame);
2322
- return;
2323
- case "error":
2324
- handleErrorFrame(frame);
2325
- return;
2326
- default:
2327
- return;
2328
- }
2329
- };
2330
- ws.onopen = () => {
2331
- armWatchdog();
2332
- ws.send(
2333
- JSON.stringify({ type: "init", startSeq: 0, token: options.token })
2334
- );
2335
- };
2336
- ws.onmessage = (messageEvent) => {
2337
- armWatchdog();
2338
- handleFrame(messageEvent.data);
2339
- };
2340
- ws.onerror = () => {
2341
- log.debug("fusor ws: socket error event");
2342
- };
2343
- ws.onclose = (closeEvent) => {
2344
- if (closedByUs) {
2345
- settle();
2346
- return;
2347
- }
2348
- const detail = pendingError ? `${pendingError.code}${pendingError.reason ? `:${pendingError.reason}` : ""} \u2014 ${pendingError.message}` : closeEvent.reason || "connection closed";
2349
- settle(
2350
- new FusorWsError(
2351
- `fusor websocket closed (${closeEvent.code}): ${detail}`,
2352
- closeEvent.code,
2353
- pendingError?.code ?? (closeEvent.reason || void 0)
2354
- )
2355
- );
2356
- };
2357
- armWatchdog();
2358
- return {
2359
- done,
2360
- close() {
2361
- closedByUs = true;
2362
- try {
2363
- ws.close(1e3);
2364
- } catch {
2365
- }
2366
- const failsafe = setTimeout(() => settle(), 2e3);
2367
- failsafe.unref?.();
2368
- }
2369
- };
2370
- }
2371
-
2372
- // src/fusor/core.ts
2373
- var DEFAULT_FUSOR_WS_URL = "wss://fusor-ws.spectrum.photon.codes/v1/subscribe";
2374
- var RECONNECT_BASE_MS = 1e3;
2375
- var RECONNECT_MAX_MS = 3e4;
2376
- var log2 = createLogger2("spectrum.fusor");
2377
- var errorText = (error) => error instanceof Error ? error.message : String(error);
2378
- function toReplyBytes(body) {
2379
- if (body === void 0) {
2380
- return new Uint8Array(0);
2381
- }
2382
- if (typeof body === "string") {
2383
- return new TextEncoder().encode(body);
2384
- }
2385
- return body;
2386
- }
2387
- function combineReplies(outcomes) {
2388
- const successes = outcomes.filter((o) => o.ok);
2389
- if (successes.length === 0) {
2390
- const firstFailure = outcomes[0];
2391
- return {
2392
- eventId: "",
2393
- errorReason: firstFailure?.errorReason ?? "no handler succeeded",
2394
- status: 0,
2395
- headers: {},
2396
- body: new Uint8Array(0)
2397
- };
2398
- }
2399
- let status = 0;
2400
- const headers = {};
2401
- let body = new Uint8Array(0);
2402
- for (const outcome of successes) {
2403
- const reply2 = outcome.reply;
2404
- if (!reply2) {
2405
- continue;
2406
- }
2407
- if (reply2.status !== void 0 && reply2.status > status) {
2408
- status = reply2.status;
2409
- }
2410
- if (reply2.headers) {
2411
- for (const [k, v] of Object.entries(reply2.headers)) {
2412
- headers[k.toLowerCase()] = v;
2413
- }
2414
- }
2415
- const candidate = toReplyBytes(reply2.body);
2416
- if (candidate.length > 0) {
2417
- body = candidate;
2418
- }
2419
- }
2420
- return {
2421
- eventId: "",
2422
- errorReason: "",
2423
- status,
2424
- headers,
2425
- body
2426
- };
2427
- }
2428
- function routeHandlerResult(result, handler, deliver) {
2429
- if (result === void 0) {
2430
- return;
2431
- }
2432
- const items = Array.isArray(result) ? result : [result];
2433
- for (const item of items) {
2434
- if (!isFusorEvent(item)) {
2435
- deliver(item);
2436
- continue;
2437
- }
2438
- if (item.name === FUSOR_MESSAGES_CHANNEL) {
2439
- deliver(item.data);
2440
- } else {
2441
- handler.pushEvent(item.name, item.data);
2442
- }
2443
- }
2444
- }
2445
- function runHandlerOnce(handler, parsedRequest, deliver = handler.pushMessage) {
2446
- return (async () => {
2447
- try {
2448
- const payload = await handler.verify(parsedRequest);
2449
- let reply2;
2450
- let respondCalled = false;
2451
- let returned = false;
2452
- const respond = (next) => {
2453
- if (returned) {
2454
- log2.warn("fusor.respond called after handler returned; ignoring");
2455
- return;
2456
- }
2457
- if (respondCalled) {
2458
- log2.debug("fusor.respond called more than once; last call wins");
2459
- }
2460
- respondCalled = true;
2461
- reply2 = next;
2462
- };
2463
- const result = await handler.messages({ payload, respond });
2464
- returned = true;
2465
- routeHandlerResult(result, handler, deliver);
2466
- return { ok: true, reply: reply2 };
2467
- } catch (error) {
2468
- return { ok: false, errorReason: errorText(error) };
2469
- }
2470
- })();
2471
- }
2472
- var FusorCore = class {
2473
- options;
2474
- websocketEndpoint;
2475
- handlers = /* @__PURE__ */ new Map();
2476
- tokenProvider;
2477
- wsSession;
2478
- connectionLoop;
2479
- started = false;
2480
- stopped = false;
2481
- stopResolve;
2482
- stoppedPromise;
2483
- // The reconnect backoff sleep, made cancelable so close() can wake it.
2484
- reconnectTimer;
2485
- reconnectResolve;
2486
- constructor(options) {
2487
- this.options = options;
2488
- this.websocketEndpoint = options.websocketEndpoint ?? process.env.SPECTRUM_FUSOR_WS_URL ?? DEFAULT_FUSOR_WS_URL;
2489
- this.stoppedPromise = new Promise((resolve) => {
2490
- this.stopResolve = resolve;
2491
- });
2492
- }
2493
- register(platform, handler) {
2494
- const list = this.handlers.get(platform) ?? [];
2495
- list.push(handler);
2496
- this.handlers.set(platform, list);
2497
- }
2498
- async start() {
2499
- if (!(this.options.projectId && this.options.projectSecret)) {
2500
- throw new Error(
2501
- "fusor: streaming via spectrum.messages requires projectId and projectSecret"
2502
- );
2503
- }
2504
- if (this.started) {
2505
- return;
2506
- }
2507
- this.started = true;
2508
- this.tokenProvider = await createFusorTokenProvider(
2509
- this.options.projectId,
2510
- this.options.projectSecret
2511
- );
2512
- this.connectionLoop = this.runConnectionLoop().catch((error) => {
2513
- log2.error("fusor connection loop crashed", { error });
2514
- });
2515
- }
2516
- // Streaming transport: the fusor.v1.json WebSocket plane. A session that
2517
- // runs to a clean end reconnects immediately; an errored session backs
2518
- // off exponentially (reset on the next clean run).
2519
- async runConnectionLoop() {
2520
- let attempt = 0;
2521
- while (!this.stopped) {
2522
- const wsRan = await this.tryWebsocketOnce();
2523
- if (this.stopped) {
2524
- return;
2525
- }
2526
- if (wsRan) {
2527
- attempt = 0;
2528
- continue;
2529
- }
2530
- attempt += 1;
2531
- await this.backoffSleep(this.backoffMs(attempt));
2532
- }
2533
- }
2534
- // True when the stream ran to a clean end; false when it errored (the
2535
- // loop then backs off before reconnecting).
2536
- async tryWebsocketOnce() {
2537
- try {
2538
- await this.runWebsocketOnce();
2539
- return true;
2540
- } catch (error) {
2541
- if (isWsAuthError(error)) {
2542
- this.tokenProvider?.invalidate();
2543
- }
2544
- if (!this.stopped) {
2545
- log2.warn("fusor websocket stream errored; reconnecting", {
2546
- error: errorText(error)
2547
- });
2548
- }
2549
- return false;
2550
- }
2551
- }
2552
- backoffMs(attempt) {
2553
- return Math.min(RECONNECT_BASE_MS * 2 ** (attempt - 1), RECONNECT_MAX_MS);
2554
- }
2555
- // Cancelable sleep: close() clears the timer and resolves it so
2556
- // shutdown doesn't wait out the (up to 30s) backoff.
2557
- async backoffSleep(backoff) {
2558
- await new Promise((resolve) => {
2559
- this.reconnectResolve = resolve;
2560
- const timer = setTimeout(resolve, backoff);
2561
- timer.unref?.();
2562
- this.reconnectTimer = timer;
2563
- });
2564
- this.reconnectTimer = void 0;
2565
- this.reconnectResolve = void 0;
2566
- }
2567
- async runWebsocketOnce() {
2568
- if (!this.tokenProvider) {
2569
- throw new Error("fusor: token not initialized");
2570
- }
2571
- const token = await this.tokenProvider.getToken();
2572
- const session = runFusorWsSession({
2573
- url: this.websocketEndpoint,
2574
- token,
2575
- onEvent: async (event, sendReply) => {
2576
- if (this.stopped) {
2577
- return;
2578
- }
2579
- const reply2 = await this.processEvent(event);
2580
- sendReply?.(reply2);
2581
- }
2582
- });
2583
- this.wsSession = session;
2584
- try {
2585
- await session.done;
2586
- } finally {
2587
- this.wsSession = void 0;
2588
- }
2589
- }
2590
- // Transport-independent event processing: route by platform, parse the wire
2591
- // request, run every registered handler (verify → messages), and combine the
2592
- // results into a single InboundReply. Returns the reply instead of writing it
2593
- // anywhere, so both the streaming session (sendReply) and the synchronous
2594
- // webhook path can drive it. `deliver` controls where produced records go:
2595
- // the streaming path defaults to each handler's pushMessage (the per-platform
2596
- // queue feeding spectrum.messages); the webhook path collects them for the
2597
- // request instead.
2598
- async processEvent(event, deliver) {
2599
- const handlers = this.handlers.get(event.platform) ?? [];
2600
- if (handlers.length === 0) {
2601
- log2.warn("fusor: no handler for platform", { platform: event.platform });
2602
- return {
2603
- eventId: event.eventId,
2604
- errorReason: `no handler for platform ${event.platform}`,
2605
- status: 0,
2606
- headers: {},
2607
- body: new Uint8Array(0)
2608
- };
2609
- }
2610
- let parsedRequest;
2611
- try {
2612
- parsedRequest = parseHttpRequest(event.rawRequest);
2613
- } catch (error) {
2614
- const errorReason = errorText(error);
2615
- log2.warn("fusor: failed to parse raw_request", {
2616
- platform: event.platform,
2617
- error: errorReason
2618
- });
2619
- return {
2620
- eventId: event.eventId,
2621
- errorReason,
2622
- status: 0,
2623
- headers: {},
2624
- body: new Uint8Array(0)
2625
- };
2626
- }
2627
- const outcomes = await Promise.all(
2628
- handlers.map((handler) => runHandlerOnce(handler, parsedRequest, deliver))
2629
- );
2630
- const combined = combineReplies(outcomes);
2631
- combined.eventId = event.eventId;
2632
- return combined;
2633
- }
2634
- async close() {
2635
- if (this.stopped) {
2636
- return;
2637
- }
2638
- this.stopped = true;
2639
- this.wsSession?.close();
2640
- if (this.reconnectTimer) {
2641
- clearTimeout(this.reconnectTimer);
2642
- this.reconnectTimer = void 0;
2643
- }
2644
- this.reconnectResolve?.();
2645
- this.reconnectResolve = void 0;
2646
- if (this.tokenProvider) {
2647
- await this.tokenProvider.dispose();
2648
- }
2649
- if (this.connectionLoop) {
2650
- await this.connectionLoop;
2651
- }
2652
- this.stopResolve?.();
2653
- }
2654
- async waitStopped() {
2655
- return this.stoppedPromise;
2656
- }
2657
- };
2658
-
2659
- // src/utils/store.ts
2660
- var isRecordObject = (value) => {
2661
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
2662
- return false;
2663
- }
2664
- const prototype = Object.getPrototypeOf(value);
2665
- return prototype === Object.prototype || prototype === null;
2666
- };
2667
- function createStore() {
2668
- const data = /* @__PURE__ */ new Map();
2669
- return {
2670
- set(key, value) {
2671
- data.set(key, value);
2672
- },
2673
- get(key) {
2674
- return data.get(key);
2675
- },
2676
- has(key) {
2677
- return data.has(key);
2678
- },
2679
- delete(key) {
2680
- return data.delete(key);
2681
- },
2682
- clear() {
2683
- data.clear();
2684
- },
2685
- keys() {
2686
- return Array.from(data.keys());
2687
- },
2688
- string(key) {
2689
- const v = data.get(key);
2690
- return typeof v === "string" ? v : void 0;
2691
- },
2692
- number(key) {
2693
- const v = data.get(key);
2694
- return typeof v === "number" ? v : void 0;
2695
- },
2696
- bool(key) {
2697
- const v = data.get(key);
2698
- return typeof v === "boolean" ? v : void 0;
2699
- },
2700
- object(key) {
2701
- const v = data.get(key);
2702
- if (!isRecordObject(v)) {
2703
- return;
2704
- }
2705
- return v;
2706
- },
2707
- array(key) {
2708
- const v = data.get(key);
2709
- return Array.isArray(v) ? v : void 0;
2710
- }
2711
- };
2712
- }
2713
-
2714
- // src/webhook/deserialize.ts
2715
- var MESSAGES_EVENT = "messages";
2716
- var DEFAULT_ATTACHMENT_NAME = "attachment";
2717
- var DEFAULT_MIME_TYPE = "application/octet-stream";
2718
- var isRecord = (value) => typeof value === "object" && value !== null;
2719
- var asString = (value) => typeof value === "string" ? value : "";
2720
- var asOptionalDate = (value) => typeof value === "string" ? new Date(value) : void 0;
2721
- function deserializeSpectrumMessage(envelope, ctx) {
2722
- if (envelope.event !== MESSAGES_EVENT) {
2723
- return null;
2724
- }
2725
- const message = envelope.message;
2726
- const platform = resolvePlatform(message);
2727
- if (!platform) {
2728
- return null;
2729
- }
2730
- const spaceRef = { ...message.space };
2731
- return {
2732
- platform,
2733
- record: {
2734
- id: message.id,
2735
- direction: "inbound",
2736
- content: deserializeContent(message.content, platform, spaceRef, ctx),
2737
- space: spaceRef,
2738
- sender: message.sender ? { ...message.sender } : void 0,
2739
- timestamp: asOptionalDate(message.timestamp)
2740
- }
2741
- };
2742
- }
2743
- var resolvePlatform = (message) => message.platform ?? message.space.platform;
2744
- var deserializeContent = (content, platform, spaceRef, ctx) => {
2745
- try {
2746
- return mapContent(content, platform, spaceRef, ctx);
2747
- } catch {
2748
- return asCustom(content);
2749
- }
2750
- };
2751
- var mapContent = (content, platform, spaceRef, ctx) => {
2752
- const raw = content;
2753
- switch (content.type) {
2754
- case "text":
2755
- return { type: "text", text: asString(raw.text) };
2756
- case "richlink":
2757
- return asRichlink({ url: asString(raw.url) });
2758
- case "contact":
2759
- return deserializeContact(raw);
2760
- case "reaction":
2761
- return deserializeReaction(raw, spaceRef);
2762
- case "group":
2763
- return deserializeGroup(raw, platform, spaceRef, ctx);
2764
- case "attachment":
2765
- return deserializeAttachment(raw, platform, spaceRef, ctx);
2766
- default:
2767
- return asCustom(content);
2768
- }
2769
- };
2770
- var deserializeAttachment = (raw, platform, spaceRef, ctx) => {
2771
- const id = asString(raw.id);
2772
- const bytes = ctx.resolveAttachment?.(platform, spaceRef, id);
2773
- const unavailable = () => Promise.reject(
2774
- UnsupportedError.action(
2775
- "getAttachment",
2776
- platform,
2777
- `attachment "${id}" arrived without bytes over the Spectrum webhook and "${platform}" exposes no getAttachment`
2778
- )
2779
- );
2780
- return asAttachment({
2781
- id,
2782
- name: asString(raw.name) || DEFAULT_ATTACHMENT_NAME,
2783
- mimeType: asString(raw.mimeType) || DEFAULT_MIME_TYPE,
2784
- size: typeof raw.size === "number" ? raw.size : void 0,
2785
- read: bytes ? bytes.read : unavailable,
2786
- stream: bytes?.stream
2787
- });
2788
- };
2789
- var deserializeReaction = (raw, spaceRef) => (
2790
- // `target` is a raw record; `wrapNestedContent` wraps it into a Message.
2791
- {
2792
- type: "reaction",
2793
- emoji: asString(raw.emoji),
2794
- target: buildTargetRecord(raw.target, spaceRef)
2795
- }
2796
- );
2797
- var buildTargetRecord = (target, spaceRef) => {
2798
- const ref = isRecord(target) ? target : {};
2799
- return {
2800
- id: asString(ref.id),
2801
- // The target's full content is not delivered; the 80-char `contentPreview`
2802
- // (text targets only) is the best available stand-in.
2803
- content: { type: "text", text: asString(ref.contentPreview) },
2804
- space: { ...spaceRef },
2805
- sender: ref.sender ? { ...ref.sender } : void 0,
2806
- timestamp: asOptionalDate(ref.timestamp)
2807
- };
2808
- };
2809
- var deserializeGroup = (raw, platform, spaceRef, ctx) => {
2810
- const rawItems = Array.isArray(raw.items) ? raw.items : [];
2811
- return {
2812
- type: "group",
2813
- items: rawItems.map(
2814
- (item) => buildItemRecord(item, platform, spaceRef, ctx)
2815
- )
2816
- };
2817
- };
2818
- var buildItemRecord = (item, platform, spaceRef, ctx) => {
2819
- const record = isRecord(item) ? item : {};
2820
- const itemSpace = isRecord(record.space) ? {
2821
- ...record.space,
2822
- id: asString(record.space.id) || spaceRef.id
2823
- } : spaceRef;
2824
- const content = isRecord(record.content) ? deserializeContent(
2825
- record.content,
2826
- platform,
2827
- itemSpace,
2828
- ctx
2829
- ) : asCustom(record.content);
2830
- return {
2831
- id: asString(record.id),
2832
- content,
2833
- space: itemSpace,
2834
- sender: isRecord(record.sender) ? { ...record.sender, id: asString(record.sender.id) } : void 0,
2835
- timestamp: asOptionalDate(record.timestamp)
2836
- };
2837
- };
2838
- var deserializeContact = (raw) => {
2839
- const input = {};
2840
- const name = normalizeContactName(raw.name);
2841
- if (name) {
2842
- input.name = name;
2843
- }
2844
- const phones = normalizeContactPhones(raw.phones);
2845
- if (phones) {
2846
- input.phones = phones;
2847
- }
2848
- if (typeof raw.note === "string") {
2849
- input.note = raw.note;
2850
- }
2851
- if (raw.raw !== void 0) {
2852
- input.raw = raw.raw;
2853
- }
2854
- return asContact(input);
2855
- };
2856
- var CONTACT_NAME_KEYS = [
2857
- "formatted",
2858
- "first",
2859
- "last",
2860
- "middle",
2861
- "prefix",
2862
- "suffix"
2863
- ];
2864
- var normalizeContactName = (value) => {
2865
- if (typeof value === "string") {
2866
- return { formatted: value };
2867
- }
2868
- if (!isRecord(value)) {
2869
- return;
2870
- }
2871
- const name = {};
2872
- for (const key of CONTACT_NAME_KEYS) {
2873
- const part = value[key];
2874
- if (typeof part === "string") {
2875
- name[key] = part;
2876
- }
2877
- }
2878
- return Object.keys(name).length > 0 ? name : void 0;
2879
- };
2880
- var normalizeContactPhones = (value) => {
2881
- if (!Array.isArray(value)) {
2882
- return;
2883
- }
2884
- const phones = [];
2885
- for (const entry of value) {
2886
- if (typeof entry === "string") {
2887
- phones.push({ value: entry });
2888
- } else if (isRecord(entry) && typeof entry.value === "string") {
2889
- phones.push({ value: entry.value });
2890
- }
2891
- }
2892
- return phones.length > 0 ? phones : void 0;
2893
- };
2894
-
2895
- // src/webhook/types.ts
2896
- import z from "zod";
2897
- var slimSenderSchema = z.looseObject({
2898
- id: z.string(),
2899
- platform: z.string().optional()
2900
- });
2901
- var slimSpaceSchema = z.looseObject({
2902
- id: z.string(),
2903
- platform: z.string().optional()
2904
- });
2905
- var slimContentSchema = z.looseObject({
2906
- type: z.string()
2907
- });
2908
- var slimMessageRefSchema = z.looseObject({
2909
- id: z.string(),
2910
- platform: z.string().optional(),
2911
- timestamp: z.string().optional(),
2912
- sender: slimSenderSchema.optional(),
2913
- contentPreview: z.string().optional()
2914
- });
2915
- var slimMessageSchema = z.looseObject({
2916
- id: z.string(),
2917
- platform: z.string().optional(),
2918
- // Webhooks are inbound-only; `direction` is left loose (not a `"inbound"`
2919
- // literal) so a future direction value cannot fail an older SDK's parse.
2920
- direction: z.string().optional(),
2921
- timestamp: z.string().optional(),
2922
- sender: slimSenderSchema.optional(),
2923
- space: slimSpaceSchema,
2924
- content: slimContentSchema
2925
- });
2926
- var slimEnvelopeSchema = z.looseObject({
2927
- event: z.string(),
2928
- space: slimSpaceSchema.optional(),
2929
- message: slimMessageSchema
2930
- });
2931
-
2932
- // src/webhook/verify.ts
2933
- import { createHmac, timingSafeEqual } from "crypto";
2934
- var SIGNATURE_HEADER = "x-spectrum-signature";
2935
- var TIMESTAMP_HEADER = "x-spectrum-timestamp";
2936
- var SIGNATURE_PREFIX = "v0=";
2937
- var SIGNATURE_SCHEME = "v0";
2938
- var REPLAY_TOLERANCE_SECONDS = 300;
2939
- var MILLIS_PER_SECOND = 1e3;
2940
- function verifySpectrumSignature(input) {
2941
- const { rawBody, headers, secret, now = Date.now() } = input;
2942
- const provided = headers[SIGNATURE_HEADER];
2943
- const timestamp = headers[TIMESTAMP_HEADER];
2944
- if (!(provided && timestamp)) {
2945
- return { ok: false, reason: "missing-headers" };
2946
- }
2947
- const timestampSeconds = Number(timestamp);
2948
- if (!Number.isFinite(timestampSeconds)) {
2949
- return { ok: false, reason: "missing-headers" };
2950
- }
2951
- const nowSeconds = Math.floor(now / MILLIS_PER_SECOND);
2952
- if (Math.abs(nowSeconds - timestampSeconds) > REPLAY_TOLERANCE_SECONDS) {
2953
- return { ok: false, reason: "expired" };
2954
- }
2955
- const base = Buffer.concat([
2956
- Buffer.from(`${SIGNATURE_SCHEME}:${timestamp}:`, "utf8"),
2957
- Buffer.from(rawBody)
2958
- ]);
2959
- const expected = createHmac("sha256", secret).update(base).digest();
2960
- const providedHex = provided.startsWith(SIGNATURE_PREFIX) ? provided.slice(SIGNATURE_PREFIX.length) : provided;
2961
- const providedBytes = Buffer.from(providedHex, "hex");
2962
- if (providedBytes.length !== expected.length || !timingSafeEqual(providedBytes, expected)) {
2963
- return { ok: false, reason: "signature-mismatch" };
2964
- }
2965
- return { ok: true };
2966
- }
2967
-
2968
- // src/spectrum.ts
2969
- var PHOTON_OTEL_ENDPOINT = "https://otlp.photon.codes";
2970
- var STREAM_CLOSE_TIMEOUT_MS = 5e3;
2971
- var lifecycleLog = createLogger3("spectrum.lifecycle");
2972
- var ignoreCleanupError = () => void 0;
2973
- var spectrumOptionsSchema = z2.object({
2974
- flattenGroups: z2.boolean().optional()
2975
- }).optional();
2976
- var spectrumConfigSchema = z2.union([
2977
- z2.object({
2978
- projectId: z2.string().min(1),
2979
- projectSecret: z2.string().min(1),
2980
- providers: z2.array(z2.custom()),
2981
- options: spectrumOptionsSchema,
2982
- telemetry: z2.boolean().optional(),
2983
- webhookSecret: z2.string().min(1).optional()
2984
- }),
2985
- z2.object({
2986
- projectId: z2.undefined().optional(),
2987
- projectSecret: z2.undefined().optional(),
2988
- providers: z2.array(z2.custom()),
2989
- options: spectrumOptionsSchema,
2990
- telemetry: z2.boolean().optional(),
2991
- webhookSecret: z2.string().min(1).optional()
2992
- })
2993
- ]);
2994
- function bootstrapTelemetry(opts) {
2995
- const headers = {};
2996
- if (opts.projectId && opts.projectSecret) {
2997
- const credential = `${opts.projectId}:${opts.projectSecret}`;
2998
- headers.Authorization = `Basic ${btoa(credential)}`;
2999
- }
3000
- const resourceAttributes = {
3001
- "deployment.environment": process.env.DEPLOYMENT_ENV ?? SPECTRUM_BUILD_ENV
3002
- };
3003
- if (opts.projectId) {
3004
- resourceAttributes["spectrum.project_id"] = opts.projectId;
3005
- }
3006
- return setupOtel({
3007
- serviceName: "spectrum-ts",
3008
- serviceVersion: SPECTRUM_SDK_VERSION,
3009
- endpoint: PHOTON_OTEL_ENDPOINT,
3010
- headers,
3011
- resourceAttributes
3012
- });
3013
- }
3014
- async function Spectrum(options) {
3015
- spectrumConfigSchema.parse(options);
3016
- const {
3017
- projectId,
3018
- projectSecret,
3019
- providers,
3020
- options: runtimeOptions,
3021
- telemetry,
3022
- webhookSecret
3023
- } = options;
3024
- const flattenGroups = runtimeOptions?.flattenGroups ?? false;
3025
- const resolvedWebhookSecret = webhookSecret ?? process.env.SPECTRUM_WEBHOOK_SECRET;
3026
- const otelHandle = telemetry ? bootstrapTelemetry({ projectId, projectSecret }) : void 0;
3027
- const projectConfig = projectId !== void 0 && projectSecret !== void 0 ? await cloud.getProject(projectId, projectSecret) : void 0;
3028
- const platformStates = /* @__PURE__ */ new Map();
3029
- const fusorMessageSources = /* @__PURE__ */ new Map();
3030
- const messageBroadcasters = /* @__PURE__ */ new Map();
3031
- const fusorEventSources = /* @__PURE__ */ new Map();
3032
- const eventBroadcasters = /* @__PURE__ */ new Map();
3033
- const customEventStreams = /* @__PURE__ */ new Map();
3034
- let stopped = false;
3035
- const adaptIterable = (iterable, project) => stream((emit, end) => {
3036
- const iterator = iterable[Symbol.asyncIterator]();
3037
- const pump = (async () => {
3038
- try {
3039
- let result = await iterator.next();
3040
- while (!result.done) {
3041
- if (project) {
3042
- await project(result.value, emit);
3043
- } else {
3044
- await emit(result.value);
3045
- }
3046
- result = await iterator.next();
3047
- }
3048
- end();
3049
- } catch (error) {
3050
- end(error);
3051
- }
3052
- })();
3053
- return async () => {
3054
- await iterator.return?.();
3055
- await pump.catch(ignoreCleanupError);
3056
- };
3057
- });
3058
- const resolveRecordToMessages = async (record, rt) => {
3059
- const { client, config, definition, store } = rt;
3060
- const built = await withSpan(
3061
- "spectrum.message.receive",
3062
- {
3063
- "spectrum.provider": definition.name,
3064
- "spectrum.message.id": record.id,
3065
- "spectrum.space.id": record.space?.id,
3066
- ...contentAttrs(record.content),
3067
- ...senderAttrs(record.sender)
3068
- },
3069
- () => {
3070
- const spaceRef = {
3071
- ...record.space,
3072
- __platform: definition.name
3073
- };
3074
- const actionCtx = { space: spaceRef, client, config, store };
3075
- const space2 = buildSpace({
3076
- spaceRef,
3077
- extras: {},
3078
- actionCtx,
3079
- definition,
3080
- client,
3081
- config,
3082
- store
3083
- });
3084
- const normalizedMessage2 = wrapProviderMessage(
3085
- record,
3086
- {
3087
- client,
3088
- config,
3089
- definition,
3090
- space: space2,
3091
- spaceRef,
3092
- store
3093
- },
3094
- "inbound"
3095
- );
3096
- return { space: space2, normalizedMessage: normalizedMessage2 };
3097
- }
3098
- );
3099
- const { space, normalizedMessage } = built;
3100
- if (flattenGroups && normalizedMessage.content.type === "group") {
3101
- return normalizedMessage.content.items.map((item) => [
3102
- space,
3103
- item
3104
- ]);
3105
- }
3106
- return [[space, normalizedMessage]];
3107
- };
3108
- const createProviderMessagesStream = (state) => {
3109
- const { client, config, definition, store } = state;
3110
- const fusorSource = fusorMessageSources.get(definition.name);
3111
- const raw = fusorSource ? fusorSource.iterable : definition.messages({
3112
- client,
3113
- config,
3114
- projectConfig,
3115
- store
3116
- });
3117
- return adaptIterable(
3118
- raw,
3119
- async (record, emit) => {
3120
- const tuples = await resolveRecordToMessages(record, {
3121
- client,
3122
- config,
3123
- definition,
3124
- store
3125
- });
3126
- for (const tuple of tuples) {
3127
- await emit(tuple);
3128
- }
3129
- }
3130
- );
3131
- };
3132
- const getOrCreateMessageBroadcast = (state) => {
3133
- if (stopped) {
3134
- throw new Error(
3135
- `Spectrum instance has been stopped; cannot subscribe to "${state.definition.name}" messages`
3136
- );
3137
- }
3138
- const name = state.definition.name;
3139
- let broadcaster = messageBroadcasters.get(name);
3140
- if (!broadcaster) {
3141
- broadcaster = broadcast(createProviderMessagesStream(state));
3142
- messageBroadcasters.set(name, broadcaster);
3143
- }
3144
- return broadcaster;
3145
- };
3146
- const getOrCreateEventBroadcast = (platform, channel) => {
3147
- const queue = fusorEventSources.get(platform)?.get(channel);
3148
- if (!queue) {
3149
- return;
3150
- }
3151
- if (stopped) {
3152
- throw new Error(
3153
- `Spectrum instance has been stopped; cannot subscribe to "${platform}" event "${channel}"`
3154
- );
3155
- }
3156
- const key = `${platform}\0${channel}`;
3157
- let broadcaster = eventBroadcasters.get(key);
3158
- if (!broadcaster) {
3159
- broadcaster = broadcast(adaptIterable(queue.iterable));
3160
- eventBroadcasters.set(key, broadcaster);
3161
- }
3162
- return broadcaster;
3163
- };
3164
- await withSpan(
3165
- "spectrum.init",
3166
- {
3167
- "spectrum.provider_count": providers.length,
3168
- "spectrum.flatten_groups": flattenGroups
3169
- },
3170
- async () => {
3171
- for (const provider of providers) {
3172
- const providerConfig = provider;
3173
- const def = providerConfig.__definition;
3174
- const userConfig = def.config.parse(providerConfig.config);
3175
- const store = createStore();
3176
- const client = await withSpan(
3177
- "spectrum.provider.create_client",
3178
- {
3179
- "spectrum.provider": def.name
3180
- },
3181
- () => def.lifecycle.createClient({
3182
- config: userConfig,
3183
- projectId,
3184
- projectSecret,
3185
- projectConfig,
3186
- store
3187
- })
3188
- );
3189
- const state = {
3190
- client,
3191
- config: userConfig,
3192
- definition: def,
3193
- store
3194
- };
3195
- platformStates.set(def.name, {
3196
- ...state,
3197
- projectConfig,
3198
- subscribeMessages: () => getOrCreateMessageBroadcast(state).subscribe(),
3199
- // Fanout subscription to a fusor event channel. Returns undefined for
3200
- // regular platforms (no per-channel queue) — callers fall back to the
3201
- // producer path. Resolved lazily, after the fusor bootstrap below has
3202
- // created the per-(platform, channel) queues.
3203
- subscribeEvent: (channel) => getOrCreateEventBroadcast(def.name, channel)?.subscribe()
3204
- });
3205
- }
3206
- }
3207
- );
3208
- let fusorCore;
3209
- let fusorStartPromise;
3210
- const fusorPlatforms = [];
3211
- for (const [name, state] of platformStates) {
3212
- if (isFusorClient(state.client)) {
3213
- fusorPlatforms.push({ name, client: state.client });
3214
- }
3215
- }
3216
- if (fusorPlatforms.length > 0) {
3217
- fusorCore = new FusorCore({ projectId, projectSecret });
3218
- for (const { name, client } of fusorPlatforms) {
3219
- const queue = createAsyncQueue();
3220
- fusorMessageSources.set(name, queue);
3221
- const runtime = platformStates.get(name);
3222
- if (!runtime) {
3223
- continue;
3224
- }
3225
- const userMessages = runtime.definition.messages;
3226
- const declaredEvents = runtime.definition.events ?? {};
3227
- const eventQueues = /* @__PURE__ */ new Map();
3228
- for (const channel of Object.keys(declaredEvents)) {
3229
- eventQueues.set(channel, createAsyncQueue());
3230
- }
3231
- fusorEventSources.set(name, eventQueues);
3232
- const handler = {
3233
- verify: client.verify,
3234
- // Enrich the transport-level `{ payload, respond }` ctx with the same
3235
- // runtime context every other platform callback receives, so fusor
3236
- // handlers can read config/store/projectConfig directly instead of
3237
- // smuggling state through the payload.
3238
- messages: async (ctx) => userMessages({
3239
- ...ctx,
3240
- config: runtime.config,
3241
- store: runtime.store,
3242
- projectConfig: runtime.projectConfig
3243
- }),
3244
- pushMessage: (record) => queue.push(record),
3245
- pushEvent: (channel, data) => {
3246
- const eventQueue = eventQueues.get(channel);
3247
- if (!eventQueue) {
3248
- lifecycleLog.warn(
3249
- `spectrum: fusorEvent("${channel}", \u2026) names a channel not declared in "${name}".events; dropping`,
3250
- { platform: name, channel }
3251
- );
3252
- return;
3253
- }
3254
- eventQueue.push(data);
3255
- }
3256
- };
3257
- fusorCore.register(client.platform, handler);
3258
- }
3259
- }
3260
- const ensureFusorStarted = () => {
3261
- if (!fusorCore) {
3262
- return Promise.resolve();
3263
- }
3264
- if (!fusorStartPromise) {
3265
- fusorStartPromise = fusorCore.start();
3266
- }
3267
- return fusorStartPromise;
3268
- };
3269
- const providerNames = providers.map((p) => p.__definition.name).join(",");
3270
- lifecycleLog.info("Spectrum started", {
3271
- providerCount: providers.length,
3272
- providers: providerNames,
3273
- telemetry: telemetry === true
3274
- });
3275
- const createMessagesStream = () => stream((emit, end) => {
3276
- ensureFusorStarted().catch((error) => end(error));
3277
- const merged = mergeStreams(
3278
- Array.from(
3279
- platformStates.values(),
3280
- (runtime) => runtime.subscribeMessages()
3281
- )
3282
- );
3283
- const pump = (async () => {
3284
- try {
3285
- for await (const value of merged) {
3286
- await emit(value);
3287
- }
3288
- end();
3289
- } catch (error) {
3290
- end(error);
3291
- }
3292
- })();
3293
- return async () => {
3294
- await merged.close();
3295
- await pump.catch(ignoreCleanupError);
3296
- };
3297
- });
3298
- const createCustomEventStream = (eventName) => stream((emit, end) => {
3299
- const providerStreams = [];
3300
- for (const state of platformStates.values()) {
3301
- const { client, config, definition, store } = state;
3302
- let source = state.subscribeEvent?.(eventName);
3303
- if (!source) {
3304
- const producer = definition.events?.[eventName];
3305
- if (typeof producer !== "function") {
3306
- continue;
3307
- }
3308
- source = producer({ client, config, projectConfig, store });
3309
- }
3310
- const providerEvents = source;
3311
- providerStreams.push(
3312
- adaptIterable(
3313
- providerEvents,
3314
- async (value, emit2) => {
3315
- const annotated = await withSpan(
3316
- "spectrum.event",
3317
- {
3318
- "spectrum.provider": definition.name,
3319
- "spectrum.event.name": eventName
3320
- },
3321
- // Object payloads are flattened and tagged with `platform`. A
3322
- // primitive/null payload can't be spread (a string would mangle
3323
- // into indexed chars, a number/bool would vanish), so wrap it
3324
- // under `payload` instead.
3325
- () => typeof value === "object" && value !== null ? { ...value, platform: definition.name } : { platform: definition.name, payload: value }
3326
- );
3327
- await emit2(annotated);
3328
- }
3329
- )
3330
- );
3331
- }
3332
- const merged = mergeStreams(providerStreams);
3333
- const pump = (async () => {
3334
- try {
3335
- for await (const value of merged) {
3336
- await emit(value);
3337
- }
3338
- end();
3339
- } catch (error) {
3340
- end(error);
3341
- }
3342
- })();
3343
- return async () => {
3344
- await merged.close();
3345
- await pump.catch(ignoreCleanupError);
3346
- };
3347
- });
3348
- const messagesStream = createMessagesStream();
3349
- const closeFusorSources = () => {
3350
- for (const queue of fusorMessageSources.values()) {
3351
- queue.close();
3352
- }
3353
- fusorMessageSources.clear();
3354
- for (const queues of fusorEventSources.values()) {
3355
- for (const queue of queues.values()) {
3356
- queue.close();
3357
- }
3358
- }
3359
- fusorEventSources.clear();
3360
- };
3361
- const stopOnce = async () => {
3362
- if (stopped) {
3363
- return;
3364
- }
3365
- stopped = true;
3366
- const streamShutdowns = [
3367
- messagesStream.close(),
3368
- ...Array.from(
3369
- customEventStreams.values(),
3370
- (eventStream) => eventStream.close()
3371
- ),
3372
- ...Array.from(
3373
- messageBroadcasters.values(),
3374
- (broadcaster) => broadcaster.close()
3375
- ),
3376
- ...Array.from(
3377
- eventBroadcasters.values(),
3378
- (broadcaster) => broadcaster.close()
3379
- )
3380
- ];
3381
- process.off("SIGINT", handleSignal);
3382
- process.off("SIGTERM", handleSignal);
3383
- const streamCloseStart = performance.now();
3384
- const streamSettled = Promise.allSettled(streamShutdowns);
3385
- let streamTimedOut = false;
3386
- await Promise.race([
3387
- streamSettled,
3388
- new Promise((resolve) => {
3389
- setTimeout(() => {
3390
- streamTimedOut = true;
3391
- resolve();
3392
- }, STREAM_CLOSE_TIMEOUT_MS).unref();
3393
- })
3394
- ]);
3395
- if (streamTimedOut) {
3396
- lifecycleLog.warn("stream close timed out; proceeding to teardown", {
3397
- timeoutMs: STREAM_CLOSE_TIMEOUT_MS
3398
- });
3399
- }
3400
- let fusorCloseMs = 0;
3401
- if (fusorCore) {
3402
- const fusorCloseStart = performance.now();
3403
- if (fusorStartPromise) {
3404
- await fusorStartPromise.catch(ignoreCleanupError);
3405
- }
3406
- await fusorCore.close().catch((error) => {
3407
- lifecycleLog.warn("fusor core close failed", { error });
3408
- });
3409
- fusorCloseMs = Math.round(performance.now() - fusorCloseStart);
3410
- closeFusorSources();
3411
- }
3412
- const clientShutdowns = [];
3413
- for (const state of platformStates.values()) {
3414
- const destroy = state.definition.lifecycle.destroyClient;
3415
- if (!destroy) {
3416
- continue;
3417
- }
3418
- clientShutdowns.push(
3419
- withSpan(
3420
- "spectrum.provider.destroy_client",
3421
- {
3422
- "spectrum.provider": state.definition.name
3423
- },
3424
- () => destroy({
3425
- client: state.client,
3426
- store: state.store
3427
- })
3428
- )
3429
- );
3430
- }
3431
- const clientCloseStart = performance.now();
3432
- await Promise.allSettled(clientShutdowns);
3433
- const clientCloseMs = Math.round(performance.now() - clientCloseStart);
3434
- await streamSettled.catch(() => void 0);
3435
- const streamCloseMs = Math.round(performance.now() - streamCloseStart);
3436
- customEventStreams.clear();
3437
- messageBroadcasters.clear();
3438
- eventBroadcasters.clear();
3439
- platformStates.clear();
3440
- lifecycleLog.info("Spectrum stopped", {
3441
- providers: providerNames,
3442
- streamCloseMs,
3443
- fusorCloseMs,
3444
- clientCloseMs
3445
- });
3446
- if (otelHandle) {
3447
- await otelHandle.shutdown();
3448
- }
3449
- };
3450
- const handleSignal = () => {
3451
- setTimeout(() => process.exit(1), 3e3).unref();
3452
- stopOnce().then(() => process.exit(0)).catch(() => process.exit(1));
3453
- };
3454
- process.on("SIGINT", handleSignal);
3455
- process.on("SIGTERM", handleSignal);
3456
- const messages = messagesStream;
3457
- const customEventProxy = new Proxy(
3458
- {},
3459
- {
3460
- get(_target, prop) {
3461
- let eventStream = customEventStreams.get(prop);
3462
- if (!eventStream) {
3463
- eventStream = createCustomEventStream(prop);
3464
- customEventStreams.set(prop, eventStream);
3465
- }
3466
- return eventStream;
3467
- }
3468
- }
3469
- );
3470
- const encodeText = (s) => new TextEncoder().encode(s);
3471
- const buildWebhookResult = (asWeb, result) => {
3472
- if (asWeb) {
3473
- return new Response(result.body, {
3474
- status: result.status,
3475
- headers: result.headers
3476
- });
3477
- }
3478
- return result;
3479
- };
3480
- const readWebhookInput = async (request) => {
3481
- if (typeof Request !== "undefined" && request instanceof Request) {
3482
- const headers2 = {};
3483
- for (const [key, value] of request.headers) {
3484
- headers2[key.toLowerCase()] = value;
3485
- }
3486
- return {
3487
- asWeb: true,
3488
- bodyBytes: new Uint8Array(await request.arrayBuffer()),
3489
- headers: headers2
3490
- };
3491
- }
3492
- const raw = request;
3493
- const bodyBytes = raw.body instanceof ArrayBuffer ? new Uint8Array(raw.body) : raw.body;
3494
- const headers = {};
3495
- for (const [key, value] of Object.entries(raw.headers ?? {})) {
3496
- headers[key.toLowerCase()] = String(value);
3497
- }
3498
- return { asWeb: false, bodyBytes, headers };
3499
- };
3500
- const deliverWebhookMessages = async (collected, runtime, handler, context) => {
3501
- for (const record of collected) {
3502
- const tuples = await resolveRecordToMessages(record, runtime);
3503
- for (const [space, message] of tuples) {
3504
- try {
3505
- await handler(space, message);
3506
- } catch (error) {
3507
- lifecycleLog.error(
3508
- `spectrum.webhook: handler threw (async), ${error}`,
3509
- {
3510
- eventId: context.eventId,
3511
- platform: context.platform,
3512
- messageId: message.id,
3513
- error: error instanceof Error ? error.message : String(error)
3514
- }
3515
- );
3516
- }
3517
- }
3518
- }
3519
- };
3520
- const decodeWebhookEvent = (bodyBytes) => {
3521
- try {
3522
- return RawInboundEvent.decode(bodyBytes);
3523
- } catch (error) {
3524
- lifecycleLog.warn("spectrum.webhook: undecodable RawInboundEvent body", {
3525
- error: error instanceof Error ? error.message : String(error)
3526
- });
3527
- return null;
3528
- }
3529
- };
3530
- const processWebhookEvent = async (core, event, handler) => {
3531
- const collected = [];
3532
- const reply2 = await core.processEvent(event, (record) => {
3533
- collected.push(record);
3534
- });
3535
- if (reply2.errorReason) {
3536
- return {
3537
- status: 400,
3538
- headers: reply2.headers ?? {},
3539
- body: encodeText(reply2.errorReason)
3540
- };
3541
- }
3542
- const result = {
3543
- status: reply2.status === 0 ? 200 : reply2.status,
3544
- headers: reply2.headers ?? {},
3545
- body: reply2.body ?? new Uint8Array(0)
3546
- };
3547
- const runtime = platformStates.get(event.platform);
3548
- if (runtime && collected.length > 0) {
3549
- deliverWebhookMessages(collected, runtime, handler, event).catch(
3550
- (error) => {
3551
- lifecycleLog.error(
3552
- `spectrum.webhook: delivery failed (async), ${error}`,
3553
- {
3554
- eventId: event.eventId,
3555
- platform: event.platform,
3556
- error: error instanceof Error ? error.message : String(error)
3557
- }
3558
- );
3559
- }
3560
- );
3561
- }
3562
- return result;
3563
- };
3564
- const looksLikeNativePayload = (bodyBytes) => {
3565
- for (const byte of bodyBytes) {
3566
- if (byte === 32 || byte === 9 || byte === 10 || byte === 13) {
3567
- continue;
3568
- }
3569
- return byte === 123;
3570
- }
3571
- return false;
3572
- };
3573
- const webhookText = (status, text2) => ({
3574
- status,
3575
- headers: {},
3576
- body: encodeText(text2)
3577
- });
3578
- const resolveWebhookAttachment = (platform, spaceRef, attachmentId) => {
3579
- const runtime = platformStates.get(platform);
3580
- const action = runtime?.definition?.actions?.getAttachment;
3581
- if (!runtime || typeof action !== "function") {
3582
- return;
3583
- }
3584
- const getAttachment = action;
3585
- const phone = typeof spaceRef.phone === "string" ? spaceRef.phone : void 0;
3586
- let cached;
3587
- const fetchOnce = () => {
3588
- cached ??= getAttachment(
3589
- {
3590
- client: runtime.client,
3591
- config: runtime.config,
3592
- store: runtime.store
3593
- },
3594
- attachmentId,
3595
- phone
3596
- );
3597
- return cached;
3598
- };
3599
- return {
3600
- read: async () => {
3601
- const found = await fetchOnce();
3602
- if (!found) {
3603
- throw new Error(
3604
- `Spectrum webhook attachment "${attachmentId}" not found on "${platform}"`
3605
- );
3606
- }
3607
- return found.read();
3608
- },
3609
- stream: async () => {
3610
- const found = await fetchOnce();
3611
- if (!found?.stream) {
3612
- throw new Error(
3613
- `Spectrum webhook attachment "${attachmentId}" has no stream on "${platform}"`
3614
- );
3615
- }
3616
- return found.stream();
3617
- }
3618
- };
3619
- };
3620
- const handleSpectrumWebhook = async (bodyBytes, headers, handler) => {
3621
- if (!resolvedWebhookSecret) {
3622
- lifecycleLog.error(
3623
- "spectrum.webhook: received a signed Spectrum webhook but no webhookSecret is configured (set Spectrum({ webhookSecret }) or SPECTRUM_WEBHOOK_SECRET)"
3624
- );
3625
- return webhookText(500, "webhook secret not configured");
3626
- }
3627
- const verification = verifySpectrumSignature({
3628
- rawBody: bodyBytes,
3629
- headers,
3630
- secret: resolvedWebhookSecret
3631
- });
3632
- if (!verification.ok) {
3633
- const status = verification.reason === "missing-headers" ? 400 : 401;
3634
- return webhookText(status, verification.reason);
3635
- }
3636
- let envelope;
3637
- try {
3638
- const parsed = JSON.parse(new TextDecoder().decode(bodyBytes));
3639
- envelope = slimEnvelopeSchema.parse(parsed);
3640
- } catch (error) {
3641
- lifecycleLog.warn(
3642
- `spectrum.webhook: malformed Spectrum webhook payload, ${error}`
3643
- );
3644
- return webhookText(400, "malformed payload");
3645
- }
3646
- const deserialized = deserializeSpectrumMessage(envelope, {
3647
- resolveAttachment: resolveWebhookAttachment
3648
- });
3649
- if (!deserialized) {
3650
- return webhookText(200, "ok");
3651
- }
3652
- const { platform, record } = deserialized;
3653
- const runtime = platformStates.get(platform);
3654
- if (!runtime) {
3655
- lifecycleLog.warn(
3656
- `spectrum.webhook: no provider configured for platform "${platform}"; acknowledging without delivery`,
3657
- { platform }
3658
- );
3659
- return webhookText(200, "ok");
3660
- }
3661
- deliverWebhookMessages([record], runtime, handler, { platform }).catch(
3662
- (error) => {
3663
- lifecycleLog.error(
3664
- `spectrum.webhook: Spectrum delivery failed (async), ${error}`,
3665
- {
3666
- platform,
3667
- messageId: record.id,
3668
- error: error instanceof Error ? error.message : String(error)
3669
- }
3670
- );
3671
- }
3672
- );
3673
- return webhookText(200, "ok");
3674
- };
3675
- const handleWebhook = async (request, handler) => {
3676
- const { asWeb, bodyBytes, headers } = await readWebhookInput(request);
3677
- if (looksLikeNativePayload(bodyBytes)) {
3678
- const spectrumResult = await handleSpectrumWebhook(
3679
- bodyBytes,
3680
- headers,
3681
- handler
3682
- );
3683
- return buildWebhookResult(asWeb, spectrumResult);
3684
- }
3685
- if (!fusorCore) {
3686
- throw new Error(
3687
- "spectrum.webhook() received a non-Spectrum (fusor) request but no fusor provider is configured"
3688
- );
3689
- }
3690
- const event = decodeWebhookEvent(bodyBytes);
3691
- if (!event) {
3692
- return buildWebhookResult(asWeb, {
3693
- status: 400,
3694
- headers: {},
3695
- body: new Uint8Array(0)
3696
- });
3697
- }
3698
- const result = await processWebhookEvent(fusorCore, event, handler);
3699
- return buildWebhookResult(asWeb, result);
3700
- };
3701
- const base = {
3702
- __providers: providers,
3703
- __internal: { platforms: platformStates },
3704
- config: projectConfig,
3705
- messages,
3706
- stop: stopOnce,
3707
- webhook: handleWebhook,
3708
- send: (async (space, ...content) => content.length === 1 ? await space.send(content[0]) : await space.send(
3709
- ...content
3710
- )),
3711
- edit: async (message, newContent) => {
3712
- await message.edit(newContent);
3713
- },
3714
- responding: async (space, fn) => space.responding(fn)
3715
- };
3716
- return new Proxy(base, {
3717
- get(target, prop, receiver) {
3718
- if (prop in target) {
3719
- return Reflect.get(target, prop, receiver);
3720
- }
3721
- if (typeof prop === "string") {
3722
- return customEventProxy[prop];
3723
- }
3724
- return;
3725
- }
3726
- });
3727
- }
3728
- export {
3729
- Emoji,
3730
- Spectrum,
3731
- SpectrumCloudError,
3732
- UnsupportedError,
3733
- attachment,
3734
- avatar,
3735
- broadcast,
3736
- cloud,
3737
- contact,
3738
- custom,
3739
- definePlatform,
3740
- edit,
3741
- fromVCard,
3742
- fusor,
3743
- fusorEvent,
3744
- group,
3745
- isFusorClient,
3746
- isFusorEvent,
3747
- markdown,
3748
- mergeStreams,
3749
- option,
3750
- poll,
3751
- reaction,
3752
- read,
3753
- rename,
3754
- reply,
3755
- resolveContents,
3756
- richlink,
3757
- stream,
3758
- text,
3759
- toVCard,
3760
- typing,
3761
- unsend,
3762
- voice
3763
- };
1
+ export * from "@spectrum-ts/core";
2
+ export {};