@lootlog/sdk 0.1.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.
@@ -0,0 +1,1443 @@
1
+ import { Result, Schema, SchemaTransformation } from "effect";
2
+ import * as Schema$1 from "effect/Schema";
3
+ import { TaggedError } from "effect/Schema";
4
+ import { decode, encode } from "@msgpack/msgpack";
5
+ //#region ../schema/src/npc-type.ts
6
+ const NpcTypeEnum = {
7
+ COMMON: "COMMON",
8
+ ELITE: "ELITE",
9
+ ELITE2: "ELITE2",
10
+ ELITE3: "ELITE3",
11
+ HERO: "HERO",
12
+ EVENT_HERO: "EVENT_HERO",
13
+ COLOSSUS: "COLOSSUS",
14
+ TITAN: "TITAN",
15
+ NPC: "NPC"
16
+ };
17
+ const NpcTypeSchema = Schema.Literals([
18
+ NpcTypeEnum.COMMON,
19
+ NpcTypeEnum.ELITE,
20
+ NpcTypeEnum.ELITE2,
21
+ NpcTypeEnum.ELITE3,
22
+ NpcTypeEnum.HERO,
23
+ NpcTypeEnum.EVENT_HERO,
24
+ NpcTypeEnum.COLOSSUS,
25
+ NpcTypeEnum.TITAN,
26
+ NpcTypeEnum.NPC
27
+ ]);
28
+ //#endregion
29
+ //#region ../schema/src/permissions.ts
30
+ const Capability = {
31
+ OWNER: "OWNER",
32
+ ADMIN: "ADMIN",
33
+ LOOTLOG_MANAGE: "LOOTLOG_MANAGE",
34
+ LOOTLOG_ACCESS: "LOOTLOG_ACCESS",
35
+ LOOTLOG_LOOTS_READ: "LOOTLOG_LOOTS_READ",
36
+ LOOTLOG_LOOTS_WRITE: "LOOTLOG_LOOTS_WRITE",
37
+ LOOTLOG_LOOTS_ARCHIVE: "LOOTLOG_LOOTS_ARCHIVE",
38
+ LOOTLOG_LOOTS_TITANS_READ: "LOOTLOG_LOOTS_TITANS_READ",
39
+ LOOTLOG_LOOTS_HEROES_READ: "LOOTLOG_LOOTS_HEROES_READ",
40
+ LOOTLOG_TIMERS_READ: "LOOTLOG_TIMERS_READ",
41
+ LOOTLOG_TIMERS_WRITE: "LOOTLOG_TIMERS_WRITE",
42
+ LOOTLOG_TIMERS_RESET: "LOOTLOG_TIMERS_RESET",
43
+ LOOTLOG_TIMERS_DELETE: "LOOTLOG_TIMERS_DELETE",
44
+ LOOTLOG_TIMERS_TITANS_READ: "LOOTLOG_TIMERS_TITANS_READ",
45
+ LOOTLOG_TIMERS_HEROES_READ: "LOOTLOG_TIMERS_HEROES_READ",
46
+ LOOTLOG_RESERVATIONS_READ: "LOOTLOG_RESERVATIONS_READ",
47
+ LOOTLOG_RESERVATIONS_WRITE: "LOOTLOG_RESERVATIONS_WRITE",
48
+ LOOTLOG_MEMBERS_READ: "LOOTLOG_MEMBERS_READ",
49
+ LOOTLOG_ONLINE_PLAYERS_READ: "LOOTLOG_ONLINE_PLAYERS_READ",
50
+ LOOTLOG_PRESENCE_LOCATION_READ: "LOOTLOG_PRESENCE_LOCATION_READ",
51
+ LOOTLOG_CHAT_READ: "LOOTLOG_CHAT_READ",
52
+ LOOTLOG_CHAT_WRITE: "LOOTLOG_CHAT_WRITE",
53
+ LOOTLOG_CHAT_TITANS_READ: "LOOTLOG_CHAT_TITANS_READ",
54
+ LOOTLOG_CHAT_HEROES_READ: "LOOTLOG_CHAT_HEROES_READ",
55
+ LOOTLOG_NOTIFICATIONS_READ: "LOOTLOG_NOTIFICATIONS_READ",
56
+ LOOTLOG_NOTIFICATIONS_SEND: "LOOTLOG_NOTIFICATIONS_SEND",
57
+ LOOTLOG_NOTIFICATIONS_TITANS_READ: "LOOTLOG_NOTIFICATIONS_TITANS_READ",
58
+ LOOTLOG_NOTIFICATIONS_HEROES_READ: "LOOTLOG_NOTIFICATIONS_HEROES_READ",
59
+ LOOTLOG_EVENTS_MANAGE: "LOOTLOG_EVENTS_MANAGE",
60
+ LOOTLOG_EVENTS_READ: "LOOTLOG_EVENTS_READ",
61
+ LOOTLOG_EVENTS_WRITE: "LOOTLOG_EVENTS_WRITE",
62
+ LOOTLOG_DOCS_READ: "LOOTLOG_DOCS_READ",
63
+ LOOTLOG_DOCS_WRITE: "LOOTLOG_DOCS_WRITE"
64
+ };
65
+ const Permission = Capability;
66
+ const PermissionSchema = Schema.Literals(Object.values(Capability));
67
+ const UserGuildPermissionsRoleSchema = Schema.Struct({
68
+ id: Schema.NonEmptyString,
69
+ lvlRangeFrom: Schema.Int,
70
+ lvlRangeTo: Schema.Int,
71
+ permissions: Schema.Array(PermissionSchema)
72
+ });
73
+ const UserGuildPermissionsGuildSchema = Schema.Struct({
74
+ id: Schema.NonEmptyString,
75
+ ownerId: Schema.NonEmptyString
76
+ });
77
+ Schema.Struct({
78
+ guild: UserGuildPermissionsGuildSchema,
79
+ roles: Schema.Array(UserGuildPermissionsRoleSchema)
80
+ });
81
+ //#endregion
82
+ //#region ../protocol/src/realtime/access-policy.ts
83
+ const LevelRange = Schema.Struct({
84
+ from: Schema.Int,
85
+ to: Schema.Int
86
+ });
87
+ const AccessPolicyArea = Schema.Literals([
88
+ "timers",
89
+ "chat",
90
+ "notifications",
91
+ "presence",
92
+ "reservations",
93
+ "loots",
94
+ "events",
95
+ "organization"
96
+ ]);
97
+ const OrganizationAccessPolicy = Schema.Struct({
98
+ organizationId: Schema.NonEmptyString,
99
+ owner: Schema.Boolean,
100
+ permissions: Schema.Array(PermissionSchema),
101
+ grants: Schema.Array(Schema.Struct({
102
+ permission: PermissionSchema,
103
+ ranges: Schema.Array(LevelRange)
104
+ }))
105
+ });
106
+ const AccessPolicySnapshot = Schema.Struct({
107
+ version: Schema.NonEmptyString,
108
+ organizations: Schema.Array(OrganizationAccessPolicy)
109
+ });
110
+ Schema.is(AccessPolicySnapshot);
111
+ const AccessPolicyChange = Schema.Struct({
112
+ organizationId: Schema.NonEmptyString,
113
+ areas: Schema.Array(AccessPolicyArea),
114
+ restricted: Schema.Boolean,
115
+ expanded: Schema.Boolean
116
+ });
117
+ const NPC_PERMISSIONS = {
118
+ timers: [
119
+ Permission.LOOTLOG_TIMERS_READ,
120
+ Permission.LOOTLOG_TIMERS_HEROES_READ,
121
+ Permission.LOOTLOG_TIMERS_TITANS_READ
122
+ ],
123
+ chat: [
124
+ Permission.LOOTLOG_CHAT_READ,
125
+ Permission.LOOTLOG_CHAT_HEROES_READ,
126
+ Permission.LOOTLOG_CHAT_TITANS_READ
127
+ ],
128
+ notifications: [
129
+ Permission.LOOTLOG_NOTIFICATIONS_READ,
130
+ Permission.LOOTLOG_NOTIFICATIONS_HEROES_READ,
131
+ Permission.LOOTLOG_NOTIFICATIONS_TITANS_READ
132
+ ],
133
+ loots: [
134
+ Permission.LOOTLOG_LOOTS_READ,
135
+ Permission.LOOTLOG_LOOTS_HEROES_READ,
136
+ Permission.LOOTLOG_LOOTS_TITANS_READ
137
+ ]
138
+ };
139
+ Object.values(NPC_PERMISSIONS).flat();
140
+ new Set(NPC_PERMISSIONS.loots);
141
+ Object.values(Permission).sort();
142
+ new Set(Object.values(NpcTypeEnum));
143
+ //#endregion
144
+ //#region ../schema/src/item-rarity.ts
145
+ const ItemRarityEnum = {
146
+ UNIQUE: "UNIQUE",
147
+ HEROIC: "HEROIC",
148
+ LEGENDARY: "LEGENDARY",
149
+ UPGRADED: "UPGRADED"
150
+ };
151
+ const ItemRaritySchema = Schema.Literals([
152
+ ItemRarityEnum.UNIQUE,
153
+ ItemRarityEnum.HEROIC,
154
+ ItemRarityEnum.LEGENDARY,
155
+ ItemRarityEnum.UPGRADED
156
+ ]);
157
+ //#endregion
158
+ //#region ../schema/src/loot.ts
159
+ const LootSourceEnum = {
160
+ LOOTBOX: "LOOTBOX",
161
+ DIALOG: "DIALOG",
162
+ FIGHT: "FIGHT"
163
+ };
164
+ Schema.Literals([
165
+ LootSourceEnum.LOOTBOX,
166
+ LootSourceEnum.DIALOG,
167
+ LootSourceEnum.FIGHT
168
+ ]);
169
+ const LootShareSourceEnum = {
170
+ NONE: "NONE",
171
+ ITEM_OWNER: "ITEM_OWNER",
172
+ CHAT_MESSAGE: "CHAT_MESSAGE"
173
+ };
174
+ Schema.Literals([
175
+ LootShareSourceEnum.NONE,
176
+ LootShareSourceEnum.ITEM_OWNER,
177
+ LootShareSourceEnum.CHAT_MESSAGE
178
+ ]);
179
+ const ProfessionEnum = {
180
+ WARRIOR: "WARRIOR",
181
+ PALADIN: "PALADIN",
182
+ HUNTER: "HUNTER",
183
+ MAGE: "MAGE",
184
+ BLADE_DANCER: "BLADE_DANCER",
185
+ TRACKER: "TRACKER"
186
+ };
187
+ const ProfessionSchema = Schema.Literals([
188
+ ProfessionEnum.WARRIOR,
189
+ ProfessionEnum.PALADIN,
190
+ ProfessionEnum.HUNTER,
191
+ ProfessionEnum.MAGE,
192
+ ProfessionEnum.BLADE_DANCER,
193
+ ProfessionEnum.TRACKER
194
+ ]);
195
+ const ItemTypeEnum = {
196
+ ONE_HAND_WEAPON: "ONE_HAND_WEAPON",
197
+ TWO_HAND_WEAPON: "TWO_HAND_WEAPON",
198
+ ONE_AND_HALF_HAND_WEAPON: "ONE_AND_HALF_HAND_WEAPON",
199
+ DISTANCE_WEAPON: "DISTANCE_WEAPON",
200
+ HELP_WEAPON: "HELP_WEAPON",
201
+ WAND_WEAPON: "WAND_WEAPON",
202
+ ORB_WEAPON: "ORB_WEAPON",
203
+ ARMOR: "ARMOR",
204
+ HELMET: "HELMET",
205
+ BOOTS: "BOOTS",
206
+ GLOVES: "GLOVES",
207
+ RING: "RING",
208
+ NECKLACE: "NECKLACE",
209
+ SHIELD: "SHIELD",
210
+ NEUTRAL: "NEUTRAL",
211
+ CONSUME: "CONSUME",
212
+ GOLD: "GOLD",
213
+ KEYS: "KEYS",
214
+ QUEST: "QUEST",
215
+ RENEWABLE: "RENEWABLE",
216
+ ARROWS: "ARROWS",
217
+ TALISMAN: "TALISMAN",
218
+ BOOK: "BOOK",
219
+ BAG: "BAG",
220
+ BLESS: "BLESS",
221
+ UPGRADE: "UPGRADE",
222
+ RECIPE: "RECIPE",
223
+ COINAGE: "COINAGE",
224
+ QUIVER: "QUIVER",
225
+ OUTFITS: "OUTFITS",
226
+ PETS: "PETS",
227
+ TELEPORTS: "TELEPORTS"
228
+ };
229
+ Schema.Literals([
230
+ ItemTypeEnum.ONE_HAND_WEAPON,
231
+ ItemTypeEnum.TWO_HAND_WEAPON,
232
+ ItemTypeEnum.ONE_AND_HALF_HAND_WEAPON,
233
+ ItemTypeEnum.DISTANCE_WEAPON,
234
+ ItemTypeEnum.HELP_WEAPON,
235
+ ItemTypeEnum.WAND_WEAPON,
236
+ ItemTypeEnum.ORB_WEAPON,
237
+ ItemTypeEnum.ARMOR,
238
+ ItemTypeEnum.HELMET,
239
+ ItemTypeEnum.BOOTS,
240
+ ItemTypeEnum.GLOVES,
241
+ ItemTypeEnum.RING,
242
+ ItemTypeEnum.NECKLACE,
243
+ ItemTypeEnum.SHIELD,
244
+ ItemTypeEnum.NEUTRAL,
245
+ ItemTypeEnum.CONSUME,
246
+ ItemTypeEnum.GOLD,
247
+ ItemTypeEnum.KEYS,
248
+ ItemTypeEnum.QUEST,
249
+ ItemTypeEnum.RENEWABLE,
250
+ ItemTypeEnum.ARROWS,
251
+ ItemTypeEnum.TALISMAN,
252
+ ItemTypeEnum.BOOK,
253
+ ItemTypeEnum.BAG,
254
+ ItemTypeEnum.BLESS,
255
+ ItemTypeEnum.UPGRADE,
256
+ ItemTypeEnum.RECIPE,
257
+ ItemTypeEnum.COINAGE,
258
+ ItemTypeEnum.QUIVER,
259
+ ItemTypeEnum.OUTFITS,
260
+ ItemTypeEnum.PETS,
261
+ ItemTypeEnum.TELEPORTS
262
+ ]);
263
+ //#endregion
264
+ //#region ../schema/src/http-scalars.ts
265
+ const FiniteNumber = Schema$1.Number.check(Schema$1.isFinite().annotate({ expected: "a finite number" }));
266
+ const dateTimeString = (pattern) => Schema$1.String.annotate({ format: "date-time" }).check(Schema$1.isPattern(new RegExp(pattern)).annotate({ expected: `a string matching the RegExp ${pattern}` }));
267
+ dateTimeString("^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$");
268
+ const DateTimeWithOffsetString = dateTimeString("^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$");
269
+ Schema$1.String.check(Schema$1.isMinLength(1).annotate({ expected: "a value with a length of at least 1" }));
270
+ Schema$1.Json.annotate({ expected: "JSON value" });
271
+ Schema$1.Number.check(Schema$1.isInt().annotate({ expected: "an integer" })).check(Schema$1.isGreaterThanOrEqualTo(-9007199254740991).annotate({ expected: "a value greater than or equal to -9007199254740991" })).check(Schema$1.isLessThanOrEqualTo(9007199254740991).annotate({ expected: "a value less than or equal to 9007199254740991" }));
272
+ const PositiveSafeInteger = Schema$1.Number.check(Schema$1.isInt().annotate({ expected: "an integer" })).check(Schema$1.isGreaterThanOrEqualTo(1).annotate({ expected: "a value greater than or equal to 1" })).check(Schema$1.isLessThanOrEqualTo(9007199254740991).annotate({ expected: "a value less than or equal to 9007199254740991" }));
273
+ Schema$1.Number.check(Schema$1.isInt().annotate({ expected: "an integer" })).check(Schema$1.isGreaterThanOrEqualTo(0).annotate({ expected: "a value greater than or equal to 0" })).check(Schema$1.isLessThanOrEqualTo(9007199254740991).annotate({ expected: "a value less than or equal to 9007199254740991" }));
274
+ Schema$1.Number.check(Schema$1.isInt().annotate({ expected: "an integer" })).check(Schema$1.isGreaterThanOrEqualTo(0).annotate({ expected: "a value greater than or equal to 0" })).check(Schema$1.isLessThanOrEqualTo(500).annotate({ expected: "a value less than or equal to 500" }));
275
+ Schema$1.Number.check(Schema$1.isInt().annotate({ expected: "an integer" })).check(Schema$1.isGreaterThanOrEqualTo(1).annotate({ expected: "a value greater than or equal to 1" })).check(Schema$1.isLessThanOrEqualTo(100).annotate({ expected: "a value less than or equal to 100" }));
276
+ //#endregion
277
+ //#region ../protocol/src/loot-summary.ts
278
+ const LootItemResponse = Schema.Struct({
279
+ id: FiniteNumber,
280
+ hid: Schema.String,
281
+ name: Schema.String,
282
+ icon: Schema.String,
283
+ stat: Schema.String,
284
+ type: Schema.NullOr(Schema.String),
285
+ rarity: Schema.NullOr(ItemRaritySchema),
286
+ lvl: FiniteNumber,
287
+ prof: Schema.Array(ProfessionSchema)
288
+ });
289
+ const LootPlayerResponse = Schema.Struct({
290
+ id: Schema.Union([Schema.String, FiniteNumber]),
291
+ name: Schema.String,
292
+ lvl: Schema.NullOr(FiniteNumber),
293
+ prof: Schema.NullOr(ProfessionSchema),
294
+ icon: Schema.NullOr(Schema.String),
295
+ characterId: Schema.NullOr(FiniteNumber),
296
+ accountId: Schema.NullOr(FiniteNumber),
297
+ hpp: Schema.NullOr(FiniteNumber)
298
+ });
299
+ const LootNpcResponse = Schema.Struct({
300
+ id: FiniteNumber,
301
+ name: Schema.String,
302
+ wt: Schema.NullOr(FiniteNumber),
303
+ lvl: Schema.NullOr(FiniteNumber),
304
+ prof: Schema.NullOr(ProfessionSchema),
305
+ icon: Schema.NullOr(Schema.String),
306
+ type: Schema.NullOr(NpcTypeSchema),
307
+ margonemType: Schema.NullOr(FiniteNumber)
308
+ });
309
+ const LootShareResponse = Schema.Record(Schema.String, Schema.Array(Schema.String));
310
+ const LootSummary = Schema.Struct({
311
+ items: Schema.Array(LootItemResponse),
312
+ players: Schema.Array(LootPlayerResponse),
313
+ npcs: Schema.Array(LootNpcResponse),
314
+ lootShare: LootShareResponse,
315
+ location: Schema.String
316
+ });
317
+ //#endregion
318
+ //#region ../protocol/src/feed.ts
319
+ const FeedNpc = Schema.Struct({
320
+ id: FiniteNumber,
321
+ name: Schema.String,
322
+ type: Schema.NullOr(Schema.String),
323
+ lvl: Schema.NullOr(FiniteNumber),
324
+ icon: Schema.NullOr(Schema.String),
325
+ prof: Schema.optionalKey(Schema.NullOr(Schema.String))
326
+ });
327
+ const common = {
328
+ id: Schema.String,
329
+ version: PositiveSafeInteger,
330
+ groupKey: Schema.optionalKey(Schema.String),
331
+ occurredAt: Schema.String,
332
+ world: Schema.String,
333
+ guild: Schema.Struct({
334
+ id: Schema.String,
335
+ name: Schema.String,
336
+ vanityUrl: Schema.NullOr(Schema.String)
337
+ }),
338
+ npc: Schema.NullOr(FeedNpc)
339
+ };
340
+ const UserFeedItem = Schema.Union([Schema.Struct({
341
+ ...common,
342
+ type: Schema.Literal("kill"),
343
+ npc: FeedNpc,
344
+ count: PositiveSafeInteger
345
+ }), Schema.Struct({
346
+ ...common,
347
+ type: Schema.Literal("loot"),
348
+ lootId: PositiveSafeInteger,
349
+ summary: Schema.optionalKey(LootSummary),
350
+ additionalItemsCount: FiniteNumber,
351
+ items: Schema.Array(Schema.Struct({
352
+ id: FiniteNumber,
353
+ name: Schema.String,
354
+ icon: Schema.String,
355
+ rarity: Schema.NullOr(Schema.String),
356
+ stat: Schema.optionalKey(Schema.String),
357
+ type: Schema.optionalKey(Schema.NullOr(Schema.String)),
358
+ lvl: Schema.optionalKey(Schema.NullOr(FiniteNumber))
359
+ }))
360
+ })]);
361
+ //#endregion
362
+ //#region ../schema/src/primitives.ts
363
+ const NonEmptyString = Schema.NonEmptyString;
364
+ const NonNegativeInt = Schema.Int.check(Schema.isGreaterThanOrEqualTo(0));
365
+ Schema.String.annotate({ format: "date-time" }).pipe(Schema.check(Schema.isPattern(/^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$/)), Schema.decodeTo(Schema.Date, SchemaTransformation.dateFromString));
366
+ //#endregion
367
+ //#region ../schema/src/air-tag.ts
368
+ const AIR_TAG_RELATIONS = [
369
+ 1,
370
+ 2,
371
+ 3,
372
+ 4,
373
+ 5,
374
+ 6,
375
+ 7,
376
+ 8
377
+ ];
378
+ const AIR_TAG_MAX_COORDINATE = 65535;
379
+ const ShortString = Schema.NonEmptyString.check(Schema.isMaxLength(64));
380
+ const GuildId = Schema.NonEmptyString.check(Schema.isMaxLength(128));
381
+ const SafeNatural = Schema.Int.check(Schema.isBetween({
382
+ minimum: 0,
383
+ maximum: Number.MAX_SAFE_INTEGER
384
+ }));
385
+ const Coordinate = Schema.Int.check(Schema.isBetween({
386
+ minimum: 0,
387
+ maximum: AIR_TAG_MAX_COORDINATE
388
+ }));
389
+ const AirTagRelationSchema = Schema.Literals(AIR_TAG_RELATIONS);
390
+ const AirTagClanSchema = Schema.Struct({
391
+ id: SafeNatural,
392
+ name: ShortString
393
+ });
394
+ const AirTagObservationSchema = Schema.Struct({
395
+ targetId: ShortString,
396
+ nickname: ShortString,
397
+ clan: Schema.optionalKey(AirTagClanSchema),
398
+ relation: AirTagRelationSchema,
399
+ x: Coordinate,
400
+ y: Coordinate
401
+ });
402
+ const AirTagObservationBatchSchema = Schema.Struct({
403
+ expectedMapId: Coordinate,
404
+ observations: Schema.Array(AirTagObservationSchema)
405
+ });
406
+ Schema.Struct({
407
+ requestId: ShortString,
408
+ enabled: Schema.Boolean,
409
+ expectedMapId: Schema.optionalKey(Coordinate)
410
+ });
411
+ const AirTagTargetSchema = Schema.Struct({
412
+ targetId: ShortString,
413
+ nickname: ShortString,
414
+ clan: Schema.optionalKey(AirTagClanSchema),
415
+ relation: AirTagRelationSchema,
416
+ x: Coordinate,
417
+ y: Coordinate,
418
+ observedAt: SafeNatural,
419
+ enemyObservedAt: Schema.optionalKey(SafeNatural),
420
+ clanEnemyObservedAt: Schema.optionalKey(SafeNatural)
421
+ });
422
+ const AirTagScopeIdentityFields = {
423
+ guildId: GuildId,
424
+ world: ShortString,
425
+ mapId: Coordinate,
426
+ epochId: ShortString,
427
+ epochStartedAt: SafeNatural,
428
+ revision: SafeNatural
429
+ };
430
+ const AirTagScopeSnapshotSchema = Schema.Struct({
431
+ ...AirTagScopeIdentityFields,
432
+ targets: Schema.Array(AirTagTargetSchema)
433
+ });
434
+ const AirTagUpdateEventSchema = Schema.Struct({
435
+ ...AirTagScopeIdentityFields,
436
+ target: AirTagTargetSchema
437
+ });
438
+ Schema.is(AirTagRelationSchema);
439
+ Schema.is(AirTagObservationSchema);
440
+ Schema.is(AirTagTargetSchema);
441
+ Schema.is(AirTagScopeSnapshotSchema);
442
+ Schema.is(AirTagUpdateEventSchema);
443
+ const MapPingTypeSchema = Schema.Literals([
444
+ "attention",
445
+ "enemy",
446
+ "regroup",
447
+ "avoid"
448
+ ]);
449
+ const MapPingRejectCodeSchema = Schema.Literals([
450
+ "forbidden",
451
+ "invalid-context",
452
+ "invalid-payload",
453
+ "rate-limited",
454
+ "temporarily-unavailable"
455
+ ]);
456
+ Schema.is(MapPingTypeSchema);
457
+ const MapPingSendPayloadSchema = Schema.Struct({
458
+ expectedMapId: NonNegativeInt,
459
+ type: MapPingTypeSchema,
460
+ x: NonNegativeInt,
461
+ y: NonNegativeInt
462
+ });
463
+ const MapPingAckSchema = Schema.Union([Schema.Struct({
464
+ status: Schema.Literal("accepted"),
465
+ pingId: Schema.NonEmptyString
466
+ }), Schema.Struct({
467
+ status: Schema.Literal("rejected"),
468
+ code: MapPingRejectCodeSchema,
469
+ retryAfterMs: Schema.optionalKey(NonNegativeInt)
470
+ })]);
471
+ const MapPingEventSchema = Schema.Struct({
472
+ pingId: Schema.NonEmptyString,
473
+ world: Schema.NonEmptyString,
474
+ mapId: NonNegativeInt,
475
+ type: MapPingTypeSchema,
476
+ x: NonNegativeInt,
477
+ y: NonNegativeInt,
478
+ sender: Schema.Struct({
479
+ characterId: Schema.NonEmptyString,
480
+ name: Schema.NonEmptyString
481
+ }),
482
+ createdAt: NonNegativeInt
483
+ });
484
+ //#endregion
485
+ //#region ../schema/src/discord.ts
486
+ const discordPermissionFields = {
487
+ hasRequiredPermissions: Schema.Boolean,
488
+ requiredPermissions: Schema.Array(Schema.String),
489
+ grantedPermissions: Schema.Array(Schema.String),
490
+ missingPermissions: Schema.Array(Schema.String)
491
+ };
492
+ const DiscordGuildSyncStatus = Schema.Literals([
493
+ "SYNCED",
494
+ "SYNCING",
495
+ "FAILED",
496
+ "STALE",
497
+ "NOT_FOUND"
498
+ ]);
499
+ //#endregion
500
+ //#region ../protocol/src/rabbit/topology.ts
501
+ const RabbitExchange = {
502
+ DEFAULT: "default",
503
+ DEAD_LETTER: "dlx",
504
+ RETRY: "retry"
505
+ };
506
+ const RabbitExchangeName = Schema.Literals([
507
+ RabbitExchange.DEFAULT,
508
+ RabbitExchange.DEAD_LETTER,
509
+ RabbitExchange.RETRY
510
+ ]);
511
+ const RabbitRoutingKey = {
512
+ USERS_ONLINE_CHECKPOINT_V1: "users.online.checkpoint.v1",
513
+ USERS_ONLINE_CHECKPOINT_V1_RETRY: "users.online.checkpoint.v1.retry",
514
+ USERS_ONLINE_CHECKPOINT_V1_DLQ: "users.online.checkpoint.v1.dlq",
515
+ ACTIVITY_LOG_CREATE: "activity.log.create",
516
+ ACTIVITY_LOG_CREATE_DLQ: "activity.log.create.dlq",
517
+ ACTIVITY_LOG_CREATE_RETRY: "activity.log.create.retry",
518
+ DISCORD_GUILD_CHANNEL_DELETED: "discord.guild.channel.deleted",
519
+ DISCORD_GUILD_CHANNEL_UPSERTED: "discord.guild.channel.upserted",
520
+ DISCORD_GUILD_CHANNELS_SYNC_FAILED: "discord.guild.channels.sync.failed",
521
+ DISCORD_GUILD_CHANNELS_SYNCED: "discord.guild.channels.synced",
522
+ DISCORD_GUILD_SYNC_STATE_UPDATED: "discord.guild.sync-state.updated",
523
+ EVENT_HERO_KILLED: "event.hero.killed",
524
+ EVENT_MAP_STATUS_UPDATE: "event.map-status.update",
525
+ EVENT_RANKING_UPDATE: "event.ranking.update",
526
+ EVENT_RESPAWN_WINDOW_CLOSED: "event.respawn-window.closed",
527
+ EVENT_RESPAWN_WINDOW_OPENED: "event.respawn-window.opened",
528
+ GUILDS_CLEAR_MESSAGES: "guilds.clear.messages",
529
+ GUILDS_CREATE: "guilds.create",
530
+ GUILDS_CREATE_DLQ: "guilds.create.dlq",
531
+ GUILDS_CREATE_RETRY: "guilds.create.retry",
532
+ GUILDS_CREATE_ROLE: "guilds.create.role",
533
+ GUILDS_CREATE_ROLE_DLQ: "guilds.create.role.dlq",
534
+ GUILDS_CREATE_ROLE_RETRY: "guilds.create.role.retry",
535
+ GUILDS_DELETE: "guilds.delete",
536
+ GUILDS_DELETE_DLQ: "guilds.delete.dlq",
537
+ GUILDS_DELETE_MESSAGE: "guilds.delete.message",
538
+ GUILDS_DELETE_RETRY: "guilds.delete.retry",
539
+ GUILDS_DELETE_ROLE: "guilds.delete.role",
540
+ GUILDS_DELETE_ROLE_DLQ: "guilds.delete.role.dlq",
541
+ GUILDS_DELETE_ROLE_RETRY: "guilds.delete.role.retry",
542
+ GUILDS_INITIALIZE: "guilds.initialize",
543
+ GUILDS_INITIALIZE_BOT: "guilds.initialize-bot",
544
+ GUILDS_INITIALIZE_DLQ: "guilds.initialize.dlq",
545
+ GUILDS_INITIALIZE_RETRY: "guilds.initialize.retry",
546
+ GUILDS_KILLS_ACCEPTED_V1: "guilds.kills.accepted.v1",
547
+ GUILDS_KILLS_ACCEPTED_V1_RETRY: "guilds.kills.accepted.v1.retry",
548
+ GUILDS_KILLS_ACCEPTED_V1_DLQ: "guilds.kills.accepted.v1.dlq",
549
+ GUILDS_LOOTS_CREATE: "guilds.loots.create",
550
+ GUILDS_LOOTS_CREATE_DLQ: "guilds.loots.create.dlq",
551
+ GUILDS_LOOTS_CREATE_RETRY: "guilds.loots.create.retry",
552
+ GUILDS_LOOTS_DELETE: "guilds.loots.delete",
553
+ GUILDS_LOOTS_SHARE_UPDATE: "guilds.loots.share.update",
554
+ GUILDS_LOOTS_SHARE_UPDATE_DLQ: "guilds.loots.share.update.dlq",
555
+ GUILDS_LOOTS_SHARE_UPDATE_RETRY: "guilds.loots.share.update.retry",
556
+ GUILDS_LOOTS_UPDATE: "guilds.loots.update",
557
+ GUILDS_MEMBERS_ADD: "guilds.members.add",
558
+ GUILDS_MEMBERS_ADD_DLQ: "guilds.members.add.dlq",
559
+ GUILDS_MEMBERS_ADD_RETRY: "guilds.members.add.retry",
560
+ GUILDS_MEMBERS_ADD_ROLE: "guilds.members.add.role",
561
+ GUILDS_MEMBERS_ADD_ROLE_DLQ: "guilds.members.add.role.dlq",
562
+ GUILDS_MEMBERS_ADD_ROLE_RETRY: "guilds.members.add.role.retry",
563
+ GUILDS_MEMBERS_REFRESH_JOB_UPDATE: "guilds.members.refresh.job.update",
564
+ GUILDS_MEMBERS_REMOVE: "guilds.members.remove",
565
+ GUILDS_MEMBERS_REMOVE_DLQ: "guilds.members.remove.dlq",
566
+ GUILDS_MEMBERS_REMOVE_RETRY: "guilds.members.remove.retry",
567
+ GUILDS_MEMBERS_REMOVE_ROLE: "guilds.members.remove.role",
568
+ GUILDS_MEMBERS_REMOVE_ROLE_DLQ: "guilds.members.remove.role.dlq",
569
+ GUILDS_MEMBERS_REMOVE_ROLE_RETRY: "guilds.members.remove.role.retry",
570
+ GUILDS_MEMBERS_UPDATE: "guilds.members.update",
571
+ GUILDS_MEMBERS_UPDATE_DLQ: "guilds.members.update.dlq",
572
+ GUILDS_MEMBERS_UPDATE_RETRY: "guilds.members.update.retry",
573
+ GUILDS_NOTIFICATIONS_SEND: "guilds.notifications.send",
574
+ GUILDS_NOTIFICATIONS_SEND_DLQ: "guilds.notifications.send.dlq",
575
+ GUILDS_NOTIFICATIONS_SEND_RETRY: "guilds.notifications.send.retry",
576
+ GUILDS_NOTIFICATIONS_VOLUNTEER: "guilds.notifications.volunteer",
577
+ GUILDS_NOTIFICATIONS_VOLUNTEER_DLQ: "guilds.notifications.volunteer.dlq",
578
+ GUILDS_NOTIFICATIONS_VOLUNTEER_RETRY: "guilds.notifications.volunteer.retry",
579
+ GUILDS_PARTY_GATHERING: "guilds.party-gathering",
580
+ GUILDS_PARTY_GATHERING_CANCEL: "guilds.party-gathering.cancel",
581
+ GUILDS_PARTY_GATHERING_DLQ: "guilds.party-gathering.dlq",
582
+ GUILDS_PARTY_GATHERING_RETRY: "guilds.party-gathering.retry",
583
+ GUILDS_RESERVATIONS_CHANGED_V2: "guilds.reservations.v2.changed",
584
+ GUILDS_RESERVATIONS_CHANGED_V2_DLQ: "guilds.reservations.v2.changed.dlq",
585
+ GUILDS_RESERVATIONS_CHANGED_V2_RETRY: "guilds.reservations.v2.changed.retry",
586
+ GUILDS_RESERVATIONS_CREATE: "guilds.reservations.create",
587
+ GUILDS_RESERVATIONS_CREATE_DLQ: "guilds.reservations.create.dlq",
588
+ GUILDS_RESERVATIONS_CREATE_RETRY: "guilds.reservations.create.retry",
589
+ GUILDS_RESERVATIONS_DELETE: "guilds.reservations.delete",
590
+ GUILDS_RESERVATIONS_DELETE_DLQ: "guilds.reservations.delete.dlq",
591
+ GUILDS_RESERVATIONS_DELETE_RETRY: "guilds.reservations.delete.retry",
592
+ GUILDS_SEND_MESSAGE: "guilds.send.message",
593
+ GUILDS_SEND_MESSAGE_DLQ: "guilds.send.message.dlq",
594
+ GUILDS_SEND_MESSAGE_RETRY: "guilds.send.message.retry",
595
+ GUILDS_SYNC: "guilds.sync",
596
+ GUILDS_SYNC_TRIGGER: "guilds.sync.trigger",
597
+ GUILDS_TIMERS_DELETE: "guilds.timers.delete",
598
+ GUILDS_TIMERS_DELETE_DLQ: "guilds.timers.delete.dlq",
599
+ GUILDS_TIMERS_DELETE_RETRY: "guilds.timers.delete.retry",
600
+ GUILDS_TIMERS_UPDATE: "guilds.timers.update",
601
+ GUILDS_TIMERS_UPDATE_DLQ: "guilds.timers.update.dlq",
602
+ GUILDS_TIMERS_UPDATE_RETRY: "guilds.timers.update.retry",
603
+ GUILDS_UPDATE: "guilds.update",
604
+ GUILDS_UPDATE_DLQ: "guilds.update.dlq",
605
+ GUILDS_UPDATE_MESSAGE: "guilds.update.message",
606
+ GUILDS_UPDATE_RETRY: "guilds.update.retry",
607
+ GUILDS_UPDATE_ROLE: "guilds.update.role",
608
+ GUILDS_UPDATE_ROLE_DLQ: "guilds.update.role.dlq",
609
+ GUILDS_UPDATE_ROLE_RETRY: "guilds.update.role.retry",
610
+ NOTIFICATIONS_DELIVERY_RESULT: "notifications.delivery.result",
611
+ NOTIFICATIONS_DISCORD_SEND: "notifications.discord.send",
612
+ NOTIFICATIONS_LOOT_CREATED: "notifications.loot.created",
613
+ NOTIFICATIONS_TIMER_DELETED: "notifications.timer.deleted",
614
+ NOTIFICATIONS_TIMER_UPDATED: "notifications.timer.updated",
615
+ GAME_CHARACTER_OFFLINE: "game.character.offline",
616
+ PRESENCE_CHECK_REQUEST: "presence.check.request",
617
+ PRESENCE_COVERAGE_CHECK: "presence.coverage.check",
618
+ SEARCH_ITEMS_INDEX: "search.items.index",
619
+ SEARCH_NPCS_INDEX: "search.npcs.index",
620
+ SEARCH_PLAYERS_INDEX: "search.players.index",
621
+ USERS_PARTY_READY_ROOM_UPDATED: "users.party-ready-room.updated",
622
+ USERS_PARTY_READY_ROOM_UPDATED_DLQ: "users.party-ready-room.updated.dlq",
623
+ USERS_PARTY_READY_ROOM_UPDATED_RETRY: "users.party-ready-room.updated.retry"
624
+ };
625
+ const RabbitRoutingKeyName = Schema.Literals(Object.values(RabbitRoutingKey));
626
+ Schema.Struct({
627
+ name: RabbitExchangeName,
628
+ type: Schema.Literal("topic"),
629
+ durable: Schema.Boolean
630
+ });
631
+ Schema.Struct({
632
+ name: Schema.String,
633
+ exchange: RabbitExchangeName,
634
+ routingKey: RabbitRoutingKeyName,
635
+ durable: Schema.Boolean,
636
+ messageTtl: Schema.optional(Schema.Int),
637
+ singleActiveConsumer: Schema.optional(Schema.Boolean),
638
+ deadLetterExchange: Schema.optional(RabbitExchangeName),
639
+ deadLetterRoutingKey: Schema.optional(RabbitRoutingKeyName)
640
+ });
641
+ RabbitExchange.DEFAULT, RabbitExchange.RETRY, RabbitExchange.DEAD_LETTER;
642
+ Schema.Struct({
643
+ userId: NonEmptyString,
644
+ discordId: NonEmptyString,
645
+ world: NonEmptyString,
646
+ characterId: NonEmptyString,
647
+ organizationIds: Schema.Array(NonEmptyString),
648
+ disconnectedAt: NonNegativeInt
649
+ });
650
+ const NullableString = Schema.NullOr(Schema.String);
651
+ const NullableNumber = Schema.NullOr(Schema.Number);
652
+ const GuildRole = Schema.Struct({
653
+ id: NonEmptyString,
654
+ name: NonEmptyString,
655
+ color: Schema.Number,
656
+ admin: Schema.Boolean,
657
+ position: Schema.Number
658
+ });
659
+ Schema.Struct({
660
+ guildId: NonEmptyString,
661
+ name: NonEmptyString,
662
+ icon: NullableString,
663
+ ownerId: NonEmptyString,
664
+ roles: Schema.Array(GuildRole)
665
+ });
666
+ Schema.Struct({
667
+ guildId: NonEmptyString,
668
+ name: NonEmptyString,
669
+ icon: NullableString,
670
+ ownerId: NonEmptyString
671
+ });
672
+ Schema.Struct({ guildId: NonEmptyString });
673
+ Schema.Struct({
674
+ guildId: NonEmptyString,
675
+ ...GuildRole.fields
676
+ });
677
+ Schema.Struct({
678
+ guildId: NonEmptyString,
679
+ id: NonEmptyString
680
+ });
681
+ Schema.Struct({
682
+ guildId: NonEmptyString,
683
+ discordId: NonEmptyString,
684
+ userId: NonEmptyString
685
+ });
686
+ Schema.Struct({
687
+ guildId: NonEmptyString,
688
+ mapName: NonEmptyString
689
+ });
690
+ Schema.Struct({
691
+ guildId: NonEmptyString,
692
+ mapName: NonEmptyString,
693
+ discordId: NonEmptyString,
694
+ hasPlayer: Schema.Boolean,
695
+ isAfk: Schema.optional(Schema.Boolean)
696
+ });
697
+ const DiscordGuildChannel = Schema.Struct({
698
+ guildId: NonEmptyString,
699
+ channelId: NonEmptyString,
700
+ name: NonEmptyString,
701
+ channelType: NonEmptyString,
702
+ parentId: NullableString,
703
+ position: Schema.Number,
704
+ active: Schema.Boolean,
705
+ canView: Schema.Boolean,
706
+ canSend: Schema.Boolean,
707
+ ...discordPermissionFields,
708
+ lastSyncedAt: NonEmptyString
709
+ });
710
+ const DiscordGuildSyncState = Schema.Struct({
711
+ guildId: NonEmptyString,
712
+ status: DiscordGuildSyncStatus,
713
+ ...discordPermissionFields,
714
+ channelCount: NonNegativeInt,
715
+ selectableChannelCount: NonNegativeInt,
716
+ lastAttemptAt: NullableString,
717
+ lastSuccessAt: NullableString,
718
+ lastError: NullableString,
719
+ updatedAt: NonEmptyString
720
+ });
721
+ Schema.Struct({
722
+ guildId: NonEmptyString,
723
+ channels: Schema.Array(DiscordGuildChannel),
724
+ syncState: DiscordGuildSyncState
725
+ });
726
+ Schema.Struct({
727
+ guildId: NonEmptyString,
728
+ channel: DiscordGuildChannel,
729
+ syncState: DiscordGuildSyncState
730
+ });
731
+ Schema.Struct({
732
+ guildId: NonEmptyString,
733
+ status: DiscordGuildSyncStatus,
734
+ lastAttemptAt: NonEmptyString,
735
+ lastError: NonEmptyString
736
+ });
737
+ Schema.Struct({
738
+ guildId: NonEmptyString,
739
+ syncState: DiscordGuildSyncState
740
+ });
741
+ Schema.Union([Schema.Struct({ guildId: NonEmptyString }), Schema.Struct({ organizationId: NonEmptyString })]);
742
+ Schema.Struct({
743
+ recipientDiscordId: NonEmptyString,
744
+ eligibleGuildIds: Schema.Array(NonEmptyString),
745
+ update: Schema.Unknown
746
+ });
747
+ Schema.Struct({
748
+ notificationId: NonEmptyString,
749
+ targetDiscordId: NonEmptyString,
750
+ volunteerDiscordId: NonEmptyString,
751
+ world: NonEmptyString,
752
+ character: Schema.Unknown
753
+ });
754
+ const GuildLootEventNpc = Schema.Struct({
755
+ lvl: Schema.optional(NullableNumber),
756
+ prof: Schema.optional(NullableString),
757
+ type: Schema.optional(Schema.NullOr(Schema.Union([Schema.Number, Schema.String]))),
758
+ wt: Schema.optional(Schema.NullOr(Schema.Union([Schema.Number, Schema.String])))
759
+ });
760
+ Schema.Struct({
761
+ sourceNpcs: Schema.optional(Schema.Array(Schema.Struct({
762
+ level: NonNegativeInt,
763
+ type: NpcTypeSchema
764
+ }))),
765
+ feedEntry: Schema.optional(UserFeedItem),
766
+ version: Schema.Literal(1),
767
+ guildId: NonEmptyString,
768
+ world: NonEmptyString,
769
+ npc: Schema.Struct({
770
+ type: NpcTypeSchema,
771
+ lvl: NonNegativeInt
772
+ })
773
+ });
774
+ const GuildLootCreatedEventV2 = Schema.Struct({
775
+ feedEntry: Schema.optional(UserFeedItem),
776
+ version: Schema.Literal(2),
777
+ guildId: NonEmptyString,
778
+ lootId: NonNegativeInt,
779
+ npcs: Schema.Array(GuildLootEventNpc)
780
+ });
781
+ const GuildLootShareUpdatedEventV2 = Schema.Struct({
782
+ ...GuildLootCreatedEventV2.fields,
783
+ lootShare: Schema.Record(Schema.String, Schema.Array(Schema.String))
784
+ });
785
+ const ReservationChangedEventV2 = Schema.Struct({
786
+ version: Schema.Literal(2),
787
+ action: Schema.Literals([
788
+ "created",
789
+ "updated",
790
+ "deleted",
791
+ "sharing-changed"
792
+ ]),
793
+ sourceGuildId: NonEmptyString,
794
+ audienceGuildIds: Schema.Array(NonEmptyString),
795
+ reservationId: Schema.NullOr(NonNegativeInt),
796
+ spotId: Schema.NullOr(Schema.String)
797
+ });
798
+ const EventScope = Schema.Struct({
799
+ guildId: NonEmptyString,
800
+ eventId: NonEmptyString
801
+ });
802
+ Schema.Struct({
803
+ ...EventScope.fields,
804
+ heroNpcLvl: Schema.optional(Schema.NullOr(NonNegativeInt)),
805
+ mapId: NonEmptyString,
806
+ reason: Schema.optional(Schema.String)
807
+ });
808
+ Schema.Struct({
809
+ ...EventScope.fields,
810
+ heroNpcLvl: Schema.optional(Schema.NullOr(NonNegativeInt)),
811
+ heroId: Schema.optional(NonEmptyString)
812
+ });
813
+ Schema.Struct({
814
+ ...EventScope.fields,
815
+ heroNpcLvl: Schema.optional(Schema.NullOr(NonNegativeInt)),
816
+ heroId: NonEmptyString
817
+ });
818
+ Schema.Struct({
819
+ userId: NonEmptyString,
820
+ guildId: Schema.optional(NonEmptyString),
821
+ action: NonEmptyString,
822
+ entityType: Schema.optional(NonEmptyString),
823
+ entityId: Schema.optional(Schema.Union([Schema.String, Schema.Number])),
824
+ metadata: Schema.optional(Schema.Record(Schema.String, Schema.Unknown))
825
+ });
826
+ Schema.Struct({
827
+ guildId: NonEmptyString,
828
+ world: NonEmptyString,
829
+ npcId: NonNegativeInt,
830
+ timerKey: NonEmptyString,
831
+ minSpawnTime: NonEmptyString,
832
+ maxSpawnTime: NonEmptyString,
833
+ npc: Schema.optional(Schema.NullOr(Schema.Struct({ name: Schema.optional(Schema.String) })))
834
+ });
835
+ Schema.Struct({
836
+ guildId: NonEmptyString,
837
+ world: NonEmptyString,
838
+ timerKey: NonEmptyString,
839
+ npcId: Schema.optional(NonNegativeInt)
840
+ });
841
+ Schema.Struct({
842
+ version: Schema.Literal(2),
843
+ lootId: NonNegativeInt,
844
+ world: NonEmptyString,
845
+ guildIds: Schema.Array(NonEmptyString),
846
+ itemIds: Schema.Array(NonNegativeInt),
847
+ itemNames: Schema.Array(Schema.String),
848
+ npcs: Schema.Array(Schema.Struct({
849
+ type: NullableString,
850
+ lvl: NullableNumber
851
+ }))
852
+ });
853
+ Schema.Struct({
854
+ notificationJobId: NonEmptyString,
855
+ success: Schema.Boolean,
856
+ retryable: Schema.Boolean,
857
+ providerMessageId: Schema.optional(NullableString),
858
+ errorCode: Schema.optional(NullableString),
859
+ errorMessage: Schema.optional(NullableString),
860
+ deliveredAt: NonEmptyString
861
+ });
862
+ Schema.Struct({
863
+ guildId: NonEmptyString,
864
+ channelId: NonEmptyString,
865
+ syncState: DiscordGuildSyncState
866
+ });
867
+ /** Cumulative confirmed game presence. A gap starts a new segment; retries never extend it. */
868
+ const UserOnlineCheckpointV1 = Schema.Struct({
869
+ version: Schema.Literal(1),
870
+ type: Schema.Literal("checkpoint"),
871
+ userId: NonEmptyString,
872
+ sessionId: NonEmptyString,
873
+ segmentId: NonEmptyString,
874
+ world: Schema.optional(NonEmptyString),
875
+ startedAt: DateTimeWithOffsetString,
876
+ endedAt: DateTimeWithOffsetString,
877
+ observedAt: DateTimeWithOffsetString
878
+ }).check(Schema.makeFilter((event) => Date.parse(event.startedAt) <= Date.parse(event.endedAt) && Date.parse(event.endedAt) <= Date.parse(event.observedAt), { expected: "ordered confirmed interval timestamps" }));
879
+ /** Published only after the durable publisher has drained its pending checkpoints. */
880
+ const UserOnlineHealthV1 = Schema.Struct({
881
+ version: Schema.Literal(1),
882
+ type: Schema.Literal("collector"),
883
+ observedAt: DateTimeWithOffsetString,
884
+ status: Schema.Literals(["healthy", "degraded"])
885
+ });
886
+ Schema.Union([UserOnlineCheckpointV1, UserOnlineHealthV1]);
887
+ RabbitRoutingKey.GAME_CHARACTER_OFFLINE, RabbitRoutingKey.GUILDS_KILLS_ACCEPTED_V1, RabbitRoutingKey.USERS_ONLINE_CHECKPOINT_V1, RabbitRoutingKey.ACTIVITY_LOG_CREATE, RabbitRoutingKey.DISCORD_GUILD_CHANNEL_DELETED, RabbitRoutingKey.DISCORD_GUILD_CHANNEL_UPSERTED, RabbitRoutingKey.DISCORD_GUILD_CHANNELS_SYNC_FAILED, RabbitRoutingKey.DISCORD_GUILD_CHANNELS_SYNCED, RabbitRoutingKey.DISCORD_GUILD_SYNC_STATE_UPDATED, RabbitRoutingKey.EVENT_HERO_KILLED, RabbitRoutingKey.EVENT_MAP_STATUS_UPDATE, RabbitRoutingKey.EVENT_RANKING_UPDATE, RabbitRoutingKey.EVENT_RESPAWN_WINDOW_CLOSED, RabbitRoutingKey.EVENT_RESPAWN_WINDOW_OPENED, RabbitRoutingKey.GUILDS_LOOTS_CREATE, RabbitRoutingKey.GUILDS_LOOTS_SHARE_UPDATE, RabbitRoutingKey.GUILDS_CREATE, RabbitRoutingKey.GUILDS_DELETE, RabbitRoutingKey.GUILDS_UPDATE, RabbitRoutingKey.GUILDS_CREATE_ROLE, RabbitRoutingKey.GUILDS_DELETE_ROLE, RabbitRoutingKey.GUILDS_UPDATE_ROLE, RabbitRoutingKey.GUILDS_MEMBERS_ADD, RabbitRoutingKey.GUILDS_MEMBERS_ADD_ROLE, RabbitRoutingKey.GUILDS_MEMBERS_REMOVE, RabbitRoutingKey.GUILDS_MEMBERS_REMOVE_ROLE, RabbitRoutingKey.GUILDS_MEMBERS_UPDATE, RabbitRoutingKey.GUILDS_TIMERS_UPDATE, RabbitRoutingKey.GUILDS_TIMERS_DELETE, RabbitRoutingKey.GUILDS_RESERVATIONS_CREATE, RabbitRoutingKey.GUILDS_RESERVATIONS_DELETE, RabbitRoutingKey.GUILDS_SEND_MESSAGE, RabbitRoutingKey.GUILDS_UPDATE_MESSAGE, RabbitRoutingKey.GUILDS_DELETE_MESSAGE, RabbitRoutingKey.GUILDS_CLEAR_MESSAGES, RabbitRoutingKey.GUILDS_NOTIFICATIONS_SEND, RabbitRoutingKey.GUILDS_NOTIFICATIONS_VOLUNTEER, RabbitRoutingKey.GUILDS_PARTY_GATHERING, RabbitRoutingKey.GUILDS_PARTY_GATHERING_CANCEL, RabbitRoutingKey.GUILDS_MEMBERS_REFRESH_JOB_UPDATE, RabbitRoutingKey.GUILDS_RESERVATIONS_CHANGED_V2, RabbitRoutingKey.NOTIFICATIONS_DELIVERY_RESULT, RabbitRoutingKey.NOTIFICATIONS_LOOT_CREATED, RabbitRoutingKey.NOTIFICATIONS_TIMER_DELETED, RabbitRoutingKey.NOTIFICATIONS_TIMER_UPDATED, RabbitRoutingKey.PRESENCE_CHECK_REQUEST, RabbitRoutingKey.PRESENCE_COVERAGE_CHECK, RabbitRoutingKey.USERS_PARTY_READY_ROOM_UPDATED;
888
+ const REALTIME_SUBPROTOCOL = "lootlog.realtime.v1";
889
+ const REALTIME_JSON_SUBPROTOCOL = "lootlog.realtime.json.v1";
890
+ const PRESENCE_HEARTBEAT_INTERVAL_MS = 25e3;
891
+ const RequestId = NonEmptyString;
892
+ const Revision = NonNegativeInt;
893
+ const Timestamp = NonNegativeInt;
894
+ const RealtimeLogicalTopic = Schema.Literals([
895
+ "organization.activity",
896
+ "organization.chat",
897
+ "organization.loots",
898
+ "organization.members",
899
+ "organization.notifications",
900
+ "organization.presence",
901
+ "organization.reservations",
902
+ "organization.timers",
903
+ "event.coordination",
904
+ "map.air-tags",
905
+ "map.pings",
906
+ "party.ready-room"
907
+ ]);
908
+ const SubscriptionScope = Schema.Struct({
909
+ topic: RealtimeLogicalTopic,
910
+ organizationId: Schema.optional(NonEmptyString),
911
+ eventId: Schema.optional(NonEmptyString),
912
+ world: Schema.optional(NonEmptyString),
913
+ mapId: Schema.optional(NonNegativeInt)
914
+ });
915
+ const PresencePlatform = Schema.Literals(["game", "web-app"]);
916
+ const PresenceStatus = Schema.Literals(["online", "offline"]);
917
+ const PresenceConfidence = Schema.Literals(["verified", "reported"]);
918
+ const PresenceClan = Schema.Struct({
919
+ id: Schema.optional(NonNegativeInt),
920
+ name: Schema.optional(Schema.String),
921
+ rank: Schema.optional(Schema.Int)
922
+ });
923
+ const PresenceCharacter = Schema.Struct({
924
+ world: NonEmptyString,
925
+ name: NonEmptyString,
926
+ lvl: NonNegativeInt,
927
+ icon: Schema.String,
928
+ characterId: NonEmptyString,
929
+ accountId: NonEmptyString,
930
+ prof: Schema.String,
931
+ clan: Schema.optional(PresenceClan)
932
+ });
933
+ const PrecisePresenceLocation = Schema.Struct({
934
+ mapId: Schema.optional(NonNegativeInt),
935
+ map: NonEmptyString,
936
+ x: Schema.optional(NonNegativeInt),
937
+ y: Schema.optional(NonNegativeInt)
938
+ });
939
+ const BasicPresence = Schema.Struct({
940
+ userId: NonEmptyString,
941
+ discordId: Schema.optional(NonEmptyString),
942
+ sessionId: NonEmptyString,
943
+ organizationIds: Schema.Array(NonEmptyString),
944
+ platform: PresencePlatform,
945
+ status: PresenceStatus,
946
+ confidence: PresenceConfidence,
947
+ isAfk: Schema.Boolean,
948
+ lastSeen: Timestamp,
949
+ character: Schema.optional(PresenceCharacter)
950
+ });
951
+ const PresenceWithLocation = Schema.Struct({
952
+ ...BasicPresence.fields,
953
+ location: PrecisePresenceLocation
954
+ });
955
+ const PublishedPresence = Schema.Struct({
956
+ organizationIds: Schema.Array(NonEmptyString),
957
+ isAfk: Schema.optional(Schema.Boolean),
958
+ character: Schema.optional(PresenceCharacter),
959
+ location: Schema.optional(PrecisePresenceLocation),
960
+ clientObservedAt: Schema.optional(Timestamp)
961
+ });
962
+ const PresenceSnapshot = Schema.Struct({
963
+ organizationId: NonEmptyString,
964
+ world: Schema.optional(NonEmptyString),
965
+ revision: Revision,
966
+ presences: Schema.Array(Schema.Union([PresenceWithLocation, BasicPresence]))
967
+ });
968
+ const PresenceDelta = Schema.Struct({
969
+ organizationId: NonEmptyString,
970
+ revision: Revision,
971
+ changes: Schema.Array(Schema.Union([Schema.Struct({
972
+ action: Schema.Literal("upsert"),
973
+ presence: Schema.Union([PresenceWithLocation, BasicPresence])
974
+ }), Schema.Struct({
975
+ action: Schema.Literal("remove"),
976
+ userId: NonEmptyString,
977
+ discordId: Schema.optional(NonEmptyString),
978
+ sessionId: NonEmptyString
979
+ })]))
980
+ });
981
+ const command = (type, data) => Schema.Struct({
982
+ v: Schema.Literal(1),
983
+ type: Schema.Literal(type),
984
+ requestId: Schema.optional(RequestId),
985
+ data
986
+ });
987
+ const SessionJoinCommand = command("session.join", Schema.Struct({
988
+ world: Schema.optional(NonEmptyString),
989
+ character: Schema.optional(PresenceCharacter),
990
+ margonemAccountProof: Schema.optional(Schema.Unknown)
991
+ }));
992
+ const HeartbeatCommand = command("presence.heartbeat", Schema.Struct({ sessionId: NonEmptyString }));
993
+ const PresencePublishCommand = command("presence.publish", PublishedPresence);
994
+ const PresenceFetchCommand = command("presence.fetch", Schema.Struct({
995
+ organizationId: NonEmptyString,
996
+ world: Schema.optional(NonEmptyString)
997
+ }));
998
+ const SubscribeCommand = command("subscription.subscribe", SubscriptionScope);
999
+ const UnsubscribeCommand = command("subscription.unsubscribe", SubscriptionScope);
1000
+ const MapPingCommand = command("map-ping.send", MapPingSendPayloadSchema);
1001
+ const AirTagSubscriptionCommand = command("air-tag.subscription", Schema.Struct({
1002
+ requestId: RequestId,
1003
+ enabled: Schema.Boolean,
1004
+ expectedMapId: Schema.optional(NonNegativeInt)
1005
+ }));
1006
+ const AirTagObservationCommand = command("air-tag.observation", AirTagObservationBatchSchema);
1007
+ const AirTagRejectCode = Schema.Literals([
1008
+ "forbidden",
1009
+ "invalid-context",
1010
+ "invalid-payload",
1011
+ "rate-limited",
1012
+ "temporarily-unavailable"
1013
+ ]);
1014
+ const AirTagSubscriptionAck = Schema.Union([Schema.Struct({
1015
+ status: Schema.Literal("accepted"),
1016
+ requestId: RequestId,
1017
+ scopes: Schema.Array(AirTagScopeSnapshotSchema)
1018
+ }), Schema.Struct({
1019
+ status: Schema.Literal("rejected"),
1020
+ requestId: RequestId,
1021
+ code: AirTagRejectCode
1022
+ })]);
1023
+ const AirTagObservationAck = Schema.Union([Schema.Struct({
1024
+ status: Schema.Literal("accepted"),
1025
+ acceptedScopes: NonNegativeInt,
1026
+ acceptedTargets: NonNegativeInt
1027
+ }), Schema.Struct({
1028
+ status: Schema.Literal("rejected"),
1029
+ code: AirTagRejectCode,
1030
+ retryAfterMs: Schema.optional(NonNegativeInt)
1031
+ })]);
1032
+ const ClientCommand = Schema.Union([
1033
+ SessionJoinCommand,
1034
+ HeartbeatCommand,
1035
+ PresencePublishCommand,
1036
+ PresenceFetchCommand,
1037
+ SubscribeCommand,
1038
+ UnsubscribeCommand,
1039
+ MapPingCommand,
1040
+ AirTagSubscriptionCommand,
1041
+ AirTagObservationCommand
1042
+ ]);
1043
+ const RealtimeError = Schema.Struct({
1044
+ code: NonEmptyString,
1045
+ message: NonEmptyString,
1046
+ retryable: Schema.Boolean,
1047
+ retryAfterMs: Schema.optional(NonNegativeInt),
1048
+ details: Schema.optional(Schema.Record(Schema.String, Schema.Unknown))
1049
+ });
1050
+ const Response = Schema.Union([Schema.Struct({
1051
+ v: Schema.Literal(1),
1052
+ requestId: RequestId,
1053
+ status: Schema.Literal("success"),
1054
+ data: Schema.optional(Schema.Unknown)
1055
+ }), Schema.Struct({
1056
+ v: Schema.Literal(1),
1057
+ requestId: RequestId,
1058
+ status: Schema.Literal("error"),
1059
+ error: RealtimeError
1060
+ })]);
1061
+ const serverEvent = (type, data) => Schema.Struct({
1062
+ v: Schema.Literal(1),
1063
+ type: Schema.Literal(type),
1064
+ sequence: Schema.optional(Revision),
1065
+ data
1066
+ });
1067
+ const OrganizationEvent = Schema.Struct({
1068
+ organizationId: NonEmptyString,
1069
+ payload: Schema.Unknown
1070
+ });
1071
+ const ServerEvent = Schema.Union([
1072
+ serverEvent("session.joined", Schema.Struct({
1073
+ connectionId: NonEmptyString,
1074
+ organizationIds: Schema.Array(NonEmptyString),
1075
+ subscriptionScopes: Schema.Array(SubscriptionScope),
1076
+ accessPolicy: Schema.optional(AccessPolicySnapshot)
1077
+ })),
1078
+ serverEvent("permissions.updated", Schema.Struct({
1079
+ changes: Schema.optional(Schema.Array(AccessPolicyChange)),
1080
+ organizationIds: Schema.Array(NonEmptyString),
1081
+ subscriptionScopes: Schema.Array(SubscriptionScope),
1082
+ accessPolicy: Schema.optional(AccessPolicySnapshot)
1083
+ })),
1084
+ serverEvent("presence.snapshot", PresenceSnapshot),
1085
+ serverEvent("presence.delta", PresenceDelta),
1086
+ serverEvent("chat.created", OrganizationEvent),
1087
+ serverEvent("chat.updated", OrganizationEvent),
1088
+ serverEvent("chat.deleted", OrganizationEvent),
1089
+ serverEvent("chat.cleared", OrganizationEvent),
1090
+ serverEvent("feed.entry", UserFeedItem),
1091
+ serverEvent("kills.changed", Schema.Struct({ guildId: NonEmptyString })),
1092
+ serverEvent("loot.created", GuildLootCreatedEventV2),
1093
+ serverEvent("loot.share-updated", GuildLootShareUpdatedEventV2),
1094
+ serverEvent("timer.created", OrganizationEvent),
1095
+ serverEvent("timer.deleted", OrganizationEvent),
1096
+ serverEvent("reservation.created", OrganizationEvent),
1097
+ serverEvent("reservation.deleted", OrganizationEvent),
1098
+ serverEvent("reservation.changed", ReservationChangedEventV2),
1099
+ serverEvent("notification.sent", OrganizationEvent),
1100
+ serverEvent("member-refresh.updated", OrganizationEvent),
1101
+ serverEvent("party-gathering.updated", OrganizationEvent),
1102
+ serverEvent("party-gathering.cancelled", OrganizationEvent),
1103
+ serverEvent("party-ready-room.updated", OrganizationEvent),
1104
+ serverEvent("map-ping.received", MapPingEventSchema),
1105
+ serverEvent("air-tag.updated", AirTagUpdateEventSchema),
1106
+ serverEvent("event.map-status-updated", OrganizationEvent),
1107
+ serverEvent("event.hero-killed", OrganizationEvent),
1108
+ serverEvent("event.ranking-updated", OrganizationEvent),
1109
+ serverEvent("event.respawn-window-opened", OrganizationEvent),
1110
+ serverEvent("event.respawn-window-closed", OrganizationEvent),
1111
+ serverEvent("notification.volunteer", Schema.Struct({
1112
+ notificationId: NonEmptyString,
1113
+ volunteer: Schema.StructWithRest(Schema.Struct({
1114
+ discordId: NonEmptyString,
1115
+ world: NonEmptyString
1116
+ }), [Schema.Record(Schema.String, Schema.Unknown)])
1117
+ }))
1118
+ ]);
1119
+ const RealtimeFrame = Schema.Union([
1120
+ ClientCommand,
1121
+ Response,
1122
+ ServerEvent
1123
+ ]);
1124
+ Schema.decodeUnknownSync(ClientCommand);
1125
+ Schema.decodeUnknownSync(Response);
1126
+ Schema.decodeUnknownSync(ServerEvent);
1127
+ const decodeRealtimeFrame = Schema.decodeUnknownSync(RealtimeFrame);
1128
+ const serverEventTypes = new Set(ServerEvent.members.map((member) => member.fields.type.literal));
1129
+ const isServerEventFrame = (frame) => "type" in frame && serverEventTypes.has(frame.type);
1130
+ //#endregion
1131
+ //#region ../protocol/src/realtime/codec.ts
1132
+ var RealtimeCodecError = class extends TaggedError()("RealtimeCodecError", {
1133
+ operation: Schema.Literals(["decode", "encode"]),
1134
+ message: Schema.String,
1135
+ cause: Schema.Defect()
1136
+ }) {};
1137
+ const getErrorMessage = (cause) => cause instanceof Error ? cause.message : String(cause);
1138
+ const tryEncodeFrame = (frame) => Result.try({
1139
+ try: () => {
1140
+ const validated = Schema.encodeUnknownSync(RealtimeFrame)(frame);
1141
+ return {
1142
+ frame: validated,
1143
+ bytes: encode(validated, { ignoreUndefined: true })
1144
+ };
1145
+ },
1146
+ catch: (error) => new RealtimeCodecError({
1147
+ operation: "encode",
1148
+ message: getErrorMessage(error),
1149
+ cause: error
1150
+ })
1151
+ });
1152
+ const tryEncodeRealtimeFrame = (frame) => Result.map(tryEncodeFrame(frame), ({ bytes }) => bytes);
1153
+ const encodeRealtimeFrame = (frame) => Result.getOrThrow(tryEncodeRealtimeFrame(frame));
1154
+ const tryDecodeRealtimeFrame = (bytes) => Result.try({
1155
+ try: () => {
1156
+ const input = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);
1157
+ return Schema.decodeUnknownSync(RealtimeFrame)(decode(input));
1158
+ },
1159
+ catch: (error) => new RealtimeCodecError({
1160
+ operation: "decode",
1161
+ message: getErrorMessage(error),
1162
+ cause: error
1163
+ })
1164
+ });
1165
+ Schema.decodeUnknownSync(PresenceSnapshot);
1166
+ Schema.is(MapPingAckSchema);
1167
+ Schema.is(AirTagSubscriptionAck);
1168
+ Schema.is(AirTagObservationAck);
1169
+ Schema.is(Schema.Struct({ presences: Schema.optional(PresenceSnapshot.fields.presences) }));
1170
+ //#endregion
1171
+ //#region ../client/src/realtime/realtime-client.ts
1172
+ var RealtimeRequestError = class extends Error {
1173
+ code;
1174
+ retryable;
1175
+ retryAfterMs;
1176
+ constructor(code, message, retryable, retryAfterMs) {
1177
+ super(message);
1178
+ this.code = code;
1179
+ this.retryable = retryable;
1180
+ this.retryAfterMs = retryAfterMs;
1181
+ this.name = "RealtimeRequestError";
1182
+ }
1183
+ };
1184
+ const normalizeUrl = (baseUrl, path) => {
1185
+ const fallbackBase = typeof globalThis.location === "undefined" ? "http://localhost" : globalThis.location.href;
1186
+ const url = new URL(baseUrl, fallbackBase);
1187
+ if (url.protocol === "http:") url.protocol = "ws:";
1188
+ if (url.protocol === "https:") url.protocol = "wss:";
1189
+ url.pathname = path.startsWith("/") ? path : `/${path}`;
1190
+ url.search = "";
1191
+ url.hash = "";
1192
+ return url.toString();
1193
+ };
1194
+ const nativeWebSocketFactory = (url, protocols) => new WebSocket(url, protocols);
1195
+ const WEBSOCKET_OPEN = 1;
1196
+ const scopeKey = (scope) => JSON.stringify([
1197
+ scope.topic,
1198
+ scope.organizationId ?? null,
1199
+ scope.eventId ?? null,
1200
+ scope.world ?? null,
1201
+ scope.mapId ?? null
1202
+ ]);
1203
+ const toBytes = async (data) => {
1204
+ if (data instanceof Uint8Array) return data;
1205
+ if (data instanceof ArrayBuffer) return new Uint8Array(data);
1206
+ if (data instanceof Blob) return new Uint8Array(await data.arrayBuffer());
1207
+ throw new Error("Gateway returned a non-binary realtime frame");
1208
+ };
1209
+ const getPresenceSessionId = (data) => {
1210
+ if (!data || typeof data !== "object" || !("sessionId" in data)) return null;
1211
+ return typeof data.sessionId === "string" ? data.sessionId : null;
1212
+ };
1213
+ var RealtimeClient = class {
1214
+ url;
1215
+ protocols;
1216
+ requestTimeoutMs;
1217
+ reconnectBaseDelayMs;
1218
+ reconnectMaxDelayMs;
1219
+ random;
1220
+ webSocketFactory;
1221
+ frameEncoding;
1222
+ eventListeners = /* @__PURE__ */ new Set();
1223
+ stateListeners = /* @__PURE__ */ new Set();
1224
+ pending = /* @__PURE__ */ new Map();
1225
+ subscriptions = /* @__PURE__ */ new Map();
1226
+ socket = null;
1227
+ stateValue = "disconnected";
1228
+ joinData = null;
1229
+ reconnectAttempt = 0;
1230
+ reconnectTimeout = null;
1231
+ heartbeatTimeout = null;
1232
+ presenceSessionId = null;
1233
+ manuallyClosed = false;
1234
+ messageChain = Promise.resolve();
1235
+ rejoinHandler = null;
1236
+ constructor(options) {
1237
+ this.url = normalizeUrl(options.url, options.path ?? "/ws");
1238
+ this.protocols = options.protocols ? [...options.protocols] : void 0;
1239
+ this.requestTimeoutMs = options.requestTimeoutMs ?? 2e4;
1240
+ this.reconnectBaseDelayMs = options.reconnectBaseDelayMs ?? 1e3;
1241
+ this.reconnectMaxDelayMs = options.reconnectMaxDelayMs ?? 3e4;
1242
+ this.random = options.random ?? Math.random;
1243
+ this.webSocketFactory = options.webSocketFactory ?? nativeWebSocketFactory;
1244
+ this.frameEncoding = options.frameEncoding ?? "messagepack";
1245
+ }
1246
+ get state() {
1247
+ return this.stateValue;
1248
+ }
1249
+ get connected() {
1250
+ return this.socket?.readyState === WEBSOCKET_OPEN;
1251
+ }
1252
+ connect() {
1253
+ if (this.socket && this.socket.readyState <= WEBSOCKET_OPEN) return;
1254
+ this.manuallyClosed = false;
1255
+ this.clearReconnect();
1256
+ this.open(this.reconnectAttempt > 0 ? "reconnecting" : "connecting");
1257
+ }
1258
+ disconnect() {
1259
+ this.manuallyClosed = true;
1260
+ this.clearReconnect();
1261
+ this.clearHeartbeat();
1262
+ this.rejectPending(/* @__PURE__ */ new Error("Realtime client disconnected"));
1263
+ const activeSocket = this.socket;
1264
+ this.socket = null;
1265
+ activeSocket?.close(1e3, "client disconnect");
1266
+ this.setState("disconnected");
1267
+ }
1268
+ subscribe(listener) {
1269
+ this.eventListeners.add(listener);
1270
+ return () => this.eventListeners.delete(listener);
1271
+ }
1272
+ subscribeState(listener) {
1273
+ this.stateListeners.add(listener);
1274
+ listener(this.stateValue);
1275
+ return () => this.stateListeners.delete(listener);
1276
+ }
1277
+ setReconnectHandler(handler) {
1278
+ this.rejoinHandler = handler;
1279
+ }
1280
+ join(data) {
1281
+ this.joinData = data;
1282
+ return this.performJoin();
1283
+ }
1284
+ subscribeScope(scope) {
1285
+ this.subscriptions.set(scopeKey(scope), scope);
1286
+ if (!this.connected || this.stateValue !== "ready") return Promise.resolve(void 0);
1287
+ return this.request("subscription.subscribe", scope);
1288
+ }
1289
+ unsubscribeScope(scope) {
1290
+ this.subscriptions.delete(scopeKey(scope));
1291
+ if (!this.connected || this.stateValue !== "ready") return Promise.resolve(void 0);
1292
+ return this.request("subscription.unsubscribe", scope);
1293
+ }
1294
+ request(type, data) {
1295
+ const activeSocket = this.socket;
1296
+ if (!activeSocket || activeSocket.readyState !== WEBSOCKET_OPEN) return Promise.reject(/* @__PURE__ */ new Error("Realtime connection is not open"));
1297
+ const requestId = crypto.randomUUID();
1298
+ return new Promise((resolve, reject) => {
1299
+ const timeout = setTimeout(() => {
1300
+ this.pending.delete(requestId);
1301
+ reject(/* @__PURE__ */ new Error(`Realtime request timed out: ${type}`));
1302
+ }, this.requestTimeoutMs);
1303
+ this.pending.set(requestId, {
1304
+ type,
1305
+ resolve,
1306
+ reject,
1307
+ timeout
1308
+ });
1309
+ try {
1310
+ activeSocket.send(this.encodeFrame({
1311
+ v: 1,
1312
+ type,
1313
+ requestId,
1314
+ data
1315
+ }));
1316
+ } catch (error) {
1317
+ clearTimeout(timeout);
1318
+ this.pending.delete(requestId);
1319
+ reject(error instanceof Error ? error : new Error(String(error)));
1320
+ }
1321
+ });
1322
+ }
1323
+ send(type, data) {
1324
+ const activeSocket = this.socket;
1325
+ if (!activeSocket || activeSocket.readyState !== WEBSOCKET_OPEN) return;
1326
+ activeSocket.send(this.encodeFrame({
1327
+ v: 1,
1328
+ type,
1329
+ data
1330
+ }));
1331
+ }
1332
+ open(state) {
1333
+ this.setState(state);
1334
+ if (this.manuallyClosed) return;
1335
+ const socket = this.webSocketFactory(this.url, this.protocols);
1336
+ socket.binaryType = "arraybuffer";
1337
+ this.socket = socket;
1338
+ socket.addEventListener("open", () => {
1339
+ if (this.socket !== socket) return;
1340
+ this.setState("connected");
1341
+ if (this.joinData) (this.rejoinHandler ?? (() => this.performJoin().then(() => void 0)))().catch(() => socket.close(4008, "session rejoin failed"));
1342
+ });
1343
+ socket.addEventListener("message", (event) => {
1344
+ this.messageChain = this.messageChain.then(() => this.handleMessage(event.data)).catch(() => socket.close(4007, "malformed realtime frame"));
1345
+ });
1346
+ socket.addEventListener("error", () => {
1347
+ if (this.socket === socket) socket.close();
1348
+ });
1349
+ socket.addEventListener("close", () => {
1350
+ if (this.socket !== socket) return;
1351
+ this.socket = null;
1352
+ this.clearHeartbeat();
1353
+ this.rejectPending(/* @__PURE__ */ new Error("Realtime connection closed"));
1354
+ this.setState("disconnected");
1355
+ if (!this.manuallyClosed) this.scheduleReconnect();
1356
+ });
1357
+ }
1358
+ async performJoin() {
1359
+ if (!this.joinData || !this.connected) return void 0;
1360
+ this.setState("joining");
1361
+ try {
1362
+ const result = await this.request("session.join", this.joinData);
1363
+ await Promise.all([...this.subscriptions.values()].map((scope) => this.request("subscription.subscribe", scope)));
1364
+ this.reconnectAttempt = 0;
1365
+ this.setState("ready");
1366
+ return result;
1367
+ } catch (error) {
1368
+ if (this.connected) this.socket?.close(4008, "session join failed");
1369
+ throw error;
1370
+ }
1371
+ }
1372
+ async handleMessage(data) {
1373
+ const frame = await this.decodeFrame(data);
1374
+ if ("status" in frame) {
1375
+ const pending = this.pending.get(frame.requestId);
1376
+ if (!pending) return;
1377
+ clearTimeout(pending.timeout);
1378
+ this.pending.delete(frame.requestId);
1379
+ if (frame.status === "success") {
1380
+ if (pending.type === "presence.publish") {
1381
+ this.presenceSessionId = getPresenceSessionId(frame.data);
1382
+ this.scheduleHeartbeat();
1383
+ }
1384
+ pending.resolve(frame.data);
1385
+ } else pending.reject(new RealtimeRequestError(frame.error.code, frame.error.message, frame.error.retryable, frame.error.retryAfterMs));
1386
+ return;
1387
+ }
1388
+ if (!isServerEventFrame(frame)) return;
1389
+ for (const listener of this.eventListeners) listener(frame);
1390
+ }
1391
+ encodeFrame(frame) {
1392
+ return this.frameEncoding === "json" ? JSON.stringify(frame) : encodeRealtimeFrame(frame);
1393
+ }
1394
+ async decodeFrame(data) {
1395
+ if (this.frameEncoding === "json") {
1396
+ if (typeof data !== "string") throw new Error("Gateway returned a non-text realtime frame");
1397
+ return decodeRealtimeFrame(JSON.parse(data));
1398
+ }
1399
+ const decoded = tryDecodeRealtimeFrame(await toBytes(data));
1400
+ if (Result.isFailure(decoded)) throw decoded.failure;
1401
+ return decoded.success;
1402
+ }
1403
+ scheduleReconnect() {
1404
+ this.reconnectAttempt += 1;
1405
+ const exponential = Math.min(this.reconnectMaxDelayMs, this.reconnectBaseDelayMs * 2 ** (this.reconnectAttempt - 1));
1406
+ const jittered = Math.round(exponential * (.5 + this.random()));
1407
+ this.setState("reconnecting");
1408
+ this.reconnectTimeout = setTimeout(() => this.open("reconnecting"), jittered);
1409
+ }
1410
+ scheduleHeartbeat() {
1411
+ this.clearHeartbeat();
1412
+ if (!this.presenceSessionId || !this.connected) return;
1413
+ this.heartbeatTimeout = setTimeout(() => {
1414
+ const sessionId = this.presenceSessionId;
1415
+ if (!sessionId) return;
1416
+ this.request("presence.heartbeat", { sessionId }).then(() => this.scheduleHeartbeat()).catch(() => this.socket?.close());
1417
+ }, PRESENCE_HEARTBEAT_INTERVAL_MS);
1418
+ }
1419
+ rejectPending(error) {
1420
+ for (const pending of this.pending.values()) {
1421
+ clearTimeout(pending.timeout);
1422
+ pending.reject(error);
1423
+ }
1424
+ this.pending.clear();
1425
+ }
1426
+ setState(state) {
1427
+ if (this.stateValue === state) return;
1428
+ this.stateValue = state;
1429
+ for (const listener of this.stateListeners) listener(state);
1430
+ }
1431
+ clearReconnect() {
1432
+ if (this.reconnectTimeout) clearTimeout(this.reconnectTimeout);
1433
+ this.reconnectTimeout = null;
1434
+ }
1435
+ clearHeartbeat() {
1436
+ if (this.heartbeatTimeout) clearTimeout(this.heartbeatTimeout);
1437
+ this.heartbeatTimeout = null;
1438
+ }
1439
+ };
1440
+ //#endregion
1441
+ export { REALTIME_JSON_SUBPROTOCOL, REALTIME_SUBPROTOCOL, RealtimeClient, RealtimeRequestError };
1442
+
1443
+ //# sourceMappingURL=realtime.js.map