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.
- package/README.md +28 -5
- package/esm/index.d.ts +4 -3
- package/esm/index.js +4 -2
- package/esm/live-query/ably.d.ts +6 -6
- package/esm/live-query/ably.js +5 -5
- package/esm/postgres/postgres-data-provider.d.ts +3 -1
- package/esm/postgres/postgres-data-provider.js +11 -1
- package/esm/postgres/schema-builder.d.ts +1 -1
- package/esm/postgres/schema-builder.js +43 -27
- package/esm/remult-express.d.ts +4 -22
- package/esm/remult-express.js +30 -28
- package/esm/remult-fastify.js +1 -1
- package/esm/remult-fresh.js +2 -2
- package/esm/remult-knex/index.d.ts +7 -31
- package/esm/remult-knex/index.js +71 -42
- package/esm/remult-mongo.d.ts +9 -2
- package/esm/remult-mongo.js +24 -8
- package/esm/remult-next.d.ts +11 -0
- package/esm/remult-next.js +27 -0
- package/esm/server/expressBridge.d.ts +20 -52
- package/esm/server/expressBridge.js +55 -29
- package/esm/server/index.d.ts +3 -1
- package/esm/server/index.js +6 -1
- package/esm/src/column-interfaces.d.ts +44 -8
- package/esm/src/context.d.ts +31 -19
- package/esm/src/context.js +35 -42
- package/esm/src/data-api.js +17 -4
- package/esm/src/data-interfaces.d.ts +1 -1
- package/esm/src/data-providers/array-entity-data-provider.d.ts +1 -1
- package/esm/src/data-providers/array-entity-data-provider.js +1 -1
- package/esm/src/data-providers/rest-data-provider.d.ts +4 -15
- package/esm/src/data-providers/rest-data-provider.js +38 -23
- package/esm/src/data-providers/sql-database.d.ts +6 -3
- package/esm/src/data-providers/sql-database.js +57 -16
- package/esm/src/data-providers/web-sql-data-provider.d.ts +2 -0
- package/esm/src/data-providers/web-sql-data-provider.js +17 -8
- package/esm/src/entity.d.ts +2 -2
- package/esm/src/filter/filter-consumer-bridge-to-sql-request.d.ts +16 -16
- package/esm/src/filter/filter-consumer-bridge-to-sql-request.js +29 -21
- package/esm/src/filter/filter-interfaces.d.ts +5 -5
- package/esm/src/filter/filter-interfaces.js +31 -26
- package/esm/src/live-query/LiveQueryClient.d.ts +1 -19
- package/esm/src/live-query/LiveQueryClient.js +81 -50
- package/esm/src/live-query/SseSubscriptionClient.d.ts +4 -0
- package/esm/src/live-query/{EventSourceLiveQueryProvider.js → SseSubscriptionClient.js} +7 -6
- package/esm/src/live-query/SubscriptionChannel.d.ts +41 -0
- package/esm/src/live-query/{LiveQuerySubscriber.js → SubscriptionChannel.js} +40 -22
- package/esm/src/live-query/SubscriptionServer.d.ts +38 -0
- package/esm/src/live-query/{LiveQueryPublisher.js → SubscriptionServer.js} +60 -73
- package/esm/src/remult-proxy.js +44 -1
- package/esm/src/remult3/RepositoryImplementation.d.ts +8 -2
- package/esm/src/remult3/RepositoryImplementation.js +94 -30
- package/esm/src/remult3/remult3.d.ts +59 -6
- package/esm/src/remult3/remult3.js +1 -0
- package/esm/src/server-action.d.ts +1 -1
- package/esm/src/shared-tests/db-tests-setup.d.ts +1 -1
- package/esm/src/shared-tests/db-tests-setup.js +4 -3
- package/esm/src/shared-tests/db-tests.db-tests.js +55 -9
- package/esm/src/sql-command.d.ts +4 -2
- package/esm/src/tests/entityForCustomFilter.d.ts +6 -6
- package/esm/src/tests/entityForCustomFilter.js +20 -21
- package/esm/src/tests/frontend-database-tests-setup.spec.js +1 -2
- package/esm/src/tests/testHelper.spec.d.ts +2 -2
- package/esm/src/tests/testHelper.spec.js +1 -2
- package/esm/src/valueConverters.js +17 -7
- package/index.d.ts +4 -3
- package/index.js +7 -3
- package/live-query/ably.d.ts +6 -6
- package/live-query/ably.js +12 -12
- package/package.json +18 -9
- package/postgres/postgres-data-provider.d.ts +3 -1
- package/postgres/postgres-data-provider.js +13 -3
- package/postgres/schema-builder.d.ts +1 -1
- package/postgres/schema-builder.js +64 -50
- package/remult-express.d.ts +4 -22
- package/remult-express.js +39 -47
- package/remult-fastify.js +2 -2
- package/remult-fresh.js +1 -1
- package/remult-knex/index.d.ts +7 -31
- package/remult-knex/index.js +100 -53
- package/remult-mongo.d.ts +9 -2
- package/remult-mongo.js +39 -7
- package/remult-next.d.ts +11 -0
- package/remult-next.js +73 -0
- package/server/JsonEntityFileStorage.js +2 -2
- package/server/expressBridge.d.ts +20 -52
- package/server/expressBridge.js +68 -41
- package/server/index.d.ts +3 -1
- package/server/index.js +7 -2
- package/src/column-interfaces.d.ts +44 -8
- package/src/context.d.ts +31 -19
- package/src/context.js +39 -50
- package/src/data-api.js +18 -5
- package/src/data-interfaces.d.ts +1 -1
- package/src/data-providers/array-entity-data-provider.d.ts +1 -1
- package/src/data-providers/array-entity-data-provider.js +1 -1
- package/src/data-providers/rest-data-provider.d.ts +4 -15
- package/src/data-providers/rest-data-provider.js +48 -24
- package/src/data-providers/sql-database.d.ts +6 -3
- package/src/data-providers/sql-database.js +75 -15
- package/src/data-providers/web-sql-data-provider.d.ts +2 -0
- package/src/data-providers/web-sql-data-provider.js +16 -7
- package/src/entity.d.ts +2 -2
- package/src/filter/filter-consumer-bridge-to-sql-request.d.ts +16 -16
- package/src/filter/filter-consumer-bridge-to-sql-request.js +59 -50
- package/src/filter/filter-interfaces.d.ts +5 -5
- package/src/filter/filter-interfaces.js +31 -26
- package/src/live-query/LiveQueryClient.d.ts +1 -19
- package/src/live-query/LiveQueryClient.js +117 -64
- package/src/live-query/SseSubscriptionClient.d.ts +4 -0
- package/src/live-query/{EventSourceLiveQueryProvider.js → SseSubscriptionClient.js} +13 -12
- package/src/live-query/SubscriptionChannel.d.ts +41 -0
- package/src/live-query/{LiveQuerySubscriber.js → SubscriptionChannel.js} +51 -47
- package/src/live-query/SubscriptionServer.d.ts +38 -0
- package/src/live-query/SubscriptionServer.js +237 -0
- package/src/remult-proxy.js +137 -2
- package/src/remult3/RepositoryImplementation.d.ts +8 -2
- package/src/remult3/RepositoryImplementation.js +121 -34
- package/src/remult3/remult3.d.ts +59 -6
- package/src/remult3/remult3.js +1 -0
- package/src/server-action.d.ts +1 -1
- package/src/shared-tests/db-tests-setup.d.ts +1 -1
- package/src/shared-tests/db-tests-setup.js +5 -4
- package/src/shared-tests/db-tests.db-tests.js +101 -8
- package/src/sql-command.d.ts +4 -2
- package/src/tests/entityForCustomFilter.d.ts +6 -6
- package/src/tests/entityForCustomFilter.js +30 -31
- package/src/tests/frontend-database-tests-setup.spec.js +4 -5
- package/src/tests/testHelper.spec.d.ts +2 -2
- package/src/tests/testHelper.spec.js +4 -5
- package/src/valueConverters.js +17 -7
- package/esm/live-query/index.d.ts +0 -3
- package/esm/live-query/index.js +0 -3
- package/esm/src/backend-tests/backend-database-test-setup.backend-spec.d.ts +0 -5
- package/esm/src/backend-tests/backend-database-test-setup.backend-spec.js +0 -113
- package/esm/src/backend-tests/my-test.backend-spec.d.ts +0 -1
- package/esm/src/backend-tests/my-test.backend-spec.js +0 -59
- package/esm/src/backend-tests/test-http-server.backend-spec.d.ts +0 -6
- package/esm/src/backend-tests/test-http-server.backend-spec.js +0 -171
- package/esm/src/live-query/EventSourceLiveQueryProvider.d.ts +0 -4
- package/esm/src/live-query/LiveQueryPublisher.d.ts +0 -49
- package/esm/src/live-query/LiveQuerySubscriber.d.ts +0 -67
- package/live-query/index.d.ts +0 -3
- package/live-query/index.js +0 -10
- package/src/backend-tests/backend-database-test-setup.backend-spec.d.ts +0 -5
- package/src/backend-tests/backend-database-test-setup.backend-spec.js +0 -231
- package/src/backend-tests/my-test.backend-spec.d.ts +0 -1
- package/src/backend-tests/my-test.backend-spec.js +0 -160
- package/src/backend-tests/test-http-server.backend-spec.d.ts +0 -6
- package/src/backend-tests/test-http-server.backend-spec.js +0 -342
- package/src/live-query/EventSourceLiveQueryProvider.d.ts +0 -4
- package/src/live-query/LiveQueryPublisher.d.ts +0 -49
- package/src/live-query/LiveQueryPublisher.js +0 -248
- 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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
}
|
package/esm/server/index.d.ts
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
|
|
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;
|
package/esm/server/index.js
CHANGED
|
@@ -1,2 +1,7 @@
|
|
|
1
|
-
|
|
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,
|
|
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,
|
|
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
|
-
|
|
68
|
-
|
|
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,
|
|
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 {
|
package/esm/src/context.d.ts
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
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
|
|
76
|
-
export declare type AllowedForInstance<T> = boolean | string | string[] | ((
|
|
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: (
|
|
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;
|
package/esm/src/context.js
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
28
|
-
this.
|
|
29
|
-
|
|
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(
|
|
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 = (
|
|
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
|
}
|
package/esm/src/data-api.js
CHANGED
|
@@ -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.
|
|
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
|
|
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
|
|
191
|
-
response.success(
|
|
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
|
-
|
|
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
|
|
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>;
|
|
@@ -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
|
|
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):
|
|
9
|
+
getEntityDataProvider(entity: EntityMetadata): RestEntityDataProvider;
|
|
10
10
|
transaction(action: (dataProvider: DataProvider) => Promise<void>): Promise<void>;
|
|
11
|
-
|
|
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
|
|
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>;
|