Skip to content

Commit 4be9597

Browse files
authored
Move SSE to Matrix (#2116)
1 parent 8f1e981 commit 4be9597

File tree

70 files changed

+2473
-2589
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+2473
-2589
lines changed

.github/workflows/ci.yaml

+4
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,10 @@ jobs:
282282
- name: create realm users
283283
run: pnpm register-realm-users
284284
working-directory: packages/matrix
285+
# TODO: remove with CS-8104
286+
- name: Install watchexec
287+
run: cargo install --locked watchexec-cli
288+
if: ${{ matrix.testModule == 'realm-endpoints-test.ts' }}
285289
- name: realm server test suite
286290
run: pnpm test:wait-for-servers
287291
working-directory: packages/realm-server

packages/base/cards-grid.gts

+4-3
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ import Captions from '@cardstack/boxel-icons/captions';
4545
import CardsIcon from '@cardstack/boxel-icons/cards';
4646
import { registerDestructor } from '@ember/destroyable';
4747

48+
import type { RealmEventContent } from 'https://cardstack.com/base/matrix-event';
49+
4850
type IconComponent = typeof Captions;
4951
interface SortOption {
5052
displayName: string;
@@ -469,9 +471,8 @@ class Isolated extends Component<typeof CardsGrid> {
469471
) ?? this.filters[0];
470472
});
471473

472-
private refreshFilterList = (ev: MessageEvent) => {
473-
let data = JSON.parse(ev.data);
474-
if (ev.type === 'index' && data.type === 'incremental') {
474+
private refreshFilterList = (ev: RealmEventContent) => {
475+
if (ev.eventName === 'index' && ev.indexType === 'incremental') {
475476
this.loadFilterList.perform();
476477
}
477478
};

packages/base/matrix-event.gts

+73
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import {
1616
APP_BOXEL_MESSAGE_MSGTYPE,
1717
APP_BOXEL_ROOM_SKILLS_EVENT_TYPE,
1818
APP_BOXEL_ACTIVE_LLM,
19+
APP_BOXEL_REALM_SERVER_EVENT_MSGTYPE,
20+
APP_BOXEL_REALM_EVENT_TYPE,
1921
APP_BOXEL_COMMAND_DEFINITIONS_MSGTYPE,
2022
} from '@cardstack/runtime-common/matrix-constants';
2123
import { type SerializedFile } from './file-api';
@@ -274,6 +276,75 @@ export interface CommandResultWithNoOutputContent {
274276
commandRequestId: string;
275277
}
276278

279+
export interface RealmServerEvent extends BaseMatrixEvent {
280+
type: 'm.room.message';
281+
content: RealmServerEventContent;
282+
}
283+
284+
export interface RealmServerEventContent {
285+
msgtype: typeof APP_BOXEL_REALM_SERVER_EVENT_MSGTYPE;
286+
body: string;
287+
}
288+
289+
export interface RealmEvent extends BaseMatrixEvent {
290+
type: typeof APP_BOXEL_REALM_EVENT_TYPE;
291+
content: RealmEventContent;
292+
}
293+
294+
export type RealmEventContent =
295+
| IndexRealmEventContent
296+
| UpdateRealmEventContent;
297+
298+
export type IndexRealmEventContent =
299+
| IncrementalIndexEventContent
300+
| FullIndexEventContent
301+
| CopiedIndexEventContent
302+
| IncrementalIndexInitiationContent;
303+
304+
export interface IncrementalIndexEventContent {
305+
eventName: 'index';
306+
indexType: 'incremental';
307+
invalidations: string[];
308+
clientRequestId?: string | null;
309+
}
310+
311+
interface FullIndexEventContent {
312+
eventName: 'index';
313+
indexType: 'full';
314+
}
315+
316+
interface CopiedIndexEventContent {
317+
eventName: 'index';
318+
indexType: 'copy';
319+
sourceRealmURL: string;
320+
}
321+
322+
interface IncrementalIndexInitiationContent {
323+
eventName: 'index';
324+
indexType: 'incremental-index-initiation';
325+
updatedFile: string;
326+
}
327+
328+
export type UpdateRealmEventContent =
329+
| FileAddedEventContent
330+
| FileUpdatedEventContent
331+
| FileRemovedEventContent;
332+
333+
interface FileAddedEventContent {
334+
eventName: 'update';
335+
added: string;
336+
}
337+
338+
interface FileUpdatedEventContent {
339+
eventName: 'update';
340+
updated: string;
341+
}
342+
343+
interface FileRemovedEventContent {
344+
eventName: 'update';
345+
removed: string;
346+
}
347+
277348
export type MatrixEvent =
278349
| RoomCreateEvent
279350
| RoomJoinRules
@@ -282,6 +353,8 @@ export type MatrixEvent =
282353
| CommandResultEvent
283354
| CommandDefinitionsEvent
284355
| CardMessageEvent
356+
| RealmServerEvent
357+
| RealmEvent
285358
| RoomNameEvent
286359
| RoomTopicEvent
287360
| InviteEvent

packages/host/app/components/prerendered-card-search.gts

+4-2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import {
2020

2121
import { type Format } from 'https://cardstack.com/base/card-api';
2222

23+
import type { RealmEventContent } from 'https://cardstack.com/base/matrix-event';
24+
2325
import SubscribeToRealms from '../helpers/subscribe-to-realms';
2426
import { type HTMLComponent, htmlComponent } from '../lib/html-component';
2527

@@ -252,8 +254,8 @@ export default class PrerenderedCardSearch extends Component<Signature> {
252254
}
253255
}
254256

255-
private markRealmNeedsRefreshing = (ev: MessageEvent, realm: string) => {
256-
if (ev.type === 'index') {
257+
private markRealmNeedsRefreshing = (ev: RealmEventContent, realm: string) => {
258+
if (ev.eventName === 'index') {
257259
this.realmsNeedingRefresh.add(realm);
258260
}
259261
};

packages/host/app/helpers/subscribe-to-realms.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ import { isEqual } from 'lodash';
44

55
import { subscribeToRealm } from '@cardstack/runtime-common';
66

7+
import type { RealmEventContent } from 'https://cardstack.com/base/matrix-event';
8+
79
interface Signature {
810
Args: {
911
Positional: [
1012
realms: string[],
11-
callback: (ev: MessageEvent, realmURL: string) => void,
13+
callback: (ev: RealmEventContent, realmURL: string) => void,
1214
];
1315
};
1416
Return: void;

packages/host/app/resources/directory.ts

+10-4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import {
1212
type Relationship,
1313
} from '@cardstack/runtime-common';
1414

15+
import type { RealmEventContent } from 'https://cardstack.com/base/matrix-event';
16+
1517
import type LoaderService from '../services/loader-service';
1618
import type MessageService from '../services/message-service';
1719
import type NetworkService from '../services/network';
@@ -65,16 +67,20 @@ export class DirectoryResource extends Resource<Args> {
6567
url: realmURL.href,
6668
unsubscribe: this.messageService.subscribe(
6769
realmURL.href,
68-
({ type, data: dataStr }) => {
70+
(event: RealmEventContent) => {
6971
if (!this.directoryURL) {
7072
return;
7173
}
72-
let eventData = JSON.parse(dataStr);
73-
if (type !== 'index' || !eventData.updatedFile) {
74+
75+
if (event.eventName !== 'index') {
76+
return;
77+
}
78+
79+
if (!('updatedFile' in event)) {
7480
return;
7581
}
7682

77-
let { updatedFile } = eventData as { updatedFile: string };
83+
let { updatedFile } = event as { updatedFile: string };
7884
let segments = updatedFile.split('/');
7985
segments.pop();
8086
let updatedDir = segments.join('/').replace(/([^/])$/, '$1/'); // directories always end in '/'

packages/host/app/resources/file.ts

+8-7
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import type CardService from '@cardstack/host/services/card-service';
1515
import type OperatorModeStateService from '@cardstack/host/services/operator-mode-state-service';
1616
import type RecentFilesService from '@cardstack/host/services/recent-files-service';
1717

18+
import type { RealmEventContent } from 'https://cardstack.com/base/matrix-event';
19+
1820
import type LoaderService from '../services/loader-service';
1921
import type MessageService from '../services/message-service';
2022
import type NetworkService from '../services/network';
@@ -91,7 +93,7 @@ class _FileResource extends Resource<Args> {
9193

9294
private setSubscription(
9395
realmURL: string,
94-
callback: (ev: { type: string; data: string }) => void,
96+
callback: (ev: RealmEventContent) => void,
9597
) {
9698
if (this.subscription && this.subscription.url !== realmURL) {
9799
this.subscription.unsubscribe();
@@ -197,19 +199,18 @@ class _FileResource extends Resource<Args> {
197199
},
198200
});
199201

200-
this.setSubscription(realmURL, (event: { type: string; data: string }) => {
201-
let eventData = JSON.parse(event.data);
202+
this.setSubscription(realmURL, (event: RealmEventContent) => {
202203
if (
203-
event.type !== 'index' ||
204+
event.eventName !== 'index' ||
204205
// we wait specifically for the index complete event ("incremental") so
205206
// that the subsequent index read retrieves the latest contents of the file
206-
eventData.type !== 'incremental' ||
207-
!Array.isArray(eventData.invalidations)
207+
event.indexType !== 'incremental' ||
208+
!Array.isArray(event.invalidations)
208209
) {
209210
return;
210211
}
211212

212-
let { invalidations } = eventData as { invalidations: string[] };
213+
let { invalidations } = event as { invalidations: string[] };
213214
let normalizedURL = this.url.endsWith('.json')
214215
? this.url.replace(/\.json$/, '')
215216
: this.url;

packages/host/app/resources/room.ts

+26-3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
APP_BOXEL_COMMAND_DEFINITIONS_MSGTYPE,
1717
APP_BOXEL_COMMAND_RESULT_EVENT_TYPE,
1818
APP_BOXEL_COMMAND_RESULT_REL_TYPE,
19+
APP_BOXEL_REALM_SERVER_EVENT_MSGTYPE,
1920
DEFAULT_LLM,
2021
} from '@cardstack/runtime-common/matrix-constants';
2122

@@ -31,6 +32,7 @@ import type {
3132
MessageEvent,
3233
CommandDefinitionsEvent,
3334
CommandResultEvent,
35+
RealmServerEvent,
3436
} from 'https://cardstack.com/base/matrix-event';
3537

3638
import type { SkillCard } from 'https://cardstack.com/base/skill-card';
@@ -115,7 +117,10 @@ export class RoomResource extends Resource<Args> {
115117
case 'm.room.message':
116118
if (this.isCardFragmentEvent(event)) {
117119
await this.loadCardFragment(event);
118-
} else if (this.isCommandDefinitionsEvent(event)) {
120+
} else if (
121+
this.isCommandDefinitionsEvent(event) ||
122+
this.isRealmServerEvent(event)
123+
) {
119124
break;
120125
} else {
121126
await this.loadRoomMessage({
@@ -319,13 +324,31 @@ export class RoomResource extends Resource<Args> {
319324
}
320325

321326
private isCommandDefinitionsEvent(
322-
event: MessageEvent | CardMessageEvent | CommandDefinitionsEvent,
327+
event:
328+
| MessageEvent
329+
| CardMessageEvent
330+
| CommandDefinitionsEvent
331+
| RealmServerEvent,
323332
): event is CommandDefinitionsEvent {
324333
return event.content.msgtype === APP_BOXEL_COMMAND_DEFINITIONS_MSGTYPE;
325334
}
326335

336+
private isRealmServerEvent(
337+
event:
338+
| MessageEvent
339+
| CardMessageEvent
340+
| CommandDefinitionsEvent
341+
| RealmServerEvent,
342+
): event is RealmServerEvent {
343+
return event.content.msgtype === APP_BOXEL_REALM_SERVER_EVENT_MSGTYPE;
344+
}
345+
327346
private isCardFragmentEvent(
328-
event: MessageEvent | CardMessageEvent | CommandDefinitionsEvent,
347+
event:
348+
| MessageEvent
349+
| CardMessageEvent
350+
| CommandDefinitionsEvent
351+
| RealmServerEvent,
329352
): event is CardMessageEvent & {
330353
content: { msgtype: typeof APP_BOXEL_CARDFRAGMENT_MSGTYPE };
331354
} {

packages/host/app/resources/search.ts

+18-17
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import type { Query } from '@cardstack/runtime-common/query';
2121

2222
import type { CardDef } from 'https://cardstack.com/base/card-api';
2323

24+
import type { RealmEventContent } from 'https://cardstack.com/base/matrix-event';
25+
2426
import { asURL } from '../services/store';
2527

2628
import { type CardResource, getCard } from './card-resource';
@@ -64,23 +66,22 @@ export class Search extends Resource<Args> {
6466
if (isLive) {
6567
this.subscriptions = this.realmsToSearch.map((realm) => ({
6668
url: `${realm}_message`,
67-
unsubscribe: subscribeToRealm(
68-
realm,
69-
({ type, data }: { type: string; data: string }) => {
70-
if (query === undefined) {
71-
return;
72-
}
73-
let eventData = JSON.parse(data);
74-
// we are only interested in incremental index events
75-
if (type !== 'index' || eventData.type !== 'incremental') {
76-
return;
77-
}
78-
this.search.perform(query);
79-
if (doWhileRefreshing) {
80-
this.doWhileRefreshing.perform(doWhileRefreshing);
81-
}
82-
},
83-
),
69+
unsubscribe: subscribeToRealm(realm, (event: RealmEventContent) => {
70+
if (query === undefined) {
71+
return;
72+
}
73+
// we are only interested in incremental index events
74+
if (
75+
event.eventName !== 'index' ||
76+
event.indexType !== 'incremental'
77+
) {
78+
return;
79+
}
80+
this.search.perform(query);
81+
if (doWhileRefreshing) {
82+
this.doWhileRefreshing.perform(doWhileRefreshing);
83+
}
84+
}),
8485
}));
8586

8687
registerDestructor(this, () => {

0 commit comments

Comments
 (0)