remult 0.17.0-exp.3 → 0.17.0-exp.4

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 (59) hide show
  1. package/esm/live-query/ably.d.ts +13 -0
  2. package/esm/live-query/ably.js +25 -0
  3. package/esm/live-query/ablyLiveQuery.d.ts +13 -0
  4. package/esm/live-query/ablyLiveQuery.js +25 -0
  5. package/esm/live-query/index.d.ts +2 -0
  6. package/esm/live-query/index.js +2 -0
  7. package/esm/live-query/live-query.d.ts +3 -0
  8. package/esm/live-query/live-query.js +2 -0
  9. package/esm/live-query.d.ts +3 -0
  10. package/esm/live-query.js +2 -0
  11. package/esm/remult-express.d.ts +9 -17
  12. package/esm/remult-express.js +59 -75
  13. package/esm/server/expressBridge.js +2 -2
  14. package/esm/src/context.d.ts +15 -1
  15. package/esm/src/context.js +11 -5
  16. package/esm/src/data-api.d.ts +2 -6
  17. package/esm/src/data-api.js +8 -5
  18. package/esm/src/live-query/EventSourceLiveQueryProvider.d.ts +2 -4
  19. package/esm/src/live-query/EventSourceLiveQueryProvider.js +37 -13
  20. package/esm/src/live-query/LiveQueryPublisher.d.ts +10 -22
  21. package/esm/src/live-query/LiveQueryPublisher.js +47 -78
  22. package/esm/src/live-query/LiveQuerySubscriber.d.ts +13 -19
  23. package/esm/src/live-query/LiveQuerySubscriber.js +78 -105
  24. package/esm/src/live-query/ablyLiveQuery.d.ts +13 -0
  25. package/esm/src/live-query/ablyLiveQuery.js +25 -0
  26. package/esm/src/live-query/index.d.ts +2 -0
  27. package/esm/src/live-query/index.js +1 -0
  28. package/esm/src/remult-proxy.js +6 -6
  29. package/esm/src/remult3/RepositoryImplementation.js +3 -3
  30. package/esm/src/server-action.js +60 -7
  31. package/live-query/ably.d.ts +13 -0
  32. package/live-query/ably.js +38 -0
  33. package/live-query/ablyLiveQuery.d.ts +13 -0
  34. package/live-query/ablyLiveQuery.js +38 -0
  35. package/live-query/index.d.ts +2 -0
  36. package/live-query/index.js +7 -0
  37. package/live-query/live-query.d.ts +3 -0
  38. package/live-query/live-query.js +10 -0
  39. package/live-query.d.ts +3 -0
  40. package/live-query.js +7 -0
  41. package/package.json +9 -1
  42. package/remult-express.d.ts +9 -17
  43. package/remult-express.js +71 -119
  44. package/server/expressBridge.js +2 -2
  45. package/src/context.d.ts +15 -1
  46. package/src/context.js +11 -5
  47. package/src/data-api.d.ts +2 -6
  48. package/src/data-api.js +8 -5
  49. package/src/live-query/EventSourceLiveQueryProvider.d.ts +2 -4
  50. package/src/live-query/EventSourceLiveQueryProvider.js +38 -15
  51. package/src/live-query/LiveQueryPublisher.d.ts +10 -22
  52. package/src/live-query/LiveQueryPublisher.js +81 -161
  53. package/src/live-query/LiveQuerySubscriber.d.ts +13 -19
  54. package/src/live-query/LiveQuerySubscriber.js +124 -125
  55. package/src/live-query/ablyLiveQuery.d.ts +13 -0
  56. package/src/live-query/ablyLiveQuery.js +38 -0
  57. package/src/remult-proxy.js +6 -6
  58. package/src/remult3/RepositoryImplementation.js +3 -3
  59. package/src/server-action.js +120 -20
@@ -0,0 +1,13 @@
1
+ import type * as Ably from 'ably';
2
+ import { ServerEventDispatcher } from '../src/live-query/LiveQueryPublisher';
3
+ import { LiveQueryProvider, PubSubClient } from '../src/live-query/LiveQuerySubscriber';
4
+ export declare class AblyLiveQueryProvider implements LiveQueryProvider {
5
+ private ably;
6
+ constructor(ably: Ably.Types.RealtimePromise);
7
+ openStreamAndReturnCloseFunction(onReconnect: VoidFunction): Promise<PubSubClient>;
8
+ }
9
+ export declare class AblyServerEventDispatcher implements ServerEventDispatcher {
10
+ private ably;
11
+ constructor(ably: Ably.Types.RealtimePromise);
12
+ sendChannelMessage<T>(channel: string, message: T): void;
13
+ }
@@ -0,0 +1,25 @@
1
+ export class AblyLiveQueryProvider {
2
+ constructor(ably) {
3
+ this.ably = ably;
4
+ }
5
+ async openStreamAndReturnCloseFunction(onReconnect) {
6
+ return {
7
+ disconnect: () => {
8
+ this.ably.connection.close();
9
+ },
10
+ subscribe: (channel, handler) => {
11
+ let myHandler = (y) => handler(y.data);
12
+ this.ably.channels.get(channel).subscribe(y => myHandler(y));
13
+ return () => { myHandler = () => { }; };
14
+ }
15
+ };
16
+ }
17
+ }
18
+ export class AblyServerEventDispatcher {
19
+ constructor(ably) {
20
+ this.ably = ably;
21
+ }
22
+ sendChannelMessage(channel, message) {
23
+ this.ably.channels.get(channel).publish({ data: message });
24
+ }
25
+ }
@@ -0,0 +1,13 @@
1
+ import type * as Ably from 'ably';
2
+ import { ServerEventDispatcher } from './LiveQueryPublisher';
3
+ import { LiveQueryProvider, PubSubClient } from './LiveQuerySubscriber';
4
+ export declare class AblyLiveQueryProvider implements LiveQueryProvider {
5
+ private ably;
6
+ constructor(ably: Ably.Types.RealtimePromise);
7
+ openStreamAndReturnCloseFunction(onReconnect: VoidFunction): Promise<PubSubClient>;
8
+ }
9
+ export declare class AblyServerEventDispatcher implements ServerEventDispatcher {
10
+ private ably;
11
+ constructor(ably: Ably.Types.RealtimePromise);
12
+ sendChannelMessage<T>(channel: string, message: T): void;
13
+ }
@@ -0,0 +1,25 @@
1
+ export class AblyLiveQueryProvider {
2
+ constructor(ably) {
3
+ this.ably = ably;
4
+ }
5
+ async openStreamAndReturnCloseFunction(onReconnect) {
6
+ return {
7
+ disconnect: () => {
8
+ this.ably.connection.close();
9
+ },
10
+ subscribe: (channel, handler) => {
11
+ let myHandler = (y) => handler(y.data);
12
+ this.ably.channels.get(channel).subscribe(y => myHandler(y));
13
+ return () => { myHandler = () => { }; };
14
+ }
15
+ };
16
+ }
17
+ }
18
+ export class AblyServerEventDispatcher {
19
+ constructor(ably) {
20
+ this.ably = ably;
21
+ }
22
+ sendChannelMessage(channel, message) {
23
+ this.ably.channels.get(channel).publish({ data: message });
24
+ }
25
+ }
@@ -0,0 +1,2 @@
1
+ export { LiveQueryPublisher, ServerEventDispatcher } from "../src/live-query/LiveQueryPublisher";
2
+ export { PubSubClient, LiveQueryProvider, LiveQueryClient } from '../src/live-query/LiveQuerySubscriber';
@@ -0,0 +1,2 @@
1
+ export { LiveQueryPublisher } from "../src/live-query/LiveQueryPublisher";
2
+ export { LiveQueryClient } from '../src/live-query/LiveQuerySubscriber';
@@ -0,0 +1,3 @@
1
+ export { LiveQueryPublisher, ServerEventDispatcher } from "./src/live-query/LiveQueryPublisher";
2
+ export { PubSubClient, LiveQueryProvider, LiveQueryClient } from './src/live-query/LiveQuerySubscriber';
3
+ export {};
@@ -0,0 +1,2 @@
1
+ export { LiveQueryPublisher, ServerEventDispatcher } from "./src/live-query/LiveQueryPublisher";
2
+ export { PubSubClient, LiveQueryProvider, LiveQueryClient } from './src/live-query/LiveQuerySubscriber';
@@ -0,0 +1,3 @@
1
+ export { LiveQueryPublisher, ServerEventDispatcher } from "./src/live-query/LiveQueryPublisher";
2
+ export { PubSubClient, LiveQueryProvider, LiveQueryClient } from './src/live-query/LiveQuerySubscriber';
3
+ export {};
@@ -0,0 +1,2 @@
1
+ export { LiveQueryPublisher } from "./src/live-query/LiveQueryPublisher";
2
+ export { LiveQueryClient } from './src/live-query/LiveQuerySubscriber';
@@ -1,38 +1,30 @@
1
1
  import * as express from 'express';
2
2
  import { RemultServer, RemultServerOptions } from './server/expressBridge';
3
3
  import { Remult } from './src/context';
4
- import { AMessageChannel, ChannelSubscribe } from './src/live-query/LiveQuerySubscriber';
5
- import { ServerEventDispatcher, ServerEventMessage } from './src/live-query/LiveQueryPublisher';
4
+ import { ServerEventChannelSubscribeDTO } from './src/live-query/LiveQuerySubscriber';
5
+ import { ServerEventDispatcher } from './src/live-query/LiveQueryPublisher';
6
6
  export declare function remultExpress(options?: RemultServerOptions<express.Request> & {
7
7
  bodyParser?: boolean;
8
8
  bodySizeLimit?: string;
9
- messageChannels?: AMessageChannel<any>[];
9
+ serverEventDispatcher?: (router: express.Router, server: RemultServer) => ServerEventDispatcher;
10
10
  }): express.RequestHandler & RemultServer;
11
11
  export declare class ServerEventsController implements ServerEventDispatcher {
12
- private channels;
13
- private messageHistoryLength;
12
+ private canUserConnectToChannel?;
13
+ subscribeToChannel({ channel, clientId }: ServerEventChannelSubscribeDTO, res: import('express').Response, remult: Remult, remove?: boolean): void;
14
14
  consoleInfo(): void;
15
- subscribeToChannel(remult: Remult, info: ChannelSubscribe, res: import('express').Response): void;
16
15
  connections: clientConnection[];
17
- constructor(channels: AMessageChannel<any>[], messageHistoryLength?: number);
18
- sendQueryMessage({ message, clientId, queryId }: ServerEventMessage): void;
16
+ constructor(canUserConnectToChannel?: (channel: string, remult: Remult) => boolean);
19
17
  sendChannelMessage<T>(channel: string, message: any): void;
20
18
  openHttpServerStream(req: import('express').Request, res: import('express').Response): clientConnection;
21
- messages: {
22
- id: number;
23
- message: any;
24
- eventType: string;
25
- }[];
26
- SendMessage(message: any, eventType?: string): void;
27
19
  }
28
20
  declare class clientConnection {
29
21
  response: import('express').Response;
30
- clientId: string;
31
22
  channels: Record<string, boolean>;
32
23
  close(): void;
33
24
  closed: boolean;
34
- write(id: number, message: any, eventType: string): void;
35
- constructor(response: import('express').Response, clientId: string);
25
+ write(eventData: string, eventType?: string): void;
26
+ connectionId: any;
27
+ constructor(response: import('express').Response);
36
28
  sendLiveMessage(): void;
37
29
  }
38
30
  export {};
@@ -2,6 +2,8 @@ import * as express from 'express';
2
2
  import { createRemultServer } from './server/expressBridge';
3
3
  import { streamUrl } from './src/live-query/LiveQuerySubscriber';
4
4
  import { LiveQueryPublisher } from './src/live-query/LiveQueryPublisher';
5
+ import { v4 as uuid } from 'uuid';
6
+ import { remult } from './src/remult-proxy';
5
7
  export function remultExpress(options) {
6
8
  let app = express.Router();
7
9
  if (!options) {
@@ -16,21 +18,12 @@ export function remultExpress(options) {
16
18
  }
17
19
  const server = createRemultServer(options);
18
20
  server.registerRouter(app);
19
- let httpServerEvents = new ServerEventsController(options.messageChannels);
20
- server.liveQueryManager = new LiveQueryPublisher(httpServerEvents);
21
- const streamPath = options.rootPath + '/' + streamUrl;
22
- app.get(streamPath, (req, res) => {
23
- httpServerEvents.openHttpServerStream(req, res);
24
- });
25
- app.post(streamPath, (r, res, next) => server.withRemult(r, res, next), (req, res) => {
26
- server.liveQueryManager.unsubscribe(req.body);
27
- res.json("ok");
28
- //httpServerEvents.subscribeToChannel(remult, req.body, res)
29
- });
30
- app.get(streamPath + '/stats', (req, res) => {
31
- httpServerEvents.consoleInfo();
32
- res.json("ok");
33
- });
21
+ if (!options.serverEventDispatcher) {
22
+ options.serverEventDispatcher = (router, server) => {
23
+ return buildHttpServerEventDispatcher(router, options.rootPath, server);
24
+ };
25
+ }
26
+ server.liveQueryManager = new LiveQueryPublisher(options.serverEventDispatcher(app, server));
34
27
  return Object.assign(app, {
35
28
  getRemult: (req) => server.getRemult(req),
36
29
  openApiDoc: (options) => server.openApiDoc(options),
@@ -39,52 +32,40 @@ export function remultExpress(options) {
39
32
  });
40
33
  }
41
34
  export class ServerEventsController {
42
- constructor(channels, messageHistoryLength = 1000) {
43
- this.channels = channels;
44
- this.messageHistoryLength = messageHistoryLength;
35
+ constructor(canUserConnectToChannel) {
36
+ this.canUserConnectToChannel = canUserConnectToChannel;
45
37
  this.connections = [];
46
- this.messages = [];
47
- if (channels) {
48
- for (const c of channels) {
49
- c.dispatcher = this;
38
+ if (!this.canUserConnectToChannel) {
39
+ this.canUserConnectToChannel = () => true;
40
+ }
41
+ }
42
+ subscribeToChannel({ channel, clientId }, res, remult, remove = false) {
43
+ for (const c of this.connections) {
44
+ if (c.connectionId === clientId) {
45
+ if (this.canUserConnectToChannel(channel, remult)) {
46
+ c.channels[channel] = !remove;
47
+ res.json("ok");
48
+ return;
49
+ }
50
+ else {
51
+ res.sendStatus(403);
52
+ return;
53
+ }
50
54
  }
51
55
  }
56
+ res.sendStatus(404);
52
57
  }
53
58
  consoleInfo() {
54
59
  console.info(this.connections.map(x => ({
55
- client: x.clientId,
60
+ client: x.connectionId,
56
61
  channels: x.channels
57
62
  })));
58
63
  }
59
- subscribeToChannel(remult, info, res) {
60
- let ok = false;
61
- if (this.channels)
62
- for (const sc of this.connections) {
63
- if (sc.clientId === info.clientId) {
64
- for (const c of this.channels) {
65
- if (c.userCanSubscribe(info.channel, remult)) {
66
- sc.channels[info.channel] = !info.remove;
67
- ok = true;
68
- }
69
- }
70
- }
71
- }
72
- if (ok)
73
- res.json("ok");
74
- else
75
- res.status(404).json(`Channel "${info.channel}" not found`);
76
- }
77
- sendQueryMessage({ message, clientId, queryId }) {
78
- for (const sc of this.connections) {
79
- if (sc.clientId === clientId) {
80
- sc.write(undefined, message, queryId);
81
- }
82
- }
83
- }
84
64
  sendChannelMessage(channel, message) {
65
+ const data = JSON.stringify({ channel, data: message });
85
66
  for (const sc of this.connections) {
86
67
  if (sc.channels[channel]) {
87
- sc.write(undefined, message, channel);
68
+ sc.write(data);
88
69
  }
89
70
  }
90
71
  }
@@ -94,11 +75,8 @@ export class ServerEventsController {
94
75
  'Cache-Control': 'no-cache',
95
76
  'Connection': 'keep-alive'
96
77
  });
97
- const cc = new clientConnection(res, req.headers["client-id"] || req.query['id']);
98
- const lastEventId = req.headers['last-event-id'];
99
- if (lastEventId) {
100
- this.messages.filter(x => x.id > +lastEventId).forEach(m => cc.write(m.id, m.message, m.eventType));
101
- }
78
+ const cc = new clientConnection(res);
79
+ //const lastEventId = req.headers['last-event-id'];
102
80
  this.connections.push(cc);
103
81
  req.on("close", () => {
104
82
  cc.close();
@@ -106,32 +84,24 @@ export class ServerEventsController {
106
84
  });
107
85
  return cc;
108
86
  }
109
- SendMessage(message, eventType = '') {
110
- let z = this;
111
- let id = i++;
112
- z.messages.push({ id, message: message, eventType });
113
- while (z.messages.length > z.messageHistoryLength)
114
- z.messages.shift();
115
- z.connections.forEach(y => y.write(id, message, eventType));
116
- }
117
87
  }
118
88
  class clientConnection {
119
- constructor(response, clientId) {
89
+ constructor(response) {
120
90
  this.response = response;
121
- this.clientId = clientId;
122
91
  this.channels = {};
123
92
  this.closed = false;
124
- response.write("event:authenticate\ndata:" + "key" + "\n\n");
93
+ this.connectionId = uuid();
94
+ this.write(this.connectionId, "connectionId");
125
95
  this.sendLiveMessage();
126
96
  }
127
97
  close() {
128
98
  this.closed = true;
129
99
  }
130
- write(id, message, eventType) {
131
- let event = "event:message";
132
- if (id != undefined)
133
- event += "\nid:" + id;
134
- this.response.write(event + "\ndata:" + JSON.stringify({ event: eventType, data: message }) + "\n\n");
100
+ write(eventData, eventType = "message") {
101
+ let event = "event:" + eventType;
102
+ // if (id != undefined)
103
+ // event += "\nid:" + id;
104
+ this.response.write(event + "\ndata:" + eventData + "\n\n");
135
105
  let r = this.response;
136
106
  if (r.flush)
137
107
  r.flush();
@@ -139,13 +109,27 @@ class clientConnection {
139
109
  sendLiveMessage() {
140
110
  if (this.closed)
141
111
  return;
142
- this.response.write("event:keep-alive\ndata:\n\n");
143
- let r = this.response;
144
- if (r.flush)
145
- r.flush();
112
+ this.write("", "keep-alive");
146
113
  setTimeout(() => {
147
114
  this.sendLiveMessage();
148
115
  }, 45000);
149
116
  }
150
117
  }
151
- let i = 0;
118
+ function buildHttpServerEventDispatcher(router, apiPath, server) {
119
+ const streamPath = apiPath + '/' + streamUrl;
120
+ let httpServerEvents = new ServerEventsController();
121
+ router.get(streamPath, (r, res, next) => server.withRemult(r, res, next), (req, res) => {
122
+ remult.liveQueryPublisher.dispatcher.openHttpServerStream(req, res);
123
+ });
124
+ router.post(streamPath + '/subscribe', (r, res, next) => server.withRemult(r, res, next), (req, res) => {
125
+ remult.liveQueryPublisher.dispatcher.subscribeToChannel(req.body, res, remult);
126
+ });
127
+ router.post(streamPath + '/unsubscribe', (r, res, next) => server.withRemult(r, res, next), (req, res) => {
128
+ remult.liveQueryPublisher.dispatcher.subscribeToChannel(req.body, res, remult, true);
129
+ });
130
+ router.get(streamPath + '/stats', (r, res, next) => server.withRemult(r, res, next), (req, res) => {
131
+ remult.liveQueryPublisher.dispatcher.consoleInfo();
132
+ res.json("ok");
133
+ });
134
+ return httpServerEvents;
135
+ }
@@ -155,7 +155,7 @@ export class RemultServerImplementation {
155
155
  let key = getEntityKey(e);
156
156
  if (key != undefined)
157
157
  this.add(key, c => {
158
- return new DataApi(c.repo(e), c, this.liveQueryManager);
158
+ return new DataApi(c.repo(e), c);
159
159
  }, r);
160
160
  });
161
161
  }
@@ -180,7 +180,7 @@ export class RemultServerImplementation {
180
180
  let myReq = new ExpressRequestBridgeToDataApiRequest(req);
181
181
  let myRes = new ExpressResponseBridgeToDataApiResponse(res, req);
182
182
  let remult = new Remult();
183
- remult._changeListener = this.liveQueryManager;
183
+ remult.liveQueryPublisher = this.liveQueryManager;
184
184
  remult.dataProvider = (await this.dataProvider);
185
185
  await new Promise(res => {
186
186
  remultObjectStorage.run(remult, async () => {
@@ -3,6 +3,7 @@ import { RestDataProviderHttpProviderUsingFetch } from './data-providers/rest-da
3
3
  import { EntityMetadata, EntityRef, FindOptions, Repository } from "./remult3";
4
4
  import { ClassType } from "../classType";
5
5
  import { LiveQueryClient } from "./live-query/LiveQuerySubscriber";
6
+ import type { ServerEventDispatcher } from "../live-query";
6
7
  export interface ExternalHttpProvider {
7
8
  post(url: string, data: any): Promise<any> | {
8
9
  toPromise(): Promise<any>;
@@ -63,10 +64,11 @@ export declare class Remult {
63
64
  constructor(http: ExternalHttpProvider | typeof fetch | ApiClient);
64
65
  constructor(p: DataProvider);
65
66
  constructor();
67
+ liveQueryPublisher: LiveQueryPublisherInterface;
66
68
  call<T extends ((...args: any[]) => Promise<any>)>(backendMethod: T, classInstance?: any, ...args: GetArguments<T>): ReturnType<T>;
67
69
  /** The current data provider */
68
70
  dataProvider: DataProvider;
69
- liveQueryProvider: LiveQueryClient;
71
+ liveQuerySubscriber: LiveQueryClient;
70
72
  /** A helper callback that can be used to debug and trace all find operations. Useful in debugging scenarios */
71
73
  static onFind: (metadata: EntityMetadata, options: FindOptions<any>) => void;
72
74
  clearAllCache(): any;
@@ -115,3 +117,15 @@ export declare class EventSource {
115
117
  fire(): Promise<void>;
116
118
  dispatcher: EventDispatcher;
117
119
  }
120
+ export interface LiveQueryPublisherInterface {
121
+ stopLiveQuery(id: any): void;
122
+ sendChannelMessage<messageType>(channel: string, message: messageType): void;
123
+ defineLiveQueryChannel(repo: Repository<any>, options: FindOptions<any>, remult: Remult, ids: any[]): string;
124
+ itemChanged(entityKey: string, changes: itemChange[]): void;
125
+ dispatcher: ServerEventDispatcher;
126
+ }
127
+ export interface itemChange {
128
+ id: any;
129
+ oldId: any;
130
+ deleted: boolean;
131
+ }
@@ -98,14 +98,20 @@ export class Remult {
98
98
  constructor(provider) {
99
99
  /* @internal */
100
100
  this.repCache = new Map();
101
- /*@internal*/
102
- this._changeListener = {
103
- saved: (ref) => { },
104
- deleted: (ref) => { }
101
+ this.liveQueryPublisher = {
102
+ itemChanged: () => { },
103
+ defineLiveQueryChannel: () => "",
104
+ dispatcher: undefined,
105
+ sendChannelMessage: function (arg0, what) {
106
+ throw new Error("invalid publisher.");
107
+ },
108
+ stopLiveQuery: function (id) {
109
+ throw new Error("Function not implemented.");
110
+ }
105
111
  };
106
112
  /** The current data provider */
107
113
  this.dataProvider = new RestDataProvider(() => this.apiClient);
108
- this.liveQueryProvider = new LiveQueryClient(new EventSourceLiveQueryProvider());
114
+ this.liveQuerySubscriber = new LiveQueryClient(new EventSourceLiveQueryProvider());
109
115
  this.context = {};
110
116
  this.apiClient = {
111
117
  url: '/api'
@@ -1,14 +1,10 @@
1
1
  import { Remult } from './context';
2
- import { FindOptions, Repository } from './remult3';
2
+ import { Repository } from './remult3';
3
3
  import { ErrorInfo } from './data-interfaces';
4
- export interface LiveQueryProvider {
5
- subscribe(repo: Repository<any>, id: string, options: FindOptions<any>, remult: Remult, ids: any[]): string;
6
- }
7
4
  export declare class DataApi<T = any> {
8
5
  private repository;
9
6
  private remult;
10
- private liveQueryProvider?;
11
- constructor(repository: Repository<T>, remult: Remult, liveQueryProvider?: LiveQueryProvider);
7
+ constructor(repository: Repository<T>, remult: Remult);
12
8
  httpGet(res: DataApiResponse, req: DataApiRequest): Promise<void>;
13
9
  httpPost(res: DataApiResponse, req: DataApiRequest, body: any): Promise<void>;
14
10
  static defaultGetLimit: number;
@@ -2,10 +2,9 @@ import { customUrlToken, buildFilterFromRequestParameters } from './filter/filte
2
2
  import { Filter } from './filter/filter-interfaces';
3
3
  import { ForbiddenError } from './server-action';
4
4
  export class DataApi {
5
- constructor(repository, remult, liveQueryProvider) {
5
+ constructor(repository, remult) {
6
6
  this.repository = repository;
7
7
  this.remult = remult;
8
- this.liveQueryProvider = liveQueryProvider;
9
8
  }
10
9
  httpGet(res, req) {
11
10
  if (req.get("__action") == "count") {
@@ -16,13 +15,17 @@ export class DataApi {
16
15
  }
17
16
  httpPost(res, req, body) {
18
17
  const action = req?.get("__action");
19
- if (action?.startsWith("subscribe"))
18
+ if (action?.startsWith("liveQuery"))
20
19
  return this.getArray(res, req, body);
21
20
  switch (action) {
22
21
  case "get":
23
22
  return this.getArray(res, req, body);
24
23
  case "count":
25
24
  return this.count(res, req, body);
25
+ case "endLiveQuery":
26
+ this.remult.liveQueryPublisher.stopLiveQuery(body.id);
27
+ res.success("ok");
28
+ return;
26
29
  default:
27
30
  return this.post(res, body);
28
31
  }
@@ -99,9 +102,9 @@ export class DataApi {
99
102
  .then(async (r) => {
100
103
  return await Promise.all(r.map(async (y) => this.repository.getEntityRef(y).toApiJson()));
101
104
  });
102
- if (this.liveQueryProvider && action?.startsWith("subscribe")) {
105
+ if (this.remult.liveQueryPublisher && action?.startsWith("liveQuery")) {
103
106
  response.success({
104
- id: this.liveQueryProvider.subscribe(this.repository, action.split('|')[1], findOptions, this.remult, r.map(y => this.repository.getEntityRef(y).getId())),
107
+ queryChannel: this.remult.liveQueryPublisher.defineLiveQueryChannel(this.repository, findOptions, this.remult, r.map(y => this.repository.getEntityRef(y).getId())),
105
108
  result: r
106
109
  });
107
110
  return;
@@ -1,6 +1,4 @@
1
- import { LiveQueryProvider, MessageHandler } from "./LiveQuerySubscriber";
1
+ import { LiveQueryProvider, PubSubClient } from "./LiveQuerySubscriber";
2
2
  export declare class EventSourceLiveQueryProvider implements LiveQueryProvider {
3
- private wrapMessage?;
4
- constructor(wrapMessage?: (what: () => void) => void);
5
- openStreamAndReturnCloseFunction(clientId: string, onMessage: MessageHandler, onReconnect: VoidFunction): Promise<VoidFunction>;
3
+ openStreamAndReturnCloseFunction(onReconnect: VoidFunction): Promise<PubSubClient>;
6
4
  }
@@ -1,31 +1,55 @@
1
+ import { buildRestDataProvider } from "../context";
1
2
  import { remult } from "../remult-proxy";
2
3
  import { streamUrl } from "./LiveQuerySubscriber";
3
4
  export class EventSourceLiveQueryProvider {
4
- constructor(wrapMessage) {
5
- this.wrapMessage = wrapMessage;
6
- if (!this.wrapMessage)
7
- this.wrapMessage = x => x();
8
- }
9
- openStreamAndReturnCloseFunction(clientId, onMessage, onReconnect) {
10
- const source = new EventSource(remult.apiClient.url + '/' + streamUrl + "?id=" + clientId, {
5
+ openStreamAndReturnCloseFunction(onReconnect) {
6
+ const source = new EventSource(remult.apiClient.url + '/' + streamUrl, {
11
7
  withCredentials: true
12
8
  });
9
+ const channels = new Map();
13
10
  source.onmessage = e => {
14
- this.wrapMessage(() => onMessage(JSON.parse(e.data)));
11
+ let message = JSON.parse(e.data);
12
+ const listeners = channels.get(message.channel);
13
+ if (listeners)
14
+ listeners.forEach(x => x(message.data));
15
15
  };
16
16
  source.onerror = e => {
17
17
  console.error("Live Query Event Source Error", e);
18
18
  };
19
+ let connectionId;
20
+ const provider = buildRestDataProvider(remult.apiClient.httpClient);
21
+ const client = {
22
+ disconnect() {
23
+ source.close();
24
+ },
25
+ subscribe(channel, handler) {
26
+ let listeners = channels.get(channel);
27
+ if (!listeners)
28
+ channels.set(channel, listeners = []);
29
+ listeners.push(handler);
30
+ provider.post(remult.apiClient.url + '/' + streamUrl + '/subscribe', {
31
+ channel: channel,
32
+ clientId: connectionId
33
+ });
34
+ return () => {
35
+ listeners.splice(listeners.indexOf(handler, 1));
36
+ provider.post(remult.apiClient.url + '/' + streamUrl + '/unsubscribe', {
37
+ channel: channel,
38
+ clientId: connectionId
39
+ });
40
+ };
41
+ },
42
+ };
19
43
  return new Promise((res) => {
20
44
  let connected = false;
21
- source.onopen = e => {
45
+ source.addEventListener("connectionId", e => {
46
+ //@ts-ignore
47
+ connectionId = e.data;
22
48
  if (connected)
23
49
  onReconnect();
24
50
  connected = true;
25
- res(() => {
26
- source.close();
27
- });
28
- };
51
+ res(client);
52
+ });
29
53
  });
30
54
  }
31
55
  }
@@ -1,33 +1,21 @@
1
1
  import { Remult } from '../..';
2
- import { Repository, EntityRef, FindOptions } from '../remult3';
3
- import { LiveQueryProvider } from '../data-api';
4
- import { ChannelSubscribe, liveQueryMessage } from './LiveQuerySubscriber';
5
- export declare class LiveQueryPublisher implements LiveQueryProvider {
6
- private dispatcher;
7
- unsubscribe(data: ChannelSubscribe): void;
2
+ import { itemChange, LiveQueryPublisherInterface } from '../context';
3
+ import { Repository, FindOptions } from '../remult3';
4
+ export declare class LiveQueryPublisher implements LiveQueryPublisherInterface {
5
+ dispatcher: ServerEventDispatcher;
8
6
  constructor(dispatcher: ServerEventDispatcher);
9
- subscribe(repo: Repository<any>, clientId: string, findOptions: FindOptions<any>, remult: Remult, ids: any[]): string;
10
- clients: clientInfo[];
11
- hasListeners(ref: EntityRef<any>): boolean;
12
- runPromise(p: Promise<any>): void;
13
- saved(ref: EntityRef<any>): void;
14
- deleted(ref: EntityRef<any>): void;
15
- }
16
- export interface clientInfo {
17
- clientId: string;
7
+ stopLiveQuery(id: any): void;
8
+ sendChannelMessage<messageType>(channel: string, message: messageType): void;
9
+ defineLiveQueryChannel(repo: Repository<any>, findOptions: FindOptions<any>, remult: Remult, ids: any[]): string;
18
10
  queries: ({
19
11
  id: string;
20
12
  repo: Repository<any>;
21
13
  findOptions: FindOptions<any>;
22
14
  ids: any[];
23
15
  })[];
16
+ runPromise(p: Promise<any>): void;
17
+ itemChanged(entityKey: string, changes: itemChange[]): void;
24
18
  }
25
19
  export interface ServerEventDispatcher {
26
- sendQueryMessage(message: ServerEventMessage): void;
27
- sendChannelMessage<T>(channel: string, message: any): void;
28
- }
29
- export interface ServerEventMessage {
30
- clientId: string;
31
- queryId: string;
32
- message: liveQueryMessage;
20
+ sendChannelMessage<T>(channel: string, message: T): void;
33
21
  }