@openora/core 0.4.1-canary.72 → 0.4.1-canary.74

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 (184) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/admin-console/plugin.d.ts +1 -0
  3. package/dist/admin-console/plugin.d.ts.map +1 -1
  4. package/dist/analytics/plugin.d.ts +1 -0
  5. package/dist/analytics/plugin.d.ts.map +1 -1
  6. package/dist/audit/plugin.d.ts +1 -0
  7. package/dist/audit/plugin.d.ts.map +1 -1
  8. package/dist/audit/plugin.js +6 -0
  9. package/dist/audit/plugin.js.map +1 -1
  10. package/dist/casino/gaming/plugin.d.ts +1 -0
  11. package/dist/casino/gaming/plugin.d.ts.map +1 -1
  12. package/dist/casino/lobby/plugin.d.ts +1 -0
  13. package/dist/casino/lobby/plugin.d.ts.map +1 -1
  14. package/dist/cms/plugin.d.ts +1 -0
  15. package/dist/cms/plugin.d.ts.map +1 -1
  16. package/dist/compliance/plugin.d.ts +1 -0
  17. package/dist/compliance/plugin.d.ts.map +1 -1
  18. package/dist/contracts/adapters/admin-user-directory.d.ts +2 -1
  19. package/dist/contracts/adapters/admin-user-directory.d.ts.map +1 -1
  20. package/dist/contracts/adapters/admin-user-directory.js.map +1 -1
  21. package/dist/contracts/adapters/chat-moderation.d.ts +75 -0
  22. package/dist/contracts/adapters/chat-moderation.d.ts.map +1 -0
  23. package/dist/contracts/adapters/chat-moderation.js +3 -0
  24. package/dist/contracts/adapters/chat-moderation.js.map +1 -0
  25. package/dist/contracts/adapters/index.d.ts +2 -0
  26. package/dist/contracts/adapters/index.d.ts.map +1 -1
  27. package/dist/contracts/adapters/index.js +1 -0
  28. package/dist/contracts/adapters/index.js.map +1 -1
  29. package/dist/contracts/adapters/realtime.d.ts +9 -1
  30. package/dist/contracts/adapters/realtime.d.ts.map +1 -1
  31. package/dist/contracts/adapters/realtime.js.map +1 -1
  32. package/dist/contracts/schemas/events.d.ts +19 -4
  33. package/dist/contracts/schemas/events.d.ts.map +1 -1
  34. package/dist/contracts/schemas/events.js +12 -5
  35. package/dist/contracts/schemas/events.js.map +1 -1
  36. package/dist/engagement/chat/__tests__/chat.service.int.test.js +314 -19
  37. package/dist/engagement/chat/__tests__/chat.service.int.test.js.map +1 -1
  38. package/dist/engagement/chat/contract/constants.d.ts +1 -1
  39. package/dist/engagement/chat/contract/constants.d.ts.map +1 -1
  40. package/dist/engagement/chat/contract/constants.js +1 -1
  41. package/dist/engagement/chat/contract/constants.js.map +1 -1
  42. package/dist/engagement/chat/contract/index.d.ts +764 -18
  43. package/dist/engagement/chat/contract/index.d.ts.map +1 -1
  44. package/dist/engagement/chat/contract/index.js +236 -5
  45. package/dist/engagement/chat/contract/index.js.map +1 -1
  46. package/dist/engagement/chat/drizzle/migrations/0005_tranquil_gwen_stacy.sql +100 -0
  47. package/dist/engagement/chat/drizzle/migrations/0006_tiny_ultron.sql +3 -0
  48. package/dist/engagement/chat/drizzle/migrations/meta/0005_snapshot.json +1422 -0
  49. package/dist/engagement/chat/drizzle/migrations/meta/0006_snapshot.json +1456 -0
  50. package/dist/engagement/chat/drizzle/migrations/meta/_journal.json +14 -0
  51. package/dist/engagement/chat/plugin.d.ts +2 -1
  52. package/dist/engagement/chat/plugin.d.ts.map +1 -1
  53. package/dist/engagement/chat/plugin.js +25 -13
  54. package/dist/engagement/chat/plugin.js.map +1 -1
  55. package/dist/engagement/chat/router/index.d.ts +518 -18
  56. package/dist/engagement/chat/router/index.d.ts.map +1 -1
  57. package/dist/engagement/chat/router/index.js +140 -20
  58. package/dist/engagement/chat/router/index.js.map +1 -1
  59. package/dist/engagement/chat/schema/index.d.ts +1017 -16
  60. package/dist/engagement/chat/schema/index.d.ts.map +1 -1
  61. package/dist/engagement/chat/schema/index.js +110 -4
  62. package/dist/engagement/chat/schema/index.js.map +1 -1
  63. package/dist/engagement/chat/service/chat-ban.service.d.ts +26 -0
  64. package/dist/engagement/chat/service/chat-ban.service.d.ts.map +1 -0
  65. package/dist/engagement/chat/service/chat-ban.service.js +139 -0
  66. package/dist/engagement/chat/service/chat-ban.service.js.map +1 -0
  67. package/dist/engagement/chat/service/chat-message-moderation.service.d.ts +14 -0
  68. package/dist/engagement/chat/service/chat-message-moderation.service.d.ts.map +1 -0
  69. package/dist/engagement/chat/service/chat-message-moderation.service.js +48 -0
  70. package/dist/engagement/chat/service/chat-message-moderation.service.js.map +1 -0
  71. package/dist/engagement/chat/service/chat-moderation.service.d.ts +30 -0
  72. package/dist/engagement/chat/service/chat-moderation.service.d.ts.map +1 -0
  73. package/dist/engagement/chat/service/chat-moderation.service.js +40 -0
  74. package/dist/engagement/chat/service/chat-moderation.service.js.map +1 -0
  75. package/dist/engagement/chat/service/chat-mute.service.d.ts +26 -0
  76. package/dist/engagement/chat/service/chat-mute.service.d.ts.map +1 -0
  77. package/dist/engagement/chat/service/chat-mute.service.js +143 -0
  78. package/dist/engagement/chat/service/chat-mute.service.js.map +1 -0
  79. package/dist/engagement/chat/service/chat-room-ban.service.d.ts +29 -0
  80. package/dist/engagement/chat/service/chat-room-ban.service.d.ts.map +1 -0
  81. package/dist/engagement/chat/service/chat-room-ban.service.js +105 -0
  82. package/dist/engagement/chat/service/chat-room-ban.service.js.map +1 -0
  83. package/dist/engagement/chat/service/chat-room-membership.service.d.ts +72 -0
  84. package/dist/engagement/chat/service/chat-room-membership.service.d.ts.map +1 -0
  85. package/dist/engagement/chat/service/chat-room-membership.service.js +176 -0
  86. package/dist/engagement/chat/service/chat-room-membership.service.js.map +1 -0
  87. package/dist/engagement/chat/service/chat-room-mute.service.d.ts +25 -0
  88. package/dist/engagement/chat/service/chat-room-mute.service.d.ts.map +1 -0
  89. package/dist/engagement/chat/service/chat-room-mute.service.js +89 -0
  90. package/dist/engagement/chat/service/chat-room-mute.service.js.map +1 -0
  91. package/dist/engagement/chat/service/chat.service.d.ts +348 -61
  92. package/dist/engagement/chat/service/chat.service.d.ts.map +1 -1
  93. package/dist/engagement/chat/service/chat.service.js +460 -194
  94. package/dist/engagement/chat/service/chat.service.js.map +1 -1
  95. package/dist/engagement/chat/service/errors/chat-moderation.errors.d.ts +21 -0
  96. package/dist/engagement/chat/service/errors/chat-moderation.errors.d.ts.map +1 -0
  97. package/dist/engagement/chat/service/errors/chat-moderation.errors.js +31 -0
  98. package/dist/engagement/chat/service/errors/chat-moderation.errors.js.map +1 -0
  99. package/dist/engagement/chat-commands/contract/index.js +1 -1
  100. package/dist/engagement/chat-commands/contract/index.js.map +1 -1
  101. package/dist/engagement/chat-commands/drizzle/migrations/0001_sturdy_barracuda.sql +1 -0
  102. package/dist/engagement/chat-commands/drizzle/migrations/meta/0001_snapshot.json +83 -0
  103. package/dist/engagement/chat-commands/drizzle/migrations/meta/_journal.json +7 -0
  104. package/dist/engagement/chat-commands/plugin.d.ts +1 -0
  105. package/dist/engagement/chat-commands/plugin.d.ts.map +1 -1
  106. package/dist/engagement/chat-commands/schema/index.d.ts +0 -199
  107. package/dist/engagement/chat-commands/schema/index.d.ts.map +1 -1
  108. package/dist/engagement/chat-commands/schema/index.js +1 -19
  109. package/dist/engagement/chat-commands/schema/index.js.map +1 -1
  110. package/dist/engagement/chat-commands/service/__tests__/chat-commands.service.test.js +15 -1
  111. package/dist/engagement/chat-commands/service/__tests__/chat-commands.service.test.js.map +1 -1
  112. package/dist/engagement/chat-commands/service/chat-commands.service.d.ts +1 -0
  113. package/dist/engagement/chat-commands/service/chat-commands.service.d.ts.map +1 -1
  114. package/dist/engagement/chat-commands/service/chat-commands.service.js +38 -3
  115. package/dist/engagement/chat-commands/service/chat-commands.service.js.map +1 -1
  116. package/dist/engagement/notifications/plugin.d.ts +1 -0
  117. package/dist/engagement/notifications/plugin.d.ts.map +1 -1
  118. package/dist/engagement/social/plugin.d.ts +1 -0
  119. package/dist/engagement/social/plugin.d.ts.map +1 -1
  120. package/dist/engagement/social-transfers/plugin.d.ts +1 -0
  121. package/dist/engagement/social-transfers/plugin.d.ts.map +1 -1
  122. package/dist/engagement/social-transfers/schema/index.d.ts +1 -2
  123. package/dist/engagement/social-transfers/schema/index.d.ts.map +1 -1
  124. package/dist/engagement/social-transfers/schema/index.js +1 -2
  125. package/dist/engagement/social-transfers/schema/index.js.map +1 -1
  126. package/dist/engagement/social-transfers/service/__tests__/social-transfers.service.test.js +10 -0
  127. package/dist/engagement/social-transfers/service/__tests__/social-transfers.service.test.js.map +1 -1
  128. package/dist/iam/plugin.d.ts +1 -0
  129. package/dist/iam/plugin.d.ts.map +1 -1
  130. package/dist/iam/seed/data/default-admin-roles.d.ts.map +1 -1
  131. package/dist/iam/seed/data/default-admin-roles.js +3 -0
  132. package/dist/iam/seed/data/default-admin-roles.js.map +1 -1
  133. package/dist/pam/identity/admin-user-directory.d.ts +10 -0
  134. package/dist/pam/identity/admin-user-directory.d.ts.map +1 -1
  135. package/dist/pam/identity/admin-user-directory.js +12 -2
  136. package/dist/pam/identity/admin-user-directory.js.map +1 -1
  137. package/dist/pam/identity/plugin.d.ts +1 -0
  138. package/dist/pam/identity/plugin.d.ts.map +1 -1
  139. package/dist/pam/player-management/plugin.d.ts +1 -0
  140. package/dist/pam/player-management/plugin.d.ts.map +1 -1
  141. package/dist/pam/player-note/plugin.d.ts +1 -0
  142. package/dist/pam/player-note/plugin.d.ts.map +1 -1
  143. package/dist/pam/profile/plugin.d.ts +1 -0
  144. package/dist/pam/profile/plugin.d.ts.map +1 -1
  145. package/dist/pam/tag/__tests__/tag-evaluation.service.int.test.js +1 -1
  146. package/dist/pam/tag/__tests__/tag-evaluation.service.int.test.js.map +1 -1
  147. package/dist/pam/tag/contract/index.d.ts +1 -0
  148. package/dist/pam/tag/contract/index.d.ts.map +1 -1
  149. package/dist/pam/tag/contract/index.js +1 -0
  150. package/dist/pam/tag/contract/index.js.map +1 -1
  151. package/dist/pam/tag/plugin.d.ts +1 -0
  152. package/dist/pam/tag/plugin.d.ts.map +1 -1
  153. package/dist/pam/tag/service/tag-evaluation.service.js +1 -1
  154. package/dist/pam/tag/service/tag-evaluation.service.js.map +1 -1
  155. package/dist/pam/tag/service/tag-mappers.d.ts +0 -1
  156. package/dist/pam/tag/service/tag-mappers.d.ts.map +1 -1
  157. package/dist/pam/tag/service/tag-mappers.js +0 -1
  158. package/dist/pam/tag/service/tag-mappers.js.map +1 -1
  159. package/dist/pam/tag/service/tag.service.d.ts +1 -1
  160. package/dist/pam/tag/service/tag.service.d.ts.map +1 -1
  161. package/dist/pam/tag/service/tag.service.js +2 -1
  162. package/dist/pam/tag/service/tag.service.js.map +1 -1
  163. package/dist/server/auth/permissions.d.ts +12 -12
  164. package/dist/server/auth/permissions.js +2 -2
  165. package/dist/server/auth/permissions.js.map +1 -1
  166. package/dist/server/kernel/__tests__/realtime-transport.test.js +7 -0
  167. package/dist/server/kernel/__tests__/realtime-transport.test.js.map +1 -1
  168. package/dist/server/kernel/orpc-error-map.d.ts.map +1 -1
  169. package/dist/server/kernel/orpc-error-map.js +4 -1
  170. package/dist/server/kernel/orpc-error-map.js.map +1 -1
  171. package/dist/server/kernel/realtime-transport.d.ts +3 -1
  172. package/dist/server/kernel/realtime-transport.d.ts.map +1 -1
  173. package/dist/server/kernel/realtime-transport.js +22 -4
  174. package/dist/server/kernel/realtime-transport.js.map +1 -1
  175. package/dist/server/runtime/core-token-catalog.d.ts +1 -0
  176. package/dist/server/runtime/core-token-catalog.d.ts.map +1 -1
  177. package/dist/server/runtime/core-token-catalog.js +2 -1
  178. package/dist/server/runtime/core-token-catalog.js.map +1 -1
  179. package/dist/testing/realtime-transport-conformance.d.ts.map +1 -1
  180. package/dist/testing/realtime-transport-conformance.js +4 -0
  181. package/dist/testing/realtime-transport-conformance.js.map +1 -1
  182. package/dist/wallet/plugin.d.ts +1 -0
  183. package/dist/wallet/plugin.d.ts.map +1 -1
  184. package/package.json +1 -1
@@ -0,0 +1,143 @@
1
+ import { and, desc, eq, gt, isNull, or } from 'drizzle-orm';
2
+ import { serializeRow } from '@openora/core/server';
3
+ import { GLOBAL_CHAT_ROOM_ID, } from '@openora/core/contracts';
4
+ import { chatMute, chatPlatformBan, chatRoomConfiguration, chatRoomMute, chatRoom, } from '../schema/index.js';
5
+ import { ChatRoomNotFoundError, ChatAdminPrivateRoomModerationError, ChatPlayerMutedError, ChatPlayerBannedError, } from './errors/chat-moderation.errors.js';
6
+ export class ChatMuteService {
7
+ drizzle;
8
+ audit;
9
+ constructor(drizzle, audit) {
10
+ this.drizzle = drizzle;
11
+ this.audit = audit;
12
+ }
13
+ async assertCanSend(userId, roomId, isPublic = true) {
14
+ const now = new Date();
15
+ const [config] = roomId === null
16
+ ? await this.drizzle.db
17
+ .select({ readOnlyMode: chatRoomConfiguration.readOnlyMode })
18
+ .from(chatRoomConfiguration)
19
+ .innerJoin(chatRoom, eq(chatRoomConfiguration.roomId, chatRoom.id))
20
+ .where(and(eq(chatRoom.slug, GLOBAL_CHAT_ROOM_ID), isNull(chatRoom.deletedAt)))
21
+ .limit(1)
22
+ : await this.drizzle.db
23
+ .select({ readOnlyMode: chatRoomConfiguration.readOnlyMode })
24
+ .from(chatRoomConfiguration)
25
+ .where(eq(chatRoomConfiguration.roomId, roomId))
26
+ .limit(1);
27
+ if (config?.readOnlyMode) {
28
+ throw new ChatPlayerMutedError(null);
29
+ }
30
+ if (roomId !== null) {
31
+ const [roomMute] = await this.drizzle.db
32
+ .select({ id: chatRoomMute.id, expiresAt: chatRoomMute.expiresAt })
33
+ .from(chatRoomMute)
34
+ .where(and(eq(chatRoomMute.userId, userId), eq(chatRoomMute.roomId, roomId), isNull(chatRoomMute.liftedAt), or(isNull(chatRoomMute.expiresAt), gt(chatRoomMute.expiresAt, now))))
35
+ .limit(1);
36
+ if (roomMute) {
37
+ throw new ChatPlayerMutedError(roomMute.expiresAt);
38
+ }
39
+ }
40
+ const [ban] = await this.drizzle.db
41
+ .select({ id: chatPlatformBan.id, expiresAt: chatPlatformBan.expiresAt })
42
+ .from(chatPlatformBan)
43
+ .where(and(eq(chatPlatformBan.userId, userId), isNull(chatPlatformBan.liftedAt), or(isNull(chatPlatformBan.expiresAt), gt(chatPlatformBan.expiresAt, now)), roomId === null
44
+ ? or(eq(chatPlatformBan.scope, '__global'), eq(chatPlatformBan.scope, '__all_public'), eq(chatPlatformBan.scope, '__all'))
45
+ : isPublic
46
+ ? or(eq(chatPlatformBan.scope, '__all_public'), eq(chatPlatformBan.scope, '__all'), and(eq(chatPlatformBan.scope, 'room'), eq(chatPlatformBan.roomId, roomId)))
47
+ : or(eq(chatPlatformBan.scope, '__all'), and(eq(chatPlatformBan.scope, 'room'), eq(chatPlatformBan.roomId, roomId))), roomId !== null
48
+ ? or(isNull(chatPlatformBan.roomId), eq(chatPlatformBan.roomId, roomId))
49
+ : isNull(chatPlatformBan.roomId)))
50
+ .limit(1);
51
+ if (ban) {
52
+ throw new ChatPlayerBannedError(ban.expiresAt);
53
+ }
54
+ const [mute] = await this.drizzle.db
55
+ .select({ id: chatMute.id, expiresAt: chatMute.expiresAt })
56
+ .from(chatMute)
57
+ .where(and(eq(chatMute.userId, userId), isNull(chatMute.liftedAt), roomId === null
58
+ ? or(eq(chatMute.scope, '__global'), eq(chatMute.scope, '__all_public'), eq(chatMute.scope, '__all'))
59
+ : or(eq(chatMute.scope, '__all'), ...(isPublic ? [eq(chatMute.scope, '__all_public')] : []), and(eq(chatMute.scope, 'room'), eq(chatMute.roomId, roomId))), or(isNull(chatMute.expiresAt), gt(chatMute.expiresAt, now))))
60
+ .limit(1);
61
+ if (mute) {
62
+ throw new ChatPlayerMutedError(mute.expiresAt);
63
+ }
64
+ }
65
+ async mute({ userId, roomId, durationSeconds, reason, actorId, ip, userAgent, }) {
66
+ const scope = roomId === '__global' || roomId === '__all_public' || roomId === '__all' ? roomId : 'room';
67
+ const concreteRoomId = scope === 'room' ? roomId : null;
68
+ if (concreteRoomId || scope === GLOBAL_CHAT_ROOM_ID) {
69
+ const [room] = await this.drizzle.db
70
+ .select()
71
+ .from(chatRoom)
72
+ .where(and(concreteRoomId
73
+ ? eq(chatRoom.id, concreteRoomId)
74
+ : eq(chatRoom.slug, GLOBAL_CHAT_ROOM_ID), isNull(chatRoom.deletedAt)))
75
+ .limit(1);
76
+ if (!room) {
77
+ throw new ChatRoomNotFoundError(concreteRoomId ?? GLOBAL_CHAT_ROOM_ID);
78
+ }
79
+ if (!room.isPublic) {
80
+ throw new ChatAdminPrivateRoomModerationError();
81
+ }
82
+ }
83
+ const expiresAt = durationSeconds === null ? null : new Date(Date.now() + durationSeconds * 1000);
84
+ const [created] = await this.drizzle.db
85
+ .insert(chatMute)
86
+ .values({ userId, roomId: concreteRoomId, scope, mutedBy: actorId, reason, expiresAt })
87
+ .returning();
88
+ await this.audit.record({
89
+ actorId,
90
+ actorType: 'admin',
91
+ action: 'chat.mute.created',
92
+ resourceType: 'chat_mute',
93
+ resourceId: created.id,
94
+ after: { userId, roomId, reason, expiresAt: expiresAt?.toISOString() ?? null },
95
+ ip: ip ?? null,
96
+ userAgent: userAgent ?? null,
97
+ });
98
+ return { success: true };
99
+ }
100
+ async unmute({ userId, roomId, actorId, ip, userAgent, }) {
101
+ const scope = roomId === '__global' || roomId === '__all_public' || roomId === '__all' ? roomId : 'room';
102
+ const concreteRoomId = scope === 'room' ? roomId : null;
103
+ const liftedAt = new Date();
104
+ const rows = await this.drizzle.db
105
+ .update(chatMute)
106
+ .set({ liftedAt, liftedBy: actorId })
107
+ .where(and(eq(chatMute.userId, userId), scope === 'room'
108
+ ? or(eq(chatMute.scope, 'room'), and(eq(chatMute.scope, '__global'), eq(chatMute.roomId, concreteRoomId ?? '')))
109
+ : eq(chatMute.scope, scope), concreteRoomId === null ? isNull(chatMute.roomId) : eq(chatMute.roomId, concreteRoomId), isNull(chatMute.liftedAt)))
110
+ .returning({ id: chatMute.id });
111
+ await this.audit.record({
112
+ actorId,
113
+ actorType: 'admin',
114
+ action: 'chat.mute.lifted',
115
+ resourceType: 'chat_mute',
116
+ resourceId: rows[0]?.id ?? null,
117
+ after: { userId, roomId, liftedAt: liftedAt.toISOString() },
118
+ ip: ip ?? null,
119
+ userAgent: userAgent ?? null,
120
+ });
121
+ return { success: true };
122
+ }
123
+ async listMutes(userId) {
124
+ const rows = await this.drizzle.db
125
+ .select({
126
+ id: chatMute.id,
127
+ userId: chatMute.userId,
128
+ roomId: chatMute.roomId,
129
+ scope: chatMute.scope,
130
+ reason: chatMute.reason,
131
+ createdAt: chatMute.createdAt,
132
+ expiresAt: chatMute.expiresAt,
133
+ })
134
+ .from(chatMute)
135
+ .where(and(isNull(chatMute.liftedAt), userId ? eq(chatMute.userId, userId) : undefined))
136
+ .orderBy(desc(chatMute.createdAt));
137
+ return rows.map((row) => ({
138
+ ...serializeRow(row, { dateFields: ['createdAt', 'expiresAt'] }),
139
+ scope: row.scope,
140
+ }));
141
+ }
142
+ }
143
+ //# sourceMappingURL=chat-mute.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-mute.service.js","sourceRoot":"","sources":["../../../../src/engagement/chat/service/chat-mute.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import { DrizzleService } from '@openora/core/server';
2
+ import type { EventBus } from '@openora/core/server';
3
+ import type { AuditWritePort, ClientMeta, IdentityReader, RealtimeTransport, Uuid } from '@openora/core/contracts';
4
+ export declare class ChatRoomBanService {
5
+ private readonly drizzle;
6
+ private readonly events;
7
+ private readonly audit;
8
+ private readonly transport;
9
+ private readonly identityReader;
10
+ constructor(drizzle: DrizzleService, events: EventBus, audit: AuditWritePort, transport: RealtimeTransport, identityReader: IdentityReader);
11
+ private assertModerator;
12
+ banMember({ moderatorId, roomId, userId, durationSeconds, reason, ip, userAgent, }: {
13
+ moderatorId: Uuid;
14
+ roomId: Uuid;
15
+ userId: Uuid;
16
+ durationSeconds?: number | null;
17
+ reason?: string;
18
+ } & ClientMeta): Promise<{
19
+ readonly success: true;
20
+ }>;
21
+ unbanMember({ roomId, userId, moderatorId, }: {
22
+ roomId: Uuid;
23
+ userId: Uuid;
24
+ moderatorId: Uuid;
25
+ }): Promise<{
26
+ readonly success: true;
27
+ }>;
28
+ }
29
+ //# sourceMappingURL=chat-room-ban.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-room-ban.service.d.ts","sourceRoot":"","sources":["../../../../src/engagement/chat/service/chat-room-ban.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAwB,MAAM,sBAAsB,CAAC;AAC5E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EACV,cAAc,EACd,UAAU,EACV,cAAc,EACd,iBAAiB,EACjB,IAAI,EACL,MAAM,yBAAyB,CAAC;AAOjC,qBAAa,kBAAkB;IAE3B,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,cAAc;gBAJd,OAAO,EAAE,cAAc,EACvB,MAAM,EAAE,QAAQ,EAChB,KAAK,EAAE,cAAc,EACrB,SAAS,EAAE,iBAAiB,EAC5B,cAAc,EAAE,cAAc;YAGnC,eAAe;IAsBvB,SAAS,CAAC,EACd,WAAW,EACX,MAAM,EACN,MAAM,EACN,eAAsB,EACtB,MAAW,EACX,EAAE,EACF,SAAS,GACV,EAAE;QACD,WAAW,EAAE,IAAI,CAAC;QAClB,MAAM,EAAE,IAAI,CAAC;QACb,MAAM,EAAE,IAAI,CAAC;QACb,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAChC,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,GAAG,UAAU;;;IA2DR,WAAW,CAAC,EAChB,MAAM,EACN,MAAM,EACN,WAAW,GACZ,EAAE;QACD,MAAM,EAAE,IAAI,CAAC;QACb,MAAM,EAAE,IAAI,CAAC;QACb,WAAW,EAAE,IAAI,CAAC;KACnB;;;CAsBF"}
@@ -0,0 +1,105 @@
1
+ import { and, eq, isNull } from 'drizzle-orm';
2
+ import { withAdvisoryXactLock } from '@openora/core/server';
3
+ import { chatRoomBan, chatRoomMember } from '../schema/index.js';
4
+ import { ChatRoomNotModeratorError, ChatRoomSelfModerationError, } from './errors/chat-moderation.errors.js';
5
+ export class ChatRoomBanService {
6
+ drizzle;
7
+ events;
8
+ audit;
9
+ transport;
10
+ identityReader;
11
+ constructor(drizzle, events, audit, transport, identityReader) {
12
+ this.drizzle = drizzle;
13
+ this.events = events;
14
+ this.audit = audit;
15
+ this.transport = transport;
16
+ this.identityReader = identityReader;
17
+ }
18
+ async assertModerator(roomId, actorId, targetId) {
19
+ const [actor] = await this.drizzle.db
20
+ .select({ role: chatRoomMember.role })
21
+ .from(chatRoomMember)
22
+ .where(and(eq(chatRoomMember.roomId, roomId), eq(chatRoomMember.userId, actorId)))
23
+ .limit(1);
24
+ if (!actor || (actor.role !== 'moderator' && actor.role !== 'owner')) {
25
+ throw new ChatRoomNotModeratorError(roomId);
26
+ }
27
+ if (targetId) {
28
+ const [target] = await this.drizzle.db
29
+ .select({ role: chatRoomMember.role })
30
+ .from(chatRoomMember)
31
+ .where(and(eq(chatRoomMember.roomId, roomId), eq(chatRoomMember.userId, targetId)))
32
+ .limit(1);
33
+ if (actor.role !== 'owner' && target && target.role !== 'member') {
34
+ throw new ChatRoomNotModeratorError(roomId);
35
+ }
36
+ }
37
+ return actor.role;
38
+ }
39
+ async banMember({ moderatorId, roomId, userId, durationSeconds = null, reason = '', ip, userAgent, }) {
40
+ if (moderatorId === userId) {
41
+ throw new ChatRoomSelfModerationError();
42
+ }
43
+ await this.assertModerator(roomId, moderatorId, userId);
44
+ await this.drizzle.db.transaction((t) => withAdvisoryXactLock(t, `chat-room:${roomId}`, async () => {
45
+ const [existing] = await t
46
+ .select({ id: chatRoomBan.id, expiresAt: chatRoomBan.expiresAt })
47
+ .from(chatRoomBan)
48
+ .where(and(eq(chatRoomBan.roomId, roomId), eq(chatRoomBan.userId, userId), isNull(chatRoomBan.liftedAt)))
49
+ .limit(1);
50
+ if (existing && (!existing.expiresAt || existing.expiresAt > new Date())) {
51
+ return;
52
+ }
53
+ if (existing) {
54
+ await t
55
+ .update(chatRoomBan)
56
+ .set({ liftedAt: new Date(), liftedBy: moderatorId })
57
+ .where(eq(chatRoomBan.id, existing.id));
58
+ }
59
+ await t.insert(chatRoomBan).values({
60
+ roomId,
61
+ userId,
62
+ bannedBy: moderatorId,
63
+ expiresAt: durationSeconds === null ? null : new Date(Date.now() + durationSeconds * 1000),
64
+ });
65
+ await t
66
+ .delete(chatRoomMember)
67
+ .where(and(eq(chatRoomMember.roomId, roomId), eq(chatRoomMember.userId, userId)));
68
+ }));
69
+ this.events.emit('chat.room.member.banned', {
70
+ roomId,
71
+ userId,
72
+ bannedBy: moderatorId,
73
+ playerId: await this.identityReader.getPlayerIdByUserIdSafe(moderatorId),
74
+ ip: ip ?? null,
75
+ userAgent: userAgent ?? null,
76
+ });
77
+ await this.audit.record({
78
+ actorId: moderatorId,
79
+ actorType: 'player',
80
+ action: 'chat.room.member.banned',
81
+ resourceType: 'chat_room_ban',
82
+ resourceId: null,
83
+ after: { roomId, userId, durationSeconds, reason },
84
+ });
85
+ await this.transport.revokeClientFromChannel?.(userId, `chat:room:${roomId}`);
86
+ return { success: true };
87
+ }
88
+ async unbanMember({ roomId, userId, moderatorId, }) {
89
+ await this.assertModerator(roomId, moderatorId);
90
+ await this.drizzle.db
91
+ .update(chatRoomBan)
92
+ .set({ liftedAt: new Date(), liftedBy: moderatorId })
93
+ .where(and(eq(chatRoomBan.roomId, roomId), eq(chatRoomBan.userId, userId), isNull(chatRoomBan.liftedAt)));
94
+ await this.audit.record({
95
+ actorId: moderatorId,
96
+ actorType: 'player',
97
+ action: 'chat.room.member.unbanned',
98
+ resourceType: 'chat_room_ban',
99
+ resourceId: null,
100
+ after: { roomId, userId },
101
+ });
102
+ return { success: true };
103
+ }
104
+ }
105
+ //# sourceMappingURL=chat-room-ban.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-room-ban.service.js","sourceRoot":"","sources":["../../../../src/engagement/chat/service/chat-room-ban.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ import { DrizzleService } from '@openora/core/server';
2
+ import type { EventBus } from '@openora/core/server';
3
+ import { type AuditWritePort, type ClientMeta, type IdentityReader, type RealtimeTransport, type Uuid } from '@openora/core/contracts';
4
+ export declare class ChatRoomMembershipService {
5
+ private readonly drizzle;
6
+ private readonly events;
7
+ private readonly audit;
8
+ private readonly transport;
9
+ private readonly identityReader;
10
+ constructor(drizzle: DrizzleService, events: EventBus, audit: AuditWritePort, transport: RealtimeTransport, identityReader: IdentityReader);
11
+ private join;
12
+ joinRoom({ userId, joinCode, ip, userAgent }: {
13
+ userId: Uuid;
14
+ joinCode: string;
15
+ } & ClientMeta): Promise<import("@openora/core/server").SerializedRow<{
16
+ isBanned: boolean;
17
+ bannedUntil: null;
18
+ id: string;
19
+ createdAt: Date;
20
+ name: string;
21
+ slug: string;
22
+ category: "games-sports" | "regions" | "languages" | "private-channels" | null;
23
+ creatorId: string | null;
24
+ isPublic: boolean;
25
+ joinCode: string | null;
26
+ }, "createdAt" | "bannedUntil">>;
27
+ joinPublicRoom({ roomId, userId, ip, userAgent }: {
28
+ roomId: Uuid;
29
+ userId: Uuid;
30
+ } & ClientMeta): Promise<import("@openora/core/server").SerializedRow<{
31
+ isBanned: boolean;
32
+ bannedUntil: null;
33
+ id: string;
34
+ createdAt: Date;
35
+ name: string;
36
+ slug: string;
37
+ category: "games-sports" | "regions" | "languages" | "private-channels" | null;
38
+ creatorId: string | null;
39
+ isPublic: boolean;
40
+ joinCode: string | null;
41
+ }, "createdAt" | "bannedUntil">>;
42
+ adminJoinRoom({ roomId, userId, ip, userAgent }: {
43
+ roomId: Uuid;
44
+ userId: Uuid;
45
+ } & ClientMeta): Promise<import("@openora/core/server").SerializedRow<{
46
+ isBanned: boolean;
47
+ bannedUntil: null;
48
+ id: string;
49
+ createdAt: Date;
50
+ name: string;
51
+ slug: string;
52
+ category: "games-sports" | "regions" | "languages" | "private-channels" | null;
53
+ creatorId: string | null;
54
+ isPublic: boolean;
55
+ joinCode: string | null;
56
+ }, "createdAt" | "bannedUntil">>;
57
+ leaveRoom({ userId, roomId, ip, userAgent }: {
58
+ userId: Uuid;
59
+ roomId: Uuid;
60
+ } & ClientMeta): Promise<{
61
+ readonly success: true;
62
+ }>;
63
+ removeMember({ moderatorId, roomId, userId, reason, ip, userAgent, }: {
64
+ moderatorId: Uuid;
65
+ roomId: Uuid;
66
+ userId: Uuid;
67
+ reason?: string;
68
+ } & ClientMeta): Promise<{
69
+ readonly success: true;
70
+ }>;
71
+ }
72
+ //# sourceMappingURL=chat-room-membership.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-room-membership.service.d.ts","sourceRoot":"","sources":["../../../../src/engagement/chat/service/chat-room-membership.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAsC,MAAM,sBAAsB,CAAC;AAC1F,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,IAAI,EACV,MAAM,yBAAyB,CAAC;AAsBjC,qBAAa,yBAAyB;IAElC,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,cAAc;gBAJd,OAAO,EAAE,cAAc,EACvB,MAAM,EAAE,QAAQ,EAChB,KAAK,EAAE,cAAc,EACrB,SAAS,EAAE,iBAAiB,EAC5B,cAAc,EAAE,cAAc;YAGnC,IAAI;IA+ClB,QAAQ,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE;QAAE,MAAM,EAAE,IAAI,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,UAAU;;;;;;;;;;;;IAuB7F,cAAc,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE;QAAE,MAAM,EAAE,IAAI,CAAC;QAAC,MAAM,EAAE,IAAI,CAAA;KAAE,GAAG,UAAU;;;;;;;;;;;;IAS7F,aAAa,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE;QAAE,MAAM,EAAE,IAAI,CAAC;QAAC,MAAM,EAAE,IAAI,CAAA;KAAE,GAAG,UAAU;;;;;;;;;;;;IAStF,SAAS,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE;QAAE,MAAM,EAAE,IAAI,CAAC;QAAC,MAAM,EAAE,IAAI,CAAA;KAAE,GAAG,UAAU;;;IAgDxF,YAAY,CAAC,EACjB,WAAW,EACX,MAAM,EACN,MAAM,EACN,MAAW,EACX,EAAE,EACF,SAAS,GACV,EAAE;QAAE,WAAW,EAAE,IAAI,CAAC;QAAC,MAAM,EAAE,IAAI,CAAC;QAAC,MAAM,EAAE,IAAI,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,UAAU;;;CAyDnF"}
@@ -0,0 +1,176 @@
1
+ import { and, count, eq, gt, inArray, isNull, or } from 'drizzle-orm';
2
+ import { withAdvisoryXactLock, serializeRow } from '@openora/core/server';
3
+ import { chatChannel, } from '@openora/core/contracts';
4
+ import { chatRoom, chatRoomBan, chatRoomMember, chatRoomRemove } from '../schema/index.js';
5
+ import { ChatRoomBannedError, ChatRoomJoinCodeNotFoundError, ChatRoomLastModeratorError, ChatRoomNotFoundError, ChatRoomNotMemberError, ChatRoomNotModeratorError, ChatRoomSelfModerationError, } from './errors/chat-moderation.errors.js';
6
+ const MODERATOR_ROLES = ['moderator', 'owner'];
7
+ function toRoom(record) {
8
+ const { deletedAt: _deletedAt, ...room } = record;
9
+ return serializeRow({ ...room, isBanned: false, bannedUntil: null }, { dateFields: ['createdAt', 'bannedUntil'] });
10
+ }
11
+ export class ChatRoomMembershipService {
12
+ drizzle;
13
+ events;
14
+ audit;
15
+ transport;
16
+ identityReader;
17
+ constructor(drizzle, events, audit, transport, identityReader) {
18
+ this.drizzle = drizzle;
19
+ this.events = events;
20
+ this.audit = audit;
21
+ this.transport = transport;
22
+ this.identityReader = identityReader;
23
+ }
24
+ async join(roomId, userId, meta, predicate) {
25
+ const { room, inserted } = await this.drizzle.db.transaction((t) => withAdvisoryXactLock(t, `chat-room:${roomId}`, async () => {
26
+ const [room] = await t.select().from(chatRoom).where(predicate).limit(1);
27
+ if (!room) {
28
+ throw new ChatRoomNotFoundError(roomId);
29
+ }
30
+ const [ban] = await t
31
+ .select({ id: chatRoomBan.id })
32
+ .from(chatRoomBan)
33
+ .where(and(eq(chatRoomBan.roomId, roomId), eq(chatRoomBan.userId, userId), isNull(chatRoomBan.liftedAt), or(isNull(chatRoomBan.expiresAt), gt(chatRoomBan.expiresAt, new Date()))))
34
+ .limit(1);
35
+ if (ban) {
36
+ throw new ChatRoomBannedError(roomId);
37
+ }
38
+ const inserted = await t
39
+ .insert(chatRoomMember)
40
+ .values({ roomId, userId })
41
+ .onConflictDoNothing()
42
+ .returning();
43
+ return { room, inserted };
44
+ }));
45
+ if (inserted.length > 0) {
46
+ this.events.emit('chat.room.member.joined', {
47
+ roomId,
48
+ userId,
49
+ playerId: await this.identityReader.getPlayerIdByUserIdSafe(userId),
50
+ ip: meta.ip ?? null,
51
+ userAgent: meta.userAgent ?? null,
52
+ });
53
+ }
54
+ return toRoom(room);
55
+ }
56
+ joinRoom({ userId, joinCode, ip, userAgent }) {
57
+ return this.drizzle.db
58
+ .select({ id: chatRoom.id })
59
+ .from(chatRoom)
60
+ .where(and(eq(chatRoom.joinCode, joinCode), isNull(chatRoom.deletedAt)))
61
+ .limit(1)
62
+ .then(([candidate]) => {
63
+ if (!candidate) {
64
+ throw new ChatRoomJoinCodeNotFoundError(joinCode);
65
+ }
66
+ return this.join(candidate.id, userId, { ip, userAgent }, and(eq(chatRoom.id, candidate.id), eq(chatRoom.joinCode, joinCode), isNull(chatRoom.deletedAt)));
67
+ });
68
+ }
69
+ joinPublicRoom({ roomId, userId, ip, userAgent }) {
70
+ return this.join(roomId, userId, { ip, userAgent }, and(eq(chatRoom.id, roomId), eq(chatRoom.isPublic, true), isNull(chatRoom.deletedAt)));
71
+ }
72
+ adminJoinRoom({ roomId, userId, ip, userAgent }) {
73
+ return this.join(roomId, userId, { ip, userAgent }, and(eq(chatRoom.id, roomId), isNull(chatRoom.deletedAt)));
74
+ }
75
+ async leaveRoom({ userId, roomId, ip, userAgent }) {
76
+ const removed = await this.drizzle.db.transaction((t) => withAdvisoryXactLock(t, `chat-room:${roomId}`, async () => {
77
+ const [room] = await t
78
+ .select({ id: chatRoom.id, isPublic: chatRoom.isPublic })
79
+ .from(chatRoom)
80
+ .where(and(eq(chatRoom.id, roomId), isNull(chatRoom.deletedAt)))
81
+ .limit(1);
82
+ if (!room) {
83
+ throw new ChatRoomNotFoundError(roomId);
84
+ }
85
+ const [member] = await t
86
+ .select({ role: chatRoomMember.role })
87
+ .from(chatRoomMember)
88
+ .where(and(eq(chatRoomMember.roomId, roomId), eq(chatRoomMember.userId, userId)))
89
+ .limit(1);
90
+ if (!room.isPublic && !member) {
91
+ throw new ChatRoomNotMemberError(roomId);
92
+ }
93
+ if (member && (member.role === 'moderator' || member.role === 'owner')) {
94
+ const [{ modCount }] = await t
95
+ .select({ modCount: count() })
96
+ .from(chatRoomMember)
97
+ .where(and(eq(chatRoomMember.roomId, roomId), inArray(chatRoomMember.role, MODERATOR_ROLES)));
98
+ if (Number(modCount) <= 1) {
99
+ throw new ChatRoomLastModeratorError();
100
+ }
101
+ }
102
+ return t
103
+ .delete(chatRoomMember)
104
+ .where(and(eq(chatRoomMember.roomId, roomId), eq(chatRoomMember.userId, userId)))
105
+ .returning();
106
+ }));
107
+ if (removed.length > 0) {
108
+ this.events.emit('chat.room.member.left', {
109
+ roomId,
110
+ userId,
111
+ playerId: await this.identityReader.getPlayerIdByUserIdSafe(userId),
112
+ ip: ip ?? null,
113
+ userAgent: userAgent ?? null,
114
+ });
115
+ }
116
+ return { success: true };
117
+ }
118
+ async removeMember({ moderatorId, roomId, userId, reason = '', ip, userAgent, }) {
119
+ if (moderatorId === userId) {
120
+ throw new ChatRoomSelfModerationError();
121
+ }
122
+ const removed = await this.drizzle.db.transaction(async (t) => {
123
+ const [moderator] = await t
124
+ .select({ role: chatRoomMember.role })
125
+ .from(chatRoomMember)
126
+ .where(and(eq(chatRoomMember.roomId, roomId), eq(chatRoomMember.userId, moderatorId)))
127
+ .limit(1);
128
+ if (!moderator) {
129
+ throw new ChatRoomNotMemberError(roomId);
130
+ }
131
+ if (moderator.role !== 'moderator' && moderator.role !== 'owner') {
132
+ throw new ChatRoomNotModeratorError(roomId);
133
+ }
134
+ const [target] = await t
135
+ .select({ role: chatRoomMember.role })
136
+ .from(chatRoomMember)
137
+ .where(and(eq(chatRoomMember.roomId, roomId), eq(chatRoomMember.userId, userId)))
138
+ .limit(1);
139
+ if (moderator.role !== 'owner' && target && target.role !== 'member') {
140
+ throw new ChatRoomNotModeratorError(roomId);
141
+ }
142
+ const removed = await t
143
+ .delete(chatRoomMember)
144
+ .where(and(eq(chatRoomMember.roomId, roomId), eq(chatRoomMember.userId, userId)))
145
+ .returning();
146
+ if (removed.length > 0) {
147
+ await t.insert(chatRoomRemove).values({ roomId, userId, removedBy: moderatorId, reason });
148
+ }
149
+ return removed;
150
+ });
151
+ if (removed.length > 0) {
152
+ await this.audit?.record({
153
+ actorId: moderatorId,
154
+ actorType: 'player',
155
+ action: 'chat.room.member.removed',
156
+ resourceType: 'chat_room_remove',
157
+ resourceId: removed[0].id,
158
+ after: { roomId, userId, reason },
159
+ ip: ip ?? null,
160
+ userAgent: userAgent ?? null,
161
+ });
162
+ this.events.emit('chat.room.member.removed', {
163
+ roomId,
164
+ userId,
165
+ removedBy: moderatorId,
166
+ ip: ip ?? null,
167
+ userAgent: userAgent ?? null,
168
+ });
169
+ }
170
+ if (removed.length > 0) {
171
+ await this.transport?.revokeClientFromChannel?.(userId, chatChannel(roomId));
172
+ }
173
+ return { success: true };
174
+ }
175
+ }
176
+ //# sourceMappingURL=chat-room-membership.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-room-membership.service.js","sourceRoot":"","sources":["../../../../src/engagement/chat/service/chat-room-membership.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import { DrizzleService } from '@openora/core/server';
2
+ import type { AuditWritePort, Uuid } from '@openora/core/contracts';
3
+ export declare class ChatRoomMuteService {
4
+ private readonly drizzle;
5
+ private readonly audit;
6
+ constructor(drizzle: DrizzleService, audit: AuditWritePort);
7
+ private assertModerator;
8
+ muteRoomMember({ roomId, userId, moderatorId, durationSeconds, reason, }: {
9
+ roomId: Uuid;
10
+ userId: Uuid;
11
+ moderatorId: Uuid;
12
+ durationSeconds?: number | null;
13
+ reason?: string;
14
+ }): Promise<{
15
+ readonly success: true;
16
+ }>;
17
+ unmuteRoomMember({ roomId, userId, moderatorId, }: {
18
+ roomId: Uuid;
19
+ userId: Uuid;
20
+ moderatorId: Uuid;
21
+ }): Promise<{
22
+ readonly success: true;
23
+ }>;
24
+ }
25
+ //# sourceMappingURL=chat-room-mute.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-room-mute.service.d.ts","sourceRoot":"","sources":["../../../../src/engagement/chat/service/chat-room-mute.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAwB,MAAM,sBAAsB,CAAC;AAC5E,OAAO,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAOpE,qBAAa,mBAAmB;IAE5B,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,KAAK;gBADL,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,cAAc;YAG1B,eAAe;IAqBvB,cAAc,CAAC,EACnB,MAAM,EACN,MAAM,EACN,WAAW,EACX,eAAsB,EACtB,MAAW,GACZ,EAAE;QACD,MAAM,EAAE,IAAI,CAAC;QACb,MAAM,EAAE,IAAI,CAAC;QACb,WAAW,EAAE,IAAI,CAAC;QAClB,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAChC,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB;;;IAkDK,gBAAgB,CAAC,EACrB,MAAM,EACN,MAAM,EACN,WAAW,GACZ,EAAE;QACD,MAAM,EAAE,IAAI,CAAC;QACb,MAAM,EAAE,IAAI,CAAC;QACb,WAAW,EAAE,IAAI,CAAC;KACnB;;;CAsBF"}