remult 0.18.1-exp.9 → 0.18.1

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 (127) hide show
  1. package/README.md +1 -0
  2. package/SseSubscriptionServer.d.ts +23 -0
  3. package/SseSubscriptionServer.js +143 -0
  4. package/{esm/live-query/ably.d.ts → ably.d.ts} +5 -5
  5. package/ably.js +59 -0
  6. package/esm/SseSubscriptionServer.d.ts +23 -0
  7. package/esm/SseSubscriptionServer.js +102 -0
  8. package/{live-query → esm}/ably.d.ts +5 -5
  9. package/esm/ably.js +28 -0
  10. package/esm/graphql.js +81 -42
  11. package/esm/index.d.ts +1 -1
  12. package/esm/live-query/data-provider-live-query-storage.d.ts +23 -0
  13. package/esm/live-query/data-provider-live-query-storage.js +79 -0
  14. package/esm/myLog.d.ts +1 -0
  15. package/esm/myLog.js +5 -0
  16. package/esm/postgres/postgres-data-provider.d.ts +1 -1
  17. package/esm/postgres/postgres-data-provider.js +7 -5
  18. package/esm/postgres/schema-builder.d.ts +2 -1
  19. package/esm/postgres/schema-builder.js +20 -8
  20. package/esm/remult-express.d.ts +0 -8
  21. package/esm/remult-express.js +0 -141
  22. package/esm/remult-knex/index.d.ts +5 -3
  23. package/esm/remult-knex/index.js +21 -17
  24. package/esm/remult-next.d.ts +11 -0
  25. package/esm/remult-next.js +27 -0
  26. package/esm/server/core.d.ts +2 -0
  27. package/esm/server/core.js +2 -0
  28. package/esm/server/expressBridge.d.ts +33 -11
  29. package/esm/server/expressBridge.js +90 -25
  30. package/esm/server/index.d.ts +2 -0
  31. package/esm/server/index.js +2 -0
  32. package/esm/server/initDataProvider.d.ts +2 -0
  33. package/esm/server/initDataProvider.js +14 -0
  34. package/esm/src/__EntityValueProvider.d.ts +1 -0
  35. package/esm/src/buildRestDataProvider.d.ts +1 -1
  36. package/esm/src/context.d.ts +2 -3
  37. package/esm/src/context.js +11 -8
  38. package/esm/src/data-api.d.ts +2 -1
  39. package/esm/src/data-api.js +13 -15
  40. package/esm/src/data-interfaces.d.ts +5 -1
  41. package/esm/src/data-providers/rest-data-provider.d.ts +2 -1
  42. package/esm/src/data-providers/rest-data-provider.js +17 -12
  43. package/esm/src/data-providers/sql-database.d.ts +1 -0
  44. package/esm/src/data-providers/sql-database.js +27 -2
  45. package/esm/src/data-providers/web-sql-data-provider.d.ts +1 -0
  46. package/esm/src/data-providers/web-sql-data-provider.js +5 -0
  47. package/esm/src/filter/filter-interfaces.js +5 -0
  48. package/esm/src/live-query/LiveQueryClient.js +61 -54
  49. package/esm/src/live-query/SseSubscriptionClient.d.ts +1 -0
  50. package/esm/src/live-query/SseSubscriptionClient.js +17 -9
  51. package/esm/src/live-query/SubscriptionChannel.d.ts +4 -4
  52. package/esm/src/live-query/SubscriptionChannel.js +22 -21
  53. package/esm/src/live-query/SubscriptionServer.d.ts +6 -5
  54. package/esm/src/live-query/SubscriptionServer.js +8 -5
  55. package/esm/src/remult-proxy.js +1 -0
  56. package/esm/src/remult3/RepositoryImplementation.d.ts +10 -7
  57. package/esm/src/remult3/RepositoryImplementation.js +65 -41
  58. package/esm/src/remult3/remult3.d.ts +8 -7
  59. package/esm/src/server-action.d.ts +1 -1
  60. package/esm/src/shared-tests/db-tests-setup.d.ts +13 -5
  61. package/esm/src/shared-tests/db-tests-setup.js +14 -4
  62. package/esm/src/shared-tests/db-tests.db-tests.js +34 -1
  63. package/esm/src/sql-command.d.ts +1 -0
  64. package/esm/src/tests/frontend-database-tests-setup.spec.js +2 -2
  65. package/esm/src/tests/testHelper.spec.d.ts +2 -2
  66. package/esm/src/tests/testHelper.spec.js +1 -1
  67. package/graphql.js +164 -97
  68. package/index.d.ts +1 -1
  69. package/live-query/data-provider-live-query-storage.d.ts +23 -0
  70. package/live-query/data-provider-live-query-storage.js +237 -0
  71. package/myLog.d.ts +1 -0
  72. package/myLog.js +20 -0
  73. package/package.json +14 -6
  74. package/postgres/postgres-data-provider.d.ts +1 -1
  75. package/postgres/postgres-data-provider.js +43 -38
  76. package/postgres/schema-builder.d.ts +2 -1
  77. package/postgres/schema-builder.js +91 -49
  78. package/remult-express.d.ts +0 -8
  79. package/remult-express.js +2 -235
  80. package/remult-knex/index.d.ts +5 -3
  81. package/remult-knex/index.js +64 -54
  82. package/remult-next.d.ts +11 -0
  83. package/remult-next.js +73 -0
  84. package/server/JsonEntityFileStorage.js +2 -2
  85. package/server/core.d.ts +2 -0
  86. package/server/core.js +5 -1
  87. package/server/expressBridge.d.ts +33 -11
  88. package/server/expressBridge.js +224 -64
  89. package/server/index.d.ts +2 -0
  90. package/server/index.js +5 -1
  91. package/server/initDataProvider.d.ts +2 -0
  92. package/server/initDataProvider.js +26 -0
  93. package/src/__EntityValueProvider.d.ts +1 -0
  94. package/src/buildRestDataProvider.d.ts +1 -1
  95. package/src/context.d.ts +2 -3
  96. package/src/context.js +102 -65
  97. package/src/data-api.d.ts +2 -1
  98. package/src/data-api.js +54 -41
  99. package/src/data-interfaces.d.ts +5 -1
  100. package/src/data-providers/rest-data-provider.d.ts +2 -1
  101. package/src/data-providers/rest-data-provider.js +23 -17
  102. package/src/data-providers/sql-database.d.ts +1 -0
  103. package/src/data-providers/sql-database.js +76 -2
  104. package/src/data-providers/web-sql-data-provider.d.ts +1 -0
  105. package/src/data-providers/web-sql-data-provider.js +39 -3
  106. package/src/filter/filter-interfaces.js +5 -0
  107. package/src/live-query/LiveQueryClient.js +95 -72
  108. package/src/live-query/SseSubscriptionClient.d.ts +1 -0
  109. package/src/live-query/SseSubscriptionClient.js +53 -22
  110. package/src/live-query/SubscriptionChannel.d.ts +4 -4
  111. package/src/live-query/SubscriptionChannel.js +34 -59
  112. package/src/live-query/SubscriptionServer.d.ts +6 -5
  113. package/src/live-query/SubscriptionServer.js +127 -103
  114. package/src/remult-proxy.js +4 -0
  115. package/src/remult3/RepositoryImplementation.d.ts +10 -7
  116. package/src/remult3/RepositoryImplementation.js +324 -246
  117. package/src/remult3/remult3.d.ts +8 -7
  118. package/src/server-action.d.ts +1 -1
  119. package/src/shared-tests/db-tests-setup.d.ts +13 -5
  120. package/src/shared-tests/db-tests-setup.js +16 -5
  121. package/src/shared-tests/db-tests.db-tests.js +74 -0
  122. package/src/sql-command.d.ts +1 -0
  123. package/src/tests/frontend-database-tests-setup.spec.js +1 -1
  124. package/src/tests/testHelper.spec.d.ts +2 -2
  125. package/src/tests/testHelper.spec.js +1 -1
  126. package/esm/live-query/ably.js +0 -25
  127. package/live-query/ably.js +0 -38
package/README.md CHANGED
@@ -61,6 +61,7 @@ backend ORM**.
61
61
  * :lock: Fine-grained code-based API authorization
62
62
  * :relieved: Incrementally adoptable
63
63
  * :rocket: Production ready
64
+ * :mega: **NEW - Zero-boilerplate realtime live-queries**
64
65
 
65
66
  ### Status
66
67
 
@@ -0,0 +1,23 @@
1
+ import { GenericResponse } from './server/index';
2
+ import { Remult } from './src/context';
3
+ import { SubscriptionServer } from './src/live-query/SubscriptionServer';
4
+ export declare class SseSubscriptionServer implements SubscriptionServer {
5
+ private canUserConnectToChannel?;
6
+ constructor(canUserConnectToChannel?: (channel: string, remult: Remult) => boolean);
7
+ publishMessage<T>(channel: string, message: any): Promise<void>;
8
+ }
9
+ export interface ResponseRequiredForSSE {
10
+ write(data: string): void;
11
+ writeHead(status: number, headers: any): void;
12
+ flush?(): void;
13
+ }
14
+ export declare class clientConnection {
15
+ response: GenericResponse & ResponseRequiredForSSE;
16
+ channels: Record<string, boolean>;
17
+ close(): void;
18
+ closed: boolean;
19
+ write(eventData: string, eventType?: string): void;
20
+ connectionId: any;
21
+ constructor(response: GenericResponse & ResponseRequiredForSSE);
22
+ sendLiveMessage(): void;
23
+ }
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.clientConnection = exports.SseSubscriptionServer = void 0;
4
+ var tslib_1 = require("tslib");
5
+ var uuid_1 = require("uuid");
6
+ var SseSubscriptionClient_1 = require("./src/live-query/SseSubscriptionClient");
7
+ var SseSubscriptionServer = /** @class */ (function () {
8
+ function SseSubscriptionServer(canUserConnectToChannel) {
9
+ this.canUserConnectToChannel = canUserConnectToChannel;
10
+ //@internal
11
+ this.connections = [];
12
+ //@internal
13
+ this.debugMessageFileSaver = function (id, channel, message) { };
14
+ //@internal
15
+ this.debugFileSaver = function () { };
16
+ if (!this.canUserConnectToChannel) {
17
+ this.canUserConnectToChannel = function () { return true; };
18
+ }
19
+ }
20
+ //@internal
21
+ SseSubscriptionServer.prototype.subscribeToChannel = function (_a, res, remult, remove) {
22
+ var e_1, _b;
23
+ var channel = _a.channel, clientId = _a.clientId;
24
+ if (remove === void 0) { remove = false; }
25
+ try {
26
+ for (var _c = tslib_1.__values(this.connections), _d = _c.next(); !_d.done; _d = _c.next()) {
27
+ var c = _d.value;
28
+ if (c.connectionId === clientId) {
29
+ if (this.canUserConnectToChannel(channel, remult)) {
30
+ if (remove)
31
+ delete c.channels[channel];
32
+ else
33
+ c.channels[channel] = true;
34
+ res.success("ok");
35
+ this.debug();
36
+ return;
37
+ }
38
+ else {
39
+ res.forbidden();
40
+ this.debug();
41
+ return;
42
+ }
43
+ }
44
+ }
45
+ }
46
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
47
+ finally {
48
+ try {
49
+ if (_d && !_d.done && (_b = _c.return)) _b.call(_c);
50
+ }
51
+ finally { if (e_1) throw e_1.error; }
52
+ }
53
+ res.success(SseSubscriptionClient_1.ConnectionNotFoundError);
54
+ };
55
+ SseSubscriptionServer.prototype.publishMessage = function (channel, message) {
56
+ return tslib_1.__awaiter(this, void 0, void 0, function () {
57
+ var data, _a, _b, sc;
58
+ var e_2, _c;
59
+ return tslib_1.__generator(this, function (_d) {
60
+ data = JSON.stringify({ channel: channel, data: message });
61
+ try {
62
+ for (_a = tslib_1.__values(this.connections), _b = _a.next(); !_b.done; _b = _a.next()) {
63
+ sc = _b.value;
64
+ if (sc.channels[channel]) {
65
+ this.debugMessageFileSaver(sc.connectionId, channel, message);
66
+ sc.write(data);
67
+ }
68
+ }
69
+ }
70
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
71
+ finally {
72
+ try {
73
+ if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
74
+ }
75
+ finally { if (e_2) throw e_2.error; }
76
+ }
77
+ return [2 /*return*/];
78
+ });
79
+ });
80
+ };
81
+ //@internal
82
+ SseSubscriptionServer.prototype.openHttpServerStream = function (req, res) {
83
+ var _this = this;
84
+ res.writeHead(200, {
85
+ 'Content-Type': 'text/event-stream',
86
+ 'Cache-Control': 'no-cache',
87
+ 'Connection': 'keep-alive'
88
+ });
89
+ var cc = new clientConnection(res);
90
+ //const lastEventId = req.headers['last-event-id'];
91
+ this.connections.push(cc);
92
+ this.debug();
93
+ //@ts-ignore
94
+ req.on("close", function () {
95
+ cc.close();
96
+ _this.connections = _this.connections.filter(function (s) { return s !== cc; });
97
+ _this.debug();
98
+ });
99
+ return cc;
100
+ };
101
+ //@internal
102
+ SseSubscriptionServer.prototype.debug = function () {
103
+ this.debugFileSaver(this.connections.map(function (x) { return ({
104
+ id: x.connectionId,
105
+ channels: x.channels
106
+ }); }));
107
+ };
108
+ return SseSubscriptionServer;
109
+ }());
110
+ exports.SseSubscriptionServer = SseSubscriptionServer;
111
+ var clientConnection = /** @class */ (function () {
112
+ function clientConnection(response) {
113
+ this.response = response;
114
+ this.channels = {};
115
+ this.closed = false;
116
+ this.connectionId = uuid_1.v4();
117
+ this.write(this.connectionId, "connectionId");
118
+ this.sendLiveMessage();
119
+ }
120
+ clientConnection.prototype.close = function () {
121
+ this.closed = true;
122
+ };
123
+ clientConnection.prototype.write = function (eventData, eventType) {
124
+ if (eventType === void 0) { eventType = "message"; }
125
+ var event = "event:" + eventType;
126
+ // if (id != undefined)
127
+ // event += "\nid:" + id;
128
+ this.response.write(event + "\ndata:" + eventData + "\n\n");
129
+ if (this.response.flush)
130
+ this.response.flush();
131
+ };
132
+ clientConnection.prototype.sendLiveMessage = function () {
133
+ var _this = this;
134
+ if (this.closed)
135
+ return;
136
+ this.write("", "keep-alive");
137
+ setTimeout(function () {
138
+ _this.sendLiveMessage();
139
+ }, 45000);
140
+ };
141
+ return clientConnection;
142
+ }());
143
+ exports.clientConnection = clientConnection;
@@ -1,6 +1,6 @@
1
- import type * as Ably from 'ably';
2
- import type { SubscriptionServer } from '../src/live-query/SubscriptionServer';
3
- import type { SubscriptionClient, SubscriptionClientConnection } from '../src/live-query/SubscriptionChannel';
1
+ import type * as Ably from '../../node_modules/ably';
2
+ import type { SubscriptionServer } from './src/live-query/SubscriptionServer';
3
+ import type { SubscriptionClient, SubscriptionClientConnection } from './src/live-query/SubscriptionChannel';
4
4
  export declare class AblySubscriptionClient implements SubscriptionClient {
5
5
  private ably;
6
6
  constructor(ably: Ably.Types.RealtimePromise);
@@ -8,6 +8,6 @@ export declare class AblySubscriptionClient implements SubscriptionClient {
8
8
  }
9
9
  export declare class AblySubscriptionServer implements SubscriptionServer {
10
10
  private ably;
11
- constructor(ably: Ably.Types.RealtimePromise);
12
- publishMessage<T>(channel: string, message: T): void;
11
+ constructor(ably: Ably.Types.RestPromise);
12
+ publishMessage<T>(channel: string, message: T): Promise<void>;
13
13
  }
package/ably.js ADDED
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AblySubscriptionServer = exports.AblySubscriptionClient = void 0;
4
+ var tslib_1 = require("tslib");
5
+ var AblySubscriptionClient = /** @class */ (function () {
6
+ function AblySubscriptionClient(ably) {
7
+ this.ably = ably;
8
+ }
9
+ AblySubscriptionClient.prototype.openConnection = function (onReconnect) {
10
+ return tslib_1.__awaiter(this, void 0, void 0, function () {
11
+ var _this = this;
12
+ return tslib_1.__generator(this, function (_a) {
13
+ return [2 /*return*/, {
14
+ close: function () {
15
+ // Since we did not open the connection, we do not close it
16
+ },
17
+ subscribe: function (channel, handler) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
18
+ var myHandler;
19
+ var _this = this;
20
+ return tslib_1.__generator(this, function (_a) {
21
+ switch (_a.label) {
22
+ case 0:
23
+ myHandler = function (y) { return handler(y.data); };
24
+ return [4 /*yield*/, this.ably.channels.get(channel).subscribe(function (y) { return myHandler(y); })];
25
+ case 1:
26
+ _a.sent();
27
+ return [2 /*return*/, function () {
28
+ myHandler = function () { };
29
+ _this.ably.channels.get(channel).unsubscribe();
30
+ }];
31
+ }
32
+ });
33
+ }); }
34
+ }];
35
+ });
36
+ });
37
+ };
38
+ return AblySubscriptionClient;
39
+ }());
40
+ exports.AblySubscriptionClient = AblySubscriptionClient;
41
+ var AblySubscriptionServer = /** @class */ (function () {
42
+ function AblySubscriptionServer(ably) {
43
+ this.ably = ably;
44
+ }
45
+ AblySubscriptionServer.prototype.publishMessage = function (channel, message) {
46
+ return tslib_1.__awaiter(this, void 0, void 0, function () {
47
+ return tslib_1.__generator(this, function (_a) {
48
+ switch (_a.label) {
49
+ case 0: return [4 /*yield*/, this.ably.channels.get(channel).publish({ data: message })];
50
+ case 1:
51
+ _a.sent();
52
+ return [2 /*return*/];
53
+ }
54
+ });
55
+ });
56
+ };
57
+ return AblySubscriptionServer;
58
+ }());
59
+ exports.AblySubscriptionServer = AblySubscriptionServer;
@@ -0,0 +1,23 @@
1
+ import { GenericResponse } from './server/index';
2
+ import { Remult } from './src/context';
3
+ import { SubscriptionServer } from './src/live-query/SubscriptionServer';
4
+ export declare class SseSubscriptionServer implements SubscriptionServer {
5
+ private canUserConnectToChannel?;
6
+ constructor(canUserConnectToChannel?: (channel: string, remult: Remult) => boolean);
7
+ publishMessage<T>(channel: string, message: any): Promise<void>;
8
+ }
9
+ export interface ResponseRequiredForSSE {
10
+ write(data: string): void;
11
+ writeHead(status: number, headers: any): void;
12
+ flush?(): void;
13
+ }
14
+ export declare class clientConnection {
15
+ response: GenericResponse & ResponseRequiredForSSE;
16
+ channels: Record<string, boolean>;
17
+ close(): void;
18
+ closed: boolean;
19
+ write(eventData: string, eventType?: string): void;
20
+ connectionId: any;
21
+ constructor(response: GenericResponse & ResponseRequiredForSSE);
22
+ sendLiveMessage(): void;
23
+ }
@@ -0,0 +1,102 @@
1
+ import { v4 as uuid } from 'uuid';
2
+ import { ConnectionNotFoundError } from './src/live-query/SseSubscriptionClient';
3
+ export class SseSubscriptionServer {
4
+ constructor(canUserConnectToChannel) {
5
+ this.canUserConnectToChannel = canUserConnectToChannel;
6
+ //@internal
7
+ this.connections = [];
8
+ //@internal
9
+ this.debugMessageFileSaver = (id, channel, message) => { };
10
+ //@internal
11
+ this.debugFileSaver = () => { };
12
+ if (!this.canUserConnectToChannel) {
13
+ this.canUserConnectToChannel = () => true;
14
+ }
15
+ }
16
+ //@internal
17
+ subscribeToChannel({ channel, clientId }, res, remult, remove = false) {
18
+ for (const c of this.connections) {
19
+ if (c.connectionId === clientId) {
20
+ if (this.canUserConnectToChannel(channel, remult)) {
21
+ if (remove)
22
+ delete c.channels[channel];
23
+ else
24
+ c.channels[channel] = true;
25
+ res.success("ok");
26
+ this.debug();
27
+ return;
28
+ }
29
+ else {
30
+ res.forbidden();
31
+ this.debug();
32
+ return;
33
+ }
34
+ }
35
+ }
36
+ res.success(ConnectionNotFoundError);
37
+ }
38
+ async publishMessage(channel, message) {
39
+ const data = JSON.stringify({ channel, data: message });
40
+ for (const sc of this.connections) {
41
+ if (sc.channels[channel]) {
42
+ this.debugMessageFileSaver(sc.connectionId, channel, message);
43
+ sc.write(data);
44
+ }
45
+ }
46
+ }
47
+ //@internal
48
+ openHttpServerStream(req, res) {
49
+ res.writeHead(200, {
50
+ 'Content-Type': 'text/event-stream',
51
+ 'Cache-Control': 'no-cache',
52
+ 'Connection': 'keep-alive'
53
+ });
54
+ const cc = new clientConnection(res);
55
+ //const lastEventId = req.headers['last-event-id'];
56
+ this.connections.push(cc);
57
+ this.debug();
58
+ //@ts-ignore
59
+ req.on("close", () => {
60
+ cc.close();
61
+ this.connections = this.connections.filter(s => s !== cc);
62
+ this.debug();
63
+ });
64
+ return cc;
65
+ }
66
+ //@internal
67
+ debug() {
68
+ this.debugFileSaver(this.connections.map(x => ({
69
+ id: x.connectionId,
70
+ channels: x.channels
71
+ })));
72
+ }
73
+ }
74
+ export class clientConnection {
75
+ constructor(response) {
76
+ this.response = response;
77
+ this.channels = {};
78
+ this.closed = false;
79
+ this.connectionId = uuid();
80
+ this.write(this.connectionId, "connectionId");
81
+ this.sendLiveMessage();
82
+ }
83
+ close() {
84
+ this.closed = true;
85
+ }
86
+ write(eventData, eventType = "message") {
87
+ let event = "event:" + eventType;
88
+ // if (id != undefined)
89
+ // event += "\nid:" + id;
90
+ this.response.write(event + "\ndata:" + eventData + "\n\n");
91
+ if (this.response.flush)
92
+ this.response.flush();
93
+ }
94
+ sendLiveMessage() {
95
+ if (this.closed)
96
+ return;
97
+ this.write("", "keep-alive");
98
+ setTimeout(() => {
99
+ this.sendLiveMessage();
100
+ }, 45000);
101
+ }
102
+ }
@@ -1,6 +1,6 @@
1
- import type * as Ably from 'ably';
2
- import type { SubscriptionServer } from '../src/live-query/SubscriptionServer';
3
- import type { SubscriptionClient, SubscriptionClientConnection } from '../src/live-query/SubscriptionChannel';
1
+ import type * as Ably from '../../node_modules/ably';
2
+ import type { SubscriptionServer } from './src/live-query/SubscriptionServer';
3
+ import type { SubscriptionClient, SubscriptionClientConnection } from './src/live-query/SubscriptionChannel';
4
4
  export declare class AblySubscriptionClient implements SubscriptionClient {
5
5
  private ably;
6
6
  constructor(ably: Ably.Types.RealtimePromise);
@@ -8,6 +8,6 @@ export declare class AblySubscriptionClient implements SubscriptionClient {
8
8
  }
9
9
  export declare class AblySubscriptionServer implements SubscriptionServer {
10
10
  private ably;
11
- constructor(ably: Ably.Types.RealtimePromise);
12
- publishMessage<T>(channel: string, message: T): void;
11
+ constructor(ably: Ably.Types.RestPromise);
12
+ publishMessage<T>(channel: string, message: T): Promise<void>;
13
13
  }
package/esm/ably.js ADDED
@@ -0,0 +1,28 @@
1
+ export class AblySubscriptionClient {
2
+ constructor(ably) {
3
+ this.ably = ably;
4
+ }
5
+ async openConnection(onReconnect) {
6
+ return {
7
+ close: () => {
8
+ // Since we did not open the connection, we do not close it
9
+ },
10
+ subscribe: async (channel, handler) => {
11
+ let myHandler = (y) => handler(y.data);
12
+ await this.ably.channels.get(channel).subscribe(y => myHandler(y));
13
+ return () => {
14
+ myHandler = () => { };
15
+ this.ably.channels.get(channel).unsubscribe();
16
+ };
17
+ }
18
+ };
19
+ }
20
+ }
21
+ export class AblySubscriptionServer {
22
+ constructor(ably) {
23
+ this.ably = ably;
24
+ }
25
+ async publishMessage(channel, message) {
26
+ await this.ably.channels.get(channel).publish({ data: message });
27
+ }
28
+ }
package/esm/graphql.js CHANGED
@@ -1,17 +1,24 @@
1
1
  import { Remult } from ".";
2
2
  import { allEntities } from "./src/context";
3
- import { getEntityKey } from "./src/remult3";
4
3
  import { DataApi } from "./src/data-api";
5
4
  export function remultGraphql(api) {
6
5
  let r = new Remult();
7
- let types = '';
6
+ let types = [];
7
+ let filterTypes = '';
8
8
  let query = '';
9
9
  let root = {};
10
+ function getType(key) {
11
+ let t = types.find(t => t.key === key);
12
+ if (!t)
13
+ types.push((t = ({ fields: "", key, moreFields: "", resultProcessors: [] })));
14
+ return t;
15
+ }
10
16
  for (const e of allEntities) {
11
17
  let meta = r.repo(e).metadata;
12
- let fields = '';
13
18
  let filterFields = '';
14
- let key = getEntityKey(e);
19
+ let key = meta.key;
20
+ const t = getType(key);
21
+ let q = "\n\t" + key + "(options: options, filter:" + key + "Filter): [" + key + "]";
15
22
  if (key) {
16
23
  for (const f of meta.fields) {
17
24
  {
@@ -19,6 +26,7 @@ export function remultGraphql(api) {
19
26
  switch (f.valueType) {
20
27
  case Boolean:
21
28
  type = "Boolean";
29
+ break;
22
30
  case Number:
23
31
  {
24
32
  if (f.valueConverter?.fieldTypeInDb == 'integer')
@@ -26,8 +34,31 @@ export function remultGraphql(api) {
26
34
  else
27
35
  type = "Float";
28
36
  }
37
+ break;
38
+ }
39
+ if (allEntities.includes(f.valueType)) {
40
+ const refKey = r.repo(f.valueType).metadata.key;
41
+ t.fields += "\n\t" + f.key + ":" + refKey;
42
+ t.resultProcessors.push(r => {
43
+ const val = r[f.key];
44
+ r[f.key] = async (args, req, gqlInfo) => {
45
+ const queryResult = await root[refKey]({ ...args.filter, filter: { id: val }, options: { limit: 1 } }, req, gqlInfo);
46
+ if (queryResult.length > 0)
47
+ return queryResult[0];
48
+ return null;
49
+ };
50
+ });
51
+ let refT = getType(refKey);
52
+ refT.moreFields += q;
53
+ refT.resultProcessors.push(r => {
54
+ const val = r.id;
55
+ r[key] = async (args, req, gqlInfo) => {
56
+ return await root[key]({ filter: { ...args.filter, [f.key]: val }, options: args.options }, req, gqlInfo);
57
+ };
58
+ });
29
59
  }
30
- fields += "\n\t" + f.key + ":" + type;
60
+ else
61
+ t.fields += "\n\t" + f.key + ":" + type;
31
62
  for (const operator of ["", "_ne"]) {
32
63
  filterFields += "\n\t" + f.key + operator + ":" + type;
33
64
  }
@@ -44,41 +75,49 @@ export function remultGraphql(api) {
44
75
  filterFields += "\n\t" + f.key + "_in:[" + type + "]";
45
76
  }
46
77
  }
47
- types += "type " + key + "{" + fields + "\n}\n";
48
- types += "input " + key + "Filter{" + filterFields + "\n\tOR:[" + key + "Filter]\n}\n";
49
- query += "\n\t" + key + "(options: options, filter:" + key + "Filter): [" + key + "]";
50
- root[key] = async ({ options, filter }, req) => {
51
- let remult = await api.getRemult(req);
52
- let repo = remult.repo(e);
53
- let dApi = new DataApi(repo, remult);
54
- let result;
55
- let err;
56
- await dApi.getArray({
57
- success: x => result = x,
58
- created: undefined,
59
- deleted: undefined,
60
- error: x => err = x,
61
- forbidden: () => err = 'forbidden',
62
- notFound: () => err = 'not found',
63
- progress: undefined
64
- }, {
65
- get: key => {
66
- if (options)
67
- switch (key) {
68
- case "_limit":
69
- return options.limit;
70
- case "_page":
71
- return options.page;
72
- case "_sort":
73
- return options.sort;
74
- case "_order":
75
- return options.order;
78
+ filterTypes += "input " + key + "Filter{" + filterFields + "\n\tOR:[" + key + "Filter]\n}\n";
79
+ query += q;
80
+ root[key] = async ({ options, filter }, req, a) => {
81
+ return new Promise(async (res, error) => {
82
+ api.withRemult(req, undefined, async () => {
83
+ let remult = await api.getRemult(req);
84
+ let repo = remult.repo(e);
85
+ let dApi = new DataApi(repo, remult);
86
+ let result;
87
+ let err;
88
+ await dApi.getArray({
89
+ success: x => result = x.map(y => {
90
+ t.resultProcessors.forEach(z => z(y));
91
+ return y;
92
+ }),
93
+ created: undefined,
94
+ deleted: undefined,
95
+ error: x => err = x,
96
+ forbidden: () => err = 'forbidden',
97
+ notFound: () => err = 'not found',
98
+ progress: undefined
99
+ }, {
100
+ get: key => {
101
+ if (options)
102
+ switch (key) {
103
+ case "_limit":
104
+ return options.limit;
105
+ case "_page":
106
+ return options.page;
107
+ case "_sort":
108
+ return options.sort;
109
+ case "_order":
110
+ return options.order;
111
+ }
76
112
  }
77
- }
78
- }, filter);
79
- if (err)
80
- throw err;
81
- return result;
113
+ }, filter);
114
+ if (err) {
115
+ error(err);
116
+ return;
117
+ }
118
+ res(result);
119
+ });
120
+ });
82
121
  };
83
122
  }
84
123
  }
@@ -88,15 +127,15 @@ export function remultGraphql(api) {
88
127
  }
89
128
  return {
90
129
  rootValue: root,
91
- schema: `
130
+ schema: `${types.map(({ key, fields, moreFields }) => "type " + key + "{" + fields + moreFields + "\n}\n").join('')}
131
+ ${query}
132
+ ${filterTypes}
92
133
  input options{
93
134
  limit:Int
94
135
  page:Int
95
136
  sort:String
96
137
  order:String
97
138
  }
98
- ${types}
99
- ${query}
100
139
  `
101
140
  };
102
141
  }
package/esm/index.d.ts CHANGED
@@ -14,7 +14,7 @@ export { SqlDatabase, } from './src/data-providers/sql-database';
14
14
  export { CustomSqlFilterObject, CustomSqlFilterBuilder, dbNamesOf } from './src/filter/filter-consumer-bridge-to-sql-request';
15
15
  export { JsonDataProvider, JsonEntityStorage } from './src/data-providers/json-data-provider';
16
16
  export { Controller, BackendMethodOptions, BackendMethod, ProgressListener } from './src/server-action';
17
- export { Allowed, Allow, Remult, RemultContext, ApiClient, isBackend, AllowedForInstance, EventDispatcher, EventSource, Unobserve, UserInfo } from './src/context';
17
+ export { Allowed, Allow, Remult, RemultContext, ApiClient, isBackend, AllowedForInstance, EventDispatcher, EventSource, UserInfo } from './src/context';
18
18
  export { ExternalHttpProvider } from './src/buildRestDataProvider';
19
19
  export { IdEntity } from './src/id-entity';
20
20
  export { SortSegment, Sort } from './src/sort';
@@ -0,0 +1,23 @@
1
+ import { Storage, DataProvider } from "../src/data-interfaces";
2
+ import { LiveQueryStorage, StoredQuery } from "../src/live-query/SubscriptionServer";
3
+ import { EntityBase, Repository } from "../src/remult3";
4
+ export declare class DataProviderLiveQueryStorage implements LiveQueryStorage, Storage {
5
+ repo: Promise<Repository<LiveQueryStorageEntity>>;
6
+ dataProvider: Promise<DataProvider>;
7
+ constructor(dataProvider: DataProvider | Promise<DataProvider> | (() => Promise<DataProvider | undefined>));
8
+ ensureSchema(): Promise<void>;
9
+ add({ id, entityKey, data }: StoredQuery): Promise<void>;
10
+ remove(queryId: string): Promise<void>;
11
+ forEach(entityKey: string, callback: (args: {
12
+ query: StoredQuery;
13
+ setData(data: any): Promise<void>;
14
+ }) => Promise<void>): Promise<void>;
15
+ keepAliveAndReturnUnknownQueryIds(queryIds: string[]): Promise<string[]>;
16
+ }
17
+ declare class LiveQueryStorageEntity extends EntityBase {
18
+ id: string;
19
+ entityKey: string;
20
+ data: any;
21
+ lastUsedIso: string;
22
+ }
23
+ export {};