remult 0.17.0 → 0.18.1-exp.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 (116) hide show
  1. package/README.md +5 -28
  2. package/esm/index.d.ts +4 -2
  3. package/esm/live-query/ably.d.ts +13 -0
  4. package/esm/live-query/ably.js +25 -0
  5. package/esm/live-query/index.d.ts +3 -0
  6. package/esm/live-query/index.js +3 -0
  7. package/esm/postgres/schema-builder.js +4 -4
  8. package/esm/remult-express.d.ts +26 -0
  9. package/esm/remult-express.js +137 -1
  10. package/esm/remult-fastify.js +1 -1
  11. package/esm/remult-fresh.js +2 -2
  12. package/esm/remult-knex/index.js +4 -11
  13. package/esm/server/expressBridge.d.ts +49 -11
  14. package/esm/server/expressBridge.js +48 -24
  15. package/esm/server/index.d.ts +1 -3
  16. package/esm/server/index.js +1 -6
  17. package/esm/src/backend-tests/backend-database-test-setup.backend-spec.d.ts +5 -0
  18. package/esm/src/backend-tests/backend-database-test-setup.backend-spec.js +113 -0
  19. package/esm/src/backend-tests/my-test.backend-spec.d.ts +1 -0
  20. package/esm/src/backend-tests/my-test.backend-spec.js +59 -0
  21. package/esm/src/backend-tests/test-http-server.backend-spec.d.ts +6 -0
  22. package/esm/src/backend-tests/test-http-server.backend-spec.js +171 -0
  23. package/esm/src/buildRestDataProvider.d.ts +31 -0
  24. package/esm/src/buildRestDataProvider.js +110 -0
  25. package/esm/src/column.d.ts +3 -3
  26. package/esm/src/context.d.ts +21 -30
  27. package/esm/src/context.js +87 -110
  28. package/esm/src/data-api.d.ts +8 -3
  29. package/esm/src/data-api.js +117 -61
  30. package/esm/src/data-providers/array-entity-data-provider.js +1 -16
  31. package/esm/src/data-providers/rest-data-provider.d.ts +12 -1
  32. package/esm/src/data-providers/rest-data-provider.js +34 -9
  33. package/esm/src/data-providers/sql-database.js +1 -1
  34. package/esm/src/data-providers/web-sql-data-provider.js +1 -1
  35. package/esm/src/live-query/EventSourceLiveQueryProvider.d.ts +4 -0
  36. package/esm/src/live-query/EventSourceLiveQueryProvider.js +78 -0
  37. package/esm/src/live-query/LiveQueryClient.d.ts +19 -0
  38. package/esm/src/live-query/LiveQueryClient.js +147 -0
  39. package/esm/src/live-query/LiveQueryPublisher.d.ts +48 -0
  40. package/esm/src/live-query/LiveQueryPublisher.js +112 -0
  41. package/esm/src/live-query/LiveQuerySubscriber.d.ts +67 -0
  42. package/esm/src/live-query/LiveQuerySubscriber.js +99 -0
  43. package/esm/src/remult-proxy.d.ts +1 -1
  44. package/esm/src/remult-proxy.js +11 -9
  45. package/esm/src/remult3/RepositoryImplementation.d.ts +6 -3
  46. package/esm/src/remult3/RepositoryImplementation.js +38 -22
  47. package/esm/src/remult3/getId.d.ts +2 -0
  48. package/esm/src/remult3/getId.js +7 -0
  49. package/esm/src/remult3/remult3.d.ts +10 -6
  50. package/esm/src/server-action.d.ts +4 -0
  51. package/esm/src/server-action.js +11 -12
  52. package/esm/src/shared-tests/db-tests-setup.d.ts +1 -1
  53. package/esm/src/shared-tests/db-tests-setup.js +1 -3
  54. package/esm/src/sort.d.ts +1 -0
  55. package/esm/src/sort.js +18 -0
  56. package/esm/src/tests/testHelper.spec.d.ts +2 -0
  57. package/esm/src/tests/testHelper.spec.js +62 -58
  58. package/esm/src/valueConverters.js +1 -3
  59. package/index.d.ts +4 -2
  60. package/live-query/ably.d.ts +13 -0
  61. package/live-query/ably.js +38 -0
  62. package/live-query/index.d.ts +3 -0
  63. package/live-query/index.js +10 -0
  64. package/package.json +17 -9
  65. package/postgres/schema-builder.js +4 -4
  66. package/remult-express.d.ts +26 -0
  67. package/remult-express.js +231 -3
  68. package/remult-fastify.js +2 -2
  69. package/remult-fresh.js +1 -1
  70. package/remult-knex/index.js +10 -18
  71. package/server/expressBridge.d.ts +49 -11
  72. package/server/expressBridge.js +91 -43
  73. package/server/index.d.ts +1 -3
  74. package/server/index.js +2 -7
  75. package/src/backend-tests/backend-database-test-setup.backend-spec.d.ts +5 -0
  76. package/src/backend-tests/backend-database-test-setup.backend-spec.js +231 -0
  77. package/src/backend-tests/my-test.backend-spec.d.ts +1 -0
  78. package/src/backend-tests/my-test.backend-spec.js +160 -0
  79. package/src/backend-tests/test-http-server.backend-spec.d.ts +6 -0
  80. package/src/backend-tests/test-http-server.backend-spec.js +342 -0
  81. package/src/buildRestDataProvider.d.ts +31 -0
  82. package/src/buildRestDataProvider.js +162 -0
  83. package/src/column.d.ts +3 -3
  84. package/src/context.d.ts +21 -30
  85. package/src/context.js +142 -159
  86. package/src/data-api.d.ts +8 -3
  87. package/src/data-api.js +198 -79
  88. package/src/data-providers/array-entity-data-provider.js +1 -16
  89. package/src/data-providers/rest-data-provider.d.ts +12 -1
  90. package/src/data-providers/rest-data-provider.js +62 -24
  91. package/src/data-providers/sql-database.js +1 -1
  92. package/src/data-providers/web-sql-data-provider.js +1 -1
  93. package/src/live-query/EventSourceLiveQueryProvider.d.ts +4 -0
  94. package/src/live-query/EventSourceLiveQueryProvider.js +119 -0
  95. package/src/live-query/LiveQueryClient.d.ts +19 -0
  96. package/src/live-query/LiveQueryClient.js +208 -0
  97. package/src/live-query/LiveQueryPublisher.d.ts +48 -0
  98. package/src/live-query/LiveQueryPublisher.js +239 -0
  99. package/src/live-query/LiveQuerySubscriber.d.ts +67 -0
  100. package/src/live-query/LiveQuerySubscriber.js +201 -0
  101. package/src/remult-proxy.d.ts +1 -1
  102. package/src/remult-proxy.js +19 -9
  103. package/src/remult3/RepositoryImplementation.d.ts +6 -3
  104. package/src/remult3/RepositoryImplementation.js +68 -42
  105. package/src/remult3/getId.d.ts +2 -0
  106. package/src/remult3/getId.js +11 -0
  107. package/src/remult3/remult3.d.ts +10 -6
  108. package/src/server-action.d.ts +4 -0
  109. package/src/server-action.js +11 -11
  110. package/src/shared-tests/db-tests-setup.d.ts +1 -1
  111. package/src/shared-tests/db-tests-setup.js +1 -3
  112. package/src/sort.d.ts +1 -0
  113. package/src/sort.js +18 -0
  114. package/src/tests/testHelper.spec.d.ts +2 -0
  115. package/src/tests/testHelper.spec.js +100 -95
  116. package/src/valueConverters.js +1 -3
package/README.md CHANGED
@@ -3,7 +3,6 @@
3
3
  <img src="https://github.com/remult/remult/raw/master/docs/.vuepress/public/logo.png" width="140" height="140">
4
4
  </a>
5
5
  <h1>Remult</h1>
6
- <p>A CRUD framework for full-stack TypeScript</p>
7
6
  <a href="https://circleci.com/gh/remult/remult/tree/master" rel="nofollow">
8
7
  <img alt="CircleCI" src="https://circleci.com/gh/remult/remult/tree/master.svg?style=shield">
9
8
  </a>
@@ -25,29 +24,14 @@
25
24
  <a href="https://twitter.com/intent/tweet?text=Remult%20-%20open-source%20CRUD%20framework%20for%20full-stack%20TypeScript%0A%0A%40remultjs%0A&url=https%3A%2F%2Fgithub.com%2Fremult%2Fremult">
26
25
  <img alt="Twitter URL" src="https://img.shields.io/twitter/url?style=social&url=https%3A%2F%2Ftwitter.com%2Fintent%2Ftweet%3Ftext%3DRemult%2520-%2520open-source%2520CRUD%2520framework%2520for%2520full-stack%2520TypeScript%250A%250A%2540remultjs%250A%26url%3Dhttps%253A%252F%252Fgithub.com%252Fremult%252Fremult">
27
26
  </a>
27
+ <a href="https://twitter.com/remultjs" rel="nofollow">
28
+ <img alt="Twitter Follow" src="https://img.shields.io/twitter/url.svg?label=Follow%20%40remultjs&style=social&url=https%3A%2F%2Ftwitter.com%2FRemultJs">
29
+ </a>
28
30
  </div>
29
-
30
-
31
- <br/>
32
-
33
- <div align="center">
34
- <a href="#getting-started">Getting Started</a> |
35
- <a href="#documentation">Documentation</a> |
36
- <a href="#example-apps">Example Apps</a>
37
- </div>
38
-
39
31
  <hr/>
40
- <br/>
41
32
 
42
- <a href="https://www.youtube.com/watch?v=rEoScmSVNUE" target="_blank">
43
- <p align="center">
44
- <img src="https://github.com/remult/remult/raw/master/static/images/video-thumbnail.jpg" alt="Video thumbnail" />
45
- </p>
46
- </a>
47
-
48
- <a href="https://www.youtube.com/watch?v=rEoScmSVNUE" target="_blank">
49
- <p align="center">Watch code demo on YouTube here (14 mins)</p>
50
- </a>
33
+ [Getting Started](#getting-started) | [Documentation](#documentation) |
34
+ [Example Apps](#example-apps)
51
35
 
52
36
  ## What is Remult?
53
37
 
@@ -55,13 +39,6 @@
55
39
  as a single source of truth for your API, frontend type-safe API client and
56
40
  backend ORM**.
57
41
 
58
- * :zap: Zero-boilerplate CRUD API routes with paging, sorting, and filtering for Express / Fastify / Next.js / NestJS / Koa / others...
59
- * :ok_hand: Fullstack type-safety for API queries, mutations and RPC, without code generation
60
- * :sparkles: Input validation, defined once, runs both on the backend and on the frontend for best UX
61
- * :lock: Fine-grained code-based API authorization
62
- * :relieved: Incrementally adoptable
63
- * :rocket: Production ready
64
-
65
42
  ### Status
66
43
 
67
44
  Remult is **production-ready** and, in fact, used in production apps since 2018.
package/esm/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- export { Field, Fields, StringFieldOptions, FieldsMetadata, Entity, EntityBase, ControllerBase, FieldRef, IdFieldRef, FieldsRef, EntityMetadata, EntityOrderBy, EntityFilter, FindOptions, QueryResult, QueryOptions, Repository, FieldType, FindFirstOptions, ComparisonValueFilter, ValueFilter, IdFilter, ContainsStringValueFilter, getFields, EntityRef, getEntityRef, SortSegments, ValueListFieldType, getValueList, ValueListFieldOptions, ValueListInfo, OmitEB, Paginator, CaptionTransformer } from './src/remult3';
1
+ export { Field, Fields, FieldsMetadata, Entity, EntityBase, ControllerBase, FieldRef, IdFieldRef, FieldsRef, EntityMetadata, EntityOrderBy, EntityFilter, FindOptions, QueryResult, QueryOptions, Repository, FieldType, FindFirstOptions, ComparisonValueFilter, ValueFilter, IdFilter, ContainsStringValueFilter, getFields, EntityRef, getEntityRef, SortSegments, ValueListFieldType, getValueList, ValueListFieldOptions, ValueListInfo, OmitEB, Paginator, CaptionTransformer } from './src/remult3';
2
+ export { StringFieldOptions } from './src/remult3/RepositoryImplementation';
2
3
  export { describeClass } from './src/remult3/DecoratorReplacer';
3
4
  export { EntityOptions } from './src/entity';
4
5
  export { DataProvider, EntityDataProvider, EntityDataProviderFindOptions, ErrorInfo, RestDataProviderHttpProvider } from './src/data-interfaces';
@@ -13,7 +14,8 @@ export { SqlDatabase, } from './src/data-providers/sql-database';
13
14
  export { CustomSqlFilterObject, CustomSqlFilterBuilder } from './src/filter/filter-consumer-bridge-to-sql-request';
14
15
  export { JsonDataProvider, JsonEntityStorage } from './src/data-providers/json-data-provider';
15
16
  export { Controller, BackendMethodOptions, BackendMethod, ProgressListener } from './src/server-action';
16
- export { Allowed, Allow, Remult, RemultContext, ApiClient, isBackend, AllowedForInstance, EventDispatcher, EventSource, ExternalHttpProvider, Unobserve, UserInfo } from './src/context';
17
+ export { Allowed, Allow, Remult, RemultContext, ApiClient, isBackend, AllowedForInstance, EventDispatcher, EventSource, Unobserve, UserInfo } from './src/context';
18
+ export { ExternalHttpProvider } from './src/buildRestDataProvider';
17
19
  export { IdEntity } from './src/id-entity';
18
20
  export { SortSegment, Sort } from './src/sort';
19
21
  export { OneToMany, CompoundIdField } from './src/column';
@@ -0,0 +1,13 @@
1
+ import type * as Ably from 'ably';
2
+ import type { ServerEventDispatcher } from '../src/live-query/LiveQueryPublisher';
3
+ import type { 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,3 @@
1
+ export { LiveQueryPublisher, ServerEventDispatcher, LiveQueryStorage, LiveQueryStorageInMemoryImplementation } from "../src/live-query/LiveQueryPublisher";
2
+ export { PubSubClient, LiveQueryProvider, AMessageChannel } from '../src/live-query/LiveQuerySubscriber';
3
+ export { LiveQueryClient } from '../src/live-query/LiveQueryClient';
@@ -0,0 +1,3 @@
1
+ export { LiveQueryPublisher, LiveQueryStorageInMemoryImplementation } from "../src/live-query/LiveQueryPublisher";
2
+ export { AMessageChannel } from '../src/live-query/LiveQuerySubscriber';
3
+ export { LiveQueryClient } from '../src/live-query/LiveQueryClient';
@@ -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,6 +1,32 @@
1
1
  import * as express from 'express';
2
2
  import { RemultServer, RemultServerOptions } from './server/expressBridge';
3
+ import { Remult } from './src/context';
4
+ import { ServerEventChannelSubscribeDTO } from './src/live-query/LiveQuerySubscriber';
5
+ import { ServerEventDispatcher } from './src/live-query/LiveQueryPublisher';
3
6
  export declare function remultExpress(options?: RemultServerOptions<express.Request> & {
4
7
  bodyParser?: boolean;
5
8
  bodySizeLimit?: string;
9
+ serverEventDispatcher?: (router: express.Router, server: RemultServer) => ServerEventDispatcher;
6
10
  }): express.RequestHandler & RemultServer;
11
+ export declare class ServerEventsController implements ServerEventDispatcher {
12
+ private canUserConnectToChannel?;
13
+ subscribeToChannel({ channel, clientId }: ServerEventChannelSubscribeDTO, res: import('express').Response, remult: Remult, remove?: boolean): void;
14
+ connections: clientConnection[];
15
+ constructor(canUserConnectToChannel?: (channel: string, remult: Remult) => boolean);
16
+ sendChannelMessage<T>(channel: string, message: any): void;
17
+ debugMessageFileSaver: (id: any, channel: any, message: any) => void;
18
+ openHttpServerStream(req: import('express').Request, res: import('express').Response): clientConnection;
19
+ debug(): void;
20
+ debugFileSaver: (x: any) => void;
21
+ }
22
+ declare class clientConnection {
23
+ response: import('express').Response;
24
+ channels: Record<string, boolean>;
25
+ close(): void;
26
+ closed: boolean;
27
+ write(eventData: string, eventType?: string): void;
28
+ connectionId: any;
29
+ constructor(response: import('express').Response);
30
+ sendLiveMessage(): void;
31
+ }
32
+ export {};
@@ -1,5 +1,10 @@
1
1
  import * as express from 'express';
2
- import { createRemultServer } from './server/index';
2
+ import { createRemultServer } from './server/expressBridge';
3
+ import { liveQueryKeepAliveRoute, streamUrl } from './src/live-query/LiveQuerySubscriber';
4
+ import { LiveQueryPublisher, LiveQueryStorageInMemoryImplementation } from './src/live-query/LiveQueryPublisher';
5
+ import { v4 as uuid } from 'uuid';
6
+ import { remult } from './src/remult-proxy';
7
+ import { getEntityKey } from './src/remult3';
3
8
  export function remultExpress(options) {
4
9
  let app = express.Router();
5
10
  if (!options) {
@@ -14,6 +19,29 @@ export function remultExpress(options) {
14
19
  }
15
20
  const server = createRemultServer(options);
16
21
  server.registerRouter(app);
22
+ if (!options.serverEventDispatcher) {
23
+ options.serverEventDispatcher = (router, server) => {
24
+ return buildHttpServerEventDispatcher(router, options.rootPath, server);
25
+ };
26
+ }
27
+ app.post(options.rootPath + liveQueryKeepAliveRoute, (r, res, n) => server.withRemult(r, res, n), async (req, res) => {
28
+ res.send(await remult.liveQueryPublisher.storage.keepAliveAndReturnUnknownIds(req.body));
29
+ });
30
+ server.liveQueryManager = new LiveQueryPublisher(options.serverEventDispatcher(app, server), new LiveQueryStorageInMemoryImplementation(), async (req, entityKey, what) => {
31
+ for (const e of options.entities) {
32
+ let key = getEntityKey(e);
33
+ if (key === entityKey) {
34
+ await new Promise((result) => {
35
+ server.withRemult(options.requestSerializer.fromJson(req), undefined, async () => {
36
+ await what(remult.repo(e));
37
+ result({});
38
+ });
39
+ });
40
+ return;
41
+ }
42
+ }
43
+ throw new Error("Couldn't find entity " + entityKey);
44
+ });
17
45
  return Object.assign(app, {
18
46
  getRemult: (req) => server.getRemult(req),
19
47
  openApiDoc: (options) => server.openApiDoc(options),
@@ -21,3 +49,111 @@ export function remultExpress(options) {
21
49
  withRemult: (...args) => server.withRemult(...args)
22
50
  });
23
51
  }
52
+ export class ServerEventsController {
53
+ constructor(canUserConnectToChannel) {
54
+ this.canUserConnectToChannel = canUserConnectToChannel;
55
+ this.connections = [];
56
+ this.debugMessageFileSaver = (id, channel, message) => { };
57
+ this.debugFileSaver = () => { };
58
+ if (!this.canUserConnectToChannel) {
59
+ this.canUserConnectToChannel = () => true;
60
+ }
61
+ }
62
+ subscribeToChannel({ channel, clientId }, res, remult, remove = false) {
63
+ for (const c of this.connections) {
64
+ if (c.connectionId === clientId) {
65
+ if (this.canUserConnectToChannel(channel, remult)) {
66
+ if (remove)
67
+ delete c.channels[channel];
68
+ else
69
+ c.channels[channel] = true;
70
+ res.json("ok");
71
+ this.debug();
72
+ return;
73
+ }
74
+ else {
75
+ res.sendStatus(403);
76
+ this.debug();
77
+ return;
78
+ }
79
+ }
80
+ }
81
+ res.sendStatus(404);
82
+ }
83
+ sendChannelMessage(channel, message) {
84
+ const data = JSON.stringify({ channel, data: message });
85
+ for (const sc of this.connections) {
86
+ if (sc.channels[channel]) {
87
+ this.debugMessageFileSaver(sc.connectionId, channel, message);
88
+ sc.write(data);
89
+ }
90
+ }
91
+ }
92
+ openHttpServerStream(req, res) {
93
+ res.writeHead(200, {
94
+ 'Content-Type': 'text/event-stream',
95
+ 'Cache-Control': 'no-cache',
96
+ 'Connection': 'keep-alive'
97
+ });
98
+ const cc = new clientConnection(res);
99
+ //const lastEventId = req.headers['last-event-id'];
100
+ this.connections.push(cc);
101
+ this.debug();
102
+ req.on("close", () => {
103
+ cc.close();
104
+ this.connections = this.connections.filter(s => s !== cc);
105
+ this.debug();
106
+ });
107
+ return cc;
108
+ }
109
+ debug() {
110
+ this.debugFileSaver(this.connections.map(x => ({
111
+ id: x.connectionId,
112
+ channels: x.channels
113
+ })));
114
+ }
115
+ }
116
+ class clientConnection {
117
+ constructor(response) {
118
+ this.response = response;
119
+ this.channels = {};
120
+ this.closed = false;
121
+ this.connectionId = uuid();
122
+ this.write(this.connectionId, "connectionId");
123
+ this.sendLiveMessage();
124
+ }
125
+ close() {
126
+ this.closed = true;
127
+ }
128
+ write(eventData, eventType = "message") {
129
+ let event = "event:" + eventType;
130
+ // if (id != undefined)
131
+ // event += "\nid:" + id;
132
+ this.response.write(event + "\ndata:" + eventData + "\n\n");
133
+ let r = this.response;
134
+ if (r.flush)
135
+ r.flush();
136
+ }
137
+ sendLiveMessage() {
138
+ if (this.closed)
139
+ return;
140
+ this.write("", "keep-alive");
141
+ setTimeout(() => {
142
+ this.sendLiveMessage();
143
+ }, 45000);
144
+ }
145
+ }
146
+ function buildHttpServerEventDispatcher(router, apiPath, server) {
147
+ const streamPath = apiPath + '/' + streamUrl;
148
+ let httpServerEvents = new ServerEventsController();
149
+ router.get(streamPath, (r, res, next) => server.withRemult(r, res, next), (req, res) => {
150
+ remult.liveQueryPublisher.dispatcher.openHttpServerStream(req, res);
151
+ });
152
+ router.post(streamPath + '/subscribe', (r, res, next) => server.withRemult(r, res, next), (req, res) => {
153
+ remult.liveQueryPublisher.dispatcher.subscribeToChannel(req.body, res, remult);
154
+ });
155
+ router.post(streamPath + '/unsubscribe', (r, res, next) => server.withRemult(r, res, next), (req, res) => {
156
+ remult.liveQueryPublisher.dispatcher.subscribeToChannel(req.body, res, remult, true);
157
+ });
158
+ return httpServerEvents;
159
+ }
@@ -1,4 +1,4 @@
1
- import { createRemultServer } from './server/index';
1
+ import { createRemultServer } from './server/expressBridge';
2
2
  export function remultFastify(options) {
3
3
  function fastifyHandler(handler) {
4
4
  const response = (req, res) => {
@@ -1,6 +1,6 @@
1
- import { createRemultServerCore } from "./server/expressBridge";
1
+ import { createRemultServer } from "./server/expressBridge";
2
2
  export function remultFresh(options, response) {
3
- const server = createRemultServerCore(options);
3
+ const server = createRemultServer(options);
4
4
  return {
5
5
  getRemult: r => server.getRemult(r),
6
6
  openApiDoc: x => server.openApiDoc(x),
@@ -153,15 +153,8 @@ class KnexEntityDataProvider {
153
153
  }
154
154
  let insert = this.knex(e.entityName).insert(insertObject);
155
155
  if (isAutoIncrement(this.entity.idMetadata.field)) {
156
- let newId;
157
- if (this.knex.client.config.client === 'mysql2') {
158
- let result = await insert;
159
- newId = result[0];
160
- }
161
- else {
162
- let result = await insert.returning(this.entity.idMetadata.field.key);
163
- newId = result[0].id;
164
- }
156
+ let result = await insert.returning(this.entity.idMetadata.field.key);
157
+ let newId = result[0].id;
165
158
  resultFilter = new Filter(x => x.isEqualTo(this.entity.idMetadata.field, newId));
166
159
  }
167
160
  else
@@ -263,7 +256,7 @@ export class KnexSchemaBuilder {
263
256
  this.additionalWhere = '';
264
257
  }
265
258
  async verifyStructureOfAllEntities(remult) {
266
- console.log("start verify structure");
259
+ console.info("start verify structure");
267
260
  for (const entity of allEntities) {
268
261
  let metadata = remult.repo(entity).metadata;
269
262
  try {
@@ -326,7 +319,7 @@ export class KnexSchemaBuilder {
326
319
  }
327
320
  }
328
321
  catch (err) {
329
- console.log(err);
322
+ console.error(err);
330
323
  }
331
324
  }
332
325
  }
@@ -1,9 +1,11 @@
1
- import { queuedJobInfoResponse } from '../src/server-action';
1
+ import { ActionInterface, queuedJobInfoResponse } from '../src/server-action';
2
2
  import { DataProvider } from '../src/data-interfaces';
3
- import { Remult, UserInfo } from '../src/context';
3
+ import { DataApi, DataApiRequest, DataApiResponse } from '../src/data-api';
4
+ import { AllowedForInstance, Remult, UserInfo } from '../src/context';
4
5
  import { ClassType } from '../classType';
5
6
  import { Repository } from '../src/remult3';
6
7
  import { IdEntity } from '../src/id-entity';
8
+ import { LiveQueryPublisher } from '../src/live-query/LiveQueryPublisher';
7
9
  export interface RemultServerOptions<RequestType extends GenericRequest> {
8
10
  /** Sets a database connection for Remult.
9
11
  *
@@ -12,6 +14,10 @@ export interface RemultServerOptions<RequestType extends GenericRequest> {
12
14
  dataProvider?: DataProvider | Promise<DataProvider> | (() => Promise<DataProvider | undefined>);
13
15
  queueStorage?: QueueStorage;
14
16
  initRequest?: (remult: Remult, origReq: RequestType) => Promise<void>;
17
+ requestSerializer?: {
18
+ toJson: (request: RequestType) => any;
19
+ fromJson: (request: any) => RequestType;
20
+ };
15
21
  getUser?: (request: RequestType) => Promise<UserInfo>;
16
22
  initApi?: (remult: Remult) => void | Promise<void>;
17
23
  logApiEndPoints?: boolean;
@@ -20,7 +26,7 @@ export interface RemultServerOptions<RequestType extends GenericRequest> {
20
26
  controllers?: ClassType<any>[];
21
27
  rootPath?: string;
22
28
  }
23
- export declare function createRemultServerCore<RequestType extends GenericRequest = GenericRequest>(options?: RemultServerOptions<RequestType>): RemultServer;
29
+ export declare function createRemultServer<RequestType extends GenericRequest = GenericRequest>(options?: RemultServerOptions<RequestType>): RemultServer;
24
30
  export declare type GenericRequestHandler = (req: GenericRequest, res: GenericResponse, next: VoidFunction) => void;
25
31
  export interface ServerHandleResponse {
26
32
  data?: any;
@@ -57,14 +63,39 @@ export interface GenericResponse {
57
63
  status(statusCode: number): GenericResponse;
58
64
  end(): any;
59
65
  }
60
- export declare class RemultAsyncLocalStorage {
61
- private readonly remultObjectStorage;
62
- static enable(): void;
63
- static disable(): void;
64
- constructor(remultObjectStorage: import('async_hooks').AsyncLocalStorage<Remult>);
65
- run(remult: Remult, callback: VoidFunction): void;
66
- getRemult(): Remult;
67
- static instance: RemultAsyncLocalStorage;
66
+ export declare class RemultServerImplementation implements RemultServer {
67
+ queue: inProcessQueueHandler;
68
+ options: RemultServerOptions<GenericRequest>;
69
+ dataProvider: DataProvider | Promise<DataProvider>;
70
+ constructor(queue: inProcessQueueHandler, options: RemultServerOptions<GenericRequest>, dataProvider: DataProvider | Promise<DataProvider>);
71
+ liveQueryManager: LiveQueryPublisher;
72
+ withRemult<T>(req: GenericRequest, res: GenericResponse, next: VoidFunction): void;
73
+ routeImpl: RouteImplementation;
74
+ handle(req: GenericRequest, gRes?: GenericResponse): Promise<ServerHandleResponse>;
75
+ registeredRouter: boolean;
76
+ registerRouter(r: GenericRouter): void;
77
+ add(key: string, dataApiFactory: ((req: Remult) => DataApi), r: GenericRouter): void;
78
+ process(what: (remult: Remult, myReq: DataApiRequest, myRes: DataApiResponse, origReq: GenericRequest) => Promise<void>): (req: GenericRequest, res: GenericResponse) => Promise<void>;
79
+ getRemult(req: GenericRequest): Promise<Remult>;
80
+ hasQueue: boolean;
81
+ addAction(action: ActionInterface, r: GenericRouter): void;
82
+ openApiDoc(options: {
83
+ title: string;
84
+ version?: string;
85
+ }): any;
86
+ backendMethodsOpenApi: {
87
+ path: string;
88
+ allowed: AllowedForInstance<any>;
89
+ tag: string;
90
+ }[];
91
+ }
92
+ declare class inProcessQueueHandler {
93
+ private storage;
94
+ constructor(storage: QueueStorage);
95
+ submitJob(url: string, req: Remult, body: any): Promise<string>;
96
+ mapQueuedAction(url: string, what: (data: any, r: Remult, res: ApiActionResponse) => void): void;
97
+ actions: Map<string, (data: any, r: Remult, res: ApiActionResponse) => void>;
98
+ getJobInfo(queuedJobId: string): Promise<queuedJobInfo>;
68
99
  }
69
100
  export interface queuedJobInfo {
70
101
  info: queuedJobInfoResponse;
@@ -90,6 +121,12 @@ export declare class EntityQueueStorage implements QueueStorage {
90
121
  getJobInfo(queuedJobId: string): Promise<queuedJobInfo>;
91
122
  createJob(url: string, userId: string): Promise<string>;
92
123
  }
124
+ declare class RouteImplementation {
125
+ map: Map<string, Map<string, GenericRequestHandler>>;
126
+ route(path: string): SpecificRoute;
127
+ handle(req: GenericRequest, gRes?: GenericResponse): Promise<ServerHandleResponse | undefined>;
128
+ middleware(req: GenericRequest, res: GenericResponse, next: VoidFunction): void;
129
+ }
93
130
  export declare class JobsInQueueEntity extends IdEntity {
94
131
  userId: string;
95
132
  url: string;
@@ -100,3 +137,4 @@ export declare class JobsInQueueEntity extends IdEntity {
100
137
  error: boolean;
101
138
  progress: number;
102
139
  }
140
+ export {};
@@ -6,7 +6,7 @@ import { Entity, Fields, getEntityKey } from '../src/remult3';
6
6
  import { IdEntity } from '../src/id-entity';
7
7
  import { remult } from '../src/remult-proxy';
8
8
  ;
9
- export function createRemultServerCore(options) {
9
+ export function createRemultServer(options) {
10
10
  if (!options) {
11
11
  options = {};
12
12
  }
@@ -30,12 +30,30 @@ export function createRemultServerCore(options) {
30
30
  return dp;
31
31
  return new (await import('./JsonEntityFileStorage')).JsonFileDataProvider('./db');
32
32
  });
33
- RemultAsyncLocalStorage.enable();
33
+ if (!remultObjectStorage) {
34
+ dataProvider = dataProvider.then(async (dp) => {
35
+ try {
36
+ const asyncHooks = await import('async_hooks');
37
+ remultObjectStorage = new RemultAsyncLocalStorage(new asyncHooks.AsyncLocalStorage());
38
+ }
39
+ catch {
40
+ remultObjectStorage = new RemultAsyncLocalStorage(undefined);
41
+ }
42
+ remult.remultFactory = () => {
43
+ const r = remultObjectStorage.getRemult();
44
+ if (r)
45
+ return r;
46
+ else
47
+ throw new Error("remult object was requested outside of a valid context, try running it within initApi or a remult request cycle");
48
+ };
49
+ return dp;
50
+ });
51
+ }
34
52
  if (options.initApi) {
35
53
  dataProvider = dataProvider.then(async (dp) => {
36
54
  var remult = new Remult(dp);
37
55
  await new Promise((res) => {
38
- RemultAsyncLocalStorage.instance.run(remult, async () => {
56
+ remultObjectStorage.run(remult, async () => {
39
57
  await options.initApi(remult);
40
58
  res({});
41
59
  });
@@ -43,6 +61,23 @@ export function createRemultServerCore(options) {
43
61
  return dp;
44
62
  });
45
63
  }
64
+ if (!options.requestSerializer) {
65
+ options.requestSerializer = {
66
+ toJson: x => {
67
+ const r = {
68
+ url: x.url
69
+ };
70
+ for (const key of ['session', 'auth', 'user']) {
71
+ if (x[key])
72
+ r[key] = x[key];
73
+ }
74
+ return r;
75
+ },
76
+ fromJson: x => {
77
+ return x;
78
+ }
79
+ };
80
+ }
46
81
  {
47
82
  let allControllers = [];
48
83
  if (!options.entities)
@@ -60,22 +95,10 @@ export function createRemultServerCore(options) {
60
95
  return bridge;
61
96
  }
62
97
  ;
63
- export class RemultAsyncLocalStorage {
98
+ class RemultAsyncLocalStorage {
64
99
  constructor(remultObjectStorage) {
65
100
  this.remultObjectStorage = remultObjectStorage;
66
101
  }
67
- static enable() {
68
- remult.remultFactory = () => {
69
- const r = RemultAsyncLocalStorage.instance.getRemult();
70
- if (r)
71
- return r;
72
- else
73
- throw new Error("remult object was requested outside of a valid context, try running it within initApi or a remult request cycle");
74
- };
75
- }
76
- static disable() {
77
- remult.resetFactory();
78
- }
79
102
  run(remult, callback) {
80
103
  if (this.remultObjectStorage)
81
104
  this.remultObjectStorage.run(remult, callback);
@@ -84,13 +107,13 @@ export class RemultAsyncLocalStorage {
84
107
  }
85
108
  getRemult() {
86
109
  if (!this.remultObjectStorage) {
87
- throw new Error("can't use static remult in this environment, `async_hooks` were not initialized");
110
+ throw "can't use static remult in this environment, `async_hooks` were not found";
88
111
  }
89
112
  return this.remultObjectStorage.getStore();
90
113
  }
91
114
  }
92
- RemultAsyncLocalStorage.instance = new RemultAsyncLocalStorage(undefined);
93
- class RemultServerImplementation {
115
+ let remultObjectStorage;
116
+ export class RemultServerImplementation {
94
117
  constructor(queue, options, dataProvider) {
95
118
  this.queue = queue;
96
119
  this.options = options;
@@ -156,11 +179,11 @@ class RemultServerImplementation {
156
179
  add(key, dataApiFactory, r) {
157
180
  let myRoute = this.options.rootPath + '/' + key;
158
181
  if (this.options.logApiEndPoints)
159
- console.log("[remult] " + myRoute);
182
+ console.info("[remult] " + myRoute);
160
183
  r.route(myRoute)
161
- .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)))
184
+ .get(this.process((c, req, res, orig) => dataApiFactory(c).httpGet(res, req, () => this.options.requestSerializer.toJson(orig)))).put(this.process(async (c, req, res, orig) => dataApiFactory(c).put(res, '', orig.body)))
162
185
  .delete(this.process(async (c, req, res, orig) => dataApiFactory(c).delete(res, '')))
163
- .post(this.process(async (c, req, res, orig) => dataApiFactory(c).httpPost(res, req, orig.body)));
186
+ .post(this.process(async (c, req, res, orig) => dataApiFactory(c).httpPost(res, req, orig.body, () => this.options.requestSerializer.toJson(orig))));
164
187
  r.route(myRoute + '/:id')
165
188
  //@ts-ignore
166
189
  .get(this.process(async (c, req, res, orig) => dataApiFactory(c).get(res, orig.params.id)))
@@ -174,9 +197,10 @@ class RemultServerImplementation {
174
197
  let myReq = new ExpressRequestBridgeToDataApiRequest(req);
175
198
  let myRes = new ExpressResponseBridgeToDataApiResponse(res, req);
176
199
  let remult = new Remult();
200
+ remult.liveQueryPublisher = this.liveQueryManager;
177
201
  remult.dataProvider = (await this.dataProvider);
178
202
  await new Promise(res => {
179
- RemultAsyncLocalStorage.instance.run(remult, async () => {
203
+ remultObjectStorage.run(remult, async () => {
180
204
  if (req) {
181
205
  let user;
182
206
  if (this.options.getUser)
@@ -218,7 +242,7 @@ class RemultServerImplementation {
218
242
  })();
219
243
  this.backendMethodsOpenApi.push({ path: myUrl, allowed, tag });
220
244
  if (this.options.logApiEndPoints)
221
- console.log("[remult] " + myUrl);
245
+ console.info("[remult] " + myUrl);
222
246
  if (queue) {
223
247
  this.hasQueue = true;
224
248
  this.queue.mapQueuedAction(myUrl, what);
@@ -1,4 +1,2 @@
1
- import { GenericRequest, RemultServer, RemultServerOptions } from './expressBridge';
2
- export { queuedJobInfo, QueueStorage, GenericRequest, GenericRequestHandler, GenericResponse, GenericRouter, RemultServerOptions, SpecificRoute, RemultServer } from './expressBridge';
1
+ export { queuedJobInfo, QueueStorage, GenericRequest, GenericRequestHandler, GenericResponse, GenericRouter, createRemultServer, RemultServerOptions, SpecificRoute, RemultServer } from './expressBridge';
3
2
  export { JsonEntityFileStorage, JsonFileDataProvider } from './JsonEntityFileStorage';
4
- export declare function createRemultServer<RequestType extends GenericRequest = GenericRequest>(options?: RemultServerOptions<RequestType>): RemultServer;
@@ -1,7 +1,2 @@
1
- import { createRemultServerCore } from './expressBridge';
2
- import { initAsyncHooks } from './initAsyncHooks';
1
+ export { createRemultServer } from './expressBridge';
3
2
  export { JsonEntityFileStorage, JsonFileDataProvider } from './JsonEntityFileStorage';
4
- export function createRemultServer(options) {
5
- initAsyncHooks();
6
- return createRemultServerCore(options);
7
- }