remult 0.17.0-exp.2 → 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/postgres/schema-builder.js +4 -4
- package/esm/remult-express.d.ts +9 -17
- package/esm/remult-express.js +61 -79
- package/esm/remult-knex/index.js +2 -2
- package/esm/server/expressBridge.d.ts +1 -1
- package/esm/server/expressBridge.js +4 -4
- package/esm/src/context.d.ts +16 -2
- package/esm/src/context.js +12 -6
- package/esm/src/data-api.d.ts +2 -6
- package/esm/src/data-api.js +26 -13
- package/esm/src/data-providers/sql-database.js +1 -1
- package/esm/src/data-providers/web-sql-data-provider.js +1 -1
- package/esm/src/live-query/EventSourceLiveQueryProvider.d.ts +2 -4
- package/esm/src/live-query/EventSourceLiveQueryProvider.js +45 -14
- package/esm/src/live-query/LiveQuery.d.ts +1 -1
- package/esm/src/live-query/LiveQuery.js +3 -2
- package/esm/src/live-query/LiveQueryPublisher.d.ts +21 -0
- package/esm/src/live-query/LiveQueryPublisher.js +71 -0
- package/esm/src/live-query/LiveQuerySubscriber.d.ts +61 -0
- package/esm/src/live-query/LiveQuerySubscriber.js +211 -0
- 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 -4
- package/esm/src/server-action.d.ts +4 -0
- package/esm/src/server-action.js +61 -8
- package/esm/src/valueConverters.js +1 -1
- 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/postgres/schema-builder.js +4 -4
- package/remult-express.d.ts +9 -17
- package/remult-express.js +72 -122
- package/remult-knex/index.js +2 -2
- package/server/expressBridge.d.ts +1 -1
- package/server/expressBridge.js +4 -4
- package/src/context.d.ts +16 -2
- package/src/context.js +12 -6
- package/src/data-api.d.ts +2 -6
- package/src/data-api.js +51 -24
- package/src/data-providers/sql-database.js +1 -1
- package/src/data-providers/web-sql-data-provider.js +1 -1
- package/src/live-query/EventSourceLiveQueryProvider.d.ts +2 -4
- package/src/live-query/EventSourceLiveQueryProvider.js +46 -16
- package/src/live-query/LiveQuery.d.ts +1 -1
- package/src/live-query/LiveQuery.js +8 -7
- package/src/live-query/LiveQueryPublisher.d.ts +21 -0
- package/src/live-query/LiveQueryPublisher.js +121 -0
- package/src/live-query/LiveQuerySubscriber.d.ts +61 -0
- package/src/live-query/LiveQuerySubscriber.js +331 -0
- 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 -4
- package/src/server-action.d.ts +4 -0
- package/src/server-action.js +122 -21
- package/src/valueConverters.js +1 -1
|
@@ -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
|
+
}
|
|
@@ -43,7 +43,7 @@ export class PostgresSchemaBuilder {
|
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
async verifyStructureOfAllEntities(remult) {
|
|
46
|
-
console.log("start verify structure");
|
|
46
|
+
console.log("start verify structure"); //keep me
|
|
47
47
|
for (const entityClass of allEntities) {
|
|
48
48
|
let entity = remult.repo(entityClass).metadata;
|
|
49
49
|
let e = await getDbNameProvider(entity);
|
|
@@ -56,7 +56,7 @@ export class PostgresSchemaBuilder {
|
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
catch (err) {
|
|
59
|
-
console.log("failed verify structure of " + e.entityName + " ", err);
|
|
59
|
+
/*keep me */ console.log("failed verify structure of " + e.entityName + " ", err);
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
}
|
|
@@ -102,7 +102,7 @@ export class PostgresSchemaBuilder {
|
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
catch (err) {
|
|
105
|
-
console.
|
|
105
|
+
console.error(err);
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
async verifyAllColumns(entity) {
|
|
@@ -122,7 +122,7 @@ export class PostgresSchemaBuilder {
|
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
124
|
catch (err) {
|
|
125
|
-
console.
|
|
125
|
+
console.error(err);
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
}
|
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
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as express from 'express';
|
|
2
2
|
import { createRemultServer } from './server/expressBridge';
|
|
3
|
-
import { streamUrl } from './src/live-query/
|
|
4
|
-
import { LiveQueryPublisher } from './src/live-query/
|
|
3
|
+
import { streamUrl } from './src/live-query/LiveQuerySubscriber';
|
|
4
|
+
import { LiveQueryPublisher } from './src/live-query/LiveQueryPublisher';
|
|
5
|
+
import { v4 as uuid } from 'uuid';
|
|
5
6
|
import { remult } from './src/remult-proxy';
|
|
6
7
|
export function remultExpress(options) {
|
|
7
8
|
let app = express.Router();
|
|
@@ -17,19 +18,12 @@ export function remultExpress(options) {
|
|
|
17
18
|
}
|
|
18
19
|
const server = createRemultServer(options);
|
|
19
20
|
server.registerRouter(app);
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
app.post(streamPath, (r, res, next) => server.withRemult(r, res, next), (req, res) => {
|
|
27
|
-
httpServerEvents.subscribeToChannel(remult, req.body, res);
|
|
28
|
-
});
|
|
29
|
-
app.get(streamPath + '/stats', (req, res) => {
|
|
30
|
-
httpServerEvents.consoleInfo();
|
|
31
|
-
res.json("ok");
|
|
32
|
-
});
|
|
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));
|
|
33
27
|
return Object.assign(app, {
|
|
34
28
|
getRemult: (req) => server.getRemult(req),
|
|
35
29
|
openApiDoc: (options) => server.openApiDoc(options),
|
|
@@ -38,52 +32,40 @@ export function remultExpress(options) {
|
|
|
38
32
|
});
|
|
39
33
|
}
|
|
40
34
|
export class ServerEventsController {
|
|
41
|
-
constructor(
|
|
42
|
-
this.
|
|
43
|
-
this.messageHistoryLength = messageHistoryLength;
|
|
35
|
+
constructor(canUserConnectToChannel) {
|
|
36
|
+
this.canUserConnectToChannel = canUserConnectToChannel;
|
|
44
37
|
this.connections = [];
|
|
45
|
-
this.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
+
}
|
|
49
54
|
}
|
|
50
55
|
}
|
|
56
|
+
res.sendStatus(404);
|
|
51
57
|
}
|
|
52
58
|
consoleInfo() {
|
|
53
|
-
console.
|
|
54
|
-
client: x.
|
|
59
|
+
console.info(this.connections.map(x => ({
|
|
60
|
+
client: x.connectionId,
|
|
55
61
|
channels: x.channels
|
|
56
62
|
})));
|
|
57
63
|
}
|
|
58
|
-
subscribeToChannel(remult, info, res) {
|
|
59
|
-
let ok = false;
|
|
60
|
-
if (this.channels)
|
|
61
|
-
for (const sc of this.connections) {
|
|
62
|
-
if (sc.clientId === info.clientId) {
|
|
63
|
-
for (const c of this.channels) {
|
|
64
|
-
if (c.userCanSubscribe(info.channel, remult)) {
|
|
65
|
-
sc.channels[info.channel] = !info.remove;
|
|
66
|
-
ok = true;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
if (ok)
|
|
72
|
-
res.json("ok");
|
|
73
|
-
else
|
|
74
|
-
res.status(404).json(`Channel "${info.channel}" not found`);
|
|
75
|
-
}
|
|
76
|
-
sendQueryMessage({ message, clientId, queryId }) {
|
|
77
|
-
for (const sc of this.connections) {
|
|
78
|
-
if (sc.clientId === clientId) {
|
|
79
|
-
sc.write(undefined, message, queryId);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
64
|
sendChannelMessage(channel, message) {
|
|
65
|
+
const data = JSON.stringify({ channel, data: message });
|
|
84
66
|
for (const sc of this.connections) {
|
|
85
67
|
if (sc.channels[channel]) {
|
|
86
|
-
sc.write(
|
|
68
|
+
sc.write(data);
|
|
87
69
|
}
|
|
88
70
|
}
|
|
89
71
|
}
|
|
@@ -93,11 +75,8 @@ export class ServerEventsController {
|
|
|
93
75
|
'Cache-Control': 'no-cache',
|
|
94
76
|
'Connection': 'keep-alive'
|
|
95
77
|
});
|
|
96
|
-
const cc = new clientConnection(res
|
|
97
|
-
const lastEventId = req.headers['last-event-id'];
|
|
98
|
-
if (lastEventId) {
|
|
99
|
-
this.messages.filter(x => x.id > +lastEventId).forEach(m => cc.write(m.id, m.message, m.eventType));
|
|
100
|
-
}
|
|
78
|
+
const cc = new clientConnection(res);
|
|
79
|
+
//const lastEventId = req.headers['last-event-id'];
|
|
101
80
|
this.connections.push(cc);
|
|
102
81
|
req.on("close", () => {
|
|
103
82
|
cc.close();
|
|
@@ -105,35 +84,24 @@ export class ServerEventsController {
|
|
|
105
84
|
});
|
|
106
85
|
return cc;
|
|
107
86
|
}
|
|
108
|
-
SendMessage(message, eventType = '') {
|
|
109
|
-
let z = this;
|
|
110
|
-
let id = i++;
|
|
111
|
-
z.messages.push({ id, message: message, eventType });
|
|
112
|
-
while (z.messages.length > z.messageHistoryLength)
|
|
113
|
-
z.messages.shift();
|
|
114
|
-
//console.log({ sendingTo: z.connections.length })
|
|
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
|
-
|
|
125
|
-
|
|
93
|
+
this.connectionId = uuid();
|
|
94
|
+
this.write(this.connectionId, "connectionId");
|
|
126
95
|
this.sendLiveMessage();
|
|
127
96
|
}
|
|
128
97
|
close() {
|
|
129
|
-
//console.log("close connection");
|
|
130
98
|
this.closed = true;
|
|
131
99
|
}
|
|
132
|
-
write(
|
|
133
|
-
let event = "event:
|
|
134
|
-
if (id != undefined)
|
|
135
|
-
|
|
136
|
-
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");
|
|
137
105
|
let r = this.response;
|
|
138
106
|
if (r.flush)
|
|
139
107
|
r.flush();
|
|
@@ -141,13 +109,27 @@ class clientConnection {
|
|
|
141
109
|
sendLiveMessage() {
|
|
142
110
|
if (this.closed)
|
|
143
111
|
return;
|
|
144
|
-
this.
|
|
145
|
-
let r = this.response;
|
|
146
|
-
if (r.flush)
|
|
147
|
-
r.flush();
|
|
112
|
+
this.write("", "keep-alive");
|
|
148
113
|
setTimeout(() => {
|
|
149
114
|
this.sendLiveMessage();
|
|
150
115
|
}, 45000);
|
|
151
116
|
}
|
|
152
117
|
}
|
|
153
|
-
|
|
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
|
+
}
|
package/esm/remult-knex/index.js
CHANGED
|
@@ -256,7 +256,7 @@ export class KnexSchemaBuilder {
|
|
|
256
256
|
this.additionalWhere = '';
|
|
257
257
|
}
|
|
258
258
|
async verifyStructureOfAllEntities(remult) {
|
|
259
|
-
console.
|
|
259
|
+
console.info("start verify structure");
|
|
260
260
|
for (const entity of allEntities) {
|
|
261
261
|
let metadata = remult.repo(entity).metadata;
|
|
262
262
|
try {
|
|
@@ -319,7 +319,7 @@ export class KnexSchemaBuilder {
|
|
|
319
319
|
}
|
|
320
320
|
}
|
|
321
321
|
catch (err) {
|
|
322
|
-
console.
|
|
322
|
+
console.error(err);
|
|
323
323
|
}
|
|
324
324
|
}
|
|
325
325
|
}
|
|
@@ -5,7 +5,7 @@ import { AllowedForInstance, Remult, UserInfo } from '../src/context';
|
|
|
5
5
|
import { ClassType } from '../classType';
|
|
6
6
|
import { Repository } from '../src/remult3';
|
|
7
7
|
import { IdEntity } from '../src/id-entity';
|
|
8
|
-
import { LiveQueryPublisher } from '../src/live-query/
|
|
8
|
+
import { LiveQueryPublisher } from '../src/live-query/LiveQueryPublisher';
|
|
9
9
|
export interface RemultServerOptions<RequestType extends GenericRequest> {
|
|
10
10
|
/** Sets a database connection for Remult.
|
|
11
11
|
*
|
|
@@ -155,14 +155,14 @@ 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
|
}
|
|
162
162
|
add(key, dataApiFactory, r) {
|
|
163
163
|
let myRoute = this.options.rootPath + '/' + key;
|
|
164
164
|
if (this.options.logApiEndPoints)
|
|
165
|
-
console.
|
|
165
|
+
console.info("[remult] " + myRoute);
|
|
166
166
|
r.route(myRoute)
|
|
167
167
|
.get(this.process((c, req, res) => dataApiFactory(c).httpGet(res, req))).put(this.process(async (c, req, res, orig) => dataApiFactory(c).put(res, '', orig.body)))
|
|
168
168
|
.delete(this.process(async (c, req, res, orig) => dataApiFactory(c).delete(res, '')))
|
|
@@ -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 () => {
|
|
@@ -225,7 +225,7 @@ export class RemultServerImplementation {
|
|
|
225
225
|
})();
|
|
226
226
|
this.backendMethodsOpenApi.push({ path: myUrl, allowed, tag });
|
|
227
227
|
if (this.options.logApiEndPoints)
|
|
228
|
-
console.
|
|
228
|
+
console.info("[remult] " + myUrl);
|
|
229
229
|
if (queue) {
|
|
230
230
|
this.hasQueue = true;
|
|
231
231
|
this.queue.mapQueuedAction(myUrl, what);
|
package/esm/src/context.d.ts
CHANGED
|
@@ -2,7 +2,8 @@ import { DataProvider, RestDataProviderHttpProvider } from "./data-interfaces";
|
|
|
2
2
|
import { RestDataProviderHttpProviderUsingFetch } from './data-providers/rest-data-provider';
|
|
3
3
|
import { EntityMetadata, EntityRef, FindOptions, Repository } from "./remult3";
|
|
4
4
|
import { ClassType } from "../classType";
|
|
5
|
-
import { LiveQueryClient } from "./live-query/
|
|
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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { actionInfo, serverActionField } from './server-action';
|
|
2
2
|
import { RestDataProvider, RestDataProviderHttpProviderUsingFetch } from './data-providers/rest-data-provider';
|
|
3
3
|
import { RepositoryImplementation } from "./remult3/RepositoryImplementation";
|
|
4
|
-
import { LiveQueryClient } from "./live-query/
|
|
4
|
+
import { LiveQueryClient } from "./live-query/LiveQuerySubscriber";
|
|
5
5
|
import { EventSourceLiveQueryProvider } from "./live-query/EventSourceLiveQueryProvider";
|
|
6
6
|
import { RemultProxy } from "./remult-proxy";
|
|
7
7
|
export class HttpProviderBridgeToRestDataProviderHttpProvider {
|
|
@@ -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
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { customUrlToken, buildFilterFromRequestParameters } from './filter/filter-interfaces';
|
|
2
2
|
import { Filter } from './filter/filter-interfaces';
|
|
3
|
+
import { ForbiddenError } from './server-action';
|
|
3
4
|
export class DataApi {
|
|
4
|
-
constructor(repository, remult
|
|
5
|
+
constructor(repository, remult) {
|
|
5
6
|
this.repository = repository;
|
|
6
7
|
this.remult = remult;
|
|
7
|
-
this.liveQueryProvider = liveQueryProvider;
|
|
8
8
|
}
|
|
9
9
|
httpGet(res, req) {
|
|
10
10
|
if (req.get("__action") == "count") {
|
|
@@ -15,13 +15,17 @@ export class DataApi {
|
|
|
15
15
|
}
|
|
16
16
|
httpPost(res, req, body) {
|
|
17
17
|
const action = req?.get("__action");
|
|
18
|
-
if (action?.startsWith("
|
|
18
|
+
if (action?.startsWith("liveQuery"))
|
|
19
19
|
return this.getArray(res, req, body);
|
|
20
20
|
switch (action) {
|
|
21
21
|
case "get":
|
|
22
22
|
return this.getArray(res, req, body);
|
|
23
23
|
case "count":
|
|
24
24
|
return this.count(res, req, body);
|
|
25
|
+
case "endLiveQuery":
|
|
26
|
+
this.remult.liveQueryPublisher.stopLiveQuery(body.id);
|
|
27
|
+
res.success("ok");
|
|
28
|
+
return;
|
|
25
29
|
default:
|
|
26
30
|
return this.post(res, body);
|
|
27
31
|
}
|
|
@@ -98,9 +102,9 @@ export class DataApi {
|
|
|
98
102
|
.then(async (r) => {
|
|
99
103
|
return await Promise.all(r.map(async (y) => this.repository.getEntityRef(y).toApiJson()));
|
|
100
104
|
});
|
|
101
|
-
if (this.
|
|
105
|
+
if (this.remult.liveQueryPublisher && action?.startsWith("liveQuery")) {
|
|
102
106
|
response.success({
|
|
103
|
-
|
|
107
|
+
queryChannel: this.remult.liveQueryPublisher.defineLiveQueryChannel(this.repository, findOptions, this.remult, r.map(y => this.repository.getEntityRef(y).getId())),
|
|
104
108
|
result: r
|
|
105
109
|
});
|
|
106
110
|
return;
|
|
@@ -175,17 +179,26 @@ export class DataApi {
|
|
|
175
179
|
}
|
|
176
180
|
async post(response, body) {
|
|
177
181
|
try {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
182
|
+
const insert = async (what) => {
|
|
183
|
+
let newr = this.repository.create();
|
|
184
|
+
await this.repository.getEntityRef(newr)._updateEntityBasedOnApi(what);
|
|
185
|
+
if (!this.repository.getEntityRef(newr).apiInsertAllowed) {
|
|
186
|
+
throw new ForbiddenError();
|
|
187
|
+
}
|
|
188
|
+
await this.repository.getEntityRef(newr).save();
|
|
189
|
+
return this.repository.getEntityRef(newr).toApiJson();
|
|
190
|
+
};
|
|
191
|
+
if (Array.isArray(body)) {
|
|
192
|
+
response.created(await Promise.all(body.map(x => insert(x))));
|
|
183
193
|
}
|
|
184
|
-
|
|
185
|
-
|
|
194
|
+
else
|
|
195
|
+
response.created(await insert(body));
|
|
186
196
|
}
|
|
187
197
|
catch (err) {
|
|
188
|
-
|
|
198
|
+
if (err.isForbiddenError)
|
|
199
|
+
response.forbidden();
|
|
200
|
+
else
|
|
201
|
+
response.error(err);
|
|
189
202
|
}
|
|
190
203
|
}
|
|
191
204
|
}
|
|
@@ -76,7 +76,7 @@ class LogSQLCommand {
|
|
|
76
76
|
if (this.allQueries) {
|
|
77
77
|
var d = new Date().valueOf() - start.valueOf();
|
|
78
78
|
if (d > SqlDatabase.durationThreshold) {
|
|
79
|
-
console.
|
|
79
|
+
console.info({ query: sql, arguments: this.args, duration: d / 1000 });
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
return r;
|
|
@@ -18,7 +18,7 @@ export class WebSqlDataProvider {
|
|
|
18
18
|
let e = await getDbNameProvider(entity);
|
|
19
19
|
let sql = 'drop table if exists ' + e.entityName;
|
|
20
20
|
if (SqlDatabase.LogToConsole)
|
|
21
|
-
console.
|
|
21
|
+
console.info(sql);
|
|
22
22
|
await this.createCommand().execute(sql);
|
|
23
23
|
}
|
|
24
24
|
async createTable(entity) {
|