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.
Files changed (77) 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/postgres/schema-builder.js +4 -4
  12. package/esm/remult-express.d.ts +9 -17
  13. package/esm/remult-express.js +61 -79
  14. package/esm/remult-knex/index.js +2 -2
  15. package/esm/server/expressBridge.d.ts +1 -1
  16. package/esm/server/expressBridge.js +4 -4
  17. package/esm/src/context.d.ts +16 -2
  18. package/esm/src/context.js +12 -6
  19. package/esm/src/data-api.d.ts +2 -6
  20. package/esm/src/data-api.js +26 -13
  21. package/esm/src/data-providers/sql-database.js +1 -1
  22. package/esm/src/data-providers/web-sql-data-provider.js +1 -1
  23. package/esm/src/live-query/EventSourceLiveQueryProvider.d.ts +2 -4
  24. package/esm/src/live-query/EventSourceLiveQueryProvider.js +45 -14
  25. package/esm/src/live-query/LiveQuery.d.ts +1 -1
  26. package/esm/src/live-query/LiveQuery.js +3 -2
  27. package/esm/src/live-query/LiveQueryPublisher.d.ts +21 -0
  28. package/esm/src/live-query/LiveQueryPublisher.js +71 -0
  29. package/esm/src/live-query/LiveQuerySubscriber.d.ts +61 -0
  30. package/esm/src/live-query/LiveQuerySubscriber.js +211 -0
  31. package/esm/src/live-query/ablyLiveQuery.d.ts +13 -0
  32. package/esm/src/live-query/ablyLiveQuery.js +25 -0
  33. package/esm/src/live-query/index.d.ts +2 -0
  34. package/esm/src/live-query/index.js +1 -0
  35. package/esm/src/remult-proxy.js +6 -6
  36. package/esm/src/remult3/RepositoryImplementation.js +3 -4
  37. package/esm/src/server-action.d.ts +4 -0
  38. package/esm/src/server-action.js +61 -8
  39. package/esm/src/valueConverters.js +1 -1
  40. package/live-query/ably.d.ts +13 -0
  41. package/live-query/ably.js +38 -0
  42. package/live-query/ablyLiveQuery.d.ts +13 -0
  43. package/live-query/ablyLiveQuery.js +38 -0
  44. package/live-query/index.d.ts +2 -0
  45. package/live-query/index.js +7 -0
  46. package/live-query/live-query.d.ts +3 -0
  47. package/live-query/live-query.js +10 -0
  48. package/live-query.d.ts +3 -0
  49. package/live-query.js +7 -0
  50. package/package.json +9 -1
  51. package/postgres/schema-builder.js +4 -4
  52. package/remult-express.d.ts +9 -17
  53. package/remult-express.js +72 -122
  54. package/remult-knex/index.js +2 -2
  55. package/server/expressBridge.d.ts +1 -1
  56. package/server/expressBridge.js +4 -4
  57. package/src/context.d.ts +16 -2
  58. package/src/context.js +12 -6
  59. package/src/data-api.d.ts +2 -6
  60. package/src/data-api.js +51 -24
  61. package/src/data-providers/sql-database.js +1 -1
  62. package/src/data-providers/web-sql-data-provider.js +1 -1
  63. package/src/live-query/EventSourceLiveQueryProvider.d.ts +2 -4
  64. package/src/live-query/EventSourceLiveQueryProvider.js +46 -16
  65. package/src/live-query/LiveQuery.d.ts +1 -1
  66. package/src/live-query/LiveQuery.js +8 -7
  67. package/src/live-query/LiveQueryPublisher.d.ts +21 -0
  68. package/src/live-query/LiveQueryPublisher.js +121 -0
  69. package/src/live-query/LiveQuerySubscriber.d.ts +61 -0
  70. package/src/live-query/LiveQuerySubscriber.js +331 -0
  71. package/src/live-query/ablyLiveQuery.d.ts +13 -0
  72. package/src/live-query/ablyLiveQuery.js +38 -0
  73. package/src/remult-proxy.js +6 -6
  74. package/src/remult3/RepositoryImplementation.js +3 -4
  75. package/src/server-action.d.ts +4 -0
  76. package/src/server-action.js +122 -21
  77. 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
+ }
@@ -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';
@@ -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.log(err);
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.log(err);
125
+ console.error(err);
126
126
  }
127
127
  }
128
128
  }
@@ -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/LiveQuery';
5
- import { ServerEventDispatcher, ServerEventMessage } from './src/live-query/LiveQueryManager';
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 {};
@@ -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/LiveQuery';
4
- import { LiveQueryPublisher } from './src/live-query/LiveQueryManager';
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
- let httpServerEvents = new ServerEventsController(options.messageChannels);
21
- server.liveQueryManager = new LiveQueryPublisher(httpServerEvents);
22
- const streamPath = options.rootPath + '/' + streamUrl;
23
- app.get(streamPath, (req, res) => {
24
- httpServerEvents.openHttpServerStream(req, res);
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(channels, messageHistoryLength = 1000) {
42
- this.channels = channels;
43
- this.messageHistoryLength = messageHistoryLength;
35
+ constructor(canUserConnectToChannel) {
36
+ this.canUserConnectToChannel = canUserConnectToChannel;
44
37
  this.connections = [];
45
- this.messages = [];
46
- if (channels) {
47
- for (const c of channels) {
48
- 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
+ }
49
54
  }
50
55
  }
56
+ res.sendStatus(404);
51
57
  }
52
58
  consoleInfo() {
53
- console.log(this.connections.map(x => ({
54
- client: x.clientId,
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(undefined, message, channel);
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, req.headers["client-id"] || req.query['id']);
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, clientId) {
89
+ constructor(response) {
120
90
  this.response = response;
121
- this.clientId = clientId;
122
91
  this.channels = {};
123
92
  this.closed = false;
124
- //console.log("open connection");
125
- response.write("event:authenticate\ndata:" + "key" + "\n\n");
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(id, message, eventType) {
133
- let event = "event:message";
134
- if (id != undefined)
135
- event += "\nid:" + id;
136
- 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");
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.response.write("event:keep-alive\ndata:\n\n");
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
- 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
+ }
@@ -256,7 +256,7 @@ export class KnexSchemaBuilder {
256
256
  this.additionalWhere = '';
257
257
  }
258
258
  async verifyStructureOfAllEntities(remult) {
259
- console.log("start verify structure");
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.log(err);
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/LiveQueryManager';
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, this.liveQueryManager);
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.log("[remult] " + myRoute);
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._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 () => {
@@ -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.log("[remult] " + myUrl);
228
+ console.info("[remult] " + myUrl);
229
229
  if (queue) {
230
230
  this.hasQueue = true;
231
231
  this.queue.mapQueuedAction(myUrl, what);
@@ -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/LiveQuery";
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
+ }
@@ -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/LiveQuery";
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
- /*@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;
@@ -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, liveQueryProvider) {
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("subscribe"))
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.liveQueryProvider && action?.startsWith("subscribe")) {
105
+ if (this.remult.liveQueryPublisher && action?.startsWith("liveQuery")) {
102
106
  response.success({
103
- 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())),
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
- let newr = this.repository.create();
179
- await this.repository.getEntityRef(newr)._updateEntityBasedOnApi(body);
180
- if (!this.repository.getEntityRef(newr).apiInsertAllowed) {
181
- response.forbidden();
182
- return;
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
- await this.repository.getEntityRef(newr).save();
185
- response.created(this.repository.getEntityRef(newr).toApiJson());
194
+ else
195
+ response.created(await insert(body));
186
196
  }
187
197
  catch (err) {
188
- response.error(err);
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.log({ query: sql, arguments: this.args, duration: d / 1000 });
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.log(sql);
21
+ console.info(sql);
22
22
  await this.createCommand().execute(sql);
23
23
  }
24
24
  async createTable(entity) {