remult 0.17.2 → 0.18.0

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 (165) hide show
  1. package/SseSubscriptionServer.d.ts +23 -0
  2. package/SseSubscriptionServer.js +143 -0
  3. package/ably.d.ts +13 -0
  4. package/ably.js +59 -0
  5. package/esm/SseSubscriptionServer.d.ts +23 -0
  6. package/esm/SseSubscriptionServer.js +102 -0
  7. package/esm/ably.d.ts +13 -0
  8. package/esm/ably.js +28 -0
  9. package/esm/graphql.js +0 -1
  10. package/esm/index.d.ts +7 -4
  11. package/esm/index.js +4 -2
  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 +78 -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 +4 -2
  17. package/esm/postgres/postgres-data-provider.js +18 -6
  18. package/esm/postgres/schema-builder.d.ts +2 -1
  19. package/esm/postgres/schema-builder.js +49 -29
  20. package/esm/remult-knex/index.d.ts +10 -32
  21. package/esm/remult-knex/index.js +75 -51
  22. package/esm/remult-mongo.d.ts +9 -2
  23. package/esm/remult-mongo.js +24 -8
  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 +19 -7
  29. package/esm/server/expressBridge.js +135 -31
  30. package/esm/server/index.d.ts +2 -1
  31. package/esm/server/index.js +2 -1
  32. package/esm/server/initDataProvider.d.ts +2 -0
  33. package/esm/server/initDataProvider.js +14 -0
  34. package/esm/src/buildRestDataProvider.d.ts +31 -0
  35. package/esm/src/buildRestDataProvider.js +110 -0
  36. package/esm/src/column-interfaces.d.ts +44 -8
  37. package/esm/src/column.d.ts +3 -3
  38. package/esm/src/context.d.ts +39 -36
  39. package/esm/src/context.js +87 -117
  40. package/esm/src/data-api.d.ts +9 -3
  41. package/esm/src/data-api.js +130 -63
  42. package/esm/src/data-interfaces.d.ts +5 -1
  43. package/esm/src/data-providers/array-entity-data-provider.d.ts +1 -1
  44. package/esm/src/data-providers/array-entity-data-provider.js +2 -17
  45. package/esm/src/data-providers/rest-data-provider.d.ts +4 -3
  46. package/esm/src/data-providers/rest-data-provider.js +64 -23
  47. package/esm/src/data-providers/sql-database.d.ts +7 -3
  48. package/esm/src/data-providers/sql-database.js +60 -17
  49. package/esm/src/data-providers/web-sql-data-provider.d.ts +3 -0
  50. package/esm/src/data-providers/web-sql-data-provider.js +23 -9
  51. package/esm/src/entity.d.ts +2 -2
  52. package/esm/src/filter/filter-consumer-bridge-to-sql-request.d.ts +16 -16
  53. package/esm/src/filter/filter-consumer-bridge-to-sql-request.js +29 -21
  54. package/esm/src/filter/filter-interfaces.d.ts +5 -5
  55. package/esm/src/filter/filter-interfaces.js +31 -26
  56. package/esm/src/live-query/LiveQueryClient.d.ts +1 -0
  57. package/esm/src/live-query/LiveQueryClient.js +184 -0
  58. package/esm/src/live-query/SseSubscriptionClient.d.ts +5 -0
  59. package/esm/src/live-query/SseSubscriptionClient.js +86 -0
  60. package/esm/src/live-query/SubscriptionChannel.d.ts +41 -0
  61. package/esm/src/live-query/SubscriptionChannel.js +121 -0
  62. package/esm/src/live-query/SubscriptionServer.d.ts +38 -0
  63. package/esm/src/live-query/SubscriptionServer.js +108 -0
  64. package/esm/src/remult-proxy.d.ts +1 -1
  65. package/esm/src/remult-proxy.js +52 -7
  66. package/esm/src/remult3/RepositoryImplementation.d.ts +14 -5
  67. package/esm/src/remult3/RepositoryImplementation.js +124 -44
  68. package/esm/src/remult3/getId.d.ts +2 -0
  69. package/esm/src/remult3/getId.js +7 -0
  70. package/esm/src/remult3/remult3.d.ts +67 -10
  71. package/esm/src/remult3/remult3.js +1 -0
  72. package/esm/src/server-action.d.ts +5 -1
  73. package/esm/src/server-action.js +11 -12
  74. package/esm/src/shared-tests/db-tests-setup.d.ts +12 -5
  75. package/esm/src/shared-tests/db-tests-setup.js +14 -6
  76. package/esm/src/shared-tests/db-tests.db-tests.js +71 -10
  77. package/esm/src/sort.d.ts +1 -0
  78. package/esm/src/sort.js +18 -0
  79. package/esm/src/sql-command.d.ts +5 -2
  80. package/esm/src/tests/entityForCustomFilter.d.ts +6 -6
  81. package/esm/src/tests/entityForCustomFilter.js +20 -21
  82. package/esm/src/tests/frontend-database-tests-setup.spec.js +3 -4
  83. package/esm/src/tests/testHelper.spec.d.ts +3 -1
  84. package/esm/src/tests/testHelper.spec.js +63 -60
  85. package/esm/src/valueConverters.js +15 -7
  86. package/graphql.js +0 -1
  87. package/index.d.ts +7 -4
  88. package/index.js +7 -3
  89. package/live-query/data-provider-live-query-storage.d.ts +23 -0
  90. package/live-query/data-provider-live-query-storage.js +236 -0
  91. package/myLog.d.ts +1 -0
  92. package/myLog.js +20 -0
  93. package/package.json +18 -1
  94. package/postgres/postgres-data-provider.d.ts +4 -2
  95. package/postgres/postgres-data-provider.js +54 -39
  96. package/postgres/schema-builder.d.ts +2 -1
  97. package/postgres/schema-builder.js +118 -70
  98. package/remult-express.js +1 -1
  99. package/remult-knex/index.d.ts +10 -32
  100. package/remult-knex/index.js +134 -87
  101. package/remult-mongo.d.ts +9 -2
  102. package/remult-mongo.js +39 -7
  103. package/remult-next.d.ts +11 -0
  104. package/remult-next.js +73 -0
  105. package/server/JsonEntityFileStorage.js +2 -2
  106. package/server/core.d.ts +2 -0
  107. package/server/core.js +5 -1
  108. package/server/expressBridge.d.ts +19 -7
  109. package/server/expressBridge.js +283 -69
  110. package/server/index.d.ts +2 -1
  111. package/server/index.js +3 -1
  112. package/server/initDataProvider.d.ts +2 -0
  113. package/server/initDataProvider.js +26 -0
  114. package/src/buildRestDataProvider.d.ts +31 -0
  115. package/src/buildRestDataProvider.js +162 -0
  116. package/src/column-interfaces.d.ts +44 -8
  117. package/src/column.d.ts +3 -3
  118. package/src/context.d.ts +39 -36
  119. package/src/context.js +171 -166
  120. package/src/data-api.d.ts +9 -3
  121. package/src/data-api.js +233 -88
  122. package/src/data-interfaces.d.ts +5 -1
  123. package/src/data-providers/array-entity-data-provider.d.ts +1 -1
  124. package/src/data-providers/array-entity-data-provider.js +2 -17
  125. package/src/data-providers/rest-data-provider.d.ts +4 -3
  126. package/src/data-providers/rest-data-provider.js +105 -39
  127. package/src/data-providers/sql-database.d.ts +7 -3
  128. package/src/data-providers/sql-database.js +89 -16
  129. package/src/data-providers/web-sql-data-provider.d.ts +3 -0
  130. package/src/data-providers/web-sql-data-provider.js +56 -11
  131. package/src/entity.d.ts +2 -2
  132. package/src/filter/filter-consumer-bridge-to-sql-request.d.ts +16 -16
  133. package/src/filter/filter-consumer-bridge-to-sql-request.js +59 -50
  134. package/src/filter/filter-interfaces.d.ts +5 -5
  135. package/src/filter/filter-interfaces.js +31 -26
  136. package/src/live-query/LiveQueryClient.d.ts +1 -0
  137. package/src/live-query/LiveQueryClient.js +283 -0
  138. package/src/live-query/SseSubscriptionClient.d.ts +5 -0
  139. package/src/live-query/SseSubscriptionClient.js +150 -0
  140. package/src/live-query/SubscriptionChannel.d.ts +41 -0
  141. package/src/live-query/SubscriptionChannel.js +209 -0
  142. package/src/live-query/SubscriptionServer.d.ts +38 -0
  143. package/src/live-query/SubscriptionServer.js +258 -0
  144. package/src/remult-proxy.d.ts +1 -1
  145. package/src/remult-proxy.js +153 -8
  146. package/src/remult3/RepositoryImplementation.d.ts +14 -5
  147. package/src/remult3/RepositoryImplementation.js +200 -85
  148. package/src/remult3/getId.d.ts +2 -0
  149. package/src/remult3/getId.js +11 -0
  150. package/src/remult3/remult3.d.ts +67 -10
  151. package/src/remult3/remult3.js +1 -0
  152. package/src/server-action.d.ts +5 -1
  153. package/src/server-action.js +11 -11
  154. package/src/shared-tests/db-tests-setup.d.ts +12 -5
  155. package/src/shared-tests/db-tests-setup.js +17 -8
  156. package/src/shared-tests/db-tests.db-tests.js +140 -8
  157. package/src/sort.d.ts +1 -0
  158. package/src/sort.js +18 -0
  159. package/src/sql-command.d.ts +5 -2
  160. package/src/tests/entityForCustomFilter.d.ts +6 -6
  161. package/src/tests/entityForCustomFilter.js +30 -31
  162. package/src/tests/frontend-database-tests-setup.spec.js +5 -6
  163. package/src/tests/testHelper.spec.d.ts +3 -1
  164. package/src/tests/testHelper.spec.js +104 -100
  165. package/src/valueConverters.js +15 -7
@@ -1,10 +1,18 @@
1
1
  import { CompoundIdField, Filter } from '.';
2
- import { getDbNameProvider } from './src/filter/filter-consumer-bridge-to-sql-request';
2
+ import { dbNamesOf } from './src/filter/filter-consumer-bridge-to-sql-request';
3
+ import { getRepository } from './src/remult3';
4
+ import { remult as remultContext } from './src/remult-proxy';
3
5
  export class MongoDataProvider {
4
6
  constructor(db, client) {
5
7
  this.db = db;
6
8
  this.client = client;
7
9
  }
10
+ static getDb(remult) {
11
+ const r = (remult || remultContext).dataProvider;
12
+ if (!r.db)
13
+ throw "the data provider is not a MongoDataProvider";
14
+ return r.db;
15
+ }
8
16
  getEntityDataProvider(entity) {
9
17
  return new MongoEntityDataProvider(this.db, entity);
10
18
  }
@@ -20,6 +28,14 @@ export class MongoDataProvider {
20
28
  throw err;
21
29
  }
22
30
  }
31
+ static async filterToRaw(entity, condition) {
32
+ const repo = getRepository(entity);
33
+ var b = new FilterConsumerBridgeToMongo(await dbNamesOf(repo.metadata));
34
+ b._addWhere = false;
35
+ await (await (repo.translateWhereToFilter(condition))).__applyToConsumer(b);
36
+ let r = await b.resolveWhere();
37
+ return r;
38
+ }
23
39
  }
24
40
  const NULL = { $null: "$null" };
25
41
  function isNull(x) {
@@ -33,7 +49,7 @@ class MongoEntityDataProvider {
33
49
  translateFromJson(row, nameProvider) {
34
50
  let result = {};
35
51
  for (const col of this.entity.fields) {
36
- let val = row[nameProvider.nameOf(col)];
52
+ let val = row[nameProvider.$dbNameOf(col)];
37
53
  if (isNull(val))
38
54
  val = null;
39
55
  result[col.key] = col.valueConverter.fromDb(val);
@@ -46,7 +62,7 @@ class MongoEntityDataProvider {
46
62
  let val = col.valueConverter.toDb(row[col.key]);
47
63
  if (val === null)
48
64
  val = NULL;
49
- result[nameProvider.nameOf(col)] = val;
65
+ result[nameProvider.$dbNameOf(col)] = val;
50
66
  }
51
67
  return result;
52
68
  }
@@ -73,7 +89,7 @@ class MongoEntityDataProvider {
73
89
  if (options.orderBy) {
74
90
  op.sort = {};
75
91
  for (const s of options.orderBy.Segments) {
76
- op.sort[e.nameOf(s.field)] = s.isDescending ? -1 : 1;
92
+ op.sort[e.$dbNameOf(s.field)] = s.isDescending ? -1 : 1;
77
93
  }
78
94
  }
79
95
  return await Promise.all(await collection.find(where, op).map(x => this.translateFromJson(x, e)).toArray());
@@ -116,8 +132,8 @@ class MongoEntityDataProvider {
116
132
  return await this.translateFromJson(await collection.findOne({ _id: r.insertedId }), e);
117
133
  }
118
134
  async collection() {
119
- const e = await getDbNameProvider(this.entity);
120
- const collection = this.db.collection(e.entityName);
135
+ const e = await dbNamesOf(this.entity);
136
+ const collection = this.db.collection(e.$entityName);
121
137
  return { e, collection };
122
138
  }
123
139
  }
@@ -171,7 +187,7 @@ class FilterConsumerBridgeToMongo {
171
187
  }
172
188
  isIn(col, val) {
173
189
  this.result.push(() => ({
174
- [this.nameProvider.nameOf(col)]: {
190
+ [this.nameProvider.$dbNameOf(col)]: {
175
191
  $in: val.map(x => col.valueConverter.toDb(x))
176
192
  }
177
193
  }));
@@ -203,7 +219,7 @@ class FilterConsumerBridgeToMongo {
203
219
  }
204
220
  add(col, val, operator) {
205
221
  this.result.push(() => ({
206
- [this.nameProvider.nameOf(col)]: { [operator]: isNull(val) ? val : col.valueConverter.toDb(val) }
222
+ [this.nameProvider.$dbNameOf(col)]: { [operator]: isNull(val) ? val : col.valueConverter.toDb(val) }
207
223
  }));
208
224
  }
209
225
  databaseCustom(databaseCustom) {
@@ -0,0 +1,11 @@
1
+ /// <reference types="node" />
2
+ import type { GetServerSideProps, NextApiRequest, PreviewData, NextApiHandler } from "next";
3
+ import type { ParsedUrlQuery } from "querystring";
4
+ import { RemultServer, RemultServerOptions } from "./server";
5
+ export declare function remultNext(options?: RemultServerOptions<NextApiRequest>): RemultServer & NextApiHandler & {
6
+ getServerSideProps<P extends {
7
+ [key: string]: any;
8
+ } = {
9
+ [key: string]: any;
10
+ }, Q extends ParsedUrlQuery = ParsedUrlQuery, D extends PreviewData = PreviewData>(getServerPropsFunction: GetServerSideProps<P, Q, D>): GetServerSideProps<P, Q, D>;
11
+ };
@@ -0,0 +1,27 @@
1
+ import { createRemultServer } from "./server";
2
+ export function remultNext(options) {
3
+ let result = createRemultServer(options);
4
+ return Object.assign((req, res) => result.handle(req, res), result, {
5
+ getRemult: (...args) => result.getRemult(...args),
6
+ handle: (...args) => result.handle(...args),
7
+ openApiDoc: (...args) => result.openApiDoc(...args),
8
+ registerRouter: (...args) => result.registerRouter(...args),
9
+ withRemult: (...args) => result.withRemult(...args),
10
+ }, {
11
+ getServerSideProps: (getServerPropsFunction) => {
12
+ return (context) => {
13
+ return new Promise((res, err) => {
14
+ result.withRemult(context, undefined, async () => {
15
+ try {
16
+ let r = await getServerPropsFunction(context);
17
+ res(JSON.parse(JSON.stringify(r)));
18
+ }
19
+ catch (e) {
20
+ err(e);
21
+ }
22
+ });
23
+ });
24
+ };
25
+ }
26
+ });
27
+ }
@@ -1,2 +1,4 @@
1
1
  export { queuedJobInfo, QueueStorage, GenericRequest, GenericRequestHandler, GenericResponse, GenericRouter, createRemultServerCore, RemultServerOptions, SpecificRoute, RemultServer } from './expressBridge';
2
2
  export { JsonEntityFileStorage, JsonFileDataProvider } from './JsonEntityFileStorage';
3
+ export { SseSubscriptionServer } from '../SseSubscriptionServer';
4
+ export { DataProviderLiveQueryStorage } from '../live-query/data-provider-live-query-storage';
@@ -1,2 +1,4 @@
1
1
  export { createRemultServerCore } from './expressBridge';
2
2
  export { JsonEntityFileStorage, JsonFileDataProvider } from './JsonEntityFileStorage';
3
+ export { SseSubscriptionServer } from '../SseSubscriptionServer';
4
+ export { DataProviderLiveQueryStorage } from '../live-query/data-provider-live-query-storage';
@@ -4,6 +4,7 @@ import { Remult, UserInfo } from '../src/context';
4
4
  import { ClassType } from '../classType';
5
5
  import { Repository } from '../src/remult3';
6
6
  import { IdEntity } from '../src/id-entity';
7
+ import { LiveQueryStorage, SubscriptionServer } from '../src/live-query/SubscriptionServer';
7
8
  export interface RemultServerOptions<RequestType extends GenericRequest> {
8
9
  /** Sets a database connection for Remult.
9
10
  *
@@ -11,8 +12,20 @@ export interface RemultServerOptions<RequestType extends GenericRequest> {
11
12
  */
12
13
  dataProvider?: DataProvider | Promise<DataProvider> | (() => Promise<DataProvider | undefined>);
13
14
  queueStorage?: QueueStorage;
14
- initRequest?: (remult: Remult, origReq: RequestType) => Promise<void>;
15
- getUser?: (request: RequestType) => Promise<UserInfo>;
15
+ liveQueryStorage?: LiveQueryStorage;
16
+ subscriptionServer?: SubscriptionServer;
17
+ initRequest?: (request: RequestType, options: InitRequestOptions) => Promise<void>;
18
+ requestSerializer?: {
19
+ toJson: (request: RequestType) => any;
20
+ fromJson: (request: any) => RequestType;
21
+ };
22
+ /** Will create tables and columns in supporting databases. default: true
23
+ *
24
+ * @description
25
+ * when set to true, it'll create entities that do not exist, and add columns that are missing.
26
+ */
27
+ ensureSchema?: boolean;
28
+ getUser?: (request: RequestType) => Promise<UserInfo | undefined>;
16
29
  initApi?: (remult: Remult) => void | Promise<void>;
17
30
  logApiEndPoints?: boolean;
18
31
  defaultGetLimit?: number;
@@ -20,6 +33,10 @@ export interface RemultServerOptions<RequestType extends GenericRequest> {
20
33
  controllers?: ClassType<any>[];
21
34
  rootPath?: string;
22
35
  }
36
+ export interface InitRequestOptions {
37
+ liveQueryStorage: LiveQueryStorage;
38
+ readonly remult: Remult;
39
+ }
23
40
  export declare function createRemultServerCore<RequestType extends GenericRequest = GenericRequest>(options?: RemultServerOptions<RequestType>): RemultServer;
24
41
  export declare type GenericRequestHandler = (req: GenericRequest, res: GenericResponse, next: VoidFunction) => void;
25
42
  export interface ServerHandleResponse {
@@ -73,11 +90,6 @@ export interface queuedJobInfo {
73
90
  setResult(result: any): void;
74
91
  setProgress(progress: number): void;
75
92
  }
76
- export interface ApiActionResponse {
77
- error(error: any): void;
78
- success(result: any): void;
79
- progress(progress: number): void;
80
- }
81
93
  export interface QueueStorage {
82
94
  createJob(url: string, userId: string): Promise<string>;
83
95
  getJobInfo(queuedJobId: string): Promise<queuedJobInfo>;
@@ -5,11 +5,18 @@ import { allEntities, Remult } from '../src/context';
5
5
  import { Entity, Fields, getEntityKey } from '../src/remult3';
6
6
  import { IdEntity } from '../src/id-entity';
7
7
  import { remult } from '../src/remult-proxy';
8
+ import { LiveQueryPublisher, InMemoryLiveQueryStorage } from '../src/live-query/SubscriptionServer';
9
+ import { liveQueryKeepAliveRoute, streamUrl } from '../src/live-query/SubscriptionChannel';
10
+ import { initDataProvider } from './initDataProvider';
11
+ import { SseSubscriptionServer } from "../SseSubscriptionServer";
8
12
  ;
9
13
  export function createRemultServerCore(options) {
10
14
  if (!options) {
11
15
  options = {};
12
16
  }
17
+ if (!options.subscriptionServer) {
18
+ options.subscriptionServer = new SseSubscriptionServer();
19
+ }
13
20
  if (options.logApiEndPoints === undefined)
14
21
  options.logApiEndPoints = true;
15
22
  actionInfo.runningOnServer = true;
@@ -19,29 +26,59 @@ export function createRemultServerCore(options) {
19
26
  if (!options.queueStorage) {
20
27
  options.queueStorage = new InMemoryQueueStorage();
21
28
  }
22
- let dataProvider;
23
- if (typeof options.dataProvider === "function") {
24
- dataProvider = options.dataProvider();
25
- }
26
- else
27
- dataProvider = Promise.resolve(options.dataProvider);
28
- dataProvider = dataProvider.then(async (dp) => {
29
- if (dp)
30
- return dp;
31
- return new (await import('./JsonEntityFileStorage')).JsonFileDataProvider('./db');
32
- });
29
+ let dataProvider = initDataProvider(options.dataProvider);
30
+ if (options.ensureSchema === undefined)
31
+ options.ensureSchema = true;
33
32
  RemultAsyncLocalStorage.enable();
34
- if (options.initApi) {
35
- dataProvider = dataProvider.then(async (dp) => {
36
- var remult = new Remult(dp);
37
- await new Promise((res) => {
38
- RemultAsyncLocalStorage.instance.run(remult, async () => {
33
+ dataProvider = dataProvider.then(async (dp) => {
34
+ var remult = new Remult(dp);
35
+ await new Promise((res) => {
36
+ RemultAsyncLocalStorage.instance.run(remult, async () => {
37
+ if (options.ensureSchema) {
38
+ let started = false;
39
+ const startConsoleLog = () => {
40
+ if (started)
41
+ return;
42
+ started = true;
43
+ console.time("Ensure Schema");
44
+ };
45
+ if (dp.ensureSchema) {
46
+ startConsoleLog();
47
+ const entitiesMetadata = options.entities.map(e => remult.repo(e).metadata);
48
+ await dp.ensureSchema(entitiesMetadata);
49
+ }
50
+ for (const item of [options.liveQueryStorage, options.queueStorage]) {
51
+ if (item?.ensureSchema) {
52
+ startConsoleLog();
53
+ await item.ensureSchema();
54
+ }
55
+ }
56
+ if (started)
57
+ console.timeEnd("Ensure Schema");
58
+ }
59
+ if (options.initApi)
39
60
  await options.initApi(remult);
40
- res({});
41
- });
61
+ res({});
42
62
  });
43
- return dp;
44
63
  });
64
+ return dp;
65
+ });
66
+ if (!options.requestSerializer) {
67
+ options.requestSerializer = {
68
+ toJson: x => {
69
+ const r = {
70
+ url: x.url
71
+ };
72
+ for (const key of ['session', 'auth', 'user']) {
73
+ if (x[key])
74
+ r[key] = x[key];
75
+ }
76
+ return r;
77
+ },
78
+ fromJson: x => {
79
+ return x;
80
+ }
81
+ };
45
82
  }
46
83
  {
47
84
  let allControllers = [];
@@ -90,25 +127,50 @@ export class RemultAsyncLocalStorage {
90
127
  }
91
128
  }
92
129
  RemultAsyncLocalStorage.instance = new RemultAsyncLocalStorage(undefined);
93
- class RemultServerImplementation {
130
+ /* @internal*/
131
+ export class RemultServerImplementation {
94
132
  constructor(queue, options, dataProvider) {
95
133
  this.queue = queue;
96
134
  this.options = options;
97
135
  this.dataProvider = dataProvider;
136
+ this.liveQueryStorage = new InMemoryLiveQueryStorage();
137
+ this.runWithRequest = async (req, entityKey, what) => {
138
+ for (const e of this.options.entities) {
139
+ let key = getEntityKey(e);
140
+ if (key === entityKey) {
141
+ await new Promise((result) => {
142
+ this.withRemult(this.options.requestSerializer.fromJson(req), undefined, async () => {
143
+ await what(remult.repo(e));
144
+ result({});
145
+ });
146
+ });
147
+ return;
148
+ }
149
+ }
150
+ throw new Error("Couldn't find entity " + entityKey);
151
+ };
98
152
  this.registeredRouter = false;
99
153
  this.hasQueue = false;
100
154
  /* internal */
101
155
  this.backendMethodsOpenApi = [];
156
+ if (options.liveQueryStorage)
157
+ this.liveQueryStorage = options.liveQueryStorage;
158
+ if (options.subscriptionServer)
159
+ this.subscriptionServer = options.subscriptionServer;
102
160
  }
161
+ ;
103
162
  withRemult(req, res, next) {
104
163
  this.process(async () => { next(); })(req, res);
105
164
  }
106
- handle(req, gRes) {
165
+ getRouteImpl() {
107
166
  if (!this.routeImpl) {
108
167
  this.routeImpl = new RouteImplementation();
109
168
  this.registerRouter(this.routeImpl);
110
169
  }
111
- return this.routeImpl.handle(req, gRes);
170
+ return this.routeImpl;
171
+ }
172
+ handle(req, gRes) {
173
+ return this.getRouteImpl().handle(req, gRes);
112
174
  }
113
175
  registerRouter(r) {
114
176
  if (this.registeredRouter)
@@ -144,6 +206,21 @@ class RemultServerImplementation {
144
206
  },
145
207
  doWork: undefined
146
208
  }, r);
209
+ if (this.options.subscriptionServer instanceof SseSubscriptionServer) {
210
+ const streamPath = this.options.rootPath + '/' + streamUrl;
211
+ r.route(streamPath).get(this.process(async (remult, req, res, origReq, origRes) => {
212
+ remult.subscriptionServer.openHttpServerStream(origReq, origRes);
213
+ }));
214
+ r.route(streamPath + '/subscribe').post(this.process(async (remult, req, res, origReq, origRes) => {
215
+ remult.subscriptionServer.subscribeToChannel(origReq.body, res, remult);
216
+ }));
217
+ r.route(streamPath + '/unsubscribe').post(this.process(async (remult, req, res, origReq, origRes) => {
218
+ remult.subscriptionServer.subscribeToChannel(origReq.body, res, remult, true);
219
+ }));
220
+ }
221
+ r.route(this.options.rootPath + '/' + liveQueryKeepAliveRoute).post(this.process(async (remult, req, res, origReq, origRes) => {
222
+ res.success(await remult.liveQueryStorage.keepAliveAndReturnUnknownQueryIds(origReq.body));
223
+ }));
147
224
  }
148
225
  this.options.entities.forEach(e => {
149
226
  let key = getEntityKey(e);
@@ -156,11 +233,11 @@ class RemultServerImplementation {
156
233
  add(key, dataApiFactory, r) {
157
234
  let myRoute = this.options.rootPath + '/' + key;
158
235
  if (this.options.logApiEndPoints)
159
- console.log("[remult] " + myRoute);
236
+ console.info("[remult] " + myRoute);
160
237
  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)))
238
+ .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
239
  .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)));
240
+ .post(this.process(async (c, req, res, orig) => dataApiFactory(c).httpPost(res, req, orig.body, () => this.options.requestSerializer.toJson(orig))));
164
241
  r.route(myRoute + '/:id')
165
242
  //@ts-ignore
166
243
  .get(this.process(async (c, req, res, orig) => dataApiFactory(c).get(res, orig.params.id)))
@@ -170,11 +247,14 @@ class RemultServerImplementation {
170
247
  .delete(this.process(async (c, req, res, orig) => dataApiFactory(c).delete(res, orig.params.id)));
171
248
  }
172
249
  process(what) {
173
- return async (req, res) => {
250
+ return async (req, origRes) => {
174
251
  let myReq = new ExpressRequestBridgeToDataApiRequest(req);
175
- let myRes = new ExpressResponseBridgeToDataApiResponse(res, req);
252
+ let myRes = new ExpressResponseBridgeToDataApiResponse(origRes, req);
176
253
  let remult = new Remult();
254
+ remult.liveQueryPublisher = new LiveQueryPublisher(() => remult.subscriptionServer, () => remult.liveQueryStorage, this.runWithRequest);
177
255
  remult.dataProvider = (await this.dataProvider);
256
+ remult.subscriptionServer = this.subscriptionServer;
257
+ remult.liveQueryStorage = this.liveQueryStorage;
178
258
  await new Promise(res => {
179
259
  RemultAsyncLocalStorage.instance.run(remult, async () => {
180
260
  if (req) {
@@ -190,9 +270,17 @@ class RemultServerImplementation {
190
270
  remult.user = (user);
191
271
  }
192
272
  if (this.options.initRequest) {
193
- await this.options.initRequest(remult, req);
273
+ await this.options.initRequest(req, {
274
+ remult,
275
+ get liveQueryStorage() {
276
+ return remult.liveQueryStorage;
277
+ },
278
+ set liveQueryStorage(value) {
279
+ remult.liveQueryStorage = value;
280
+ }
281
+ });
194
282
  }
195
- await what(remult, myReq, myRes, req);
283
+ await what(remult, myReq, myRes, req, origRes);
196
284
  res({});
197
285
  });
198
286
  });
@@ -218,7 +306,7 @@ class RemultServerImplementation {
218
306
  })();
219
307
  this.backendMethodsOpenApi.push({ path: myUrl, allowed, tag });
220
308
  if (this.options.logApiEndPoints)
221
- console.log("[remult] " + myUrl);
309
+ console.info("[remult] " + myUrl);
222
310
  if (queue) {
223
311
  this.hasQueue = true;
224
312
  this.queue.mapQueuedAction(myUrl, what);
@@ -582,7 +670,11 @@ class inProcessQueueHandler {
582
670
  this.actions.get(url)(body, req, {
583
671
  error: error => job.setErrorResult(serializeError(error)),
584
672
  success: result => job.setResult(result),
585
- progress: progress => job.setProgress(progress)
673
+ progress: progress => job.setProgress(progress),
674
+ created: () => { throw Error("Created response not expected for queue"); },
675
+ deleted: () => { throw Error("deleted response not expected for queue"); },
676
+ notFound: () => { throw Error("notFound response not expected for queue"); },
677
+ forbidden: () => job.setErrorResult("Forbidden")
586
678
  });
587
679
  return id;
588
680
  }
@@ -720,6 +812,18 @@ class RouteImplementation {
720
812
  constructor() {
721
813
  this.statusCode = 200;
722
814
  }
815
+ write(data) {
816
+ gRes.write(data);
817
+ }
818
+ writeHead(statusCode, headers) {
819
+ gRes.writeHead(statusCode, headers);
820
+ this.statusCode = statusCode;
821
+ res({ statusCode });
822
+ }
823
+ flush() {
824
+ if (gRes.flush)
825
+ gRes.flush();
826
+ }
723
827
  json(data) {
724
828
  if (gRes !== undefined)
725
829
  gRes.json(data);
@@ -1,4 +1,5 @@
1
1
  import { GenericRequest, RemultServer, RemultServerOptions } from './expressBridge';
2
- export { queuedJobInfo, QueueStorage, GenericRequest, GenericRequestHandler, GenericResponse, GenericRouter, RemultServerOptions, SpecificRoute, RemultServer } from './expressBridge';
2
+ export { queuedJobInfo, QueueStorage, GenericRequest, GenericRequestHandler, GenericResponse, GenericRouter, RemultServerOptions, SpecificRoute, RemultServer, InitRequestOptions } from './expressBridge';
3
3
  export { JsonEntityFileStorage, JsonFileDataProvider } from './JsonEntityFileStorage';
4
+ export { DataProviderLiveQueryStorage } from '../live-query/data-provider-live-query-storage';
4
5
  export declare function createRemultServer<RequestType extends GenericRequest = GenericRequest>(options?: RemultServerOptions<RequestType>): RemultServer;
@@ -1,6 +1,7 @@
1
- import { createRemultServerCore } from './expressBridge';
1
+ import { createRemultServerCore, } from './expressBridge';
2
2
  import { initAsyncHooks } from './initAsyncHooks';
3
3
  export { JsonEntityFileStorage, JsonFileDataProvider } from './JsonEntityFileStorage';
4
+ export { DataProviderLiveQueryStorage } from '../live-query/data-provider-live-query-storage';
4
5
  export function createRemultServer(options) {
5
6
  initAsyncHooks();
6
7
  return createRemultServerCore(options);
@@ -0,0 +1,2 @@
1
+ import { DataProvider } from '../src/data-interfaces';
2
+ export declare function initDataProvider(optionsDataProvider?: DataProvider | Promise<DataProvider> | (() => Promise<DataProvider | undefined>)): Promise<DataProvider>;
@@ -0,0 +1,14 @@
1
+ export function initDataProvider(optionsDataProvider) {
2
+ let dataProvider;
3
+ if (typeof optionsDataProvider === "function") {
4
+ dataProvider = optionsDataProvider();
5
+ }
6
+ else
7
+ dataProvider = Promise.resolve(optionsDataProvider);
8
+ dataProvider = dataProvider.then(async (dp) => {
9
+ if (dp)
10
+ return dp;
11
+ return new (await import('./JsonEntityFileStorage')).JsonFileDataProvider('./db');
12
+ });
13
+ return dataProvider;
14
+ }
@@ -0,0 +1,31 @@
1
+ import { RestDataProviderHttpProvider } from "./data-interfaces";
2
+ import { RestDataProviderHttpProviderUsingFetch } from './data-providers/rest-data-provider';
3
+ export declare function buildRestDataProvider(provider: ExternalHttpProvider | typeof fetch): RestDataProviderHttpProviderUsingFetch | RestDataProviderHttpProvider;
4
+ export declare function isExternalHttpProvider(item: any): boolean;
5
+ export declare class HttpProviderBridgeToRestDataProviderHttpProvider implements RestDataProviderHttpProvider {
6
+ private http;
7
+ constructor(http: ExternalHttpProvider);
8
+ post(url: string, data: any): Promise<any>;
9
+ delete(url: string): Promise<void>;
10
+ put(url: string, data: any): Promise<any>;
11
+ get(url: string): Promise<any>;
12
+ }
13
+ export declare function retry<T>(what: () => Promise<T>): Promise<T>;
14
+ export declare function toPromise<T>(p: Promise<T> | {
15
+ toPromise(): Promise<T>;
16
+ }): Promise<any>;
17
+ export declare function processHttpException(ex: any): Promise<any>;
18
+ export interface ExternalHttpProvider {
19
+ post(url: string, data: any): Promise<any> | {
20
+ toPromise(): Promise<any>;
21
+ };
22
+ delete(url: string): Promise<void> | {
23
+ toPromise(): Promise<void>;
24
+ };
25
+ put(url: string, data: any): Promise<any> | {
26
+ toPromise(): Promise<any>;
27
+ };
28
+ get(url: string): Promise<any> | {
29
+ toPromise(): Promise<any>;
30
+ };
31
+ }
@@ -0,0 +1,110 @@
1
+ import { RestDataProviderHttpProviderUsingFetch } from './data-providers/rest-data-provider';
2
+ export function buildRestDataProvider(provider) {
3
+ if (!provider)
4
+ return new RestDataProviderHttpProviderUsingFetch();
5
+ let httpDataProvider;
6
+ if (!httpDataProvider) {
7
+ if (isExternalHttpProvider(provider)) {
8
+ httpDataProvider = new HttpProviderBridgeToRestDataProviderHttpProvider(provider);
9
+ }
10
+ }
11
+ if (!httpDataProvider) {
12
+ if (typeof provider === "function") {
13
+ httpDataProvider = new RestDataProviderHttpProviderUsingFetch(provider);
14
+ }
15
+ }
16
+ return httpDataProvider;
17
+ }
18
+ export function isExternalHttpProvider(item) {
19
+ let http = item;
20
+ if (http && http.get && http.put && http.post && http.delete)
21
+ return true;
22
+ return false;
23
+ }
24
+ export class HttpProviderBridgeToRestDataProviderHttpProvider {
25
+ constructor(http) {
26
+ this.http = http;
27
+ }
28
+ async post(url, data) {
29
+ return await retry(() => toPromise(this.http.post(url, data)));
30
+ }
31
+ delete(url) {
32
+ return toPromise(this.http.delete(url));
33
+ }
34
+ put(url, data) {
35
+ return toPromise(this.http.put(url, data));
36
+ }
37
+ async get(url) {
38
+ return await retry(() => toPromise(this.http.get(url)));
39
+ }
40
+ }
41
+ export async function retry(what) {
42
+ while (true) {
43
+ try {
44
+ return await what();
45
+ }
46
+ catch (err) {
47
+ if (err.message?.startsWith("Error occurred while trying to proxy") ||
48
+ err.message?.startsWith("Error occured while trying to proxy") ||
49
+ err.message?.includes("http proxy error") ||
50
+ err.message?.startsWith("Gateway Timeout")) {
51
+ await new Promise((res, req) => {
52
+ setTimeout(() => {
53
+ res({});
54
+ }, 250);
55
+ });
56
+ continue;
57
+ }
58
+ throw err;
59
+ }
60
+ }
61
+ }
62
+ export function toPromise(p) {
63
+ let r;
64
+ if (p["toPromise"] !== undefined) {
65
+ r = p["toPromise"]();
66
+ }
67
+ //@ts-ignore
68
+ else
69
+ r = p;
70
+ return r.then((x) => {
71
+ if (x && (x.status == 200 || x.status == 201) && x.headers && x.request && x.data) //for axios
72
+ return x.data;
73
+ return x;
74
+ }).catch(async (ex) => {
75
+ throw await processHttpException(ex);
76
+ });
77
+ }
78
+ export async function processHttpException(ex) {
79
+ let z = await ex;
80
+ var error;
81
+ if (z.error)
82
+ error = z.error;
83
+ else if (z.isAxiosError) {
84
+ if (typeof z.response?.data === "string")
85
+ error = z.response.data;
86
+ else
87
+ error = z?.response?.data;
88
+ }
89
+ if (!error)
90
+ error = z.message;
91
+ if (z.status == 0 && z.error.isTrusted)
92
+ error = "Network Error";
93
+ if (typeof error === 'string') {
94
+ error = {
95
+ message: error,
96
+ };
97
+ }
98
+ if (z.modelState)
99
+ error.modelState = z.modelState;
100
+ let httpStatusCode = z.status;
101
+ if (httpStatusCode === undefined)
102
+ httpStatusCode = z.response?.status;
103
+ if (httpStatusCode !== undefined && httpStatusCode !== null) {
104
+ error.httpStatusCode = httpStatusCode;
105
+ }
106
+ var result = Object.assign(error, {
107
+ // exception: ex disabled for now because JSON.stringify crashed with this
108
+ });
109
+ return result;
110
+ }