@foony/chat 0.0.1

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 (95) hide show
  1. package/LICENSE +215 -0
  2. package/README.md +67 -0
  3. package/lib/chatClient.d.ts +22 -0
  4. package/lib/chatClient.d.ts.map +1 -0
  5. package/lib/chatClient.js +28 -0
  6. package/lib/chatClient.js.map +1 -0
  7. package/lib/index.d.ts +19 -0
  8. package/lib/index.d.ts.map +1 -0
  9. package/lib/index.js +18 -0
  10. package/lib/index.js.map +1 -0
  11. package/lib/messages.d.ts +45 -0
  12. package/lib/messages.d.ts.map +1 -0
  13. package/lib/messages.js +146 -0
  14. package/lib/messages.js.map +1 -0
  15. package/lib/occupancy.d.ts +30 -0
  16. package/lib/occupancy.d.ts.map +1 -0
  17. package/lib/occupancy.js +65 -0
  18. package/lib/occupancy.js.map +1 -0
  19. package/lib/presence.d.ts +0 -0
  20. package/lib/presence.d.ts.map +1 -0
  21. package/lib/presence.js +0 -0
  22. package/lib/presence.js.map +1 -0
  23. package/lib/protocol.d.ts +52 -0
  24. package/lib/protocol.d.ts.map +1 -0
  25. package/lib/protocol.js +72 -0
  26. package/lib/protocol.js.map +1 -0
  27. package/lib/reactions.d.ts +26 -0
  28. package/lib/reactions.d.ts.map +1 -0
  29. package/lib/reactions.js +56 -0
  30. package/lib/reactions.js.map +1 -0
  31. package/lib/reconciler.d.ts +29 -0
  32. package/lib/reconciler.d.ts.map +1 -0
  33. package/lib/reconciler.js +149 -0
  34. package/lib/reconciler.js.map +1 -0
  35. package/lib/room.d.ts +53 -0
  36. package/lib/room.d.ts.map +1 -0
  37. package/lib/room.js +81 -0
  38. package/lib/room.js.map +1 -0
  39. package/lib/rooms.d.ts +20 -0
  40. package/lib/rooms.d.ts.map +1 -0
  41. package/lib/rooms.js +38 -0
  42. package/lib/rooms.js.map +1 -0
  43. package/lib/types.d.ts +139 -0
  44. package/lib/types.d.ts.map +1 -0
  45. package/lib/types.js +9 -0
  46. package/lib/types.js.map +1 -0
  47. package/lib/typing.d.ts +43 -0
  48. package/lib/typing.d.ts.map +1 -0
  49. package/lib/typing.js +108 -0
  50. package/lib/typing.js.map +1 -0
  51. package/lib/util.d.ts +9 -0
  52. package/lib/util.d.ts.map +1 -0
  53. package/lib/util.js +12 -0
  54. package/lib/util.js.map +1 -0
  55. package/lib-cjs/chatClient.js +32 -0
  56. package/lib-cjs/chatClient.js.map +1 -0
  57. package/lib-cjs/index.js +30 -0
  58. package/lib-cjs/index.js.map +1 -0
  59. package/lib-cjs/messages.js +150 -0
  60. package/lib-cjs/messages.js.map +1 -0
  61. package/lib-cjs/occupancy.js +69 -0
  62. package/lib-cjs/occupancy.js.map +1 -0
  63. package/lib-cjs/package.json +3 -0
  64. package/lib-cjs/presence.js +0 -0
  65. package/lib-cjs/presence.js.map +1 -0
  66. package/lib-cjs/protocol.js +79 -0
  67. package/lib-cjs/protocol.js.map +1 -0
  68. package/lib-cjs/reactions.js +60 -0
  69. package/lib-cjs/reactions.js.map +1 -0
  70. package/lib-cjs/reconciler.js +153 -0
  71. package/lib-cjs/reconciler.js.map +1 -0
  72. package/lib-cjs/room.js +85 -0
  73. package/lib-cjs/room.js.map +1 -0
  74. package/lib-cjs/rooms.js +42 -0
  75. package/lib-cjs/rooms.js.map +1 -0
  76. package/lib-cjs/types.js +10 -0
  77. package/lib-cjs/types.js.map +1 -0
  78. package/lib-cjs/typing.js +112 -0
  79. package/lib-cjs/typing.js.map +1 -0
  80. package/lib-cjs/util.js +15 -0
  81. package/lib-cjs/util.js.map +1 -0
  82. package/package.json +56 -0
  83. package/src/chatClient.ts +31 -0
  84. package/src/index.ts +32 -0
  85. package/src/messages.ts +159 -0
  86. package/src/occupancy.ts +78 -0
  87. package/src/presence.ts +0 -0
  88. package/src/protocol.ts +90 -0
  89. package/src/reactions.ts +64 -0
  90. package/src/reconciler.ts +169 -0
  91. package/src/room.ts +97 -0
  92. package/src/rooms.ts +42 -0
  93. package/src/types.ts +141 -0
  94. package/src/typing.ts +123 -0
  95. package/src/util.ts +12 -0
@@ -0,0 +1,150 @@
1
+ "use strict";
2
+ /**
3
+ * Per-room message API: send, edit, delete, subscribe, and history.
4
+ *
5
+ * One {@link MessageReconciler} is shared across every subscriber and the
6
+ * history backfill, so live and replayed frames converge on identical state.
7
+ * Subscribers see only live frames; history is returned as a separate page
8
+ * (the app renders backfill itself, matching Ably's historyBeforeSubscribe).
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.Messages = void 0;
12
+ const protocol_js_1 = require("./protocol.js");
13
+ const reconciler_js_1 = require("./reconciler.js");
14
+ const util_js_1 = require("./util.js");
15
+ /**
16
+ * Retention requested for chat messages: the maximum the platform offers (1
17
+ * year). The edge clamps this down to the app's plan ceiling, so a chat message
18
+ * persists as long as the plan allows — versus typing/reactions, which are left
19
+ * at the short ephemeral default. One year in milliseconds.
20
+ */
21
+ const MESSAGE_TTL_MS = 365 * 24 * 60 * 60 * 1000;
22
+ /** The message feature of a {@link Room}. */
23
+ class Messages {
24
+ channel;
25
+ roomName;
26
+ getClientId;
27
+ reconciler;
28
+ listeners = new Set();
29
+ channelUnsubscribe = null;
30
+ constructor(channel, roomName, getClientId) {
31
+ this.channel = channel;
32
+ this.roomName = roomName;
33
+ this.getClientId = getClientId;
34
+ this.reconciler = new reconciler_js_1.MessageReconciler(roomName);
35
+ }
36
+ /** Send a new message. Returns the message optimistically; its `id` is stable. */
37
+ async send(params) {
38
+ const id = (0, util_js_1.newMessageId)();
39
+ const payload = {
40
+ v: protocol_js_1.PAYLOAD_VERSION,
41
+ action: 'create',
42
+ id,
43
+ text: params.text,
44
+ ...(params.metadata === undefined ? {} : { metadata: params.metadata }),
45
+ ...(params.headers === undefined ? {} : { headers: params.headers }),
46
+ };
47
+ await this.channel.publish(protocol_js_1.MESSAGE_EVENT, payload, { ttlMs: MESSAGE_TTL_MS });
48
+ const now = new Date();
49
+ return {
50
+ id,
51
+ clientId: this.getClientId() ?? '',
52
+ roomName: this.roomName,
53
+ text: params.text,
54
+ metadata: params.metadata ?? {},
55
+ headers: params.headers ?? {},
56
+ createdAt: now,
57
+ updatedAt: now,
58
+ action: 'create',
59
+ deleted: false,
60
+ };
61
+ }
62
+ /** Edit a message by id. Replaces text/metadata/headers (omitted fields clear). */
63
+ async update(id, params) {
64
+ const payload = {
65
+ v: protocol_js_1.PAYLOAD_VERSION,
66
+ action: 'update',
67
+ id,
68
+ text: params.text,
69
+ ...(params.metadata === undefined ? {} : { metadata: params.metadata }),
70
+ ...(params.headers === undefined ? {} : { headers: params.headers }),
71
+ };
72
+ await this.channel.publish(protocol_js_1.MESSAGE_EVENT, payload, { ttlMs: MESSAGE_TTL_MS });
73
+ }
74
+ /** Delete a message by id. */
75
+ async delete(id) {
76
+ const payload = { v: protocol_js_1.PAYLOAD_VERSION, action: 'delete', id };
77
+ await this.channel.publish(protocol_js_1.MESSAGE_EVENT, payload, { ttlMs: MESSAGE_TTL_MS });
78
+ }
79
+ /**
80
+ * Subscribe to live message changes. The first subscriber attaches the
81
+ * underlying channel subscription; the last to unsubscribe removes it.
82
+ */
83
+ subscribe(listener) {
84
+ this.listeners.add(listener);
85
+ this.ensureChannelSubscription();
86
+ return () => {
87
+ this.listeners.delete(listener);
88
+ if (this.listeners.size === 0 && this.channelUnsubscribe) {
89
+ this.channelUnsubscribe();
90
+ this.channelUnsubscribe = null;
91
+ }
92
+ };
93
+ }
94
+ /**
95
+ * Fetch a page of past messages, oldest-first, materialized through the same
96
+ * reconciler as the live stream. Pass `cursor` (a previous page's
97
+ * `nextCursor`) to page further back.
98
+ */
99
+ async history(params) {
100
+ const { messages: frames, more } = await this.channel.history({
101
+ ...(params?.limit === undefined ? {} : { limit: params.limit }),
102
+ ...(params?.cursor === undefined ? {} : { start: params.cursor }),
103
+ });
104
+ const touched = [];
105
+ for (const frame of frames) {
106
+ if (frame.name !== protocol_js_1.MESSAGE_EVENT) {
107
+ continue;
108
+ }
109
+ this.reconciler.apply(frame);
110
+ const id = messageIdOf(frame);
111
+ if (id !== null && !touched.includes(id)) {
112
+ touched.push(id);
113
+ }
114
+ }
115
+ const messages = touched
116
+ .map((id) => this.reconciler.get(id))
117
+ .filter((message) => message !== undefined);
118
+ return {
119
+ messages,
120
+ hasMore: more,
121
+ ...(frames.length > 0 && frames[0] ? { nextCursor: frames[0].messageId } : {}),
122
+ };
123
+ }
124
+ /** Lazily attach the single channel subscription that feeds the reconciler. */
125
+ ensureChannelSubscription() {
126
+ if (this.channelUnsubscribe) {
127
+ return;
128
+ }
129
+ this.channelUnsubscribe = this.channel.subscribe(protocol_js_1.MESSAGE_EVENT, (frame) => {
130
+ const event = this.reconciler.apply(frame);
131
+ if (event === null) {
132
+ return;
133
+ }
134
+ for (const listener of [...this.listeners]) {
135
+ listener(event);
136
+ }
137
+ });
138
+ }
139
+ }
140
+ exports.Messages = Messages;
141
+ /** The chat message id a frame refers to (payload `id`), or null if unparseable. */
142
+ function messageIdOf(frame) {
143
+ const data = frame.data;
144
+ if (typeof data !== 'object' || data === null) {
145
+ return null;
146
+ }
147
+ const id = data['id'];
148
+ return typeof id === 'string' && id !== '' ? id : null;
149
+ }
150
+ //# sourceMappingURL=messages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messages.js","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ /**
3
+ * Per-room occupancy, derived entirely from the presence set — no edge call.
4
+ *
5
+ * `connections` counts distinct (clientId, connectionId) pairs present (a user
6
+ * on two devices = two connections); `presenceMembers` counts distinct client
7
+ * ids. This reuses presence's TTL-based ghost-leave detection, so counts
8
+ * self-heal after a crash. Limitation: it only counts members who entered
9
+ * presence — always the case in a chat room, where joining enters presence.
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.Occupancy = void 0;
13
+ /** Default debounce for occupancy change events. */
14
+ const DEFAULT_DEBOUNCE_MS = 1_000;
15
+ /** The occupancy feature of a {@link Room}. */
16
+ class Occupancy {
17
+ presence;
18
+ listeners = new Set();
19
+ debounceMs;
20
+ debounceTimer = null;
21
+ lastEmitted = null;
22
+ membersChangedUnsubscribe = null;
23
+ constructor(presence, debounceMs) {
24
+ this.presence = presence;
25
+ this.debounceMs = debounceMs ?? DEFAULT_DEBOUNCE_MS;
26
+ }
27
+ /** Current occupancy snapshot, computed from the local presence member set. */
28
+ get() {
29
+ const members = this.presence.get();
30
+ const clientIds = new Set(members.map((member) => member.clientId));
31
+ return { connections: members.length, presenceMembers: clientIds.size };
32
+ }
33
+ /** Subscribe to occupancy changes (debounced, deduplicated). */
34
+ subscribe(listener) {
35
+ this.listeners.add(listener);
36
+ if (!this.membersChangedUnsubscribe) {
37
+ this.membersChangedUnsubscribe = this.presence.onMembersChanged(() => this.scheduleEmit());
38
+ }
39
+ return () => {
40
+ this.listeners.delete(listener);
41
+ if (this.listeners.size === 0 && this.membersChangedUnsubscribe) {
42
+ this.membersChangedUnsubscribe();
43
+ this.membersChangedUnsubscribe = null;
44
+ if (this.debounceTimer) {
45
+ clearTimeout(this.debounceTimer);
46
+ this.debounceTimer = null;
47
+ }
48
+ }
49
+ };
50
+ }
51
+ scheduleEmit() {
52
+ if (this.debounceTimer) {
53
+ return;
54
+ }
55
+ this.debounceTimer = setTimeout(() => {
56
+ this.debounceTimer = null;
57
+ const current = this.get();
58
+ if (this.lastEmitted && this.lastEmitted.connections === current.connections && this.lastEmitted.presenceMembers === current.presenceMembers) {
59
+ return;
60
+ }
61
+ this.lastEmitted = current;
62
+ for (const listener of [...this.listeners]) {
63
+ listener(current);
64
+ }
65
+ }, this.debounceMs);
66
+ }
67
+ }
68
+ exports.Occupancy = Occupancy;
69
+ //# sourceMappingURL=occupancy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"occupancy.js","sourceRoot":"","sources":["../src/occupancy.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;AASH,oDAAoD;AACpD,MAAM,mBAAmB,GAAG,KAAK,CAAC;AAElC,+CAA+C;AAC/C,MAAa,SAAS;IAQD;IAPF,SAAS,GAAG,IAAI,GAAG,EAAqB,CAAC;IACzC,UAAU,CAAS;IAC5B,aAAa,GAAyC,IAAI,CAAC;IAC3D,WAAW,GAAyB,IAAI,CAAC;IACzC,yBAAyB,GAAyB,IAAI,CAAC;IAE/D,YACmB,QAAkB,EACnC,UAAmB;QADF,aAAQ,GAAR,QAAQ,CAAU;QAGnC,IAAI,CAAC,UAAU,GAAG,UAAU,IAAI,mBAAmB,CAAC;IACtD,CAAC;IAED,+EAA+E;IAC/E,GAAG;QACD,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC;QACpC,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;QACpE,OAAO,EAAE,WAAW,EAAE,OAAO,CAAC,MAAM,EAAE,eAAe,EAAE,SAAS,CAAC,IAAI,EAAE,CAAC;IAC1E,CAAC;IAED,gEAAgE;IAChE,SAAS,CAAC,QAA2B;QACnC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC7B,IAAI,CAAC,IAAI,CAAC,yBAAyB,EAAE,CAAC;YACpC,IAAI,CAAC,yBAAyB,GAAG,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC;QAC7F,CAAC;QACD,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAChC,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,IAAI,IAAI,CAAC,yBAAyB,EAAE,CAAC;gBAChE,IAAI,CAAC,yBAAyB,EAAE,CAAC;gBACjC,IAAI,CAAC,yBAAyB,GAAG,IAAI,CAAC;gBACtC,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;oBACvB,YAAY,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;oBACjC,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;gBAC5B,CAAC;YACH,CAAC;QACH,CAAC,CAAC;IACJ,CAAC;IAEO,YAAY;QAClB,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,OAAO;QACT,CAAC;QACD,IAAI,CAAC,aAAa,GAAG,UAAU,CAAC,GAAG,EAAE;YACnC,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;YAC1B,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAC3B,IAAI,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,WAAW,CAAC,WAAW,KAAK,OAAO,CAAC,WAAW,IAAI,IAAI,CAAC,WAAW,CAAC,eAAe,KAAK,OAAO,CAAC,eAAe,EAAE,CAAC;gBAC7I,OAAO;YACT,CAAC;YACD,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC;YAC3B,KAAK,MAAM,QAAQ,IAAI,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC3C,QAAQ,CAAC,OAAO,CAAC,CAAC;YACpB,CAAC;QACH,CAAC,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;IACtB,CAAC;CACF;AAxDD,8BAwDC"}
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "commonjs"
3
+ }
Binary file
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presence.js","sourceRoot":"","sources":["../src/presence.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAWH,8CAA8C;AAC9C,MAAa,QAAQ;IAOU;IAN7B,oDAAoD;IACnC,OAAO,GAAG,IAAI,GAAG,EAA0B,CAAC;IAC5C,SAAS,GAAG,IAAI,GAAG,EAAoB,CAAC;IACxC,uBAAuB,GAAG,IAAI,GAAG,EAA0B,CAAC;IACrE,kBAAkB,GAAyB,IAAI,CAAC;IAExD,YAA6B,OAAgB;QAAhB,YAAO,GAAP,OAAO,CAAS;IAAG,CAAC;IAEjD,mEAAmE;IACnE,KAAK,CAAC,KAAK,CAAC,IAAc;QACxB,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;IAED,kDAAkD;IAClD,KAAK,CAAC,MAAM,CAAC,IAAc;QACzB,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC3C,CAAC;IAED,6CAA6C;IAC7C,KAAK,CAAC,KAAK;QACT,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;IACtC,CAAC;IAED,kFAAkF;IAClF,GAAG;QACD,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACpC,CAAC;IAED,oEAAoE;IACpE,aAAa,CAAC,QAAgB;QAC5B,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;YAC3C,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;gBACjC,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,iFAAiF;IACjF,SAAS,CAAC,QAA0B;QAClC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC7B,IAAI,CAAC,yBAAyB,EAAE,CAAC;QACjC,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAClC,CAAC,CAAC;IACJ,CAAC;IAED,sFAAsF;IACtF,gBAAgB,CAAC,QAAgC;QAC/C,IAAI,CAAC,uBAAuB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC3C,IAAI,CAAC,yBAAyB,EAAE,CAAC;QACjC,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,uBAAuB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAChD,CAAC,CAAC;IACJ,CAAC;IAEO,yBAAyB;QAC/B,IAAI,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC5B,OAAO;QACT,CAAC;QACD,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC;IACpG,CAAC;IAEO,eAAe,CAAC,KAAyB;QAC/C,MAAM,GAAG,GAAG,GAAG,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;QACtD,MAAM,MAAM,GAAmB;YAC7B,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,YAAY,EAAE,KAAK,CAAC,YAAY;YAChC,GAAG,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC;YACzD,SAAS,EAAE,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC;SACrC,CAAC;QACF,IAAI,KAAK,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;YAC7B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QAChC,CAAC;QACD,MAAM,KAAK,GAAkB,EAAE,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;QAC5D,KAAK,MAAM,QAAQ,IAAI,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;YAC3C,QAAQ,CAAC,KAAK,CAAC,CAAC;QAClB,CAAC;QACD,KAAK,MAAM,QAAQ,IAAI,CAAC,GAAG,IAAI,CAAC,uBAAuB,CAAC,EAAE,CAAC;YACzD,QAAQ,EAAE,CAAC;QACb,CAAC;IACH,CAAC;CACF;AArFD,4BAqFC"}
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ /**
3
+ * Internal wire conventions for the chat layer: the reserved `name` values
4
+ * used on the underlying realtime channel and the JSON payload shapes carried
5
+ * in each. Not part of the public API — kept in one place so producers and the
6
+ * reconciler agree.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.PAYLOAD_VERSION = exports.TYPING_EVENT = exports.REACTION_EVENT = exports.MESSAGE_EVENT = void 0;
10
+ exports.roomChannelName = roomChannelName;
11
+ exports.parseMessagePayload = parseMessagePayload;
12
+ exports.parseTypingPayload = parseTypingPayload;
13
+ exports.parseReactionPayload = parseReactionPayload;
14
+ /** Channel name for a room. The edge grammar forbids `$`, so we namespace with `:`. */
15
+ function roomChannelName(roomName) {
16
+ return `chat:${roomName}`;
17
+ }
18
+ /** Event name carrying message create/update/delete payloads. */
19
+ exports.MESSAGE_EVENT = 'chat.message';
20
+ /** Event name carrying ephemeral room-level reactions. */
21
+ exports.REACTION_EVENT = 'chat.reaction';
22
+ /** Event name carrying ephemeral typing heartbeats and stops. */
23
+ exports.TYPING_EVENT = 'chat.typing';
24
+ /** Payload schema version, bumped if the on-channel shape ever changes. */
25
+ exports.PAYLOAD_VERSION = 1;
26
+ /** Parse an unknown message payload, returning null if it is malformed or a foreign frame. */
27
+ function parseMessagePayload(data) {
28
+ if (typeof data !== 'object' || data === null) {
29
+ return null;
30
+ }
31
+ const payload = data;
32
+ const action = payload['action'];
33
+ const id = payload['id'];
34
+ if (typeof id !== 'string' || id === '') {
35
+ return null;
36
+ }
37
+ if (action === 'delete') {
38
+ return { v: exports.PAYLOAD_VERSION, action, id };
39
+ }
40
+ if ((action === 'create' || action === 'update') && typeof payload['text'] === 'string') {
41
+ const text = payload['text'];
42
+ const metadata = isRecord(payload['metadata']) ? payload['metadata'] : undefined;
43
+ const headers = isRecord(payload['headers']) ? payload['headers'] : undefined;
44
+ return {
45
+ v: exports.PAYLOAD_VERSION,
46
+ action,
47
+ id,
48
+ text,
49
+ ...(metadata === undefined ? {} : { metadata }),
50
+ ...(headers === undefined ? {} : { headers }),
51
+ };
52
+ }
53
+ return null;
54
+ }
55
+ /** Parse an unknown typing payload, returning null if malformed. */
56
+ function parseTypingPayload(data) {
57
+ if (typeof data !== 'object' || data === null) {
58
+ return null;
59
+ }
60
+ const state = data['state'];
61
+ return state === 'started' || state === 'stopped' ? { state } : null;
62
+ }
63
+ /** Parse an unknown reaction payload, returning null if malformed. */
64
+ function parseReactionPayload(data) {
65
+ if (typeof data !== 'object' || data === null) {
66
+ return null;
67
+ }
68
+ const name = data['name'];
69
+ if (typeof name !== 'string' || name === '') {
70
+ return null;
71
+ }
72
+ const metadata = data['metadata'];
73
+ return metadata === undefined ? { name } : { name, metadata };
74
+ }
75
+ /** True when `value` is a non-null, non-array object. */
76
+ function isRecord(value) {
77
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
78
+ }
79
+ //# sourceMappingURL=protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ /**
3
+ * Per-room reactions — ephemeral, fire-and-forget broadcasts (e.g. a floating
4
+ * emoji). Not stored or aggregated; distinct from per-message reactions (which
5
+ * are out of scope for v1). Pure pub/sub over the channel.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.Reactions = void 0;
9
+ const protocol_js_1 = require("./protocol.js");
10
+ /** The room-reaction feature of a {@link Room}. */
11
+ class Reactions {
12
+ channel;
13
+ getClientId;
14
+ listeners = new Set();
15
+ channelUnsubscribe = null;
16
+ constructor(channel, getClientId) {
17
+ this.channel = channel;
18
+ this.getClientId = getClientId;
19
+ }
20
+ /** Broadcast a room reaction. */
21
+ async send(params) {
22
+ const payload = {
23
+ name: params.name,
24
+ ...(params.metadata === undefined ? {} : { metadata: params.metadata }),
25
+ };
26
+ await this.channel.publish(protocol_js_1.REACTION_EVENT, payload);
27
+ }
28
+ /** Subscribe to room reactions. Attaches the channel on first listener. */
29
+ subscribe(listener) {
30
+ this.listeners.add(listener);
31
+ this.ensureChannelSubscription();
32
+ return () => {
33
+ this.listeners.delete(listener);
34
+ };
35
+ }
36
+ ensureChannelSubscription() {
37
+ if (this.channelUnsubscribe) {
38
+ return;
39
+ }
40
+ this.channelUnsubscribe = this.channel.subscribe(protocol_js_1.REACTION_EVENT, (frame) => {
41
+ const payload = (0, protocol_js_1.parseReactionPayload)(frame.data);
42
+ if (payload === null) {
43
+ return;
44
+ }
45
+ const clientId = frame.clientId ?? '';
46
+ const reaction = {
47
+ name: payload.name,
48
+ clientId,
49
+ ...(payload.metadata === undefined ? {} : { metadata: payload.metadata }),
50
+ createdAt: new Date(frame.timestamp),
51
+ isSelf: clientId !== '' && clientId === this.getClientId(),
52
+ };
53
+ for (const subscriber of [...this.listeners]) {
54
+ subscriber(reaction);
55
+ }
56
+ });
57
+ }
58
+ }
59
+ exports.Reactions = Reactions;
60
+ //# sourceMappingURL=reactions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reactions.js","sourceRoot":"","sources":["../src/reactions.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;AAGH,+CAA2F;AAM3F,mDAAmD;AACnD,MAAa,SAAS;IAKD;IACA;IALF,SAAS,GAAG,IAAI,GAAG,EAAoB,CAAC;IACjD,kBAAkB,GAAyB,IAAI,CAAC;IAExD,YACmB,OAAgB,EAChB,WAAgC;QADhC,YAAO,GAAP,OAAO,CAAS;QAChB,gBAAW,GAAX,WAAW,CAAqB;IAChD,CAAC;IAEJ,iCAAiC;IACjC,KAAK,CAAC,IAAI,CAAC,MAA4C;QACrD,MAAM,OAAO,GAAoB;YAC/B,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,GAAG,CAAC,MAAM,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;SACxE,CAAC;QACF,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,4BAAc,EAAE,OAAO,CAAC,CAAC;IACtD,CAAC;IAED,2EAA2E;IAC3E,SAAS,CAAC,QAA0B;QAClC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC7B,IAAI,CAAC,yBAAyB,EAAE,CAAC;QACjC,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAClC,CAAC,CAAC;IACJ,CAAC;IAEO,yBAAyB;QAC/B,IAAI,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC5B,OAAO;QACT,CAAC;QACD,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,4BAAc,EAAE,CAAC,KAAK,EAAE,EAAE;YACzE,MAAM,OAAO,GAAG,IAAA,kCAAoB,EAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACjD,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;gBACrB,OAAO;YACT,CAAC;YACD,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,IAAI,EAAE,CAAC;YACtC,MAAM,QAAQ,GAAiB;gBAC7B,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,QAAQ;gBACR,GAAG,CAAC,OAAO,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC;gBACzE,SAAS,EAAE,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC;gBACpC,MAAM,EAAE,QAAQ,KAAK,EAAE,IAAI,QAAQ,KAAK,IAAI,CAAC,WAAW,EAAE;aAC3D,CAAC;YACF,KAAK,MAAM,UAAU,IAAI,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC7C,UAAU,CAAC,QAAQ,CAAC,CAAC;YACvB,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;CACF;AAjDD,8BAiDC"}
@@ -0,0 +1,153 @@
1
+ "use strict";
2
+ /**
3
+ * Message reconciler: folds a stream of create/update/delete frames into
4
+ * materialized {@link Message} state, keyed by message id. The same reconciler
5
+ * runs over the live subscription and over replayed history, so both paths
6
+ * converge on identical state regardless of delivery order or duplicates.
7
+ *
8
+ * Version ordering uses the transport `(timestamp, messageId)` pair: a later
9
+ * mutation wins, and a re-delivered frame (same messageId) is ignored.
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.MessageReconciler = void 0;
13
+ const protocol_js_1 = require("./protocol.js");
14
+ /** Stateful reconciler for a single room's messages. */
15
+ class MessageReconciler {
16
+ roomName;
17
+ byId = new Map();
18
+ constructor(roomName) {
19
+ this.roomName = roomName;
20
+ }
21
+ /**
22
+ * Fold one channel frame into state. Returns the event a subscriber should
23
+ * see, or null when the frame is malformed, a duplicate, or stale.
24
+ */
25
+ apply(frame) {
26
+ const payload = (0, protocol_js_1.parseMessagePayload)(frame.data);
27
+ if (payload === null) {
28
+ return null;
29
+ }
30
+ const timestamp = frame.timestamp;
31
+ const versionId = frame.messageId;
32
+ if (payload.action === 'create') {
33
+ return this.applyCreate(payload, frame.clientId ?? '', timestamp, versionId);
34
+ }
35
+ return this.applyMutation(payload, frame.clientId ?? '', timestamp, versionId);
36
+ }
37
+ /** The current materialized state of one message, if known. */
38
+ get(id) {
39
+ return this.byId.get(id)?.message;
40
+ }
41
+ /** Current materialized messages, oldest-first by creation then id. */
42
+ snapshot() {
43
+ return [...this.byId.values()]
44
+ .map((entry) => entry.message)
45
+ .sort((left, right) => left.createdAt.getTime() - right.createdAt.getTime() || (left.id < right.id ? -1 : left.id > right.id ? 1 : 0));
46
+ }
47
+ applyCreate(payload, clientId, timestamp, versionId) {
48
+ const existing = this.byId.get(payload.id);
49
+ if (existing) {
50
+ if (existing.applied.has(versionId)) {
51
+ return null;
52
+ }
53
+ existing.applied.add(versionId);
54
+ // A mutation arrived before its create: backfill author/createdAt, and
55
+ // adopt the create's body only if it is newer than the winning mutation
56
+ // (i.e. no later edit/delete has superseded it).
57
+ const createdAt = new Date(timestamp);
58
+ const adoptBody = isNewer(existing.versionTs, existing.versionId, timestamp, versionId);
59
+ existing.createSeen = true;
60
+ existing.message = {
61
+ ...existing.message,
62
+ clientId,
63
+ createdAt,
64
+ ...(adoptBody
65
+ ? {
66
+ text: payload.text,
67
+ metadata: payload.metadata ?? {},
68
+ headers: payload.headers ?? {},
69
+ updatedAt: createdAt,
70
+ action: 'create',
71
+ deleted: false,
72
+ }
73
+ : {}),
74
+ };
75
+ if (adoptBody) {
76
+ existing.versionTs = timestamp;
77
+ existing.versionId = versionId;
78
+ }
79
+ return { type: 'created', message: existing.message };
80
+ }
81
+ const createdAt = new Date(timestamp);
82
+ const message = {
83
+ id: payload.id,
84
+ clientId,
85
+ roomName: this.roomName,
86
+ text: payload.text,
87
+ metadata: payload.metadata ?? {},
88
+ headers: payload.headers ?? {},
89
+ createdAt,
90
+ updatedAt: createdAt,
91
+ action: 'create',
92
+ deleted: false,
93
+ };
94
+ this.byId.set(payload.id, { message, createSeen: true, versionTs: timestamp, versionId, applied: new Set([versionId]) });
95
+ return { type: 'created', message };
96
+ }
97
+ applyMutation(payload, clientId, timestamp, versionId) {
98
+ let entry = this.byId.get(payload.id);
99
+ if (!entry) {
100
+ // Mutation before create: stand up a placeholder the create can complete.
101
+ const createdAt = new Date(timestamp);
102
+ const placeholder = {
103
+ id: payload.id,
104
+ clientId,
105
+ roomName: this.roomName,
106
+ text: '',
107
+ metadata: {},
108
+ headers: {},
109
+ createdAt,
110
+ updatedAt: createdAt,
111
+ action: 'create',
112
+ deleted: false,
113
+ };
114
+ entry = { message: placeholder, createSeen: false, versionTs: -1, versionId: '', applied: new Set() };
115
+ this.byId.set(payload.id, entry);
116
+ }
117
+ if (entry.applied.has(versionId)) {
118
+ return null;
119
+ }
120
+ entry.applied.add(versionId);
121
+ if (!isNewer(entry.versionTs, entry.versionId, timestamp, versionId)) {
122
+ return null;
123
+ }
124
+ const updatedAt = new Date(timestamp);
125
+ if (payload.action === 'delete') {
126
+ entry.message = { ...entry.message, text: '', metadata: {}, headers: {}, updatedAt, action: 'delete', deleted: true };
127
+ entry.versionTs = timestamp;
128
+ entry.versionId = versionId;
129
+ return { type: 'deleted', message: entry.message };
130
+ }
131
+ entry.message = {
132
+ ...entry.message,
133
+ text: payload.text,
134
+ metadata: payload.metadata ?? {},
135
+ headers: payload.headers ?? {},
136
+ updatedAt,
137
+ action: 'update',
138
+ deleted: false,
139
+ };
140
+ entry.versionTs = timestamp;
141
+ entry.versionId = versionId;
142
+ return { type: 'updated', message: entry.message };
143
+ }
144
+ }
145
+ exports.MessageReconciler = MessageReconciler;
146
+ /** True when `(ts, id)` is strictly newer than `(priorTs, priorId)`. */
147
+ function isNewer(priorTs, priorId, ts, id) {
148
+ if (ts !== priorTs) {
149
+ return ts > priorTs;
150
+ }
151
+ return id > priorId;
152
+ }
153
+ //# sourceMappingURL=reconciler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconciler.js","sourceRoot":"","sources":["../src/reconciler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ /**
3
+ * A chat room: one realtime channel (`chat:<name>`) carrying messages,
4
+ * presence, typing, reactions and occupancy. Lifecycle (`attach`/`detach`/
5
+ * status) delegates to the underlying channel rather than duplicating a state
6
+ * machine.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.Room = void 0;
10
+ const messages_js_1 = require("./messages.js");
11
+ const occupancy_js_1 = require("./occupancy.js");
12
+ const presence_js_1 = require("./presence.js");
13
+ const reactions_js_1 = require("./reactions.js");
14
+ const typing_js_1 = require("./typing.js");
15
+ /** A chat room and its features. Obtain via `chatClient.rooms.get(name)`. */
16
+ class Room {
17
+ name;
18
+ channel;
19
+ /** Messages: send, edit, delete, subscribe, history. */
20
+ messages;
21
+ /** Presence: enter/update/leave plus a local member snapshot. */
22
+ presence;
23
+ /** Typing indicators. */
24
+ typing;
25
+ /** Ephemeral room-level reactions. */
26
+ reactions;
27
+ /** Occupancy counts derived from presence. */
28
+ occupancy;
29
+ discontinuityListeners = new Set();
30
+ hadBeenAttached = false;
31
+ constructor(
32
+ /** Room name (without the `chat:` channel prefix). */
33
+ name, channel, getClientId, options) {
34
+ this.name = name;
35
+ this.channel = channel;
36
+ this.messages = new messages_js_1.Messages(channel, name, getClientId);
37
+ this.presence = new presence_js_1.Presence(channel);
38
+ this.typing = new typing_js_1.Typing(channel, getClientId, options?.typing?.heartbeatThrottleMs);
39
+ this.reactions = new reactions_js_1.Reactions(channel, getClientId);
40
+ this.occupancy = new occupancy_js_1.Occupancy(this.presence, options?.occupancy?.debounceMs);
41
+ channel.on((change) => this.onChannelState(change));
42
+ }
43
+ /** Current room status. */
44
+ get status() {
45
+ return this.channel.state;
46
+ }
47
+ /** Ensure the room is attached so messages and presence flow. */
48
+ async attach() {
49
+ await this.channel.attach();
50
+ }
51
+ /** Detach from the room (stop receiving). Listeners are preserved. */
52
+ async detach() {
53
+ await this.channel.detach();
54
+ }
55
+ /** Subscribe to room status changes. Returns an unsubscribe function. */
56
+ onStatusChange(listener) {
57
+ return this.channel.on(listener);
58
+ }
59
+ /**
60
+ * Subscribe to continuity gaps — best-effort: fires when the room re-attaches
61
+ * without a resume, so the app can backfill via `messages.history()`.
62
+ */
63
+ onDiscontinuity(listener) {
64
+ this.discontinuityListeners.add(listener);
65
+ return () => {
66
+ this.discontinuityListeners.delete(listener);
67
+ };
68
+ }
69
+ onChannelState(change) {
70
+ if (change.current === 'attached') {
71
+ if (this.hadBeenAttached && !change.resumed) {
72
+ for (const listener of [...this.discontinuityListeners]) {
73
+ listener(change.reason);
74
+ }
75
+ }
76
+ this.hadBeenAttached = true;
77
+ }
78
+ else if (change.current === 'detached') {
79
+ // A deliberate detach/re-attach is not a continuity gap.
80
+ this.hadBeenAttached = false;
81
+ }
82
+ }
83
+ }
84
+ exports.Room = Room;
85
+ //# sourceMappingURL=room.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"room.js","sourceRoot":"","sources":["../src/room.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAGH,+CAAyC;AACzC,iDAA2C;AAC3C,+CAAyC;AACzC,iDAA2C;AAC3C,2CAAqC;AASrC,6EAA6E;AAC7E,MAAa,IAAI;IAiBJ;IACQ;IAjBnB,wDAAwD;IAC/C,QAAQ,CAAW;IAC5B,iEAAiE;IACxD,QAAQ,CAAW;IAC5B,yBAAyB;IAChB,MAAM,CAAS;IACxB,sCAAsC;IAC7B,SAAS,CAAY;IAC9B,8CAA8C;IACrC,SAAS,CAAY;IAEb,sBAAsB,GAAG,IAAI,GAAG,EAAyB,CAAC;IACnE,eAAe,GAAG,KAAK,CAAC;IAEhC;IACE,sDAAsD;IAC7C,IAAY,EACJ,OAAgB,EACjC,WAAgC,EAChC,OAAqB;QAHZ,SAAI,GAAJ,IAAI,CAAQ;QACJ,YAAO,GAAP,OAAO,CAAS;QAIjC,IAAI,CAAC,QAAQ,GAAG,IAAI,sBAAQ,CAAC,OAAO,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;QACzD,IAAI,CAAC,QAAQ,GAAG,IAAI,sBAAQ,CAAC,OAAO,CAAC,CAAC;QACtC,IAAI,CAAC,MAAM,GAAG,IAAI,kBAAM,CAAC,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,mBAAmB,CAAC,CAAC;QACrF,IAAI,CAAC,SAAS,GAAG,IAAI,wBAAS,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;QACrD,IAAI,CAAC,SAAS,GAAG,IAAI,wBAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;QAC9E,OAAO,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC;IACtD,CAAC;IAED,2BAA2B;IAC3B,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC;IAC5B,CAAC;IAED,iEAAiE;IACjE,KAAK,CAAC,MAAM;QACV,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;IAC9B,CAAC;IAED,sEAAsE;IACtE,KAAK,CAAC,MAAM;QACV,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;IAC9B,CAAC;IAED,yEAAyE;IACzE,cAAc,CAAC,QAA8C;QAC3D,OAAO,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC;IACnC,CAAC;IAED;;;OAGG;IACH,eAAe,CAAC,QAA+B;QAC7C,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC1C,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,sBAAsB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC/C,CAAC,CAAC;IACJ,CAAC;IAEO,cAAc,CAAC,MAA0B;QAC/C,IAAI,MAAM,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;YAClC,IAAI,IAAI,CAAC,eAAe,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBAC5C,KAAK,MAAM,QAAQ,IAAI,CAAC,GAAG,IAAI,CAAC,sBAAsB,CAAC,EAAE,CAAC;oBACxD,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;gBAC1B,CAAC;YACH,CAAC;YACD,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;QAC9B,CAAC;aAAM,IAAI,MAAM,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;YACzC,yDAAyD;YACzD,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC;QAC/B,CAAC;IACH,CAAC;CACF;AA1ED,oBA0EC"}