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.
- package/esm/live-query/ably.d.ts +13 -0
- package/esm/live-query/ably.js +25 -0
- package/esm/live-query/ablyLiveQuery.d.ts +13 -0
- package/esm/live-query/ablyLiveQuery.js +25 -0
- package/esm/live-query/index.d.ts +2 -0
- package/esm/live-query/index.js +2 -0
- package/esm/live-query/live-query.d.ts +3 -0
- package/esm/live-query/live-query.js +2 -0
- package/esm/live-query.d.ts +3 -0
- package/esm/live-query.js +2 -0
- package/esm/remult-express.d.ts +9 -17
- package/esm/remult-express.js +59 -75
- package/esm/server/expressBridge.js +2 -2
- package/esm/src/context.d.ts +15 -1
- package/esm/src/context.js +11 -5
- package/esm/src/data-api.d.ts +2 -6
- package/esm/src/data-api.js +8 -5
- package/esm/src/live-query/EventSourceLiveQueryProvider.d.ts +2 -4
- package/esm/src/live-query/EventSourceLiveQueryProvider.js +37 -13
- package/esm/src/live-query/LiveQueryPublisher.d.ts +10 -22
- package/esm/src/live-query/LiveQueryPublisher.js +47 -78
- package/esm/src/live-query/LiveQuerySubscriber.d.ts +13 -19
- package/esm/src/live-query/LiveQuerySubscriber.js +78 -105
- package/esm/src/live-query/ablyLiveQuery.d.ts +13 -0
- package/esm/src/live-query/ablyLiveQuery.js +25 -0
- package/esm/src/live-query/index.d.ts +2 -0
- package/esm/src/live-query/index.js +1 -0
- package/esm/src/remult-proxy.js +6 -6
- package/esm/src/remult3/RepositoryImplementation.js +3 -3
- package/esm/src/server-action.js +60 -7
- package/live-query/ably.d.ts +13 -0
- package/live-query/ably.js +38 -0
- package/live-query/ablyLiveQuery.d.ts +13 -0
- package/live-query/ablyLiveQuery.js +38 -0
- package/live-query/index.d.ts +2 -0
- package/live-query/index.js +7 -0
- package/live-query/live-query.d.ts +3 -0
- package/live-query/live-query.js +10 -0
- package/live-query.d.ts +3 -0
- package/live-query.js +7 -0
- package/package.json +9 -1
- package/remult-express.d.ts +9 -17
- package/remult-express.js +71 -119
- package/server/expressBridge.js +2 -2
- package/src/context.d.ts +15 -1
- package/src/context.js +11 -5
- package/src/data-api.d.ts +2 -6
- package/src/data-api.js +8 -5
- package/src/live-query/EventSourceLiveQueryProvider.d.ts +2 -4
- package/src/live-query/EventSourceLiveQueryProvider.js +38 -15
- package/src/live-query/LiveQueryPublisher.d.ts +10 -22
- package/src/live-query/LiveQueryPublisher.js +81 -161
- package/src/live-query/LiveQuerySubscriber.d.ts +13 -19
- package/src/live-query/LiveQuerySubscriber.js +124 -125
- package/src/live-query/ablyLiveQuery.d.ts +13 -0
- package/src/live-query/ablyLiveQuery.js +38 -0
- package/src/remult-proxy.js +6 -6
- package/src/remult3/RepositoryImplementation.js +3 -3
- 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
|
+
}
|
package/esm/remult-express.d.ts
CHANGED
|
@@ -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 {
|
|
5
|
-
import { ServerEventDispatcher
|
|
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
|
-
|
|
9
|
+
serverEventDispatcher?: (router: express.Router, server: RemultServer) => ServerEventDispatcher;
|
|
10
10
|
}): express.RequestHandler & RemultServer;
|
|
11
11
|
export declare class ServerEventsController implements ServerEventDispatcher {
|
|
12
|
-
private
|
|
13
|
-
|
|
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(
|
|
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(
|
|
35
|
-
|
|
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 {};
|
package/esm/remult-express.js
CHANGED
|
@@ -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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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(
|
|
43
|
-
this.
|
|
44
|
-
this.messageHistoryLength = messageHistoryLength;
|
|
35
|
+
constructor(canUserConnectToChannel) {
|
|
36
|
+
this.canUserConnectToChannel = canUserConnectToChannel;
|
|
45
37
|
this.connections = [];
|
|
46
|
-
this.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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.
|
|
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(
|
|
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
|
|
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
|
|
89
|
+
constructor(response) {
|
|
120
90
|
this.response = response;
|
|
121
|
-
this.clientId = clientId;
|
|
122
91
|
this.channels = {};
|
|
123
92
|
this.closed = false;
|
|
124
|
-
|
|
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(
|
|
131
|
-
let event = "event:
|
|
132
|
-
if (id != undefined)
|
|
133
|
-
|
|
134
|
-
this.response.write(event + "\ndata:" +
|
|
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.
|
|
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
|
-
|
|
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
|
|
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.
|
|
183
|
+
remult.liveQueryPublisher = this.liveQueryManager;
|
|
184
184
|
remult.dataProvider = (await this.dataProvider);
|
|
185
185
|
await new Promise(res => {
|
|
186
186
|
remultObjectStorage.run(remult, async () => {
|
package/esm/src/context.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
+
}
|
package/esm/src/context.js
CHANGED
|
@@ -98,14 +98,20 @@ export class Remult {
|
|
|
98
98
|
constructor(provider) {
|
|
99
99
|
/* @internal */
|
|
100
100
|
this.repCache = new Map();
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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.
|
|
114
|
+
this.liveQuerySubscriber = new LiveQueryClient(new EventSourceLiveQueryProvider());
|
|
109
115
|
this.context = {};
|
|
110
116
|
this.apiClient = {
|
|
111
117
|
url: '/api'
|
package/esm/src/data-api.d.ts
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
import { Remult } from './context';
|
|
2
|
-
import {
|
|
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
|
-
|
|
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;
|
package/esm/src/data-api.js
CHANGED
|
@@ -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
|
|
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("
|
|
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.
|
|
105
|
+
if (this.remult.liveQueryPublisher && action?.startsWith("liveQuery")) {
|
|
103
106
|
response.success({
|
|
104
|
-
|
|
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,
|
|
1
|
+
import { LiveQueryProvider, PubSubClient } from "./LiveQuerySubscriber";
|
|
2
2
|
export declare class EventSourceLiveQueryProvider implements LiveQueryProvider {
|
|
3
|
-
|
|
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
|
-
|
|
5
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
27
|
-
});
|
|
28
|
-
};
|
|
51
|
+
res(client);
|
|
52
|
+
});
|
|
29
53
|
});
|
|
30
54
|
}
|
|
31
55
|
}
|
|
@@ -1,33 +1,21 @@
|
|
|
1
1
|
import { Remult } from '../..';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
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
|
}
|