remult 0.18.1-exp.2 → 0.18.1-exp.21

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 (154) hide show
  1. package/README.md +28 -5
  2. package/esm/index.d.ts +4 -3
  3. package/esm/index.js +4 -2
  4. package/esm/live-query/ably.d.ts +6 -6
  5. package/esm/live-query/ably.js +5 -5
  6. package/esm/postgres/postgres-data-provider.d.ts +3 -1
  7. package/esm/postgres/postgres-data-provider.js +11 -1
  8. package/esm/postgres/schema-builder.d.ts +1 -1
  9. package/esm/postgres/schema-builder.js +43 -27
  10. package/esm/remult-express.d.ts +4 -22
  11. package/esm/remult-express.js +30 -28
  12. package/esm/remult-fastify.js +1 -1
  13. package/esm/remult-fresh.js +2 -2
  14. package/esm/remult-knex/index.d.ts +7 -31
  15. package/esm/remult-knex/index.js +71 -42
  16. package/esm/remult-mongo.d.ts +9 -2
  17. package/esm/remult-mongo.js +24 -8
  18. package/esm/remult-next.d.ts +11 -0
  19. package/esm/remult-next.js +27 -0
  20. package/esm/server/expressBridge.d.ts +20 -52
  21. package/esm/server/expressBridge.js +55 -29
  22. package/esm/server/index.d.ts +3 -1
  23. package/esm/server/index.js +6 -1
  24. package/esm/src/column-interfaces.d.ts +44 -8
  25. package/esm/src/context.d.ts +31 -19
  26. package/esm/src/context.js +35 -42
  27. package/esm/src/data-api.js +17 -4
  28. package/esm/src/data-interfaces.d.ts +1 -1
  29. package/esm/src/data-providers/array-entity-data-provider.d.ts +1 -1
  30. package/esm/src/data-providers/array-entity-data-provider.js +1 -1
  31. package/esm/src/data-providers/rest-data-provider.d.ts +4 -15
  32. package/esm/src/data-providers/rest-data-provider.js +38 -23
  33. package/esm/src/data-providers/sql-database.d.ts +6 -3
  34. package/esm/src/data-providers/sql-database.js +57 -16
  35. package/esm/src/data-providers/web-sql-data-provider.d.ts +2 -0
  36. package/esm/src/data-providers/web-sql-data-provider.js +17 -8
  37. package/esm/src/entity.d.ts +2 -2
  38. package/esm/src/filter/filter-consumer-bridge-to-sql-request.d.ts +16 -16
  39. package/esm/src/filter/filter-consumer-bridge-to-sql-request.js +29 -21
  40. package/esm/src/filter/filter-interfaces.d.ts +5 -5
  41. package/esm/src/filter/filter-interfaces.js +31 -26
  42. package/esm/src/live-query/LiveQueryClient.d.ts +1 -19
  43. package/esm/src/live-query/LiveQueryClient.js +81 -50
  44. package/esm/src/live-query/SseSubscriptionClient.d.ts +4 -0
  45. package/esm/src/live-query/{EventSourceLiveQueryProvider.js → SseSubscriptionClient.js} +7 -6
  46. package/esm/src/live-query/SubscriptionChannel.d.ts +41 -0
  47. package/esm/src/live-query/{LiveQuerySubscriber.js → SubscriptionChannel.js} +40 -22
  48. package/esm/src/live-query/SubscriptionServer.d.ts +38 -0
  49. package/esm/src/live-query/{LiveQueryPublisher.js → SubscriptionServer.js} +60 -73
  50. package/esm/src/remult-proxy.js +44 -1
  51. package/esm/src/remult3/RepositoryImplementation.d.ts +8 -2
  52. package/esm/src/remult3/RepositoryImplementation.js +94 -30
  53. package/esm/src/remult3/remult3.d.ts +59 -6
  54. package/esm/src/remult3/remult3.js +1 -0
  55. package/esm/src/server-action.d.ts +1 -1
  56. package/esm/src/shared-tests/db-tests-setup.d.ts +1 -1
  57. package/esm/src/shared-tests/db-tests-setup.js +4 -3
  58. package/esm/src/shared-tests/db-tests.db-tests.js +55 -9
  59. package/esm/src/sql-command.d.ts +4 -2
  60. package/esm/src/tests/entityForCustomFilter.d.ts +6 -6
  61. package/esm/src/tests/entityForCustomFilter.js +20 -21
  62. package/esm/src/tests/frontend-database-tests-setup.spec.js +1 -2
  63. package/esm/src/tests/testHelper.spec.d.ts +2 -2
  64. package/esm/src/tests/testHelper.spec.js +1 -2
  65. package/esm/src/valueConverters.js +17 -7
  66. package/index.d.ts +4 -3
  67. package/index.js +7 -3
  68. package/live-query/ably.d.ts +6 -6
  69. package/live-query/ably.js +12 -12
  70. package/package.json +18 -9
  71. package/postgres/postgres-data-provider.d.ts +3 -1
  72. package/postgres/postgres-data-provider.js +13 -3
  73. package/postgres/schema-builder.d.ts +1 -1
  74. package/postgres/schema-builder.js +64 -50
  75. package/remult-express.d.ts +4 -22
  76. package/remult-express.js +39 -47
  77. package/remult-fastify.js +2 -2
  78. package/remult-fresh.js +1 -1
  79. package/remult-knex/index.d.ts +7 -31
  80. package/remult-knex/index.js +100 -53
  81. package/remult-mongo.d.ts +9 -2
  82. package/remult-mongo.js +39 -7
  83. package/remult-next.d.ts +11 -0
  84. package/remult-next.js +73 -0
  85. package/server/JsonEntityFileStorage.js +2 -2
  86. package/server/expressBridge.d.ts +20 -52
  87. package/server/expressBridge.js +68 -41
  88. package/server/index.d.ts +3 -1
  89. package/server/index.js +7 -2
  90. package/src/column-interfaces.d.ts +44 -8
  91. package/src/context.d.ts +31 -19
  92. package/src/context.js +39 -50
  93. package/src/data-api.js +18 -5
  94. package/src/data-interfaces.d.ts +1 -1
  95. package/src/data-providers/array-entity-data-provider.d.ts +1 -1
  96. package/src/data-providers/array-entity-data-provider.js +1 -1
  97. package/src/data-providers/rest-data-provider.d.ts +4 -15
  98. package/src/data-providers/rest-data-provider.js +48 -24
  99. package/src/data-providers/sql-database.d.ts +6 -3
  100. package/src/data-providers/sql-database.js +75 -15
  101. package/src/data-providers/web-sql-data-provider.d.ts +2 -0
  102. package/src/data-providers/web-sql-data-provider.js +16 -7
  103. package/src/entity.d.ts +2 -2
  104. package/src/filter/filter-consumer-bridge-to-sql-request.d.ts +16 -16
  105. package/src/filter/filter-consumer-bridge-to-sql-request.js +59 -50
  106. package/src/filter/filter-interfaces.d.ts +5 -5
  107. package/src/filter/filter-interfaces.js +31 -26
  108. package/src/live-query/LiveQueryClient.d.ts +1 -19
  109. package/src/live-query/LiveQueryClient.js +117 -64
  110. package/src/live-query/SseSubscriptionClient.d.ts +4 -0
  111. package/src/live-query/{EventSourceLiveQueryProvider.js → SseSubscriptionClient.js} +13 -12
  112. package/src/live-query/SubscriptionChannel.d.ts +41 -0
  113. package/src/live-query/{LiveQuerySubscriber.js → SubscriptionChannel.js} +51 -47
  114. package/src/live-query/SubscriptionServer.d.ts +38 -0
  115. package/src/live-query/SubscriptionServer.js +237 -0
  116. package/src/remult-proxy.js +137 -2
  117. package/src/remult3/RepositoryImplementation.d.ts +8 -2
  118. package/src/remult3/RepositoryImplementation.js +121 -34
  119. package/src/remult3/remult3.d.ts +59 -6
  120. package/src/remult3/remult3.js +1 -0
  121. package/src/server-action.d.ts +1 -1
  122. package/src/shared-tests/db-tests-setup.d.ts +1 -1
  123. package/src/shared-tests/db-tests-setup.js +5 -4
  124. package/src/shared-tests/db-tests.db-tests.js +101 -8
  125. package/src/sql-command.d.ts +4 -2
  126. package/src/tests/entityForCustomFilter.d.ts +6 -6
  127. package/src/tests/entityForCustomFilter.js +30 -31
  128. package/src/tests/frontend-database-tests-setup.spec.js +4 -5
  129. package/src/tests/testHelper.spec.d.ts +2 -2
  130. package/src/tests/testHelper.spec.js +4 -5
  131. package/src/valueConverters.js +17 -7
  132. package/esm/live-query/index.d.ts +0 -3
  133. package/esm/live-query/index.js +0 -3
  134. package/esm/src/backend-tests/backend-database-test-setup.backend-spec.d.ts +0 -5
  135. package/esm/src/backend-tests/backend-database-test-setup.backend-spec.js +0 -113
  136. package/esm/src/backend-tests/my-test.backend-spec.d.ts +0 -1
  137. package/esm/src/backend-tests/my-test.backend-spec.js +0 -59
  138. package/esm/src/backend-tests/test-http-server.backend-spec.d.ts +0 -6
  139. package/esm/src/backend-tests/test-http-server.backend-spec.js +0 -171
  140. package/esm/src/live-query/EventSourceLiveQueryProvider.d.ts +0 -4
  141. package/esm/src/live-query/LiveQueryPublisher.d.ts +0 -49
  142. package/esm/src/live-query/LiveQuerySubscriber.d.ts +0 -67
  143. package/live-query/index.d.ts +0 -3
  144. package/live-query/index.js +0 -10
  145. package/src/backend-tests/backend-database-test-setup.backend-spec.d.ts +0 -5
  146. package/src/backend-tests/backend-database-test-setup.backend-spec.js +0 -231
  147. package/src/backend-tests/my-test.backend-spec.d.ts +0 -1
  148. package/src/backend-tests/my-test.backend-spec.js +0 -160
  149. package/src/backend-tests/test-http-server.backend-spec.d.ts +0 -6
  150. package/src/backend-tests/test-http-server.backend-spec.js +0 -342
  151. package/src/live-query/EventSourceLiveQueryProvider.d.ts +0 -4
  152. package/src/live-query/LiveQueryPublisher.d.ts +0 -49
  153. package/src/live-query/LiveQueryPublisher.js +0 -248
  154. package/src/live-query/LiveQuerySubscriber.d.ts +0 -67
@@ -5,8 +5,10 @@ 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 } from '../src/live-query/SubscriptionChannel';
8
10
  ;
9
- export function createRemultServer(options) {
11
+ export function createRemultServerCore(options) {
10
12
  if (!options) {
11
13
  options = {};
12
14
  }
@@ -30,30 +32,12 @@ export function createRemultServer(options) {
30
32
  return dp;
31
33
  return new (await import('./JsonEntityFileStorage')).JsonFileDataProvider('./db');
32
34
  });
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
- }
35
+ RemultAsyncLocalStorage.enable();
52
36
  if (options.initApi) {
53
37
  dataProvider = dataProvider.then(async (dp) => {
54
38
  var remult = new Remult(dp);
55
39
  await new Promise((res) => {
56
- remultObjectStorage.run(remult, async () => {
40
+ RemultAsyncLocalStorage.instance.run(remult, async () => {
57
41
  await options.initApi(remult);
58
42
  res({});
59
43
  });
@@ -95,10 +79,22 @@ export function createRemultServer(options) {
95
79
  return bridge;
96
80
  }
97
81
  ;
98
- class RemultAsyncLocalStorage {
82
+ export class RemultAsyncLocalStorage {
99
83
  constructor(remultObjectStorage) {
100
84
  this.remultObjectStorage = remultObjectStorage;
101
85
  }
86
+ static enable() {
87
+ remult.remultFactory = () => {
88
+ const r = RemultAsyncLocalStorage.instance.getRemult();
89
+ if (r)
90
+ return r;
91
+ else
92
+ throw new Error("remult object was requested outside of a valid context, try running it within initApi or a remult request cycle");
93
+ };
94
+ }
95
+ static disable() {
96
+ remult.resetFactory();
97
+ }
102
98
  run(remult, callback) {
103
99
  if (this.remultObjectStorage)
104
100
  this.remultObjectStorage.run(remult, callback);
@@ -107,21 +103,27 @@ class RemultAsyncLocalStorage {
107
103
  }
108
104
  getRemult() {
109
105
  if (!this.remultObjectStorage) {
110
- throw "can't use static remult in this environment, `async_hooks` were not found";
106
+ throw new Error("can't use static remult in this environment, `async_hooks` were not initialized");
111
107
  }
112
108
  return this.remultObjectStorage.getStore();
113
109
  }
114
110
  }
115
- let remultObjectStorage;
111
+ RemultAsyncLocalStorage.instance = new RemultAsyncLocalStorage(undefined);
112
+ /* @internal*/
116
113
  export class RemultServerImplementation {
117
114
  constructor(queue, options, dataProvider) {
118
115
  this.queue = queue;
119
116
  this.options = options;
120
117
  this.dataProvider = dataProvider;
118
+ this.liveQueryStorage = new InMemoryLiveQueryStorage();
121
119
  this.registeredRouter = false;
122
120
  this.hasQueue = false;
123
121
  /* internal */
124
122
  this.backendMethodsOpenApi = [];
123
+ if (options.liveQueryStorage)
124
+ this.liveQueryStorage = options.liveQueryStorage;
125
+ if (options.subscriptionServer)
126
+ this.subscriptionServer = options.subscriptionServer;
125
127
  }
126
128
  withRemult(req, res, next) {
127
129
  this.process(async () => { next(); })(req, res);
@@ -167,6 +169,14 @@ export class RemultServerImplementation {
167
169
  },
168
170
  doWork: undefined
169
171
  }, r);
172
+ this.addAction({
173
+ __register: x => {
174
+ x(liveQueryKeepAliveRoute, false, () => true, async (data, req, res) => {
175
+ res.success(await remult.liveQueryStorage.keepAliveAndReturnUnknownQueryIds(data));
176
+ });
177
+ },
178
+ doWork: undefined
179
+ }, r);
170
180
  }
171
181
  this.options.entities.forEach(e => {
172
182
  let key = getEntityKey(e);
@@ -197,10 +207,12 @@ export class RemultServerImplementation {
197
207
  let myReq = new ExpressRequestBridgeToDataApiRequest(req);
198
208
  let myRes = new ExpressResponseBridgeToDataApiResponse(res, req);
199
209
  let remult = new Remult();
200
- remult.liveQueryPublisher = this.liveQueryManager;
210
+ remult.liveQueryPublisher = new LiveQueryPublisher(() => remult.subscriptionServer, () => remult.liveQueryStorage, this.runWithRequest);
201
211
  remult.dataProvider = (await this.dataProvider);
212
+ remult.subscriptionServer = this.subscriptionServer;
213
+ remult.liveQueryStorage = this.liveQueryStorage;
202
214
  await new Promise(res => {
203
- remultObjectStorage.run(remult, async () => {
215
+ RemultAsyncLocalStorage.instance.run(remult, async () => {
204
216
  if (req) {
205
217
  let user;
206
218
  if (this.options.getUser)
@@ -214,7 +226,15 @@ export class RemultServerImplementation {
214
226
  remult.user = (user);
215
227
  }
216
228
  if (this.options.initRequest) {
217
- await this.options.initRequest(remult, req);
229
+ await this.options.initRequest(req, {
230
+ remult,
231
+ get liveQueryStorage() {
232
+ return remult.liveQueryStorage;
233
+ },
234
+ set liveQueryStorage(value) {
235
+ remult.liveQueryStorage = value;
236
+ }
237
+ });
218
238
  }
219
239
  await what(remult, myReq, myRes, req);
220
240
  res({});
@@ -242,7 +262,9 @@ export class RemultServerImplementation {
242
262
  })();
243
263
  this.backendMethodsOpenApi.push({ path: myUrl, allowed, tag });
244
264
  if (this.options.logApiEndPoints)
245
- console.info("[remult] " + myUrl);
265
+ //TODO - should I hide this route in the list of routes
266
+ if (url !== liveQueryKeepAliveRoute)
267
+ console.info("[remult] " + myUrl);
246
268
  if (queue) {
247
269
  this.hasQueue = true;
248
270
  this.queue.mapQueuedAction(myUrl, what);
@@ -606,7 +628,11 @@ class inProcessQueueHandler {
606
628
  this.actions.get(url)(body, req, {
607
629
  error: error => job.setErrorResult(serializeError(error)),
608
630
  success: result => job.setResult(result),
609
- progress: progress => job.setProgress(progress)
631
+ progress: progress => job.setProgress(progress),
632
+ created: () => { throw Error("Created response not expected for queue"); },
633
+ deleted: () => { throw Error("deleted response not expected for queue"); },
634
+ notFound: () => { throw Error("notFound response not expected for queue"); },
635
+ forbidden: () => job.setErrorResult("Forbidden")
610
636
  });
611
637
  return id;
612
638
  }
@@ -1,2 +1,4 @@
1
- export { queuedJobInfo, QueueStorage, GenericRequest, GenericRequestHandler, GenericResponse, GenericRouter, createRemultServer, RemultServerOptions, SpecificRoute, RemultServer } from './expressBridge';
1
+ import { GenericRequest, RemultServer, RemultServerOptions } from './expressBridge';
2
+ export { queuedJobInfo, QueueStorage, GenericRequest, GenericRequestHandler, GenericResponse, GenericRouter, RemultServerOptions, SpecificRoute, RemultServer, InitRequestOptions } from './expressBridge';
2
3
  export { JsonEntityFileStorage, JsonFileDataProvider } from './JsonEntityFileStorage';
4
+ export declare function createRemultServer<RequestType extends GenericRequest = GenericRequest>(options?: RemultServerOptions<RequestType>): RemultServer;
@@ -1,2 +1,7 @@
1
- export { createRemultServer } from './expressBridge';
1
+ import { createRemultServerCore, } from './expressBridge';
2
+ import { initAsyncHooks } from './initAsyncHooks';
2
3
  export { JsonEntityFileStorage, JsonFileDataProvider } from './JsonEntityFileStorage';
4
+ export function createRemultServer(options) {
5
+ initAsyncHooks();
6
+ return createRemultServerCore(options);
7
+ }
@@ -2,7 +2,10 @@ import { ClassType } from '../classType';
2
2
  import { Allowed, AllowedForInstance } from './context';
3
3
  import { EntityMetadata, FieldRef } from './remult3';
4
4
  export interface FieldOptions<entityType = any, valueType = any> {
5
- /**A human readable name for the field */
5
+ /** A human readable name for the field. Can be used to achieve a consistent caption for a field throughout the app
6
+ * @example
7
+ * <input placeholder={taskRepo.metadata.fields.title.caption}/>
8
+ */
6
9
  caption?: string;
7
10
  /** If it can store null in the database */
8
11
  allowNull?: boolean;
@@ -32,14 +35,23 @@ export interface FieldOptions<entityType = any, valueType = any> {
32
35
  * }
33
36
  * })
34
37
  */
35
- validate?: ((entity: entityType, col: FieldRef<entityType, valueType>) => (any | Promise<any>)) | ((entity: entityType, col: FieldRef<entityType, valueType>) => (any | Promise<any>))[];
38
+ validate?: ((entity: entityType, fieldRef: FieldRef<entityType, valueType>) => (any | Promise<any>)) | ((entity: entityType, fieldRef: FieldRef<entityType, valueType>) => (any | Promise<any>))[];
36
39
  /** Will be fired before this field is saved to the server/database */
37
- saving?: ((entity: entityType, col: FieldRef<entityType, valueType>) => (any | Promise<any>));
40
+ saving?: ((entity: entityType, fieldRef: FieldRef<entityType, valueType>) => (any | Promise<any>));
38
41
  /** An expression that will determine this fields value on the backend and be provided to the front end*/
39
42
  serverExpression?: (entity: entityType) => valueType | Promise<valueType>;
40
43
  /** The name of the column in the database that holds the data for this field. If no name is set, the key will be used instead. */
41
44
  dbName?: string;
42
- /** Used or fields that are based on an sql expressions, instead of a physical table column */
45
+ /** Used or fields that are based on an sql expressions, instead of a physical table column
46
+ * @example
47
+ *
48
+ * @Fields.integer({
49
+ * sqlExpression:e=> 'length(title)'
50
+ * })
51
+ * titleLength = 0;
52
+ * @Fields.string()
53
+ * title='';
54
+ */
43
55
  sqlExpression?: string | ((entity: EntityMetadata<entityType>) => string | Promise<string>);
44
56
  /** For fields that shouldn't be part of an update or insert statement */
45
57
  dbReadOnly?: boolean;
@@ -62,18 +74,42 @@ export interface FieldOptions<entityType = any, valueType = any> {
62
74
  /** The key to be used for this field */
63
75
  key?: string;
64
76
  }
77
+ /**Metadata for a `Field`, this metadata can be used in the user interface to provide a richer UI experience */
65
78
  export interface FieldMetadata<valueType = any> {
79
+ /** The field's member name in an object.
80
+ * @example
81
+ * const taskRepo = remult.repo(Task);
82
+ * console.log(taskRepo.metadata.fields.title.key);
83
+ * // result: title
84
+ */
66
85
  readonly key: string;
67
- readonly target: ClassType<valueType>;
68
- readonly valueType: any;
86
+ /** A human readable caption for the field. Can be used to achieve a consistent caption for a field throughout the app
87
+ * @example
88
+ * <input placeholder={taskRepo.metadata.fields.title.caption}/>
89
+ */
69
90
  readonly caption: string;
91
+ /** The field's value type (number,string etc...) */
92
+ readonly valueType: any;
93
+ /** The options sent to this field's decorator */
94
+ readonly options: FieldOptions;
95
+ /** The `inputType` relevant for this field, determined by the options sent to it's decorator and the valueConverter in these options */
70
96
  readonly inputType: string;
97
+ /** if null is allowed for this field */
71
98
  readonly allowNull: boolean;
99
+ /** The class that contains this field
100
+ * @example
101
+ * const taskRepo = remult.repo(Task);
102
+ * Task == taskRepo.metadata.fields.title.target //will return true
103
+ */
104
+ readonly target: ClassType<valueType>;
105
+ /** Returns the dbName - based on it's `dbName` option and it's `sqlExpression` option */
72
106
  getDbName(): Promise<string>;
107
+ /** Indicates if this field is based on a server express */
73
108
  readonly isServerExpression: boolean;
109
+ /** indicates that this field should only be included in select statement, and excluded from update or insert. useful for db generated ids etc... */
74
110
  readonly dbReadOnly: boolean;
111
+ /** the Value converter for this field */
75
112
  readonly valueConverter: ValueConverter<valueType>;
76
- readonly options: FieldOptions;
77
113
  }
78
114
  export interface ValueConverter<valueType> {
79
115
  fromJson?(val: any): valueType;
@@ -86,7 +122,7 @@ export interface ValueConverter<valueType> {
86
122
  readonly fieldTypeInDb?: string;
87
123
  readonly inputType?: string;
88
124
  }
89
- export declare type FieldValidator<entityType = any, valueType = any> = (entity: entityType, col: FieldRef<entityType, valueType>) => void | Promise<void>;
125
+ export declare type FieldValidator<entityType = any, valueType = any> = (entity: entityType, fieldRef: FieldRef<entityType, valueType>) => void | Promise<void>;
90
126
  export declare type ValueOrExpression<valueType> = valueType | (() => valueType);
91
127
  export declare function valueOrExpressionToValue<valueType>(f: ValueOrExpression<valueType>): valueType;
92
128
  export interface ValueListItem {
@@ -1,19 +1,20 @@
1
1
  import { DataProvider } from "./data-interfaces";
2
2
  import { EntityMetadata, EntityRef, FindOptions, Repository } from "./remult3";
3
3
  import { ClassType } from "../classType";
4
- import { LiveQueryClient } from "./live-query/LiveQueryClient";
5
- import type { ServerEventDispatcher } from "../live-query";
6
- import { LiveQueryStorage } from "./live-query/LiveQueryPublisher";
4
+ import type { SubscriptionServer } from "./live-query/SubscriptionServer";
7
5
  import { ExternalHttpProvider } from "./buildRestDataProvider";
6
+ import { SubscriptionClient } from "./live-query/SubscriptionChannel";
8
7
  export declare function isBackend(): boolean;
9
8
  export declare class Remult {
10
9
  /**Return's a `Repository` of the specific entity type
11
10
  * @example
12
11
  * const taskRepo = remult.repo(Task);
13
12
  * @see [Repository](https://remult.dev/docs/ref_repository.html)
14
- *
13
+ * @param entity - the entity to use
14
+ * @param dataProvider - an optional alternative data provider to use. Useful for writing to offline storage or an alternative data provider
15
15
  */
16
16
  repo<T>(entity: ClassType<T>, dataProvider?: DataProvider): Repository<T>;
17
+ /** Returns the current user's info */
17
18
  user?: UserInfo;
18
19
  /** Checks if a user was authenticated */
19
20
  authenticated(): boolean;
@@ -29,6 +30,8 @@ export declare class Remult {
29
30
  * [Allowed](https://remult.dev/docs/allowed.html)
30
31
  */
31
32
  isAllowedForInstance(instance: any, allowed?: AllowedForInstance<any>): boolean;
33
+ /** The current data provider */
34
+ dataProvider: DataProvider;
32
35
  /** Creates a new instance of the `remult` object.
33
36
  *
34
37
  * Can receive either an HttpProvider or a DataProvider as a parameter - which will be used to fetch data from.
@@ -38,25 +41,42 @@ export declare class Remult {
38
41
  constructor(http: ExternalHttpProvider | typeof fetch | ApiClient);
39
42
  constructor(p: DataProvider);
40
43
  constructor();
41
- liveQueryPublisher: LiveQueryPublisherInterface;
44
+ subscriptionServer?: SubscriptionServer;
45
+ /** Used to call a `backendMethod` using a specific `remult` object
46
+ * @example
47
+ * await remult.call(TasksController.setAll, undefined, true);
48
+ * @param backendMethod - the backend method to call
49
+ * @param classInstance - the class instance of the backend method, for static backend methods use undefined
50
+ * @param args - the arguments to send to the backend method
51
+ */
42
52
  call<T extends ((...args: any[]) => Promise<any>)>(backendMethod: T, classInstance?: any, ...args: GetArguments<T>): ReturnType<T>;
43
- /** The current data provider */
44
- dataProvider: DataProvider;
45
- liveQuerySubscriber: LiveQueryClient;
46
53
  /** A helper callback that can be used to debug and trace all find operations. Useful in debugging scenarios */
47
54
  static onFind: (metadata: EntityMetadata, options: FindOptions<any>) => void;
48
55
  clearAllCache(): any;
49
56
  /** A helper callback that is called whenever an entity is created. */
50
57
  static entityRefInit?: (ref: EntityRef<any>, row: any) => void;
58
+ /** context information that can be used to store custom information that will be disposed as part of the `remult` object */
51
59
  readonly context: RemultContext;
60
+ /** The api client that will be used by `remult` to perform calls to the `api` */
52
61
  apiClient: ApiClient;
53
62
  }
54
63
  export declare type GetArguments<T> = T extends (...args: infer FirstArgument) => any ? FirstArgument : never;
55
64
  export interface RemultContext {
56
65
  }
57
66
  export interface ApiClient {
67
+ /** The http client to use when making api calls.
68
+ * @example
69
+ * remult.apiClient.httpClient = axios;
70
+ * @example
71
+ * remult.apiClient.httpClient = httpClient;//angular http client
72
+ * @example
73
+ * remult.apiClient.httpClient = fetch; //this is the default
74
+ */
58
75
  httpClient?: ExternalHttpProvider | typeof fetch;
76
+ /** The base url to for making api calls */
59
77
  url?: string;
78
+ subscriptionClient?: SubscriptionClient;
79
+ wrapMessageHandling?: (x: VoidFunction) => void;
60
80
  }
61
81
  export declare const allEntities: ClassType<any>[];
62
82
  export interface ControllerOptions {
@@ -72,11 +92,11 @@ export interface UserInfo {
72
92
  name?: string;
73
93
  roles?: string[];
74
94
  }
75
- export declare type Allowed = boolean | string | string[] | ((c: Remult) => boolean);
76
- export declare type AllowedForInstance<T> = boolean | string | string[] | ((c: Remult, entity?: T) => boolean);
95
+ export declare type Allowed = boolean | string | string[] | ((c?: Remult) => boolean);
96
+ export declare type AllowedForInstance<T> = boolean | string | string[] | ((entity?: T, c?: Remult) => boolean);
77
97
  export declare class Allow {
78
98
  static everyone: () => boolean;
79
- static authenticated: (remult: Remult) => boolean;
99
+ static authenticated: (...args: any[]) => any;
80
100
  }
81
101
  export declare const queryConfig: {
82
102
  defaultPageSize: number;
@@ -90,14 +110,6 @@ export declare class EventSource {
90
110
  fire(): Promise<void>;
91
111
  dispatcher: EventDispatcher;
92
112
  }
93
- export interface LiveQueryPublisherInterface {
94
- stopLiveQuery(id: any): void;
95
- sendChannelMessage<messageType>(channel: string, message: messageType): void;
96
- defineLiveQueryChannel(serializeRequest: () => any, entityKey: string, options: FindOptions<any>, ids: any[], userId: string, repo: Repository<any>): string;
97
- itemChanged(entityKey: string, changes: itemChange[]): void;
98
- dispatcher: ServerEventDispatcher;
99
- storage: LiveQueryStorage;
100
- }
101
113
  export interface itemChange {
102
114
  id: any;
103
115
  oldId: any;
@@ -2,7 +2,7 @@ import { actionInfo, serverActionField } from './server-action';
2
2
  import { RestDataProvider } from './data-providers/rest-data-provider';
3
3
  import { RepositoryImplementation } from "./remult3/RepositoryImplementation";
4
4
  import { LiveQueryClient } from "./live-query/LiveQueryClient";
5
- import { EventSourceLiveQueryProvider } from "./live-query/EventSourceLiveQueryProvider";
5
+ import { SseSubscriptionClient } from "./live-query/SseSubscriptionClient";
6
6
  import { RemultProxy } from "./remult-proxy";
7
7
  import { buildRestDataProvider, isExternalHttpProvider } from "./buildRestDataProvider";
8
8
  export function isBackend() {
@@ -10,26 +10,22 @@ export function isBackend() {
10
10
  }
11
11
  export class Remult {
12
12
  constructor(provider) {
13
+ /** The current data provider */
14
+ this.dataProvider = new RestDataProvider(() => this.apiClient);
13
15
  /* @internal */
14
16
  this.repCache = new Map();
17
+ /* @internal*/
15
18
  this.liveQueryPublisher = {
16
- itemChanged: () => { },
17
- defineLiveQueryChannel: () => "",
18
- dispatcher: undefined,
19
- sendChannelMessage: function (arg0, what) {
20
- throw new Error("invalid publisher.");
21
- },
22
- stopLiveQuery: function (id) {
23
- throw new Error("Function not implemented.");
24
- },
25
- storage: undefined
19
+ itemChanged: () => { }
26
20
  };
27
- /** The current data provider */
28
- this.dataProvider = new RestDataProvider(() => this.apiClient);
29
- this.liveQuerySubscriber = new LiveQueryClient(new EventSourceLiveQueryProvider());
21
+ /* @internal*/
22
+ this.liveQuerySubscriber = new LiveQueryClient(() => this.apiClient);
23
+ /** context information that can be used to store custom information that will be disposed as part of the `remult` object */
30
24
  this.context = {};
25
+ /** The api client that will be used by `remult` to perform calls to the `api` */
31
26
  this.apiClient = {
32
- url: '/api'
27
+ url: '/api',
28
+ subscriptionClient: new SseSubscriptionClient()
33
29
  };
34
30
  if (provider && provider.getEntityDataProvider) {
35
31
  this.dataProvider = provider;
@@ -46,13 +42,18 @@ export class Remult {
46
42
  this.apiClient.httpClient = apiClient.httpClient;
47
43
  if (apiClient.url)
48
44
  this.apiClient.url = apiClient.url;
45
+ if (apiClient.subscriptionClient)
46
+ this.apiClient.subscriptionClient = apiClient.subscriptionClient;
47
+ if (apiClient.wrapMessageHandling)
48
+ this.apiClient.wrapMessageHandling = apiClient.wrapMessageHandling;
49
49
  }
50
50
  }
51
51
  /**Return's a `Repository` of the specific entity type
52
52
  * @example
53
53
  * const taskRepo = remult.repo(Task);
54
54
  * @see [Repository](https://remult.dev/docs/ref_repository.html)
55
- *
55
+ * @param entity - the entity to use
56
+ * @param dataProvider - an optional alternative data provider to use. Useful for writing to offline storage or an alternative data provider
56
57
  */
57
58
  repo(entity, dataProvider) {
58
59
  if (dataProvider === undefined)
@@ -66,7 +67,6 @@ export class Remult {
66
67
  }
67
68
  return r;
68
69
  }
69
- /* delete me */
70
70
  /** Checks if a user was authenticated */
71
71
  authenticated() {
72
72
  return this.user?.id !== undefined;
@@ -112,12 +112,19 @@ export class Remult {
112
112
  }
113
113
  }
114
114
  else if (typeof (allowed) === "function") {
115
- return allowed(this, instance);
115
+ return allowed(instance, this);
116
116
  }
117
117
  else
118
118
  return this.isAllowed(allowed);
119
119
  }
120
120
  //@ts-ignore // type error of typescript regarding args that doesn't appear in my normal development
121
+ /** Used to call a `backendMethod` using a specific `remult` object
122
+ * @example
123
+ * await remult.call(TasksController.setAll, undefined, true);
124
+ * @param backendMethod - the backend method to call
125
+ * @param classInstance - the class instance of the backend method, for static backend methods use undefined
126
+ * @param args - the arguments to send to the backend method
127
+ */
121
128
  call(backendMethod, classInstance, ...args) {
122
129
  const z = (backendMethod[serverActionField]);
123
130
  if (!z.doWork)
@@ -156,7 +163,16 @@ export function setControllerSettings(target, options) {
156
163
  export class Allow {
157
164
  }
158
165
  Allow.everyone = () => true;
159
- Allow.authenticated = (remult) => remult.authenticated();
166
+ Allow.authenticated = (...args) => {
167
+ if (args.length > 1) {
168
+ return args[1].authenticated();
169
+ }
170
+ else if (args.length == 1) {
171
+ if (args[0].authenticated)
172
+ return args[0].authenticated();
173
+ }
174
+ return RemultProxy.defaultRemult.authenticated();
175
+ };
160
176
  export const queryConfig = {
161
177
  defaultPageSize: 200
162
178
  };
@@ -188,22 +204,11 @@ export async function doTransaction(remult, what) {
188
204
  trans.flush();
189
205
  });
190
206
  }
191
- // TODO - talk about message size limit in ably - something that we may reach if we group the messages
192
207
  class transactionLiveQueryPublisher {
193
208
  constructor(orig) {
194
209
  this.orig = orig;
195
210
  this.transactionItems = new Map();
196
211
  }
197
- get storage() {
198
- return this.orig.storage;
199
- }
200
- ;
201
- set storage(value) {
202
- this.orig.storage = value;
203
- }
204
- stopLiveQuery(id) {
205
- this.orig.stopLiveQuery(id);
206
- }
207
212
  itemChanged(entityKey, changes) {
208
213
  let items = this.transactionItems.get(entityKey);
209
214
  if (!items) {
@@ -230,16 +235,4 @@ class transactionLiveQueryPublisher {
230
235
  this.orig.itemChanged(key, this.transactionItems.get(key));
231
236
  }
232
237
  }
233
- sendChannelMessage(channel, message) {
234
- this.orig.sendChannelMessage(channel, message);
235
- }
236
- defineLiveQueryChannel(serializeRequest, entityKey, options, ids, userId, repo) {
237
- return this.orig.defineLiveQueryChannel(serializeRequest, entityKey, options, ids, userId, repo);
238
- }
239
- get dispatcher() {
240
- return this.orig.dispatcher;
241
- }
242
- set dispatcher(value) {
243
- this.orig.dispatcher = value;
244
- }
245
238
  }
@@ -1,8 +1,10 @@
1
+ import { v4 as uuid } from 'uuid';
1
2
  import { customUrlToken, buildFilterFromRequestParameters } from './filter/filter-interfaces';
2
3
  import { doTransaction } from './context';
3
4
  import { Filter } from './filter/filter-interfaces';
4
5
  import { ForbiddenError } from './server-action';
5
6
  import { getId } from './remult3/getId';
7
+ import { findOptionsToJson } from './data-providers/rest-data-provider';
6
8
  export class DataApi {
7
9
  constructor(repository, remult) {
8
10
  this.repository = repository;
@@ -29,7 +31,7 @@ export class DataApi {
29
31
  case "count":
30
32
  return this.count(res, req, body);
31
33
  case "endLiveQuery":
32
- this.remult.liveQueryPublisher.stopLiveQuery(body.id);
34
+ this.remult.liveQueryStorage.remove(body.id);
33
35
  res.success("ok");
34
36
  return;
35
37
  default:
@@ -127,8 +129,19 @@ export class DataApi {
127
129
  }
128
130
  try {
129
131
  const r = await this.getArrayImpl(response, request, filterBody);
132
+ const queryChannel = `users:${this.remult.user?.id}:queries:${uuid()}`;
133
+ const data = {
134
+ requestJson: serializeRequest(),
135
+ findOptionsJson: findOptionsToJson(r.findOptions, this.repository.metadata),
136
+ lastIds: r.r.map(y => getId(this.repository.metadata, y))
137
+ };
138
+ this.remult.liveQueryStorage.add({
139
+ entityKey: this.repository.metadata.key,
140
+ id: queryChannel,
141
+ data
142
+ });
130
143
  response.success({
131
- queryChannel: this.remult.liveQueryPublisher.defineLiveQueryChannel(serializeRequest, this.repository.metadata.key, r.findOptions, r.r.map(y => getId(this.repository.metadata, y)), this.remult.user?.id, this.repository),
144
+ queryChannel,
132
145
  result: r.r
133
146
  });
134
147
  }
@@ -187,8 +200,8 @@ export class DataApi {
187
200
  response.forbidden();
188
201
  return;
189
202
  }
190
- await this.repository.getEntityRef(row).save();
191
- response.success(this.repository.getEntityRef(row).toApiJson());
203
+ await ref.save();
204
+ response.success(ref.toApiJson());
192
205
  });
193
206
  }
194
207
  async delete(response, id) {
@@ -4,7 +4,7 @@ import { EntityMetadata, OmitEB } from './remult3';
4
4
  export interface DataProvider {
5
5
  getEntityDataProvider(entity: EntityMetadata): EntityDataProvider;
6
6
  transaction(action: (dataProvider: DataProvider) => Promise<void>): Promise<void>;
7
- supportsCustomFilter?: boolean;
7
+ supportsrawFilter?: boolean;
8
8
  }
9
9
  export interface EntityDataProvider {
10
10
  count(where: Filter): Promise<number>;
@@ -4,7 +4,7 @@ import { EntityMetadata, EntityFilter } from '../remult3';
4
4
  export declare class ArrayEntityDataProvider implements EntityDataProvider {
5
5
  private entity;
6
6
  private rows?;
7
- static customFilter(filter: CustomArrayFilter): EntityFilter<any>;
7
+ static rawFilter(filter: CustomArrayFilter): EntityFilter<any>;
8
8
  constructor(entity: EntityMetadata, rows?: any[]);
9
9
  private verifyThatRowHasAllNotNullColumns;
10
10
  count(where?: Filter): Promise<number>;
@@ -12,7 +12,7 @@ export class ArrayEntityDataProvider {
12
12
  }
13
13
  }
14
14
  }
15
- static customFilter(filter) {
15
+ static rawFilter(filter) {
16
16
  return {
17
17
  [customDatabaseFilterToken]: {
18
18
  arrayFilter: filter
@@ -1,22 +1,15 @@
1
1
  import { EntityDataProvider, DataProvider, EntityDataProviderFindOptions, RestDataProviderHttpProvider } from '../data-interfaces';
2
2
  import { UrlBuilder } from '../../urlBuilder';
3
3
  import { Filter } from '../filter/filter-interfaces';
4
- import { EntityMetadata, FindOptions } from '../remult3';
4
+ import { EntityMetadata } from '../remult3';
5
5
  import { ApiClient } from '../context';
6
6
  export declare class RestDataProvider implements DataProvider {
7
7
  private apiProvider;
8
8
  constructor(apiProvider: () => ApiClient);
9
- getEntityDataProvider(entity: EntityMetadata): EntityDataProvider;
9
+ getEntityDataProvider(entity: EntityMetadata): RestEntityDataProvider;
10
10
  transaction(action: (dataProvider: DataProvider) => Promise<void>): Promise<void>;
11
- supportsCustomFilter: boolean;
11
+ supportsrawFilter: boolean;
12
12
  }
13
- export declare function findOptionsToJson(options: FindOptions<any>, meta: EntityMetadata): {
14
- limit: number;
15
- page: number;
16
- where: any;
17
- orderBy: import("../remult3").EntityOrderBy<any>;
18
- };
19
- export declare function findOptionsFromJson(json: any, meta: EntityMetadata): FindOptions<any>;
20
13
  export declare class RestEntityDataProvider implements EntityDataProvider {
21
14
  private url;
22
15
  private http;
@@ -25,11 +18,7 @@ export declare class RestEntityDataProvider implements EntityDataProvider {
25
18
  translateFromJson(row: any): {};
26
19
  translateToJson(row: any): {};
27
20
  count(where: Filter): Promise<number>;
28
- find(options: EntityDataProviderFindOptions): Promise<Array<any>>;
29
- buildFindRequest(options: EntityDataProviderFindOptions): {
30
- filterObject: any;
31
- url: UrlBuilder;
32
- };
21
+ find(options?: EntityDataProviderFindOptions): Promise<Array<any>>;
33
22
  update(id: any, data: any): Promise<any>;
34
23
  delete(id: any): Promise<void>;
35
24
  insert(data: any): Promise<any>;