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
|
@@ -1,78 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
export class LiveQueryStorageInMemoryImplementation {
|
|
4
|
-
constructor() {
|
|
5
|
-
this.debugFileSaver = (x) => { };
|
|
6
|
-
this.queries = [];
|
|
7
|
-
}
|
|
8
|
-
debug() {
|
|
9
|
-
this.debugFileSaver(this.queries);
|
|
10
|
-
}
|
|
11
|
-
async keepAliveAndReturnUnknownIds(ids) {
|
|
12
|
-
const result = [];
|
|
13
|
-
for (const id of ids) {
|
|
14
|
-
let q = this.queries.find(q => q.id === id);
|
|
15
|
-
if (q) {
|
|
16
|
-
q.lastUsed = new Date().toISOString();
|
|
17
|
-
}
|
|
18
|
-
else
|
|
19
|
-
result.push(id);
|
|
20
|
-
}
|
|
21
|
-
this.debug();
|
|
22
|
-
return result;
|
|
23
|
-
}
|
|
24
|
-
store(query) {
|
|
25
|
-
this.queries.push({ ...query, lastUsed: new Date().toISOString() });
|
|
26
|
-
this.debug();
|
|
27
|
-
}
|
|
28
|
-
remove(id) {
|
|
29
|
-
this.queries = this.queries.filter(q => q.id !== id);
|
|
30
|
-
this.debug();
|
|
31
|
-
}
|
|
32
|
-
async provideListeners(entityKey, handle) {
|
|
33
|
-
let d = new Date();
|
|
34
|
-
d.setMinutes(d.getMinutes() - 5);
|
|
35
|
-
this.queries = this.queries.filter(x => x.lastUsed > d.toISOString());
|
|
36
|
-
for (const q of this.queries) {
|
|
37
|
-
if (q.entityKey === entityKey) {
|
|
38
|
-
await handle({
|
|
39
|
-
query: q,
|
|
40
|
-
setLastIds: async (ids) => { q.lastIds = ids; },
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
this.debug();
|
|
45
|
-
}
|
|
46
|
-
}
|
|
1
|
+
import { findOptionsFromJson } from '../data-providers/rest-data-provider';
|
|
2
|
+
/* @internal*/
|
|
47
3
|
export class LiveQueryPublisher {
|
|
48
|
-
constructor(
|
|
49
|
-
this.
|
|
50
|
-
this.
|
|
4
|
+
constructor(subscriptionServer, liveQueryStorage, performWithRequest) {
|
|
5
|
+
this.subscriptionServer = subscriptionServer;
|
|
6
|
+
this.liveQueryStorage = liveQueryStorage;
|
|
51
7
|
this.performWithRequest = performWithRequest;
|
|
52
8
|
this.debugFileSaver = (x) => { };
|
|
53
9
|
}
|
|
54
|
-
|
|
55
|
-
this.storage.remove(id);
|
|
56
|
-
}
|
|
57
|
-
sendChannelMessage(channel, message) {
|
|
58
|
-
this.dispatcher.sendChannelMessage(channel, message);
|
|
59
|
-
}
|
|
60
|
-
defineLiveQueryChannel(serializeRequest, entityKey, findOptions, ids, userId, repo) {
|
|
61
|
-
const id = `users:${userId}:queries:${uuid()}`;
|
|
62
|
-
this.storage.store({
|
|
63
|
-
requestJson: serializeRequest(),
|
|
64
|
-
entityKey,
|
|
65
|
-
id,
|
|
66
|
-
findOptionsJson: findOptionsToJson(findOptions, repo.metadata),
|
|
67
|
-
lastIds: ids
|
|
68
|
-
});
|
|
69
|
-
return id;
|
|
70
|
-
}
|
|
71
|
-
runPromise(p) {
|
|
72
|
-
}
|
|
10
|
+
runPromise(p) { }
|
|
73
11
|
itemChanged(entityKey, changes) {
|
|
74
|
-
//TODO - optimize so that the user will get their messages first. Based on user id
|
|
75
|
-
this.runPromise(this.
|
|
12
|
+
//TODO 2 - optimize so that the user will get their messages first. Based on user id
|
|
13
|
+
this.runPromise(this.liveQueryStorage().forEach(entityKey, async ({ query: q, setData }) => {
|
|
14
|
+
let query = { ...q.data };
|
|
76
15
|
await this.performWithRequest(query.requestJson, entityKey, async (repo) => {
|
|
77
16
|
const messages = [];
|
|
78
17
|
const currentItems = await repo.find(findOptionsFromJson(query.findOptionsJson, repo.metadata));
|
|
@@ -107,15 +46,63 @@ export class LiveQueryPublisher {
|
|
|
107
46
|
}
|
|
108
47
|
}
|
|
109
48
|
this.debugFileSaver({
|
|
110
|
-
query:
|
|
49
|
+
query: q.id,
|
|
111
50
|
currentIds,
|
|
112
51
|
changes,
|
|
113
52
|
lastIds: query.lastIds,
|
|
114
53
|
messages
|
|
115
54
|
});
|
|
116
|
-
|
|
117
|
-
|
|
55
|
+
query.lastIds = currentIds;
|
|
56
|
+
await setData(query);
|
|
57
|
+
if (messages.length > 0)
|
|
58
|
+
this.subscriptionServer().publishMessage(q.id, messages);
|
|
118
59
|
});
|
|
119
60
|
}));
|
|
120
61
|
}
|
|
121
62
|
}
|
|
63
|
+
export class InMemoryLiveQueryStorage {
|
|
64
|
+
constructor() {
|
|
65
|
+
this.debugFileSaver = (x) => { };
|
|
66
|
+
this.queries = [];
|
|
67
|
+
this.removeCountForTesting = 0;
|
|
68
|
+
}
|
|
69
|
+
debug() {
|
|
70
|
+
this.debugFileSaver(this.queries);
|
|
71
|
+
}
|
|
72
|
+
async keepAliveAndReturnUnknownQueryIds(ids) {
|
|
73
|
+
const result = [];
|
|
74
|
+
for (const id of ids) {
|
|
75
|
+
let q = this.queries.find(q => q.id === id);
|
|
76
|
+
if (q) {
|
|
77
|
+
q.lastUsed = new Date().toISOString();
|
|
78
|
+
}
|
|
79
|
+
else
|
|
80
|
+
result.push(id);
|
|
81
|
+
}
|
|
82
|
+
this.debug();
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
add(query) {
|
|
86
|
+
this.queries.push({ ...query, lastUsed: new Date().toISOString() });
|
|
87
|
+
this.debug();
|
|
88
|
+
}
|
|
89
|
+
remove(id) {
|
|
90
|
+
this.queries = this.queries.filter(q => q.id !== id);
|
|
91
|
+
this.removeCountForTesting++;
|
|
92
|
+
this.debug();
|
|
93
|
+
}
|
|
94
|
+
async forEach(entityKey, handle) {
|
|
95
|
+
let d = new Date();
|
|
96
|
+
d.setMinutes(d.getMinutes() - 5);
|
|
97
|
+
this.queries = this.queries.filter(x => x.lastUsed > d.toISOString());
|
|
98
|
+
for (const q of this.queries) {
|
|
99
|
+
if (q.entityKey === entityKey) {
|
|
100
|
+
await handle({
|
|
101
|
+
query: q,
|
|
102
|
+
setData: async (data) => { q.data = data; },
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
this.debug();
|
|
107
|
+
}
|
|
108
|
+
}
|
package/esm/src/remult-proxy.js
CHANGED
|
@@ -3,15 +3,47 @@ export class RemultProxy {
|
|
|
3
3
|
constructor() {
|
|
4
4
|
/*@internal*/
|
|
5
5
|
this.remultFactory = () => RemultProxy.defaultRemult;
|
|
6
|
-
|
|
6
|
+
//@ts-ignore
|
|
7
|
+
this.repo = (...args) => {
|
|
8
|
+
let self = this;
|
|
9
|
+
return {
|
|
10
|
+
addEventListener: (...args2) => self.remultFactory().repo(...args).addEventListener(...args2),
|
|
11
|
+
count: (...args2) => self.remultFactory().repo(...args).count(...args2),
|
|
12
|
+
create: (...args2) => self.remultFactory().repo(...args).create(...args2),
|
|
13
|
+
delete: (args2) => self.remultFactory().repo(...args).delete(args2),
|
|
14
|
+
find: (...args2) => self.remultFactory().repo(...args).find(...args2),
|
|
15
|
+
findFirst: (...args2) => self.remultFactory().repo(...args).findFirst(...args2),
|
|
16
|
+
findId: (...args2) => self.remultFactory().repo(...args).findId(...args2),
|
|
17
|
+
fromJson: (...args2) => self.remultFactory().repo(...args).fromJson(...args2),
|
|
18
|
+
getEntityRef: (...args2) => self.remultFactory().repo(...args).getEntityRef(...args2),
|
|
19
|
+
insert: (args2) => self.remultFactory().repo(...args).insert(args2),
|
|
20
|
+
liveQuery: (...args2) => self.remultFactory().repo(...args).liveQuery(...args2),
|
|
21
|
+
get metadata() {
|
|
22
|
+
return self.remultFactory().repo(...args).metadata;
|
|
23
|
+
},
|
|
24
|
+
query: (...args2) => self.remultFactory().repo(...args).query(...args2),
|
|
25
|
+
save: (args2) => self.remultFactory().repo(...args).save(args2),
|
|
26
|
+
update: (...args2) => self.remultFactory().repo(...args).update(...args2),
|
|
27
|
+
};
|
|
28
|
+
};
|
|
7
29
|
}
|
|
30
|
+
/* @internal*/
|
|
8
31
|
get liveQuerySubscriber() { return this.remultFactory().liveQuerySubscriber; }
|
|
9
32
|
;
|
|
33
|
+
/* @internal*/
|
|
10
34
|
set liveQuerySubscriber(val) { this.remultFactory().liveQuerySubscriber = val; }
|
|
11
35
|
;
|
|
36
|
+
/* @internal*/
|
|
37
|
+
get liveQueryStorage() { return this.remultFactory().liveQueryStorage; }
|
|
38
|
+
;
|
|
39
|
+
/* @internal*/
|
|
40
|
+
set liveQueryStorage(val) { this.remultFactory().liveQueryStorage = val; }
|
|
41
|
+
;
|
|
42
|
+
/* @internal*/
|
|
12
43
|
get liveQueryPublisher() {
|
|
13
44
|
return this.remultFactory().liveQueryPublisher;
|
|
14
45
|
}
|
|
46
|
+
/* @internal*/
|
|
15
47
|
set liveQueryPublisher(val) {
|
|
16
48
|
this.remultFactory().liveQueryPublisher = val;
|
|
17
49
|
}
|
|
@@ -24,6 +56,7 @@ export class RemultProxy {
|
|
|
24
56
|
get dataProvider() { return this.remultFactory().dataProvider; }
|
|
25
57
|
;
|
|
26
58
|
set dataProvider(provider) { this.remultFactory().dataProvider = provider; }
|
|
59
|
+
/*@internal*/
|
|
27
60
|
get repCache() { return this.remultFactory().repCache; }
|
|
28
61
|
;
|
|
29
62
|
authenticated() {
|
|
@@ -38,6 +71,10 @@ export class RemultProxy {
|
|
|
38
71
|
clearAllCache() {
|
|
39
72
|
return this.remultFactory().clearAllCache();
|
|
40
73
|
}
|
|
74
|
+
/*@internal*/
|
|
75
|
+
resetFactory() {
|
|
76
|
+
this.remultFactory = () => RemultProxy.defaultRemult;
|
|
77
|
+
}
|
|
41
78
|
get user() {
|
|
42
79
|
return this.remultFactory().user;
|
|
43
80
|
}
|
|
@@ -50,5 +87,11 @@ export class RemultProxy {
|
|
|
50
87
|
set apiClient(client) {
|
|
51
88
|
this.remultFactory().apiClient = client;
|
|
52
89
|
}
|
|
90
|
+
get subscriptionServer() {
|
|
91
|
+
return this.remultFactory().subscriptionServer;
|
|
92
|
+
}
|
|
93
|
+
set subscriptionServer(value) {
|
|
94
|
+
this.remultFactory().subscriptionServer = value;
|
|
95
|
+
}
|
|
53
96
|
}
|
|
54
97
|
export const remult = new RemultProxy();
|
|
@@ -41,6 +41,7 @@ export declare class RepositoryImplementation<entityType> implements Repository<
|
|
|
41
41
|
private getRefForExistingRow;
|
|
42
42
|
save(item: Partial<OmitEB<entityType>>[]): Promise<entityType[]>;
|
|
43
43
|
save(item: Partial<OmitEB<entityType>>): Promise<entityType>;
|
|
44
|
+
liveQuery(options?: FindOptions<entityType>): any;
|
|
44
45
|
find(options: FindOptions<entityType>): Promise<entityType[]>;
|
|
45
46
|
buildEntityDataProviderFindOptions(options: FindOptions<entityType>): Promise<EntityDataProviderFindOptions>;
|
|
46
47
|
private mapRawDataToResult;
|
|
@@ -51,7 +52,6 @@ export declare class RepositoryImplementation<entityType> implements Repository<
|
|
|
51
52
|
create(item?: Partial<OmitEB<entityType>>): entityType;
|
|
52
53
|
fromJson(json: any, newRow?: boolean): Promise<entityType>;
|
|
53
54
|
findId(id: any, options?: FindFirstOptionsBase<entityType>): Promise<entityType>;
|
|
54
|
-
private translateWhereToFilter;
|
|
55
55
|
}
|
|
56
56
|
export declare function __updateEntityBasedOnWhere<T>(entityDefs: EntityMetadata<T>, where: EntityFilter<T>, r: T): void;
|
|
57
57
|
export declare type EntityOptionsFactory = (remult: Remult) => EntityOptions;
|
|
@@ -178,7 +178,6 @@ export declare class columnDefsImpl implements FieldMetadata {
|
|
|
178
178
|
private settings;
|
|
179
179
|
private entityDefs;
|
|
180
180
|
constructor(settings: FieldOptions, entityDefs: EntityFullInfo<any>, remult: Remult);
|
|
181
|
-
dbNamePromise: Promise<string>;
|
|
182
181
|
getDbName(): Promise<string>;
|
|
183
182
|
options: FieldOptions<any, any>;
|
|
184
183
|
target: ClassType<any>;
|
|
@@ -220,7 +219,10 @@ export declare class Fields {
|
|
|
220
219
|
static integer<entityType = any>(...options: (FieldOptions<entityType, Number> | ((options: FieldOptions<entityType, Number>, remult: Remult) => void))[]): (target: any, key: any, c?: any) => void;
|
|
221
220
|
static autoIncrement<entityType = any>(...options: (FieldOptions<entityType, Number> | ((options: FieldOptions<entityType, Number>, remult: Remult) => void))[]): (target: any, key: any, c?: any) => void;
|
|
222
221
|
static number<entityType = any>(...options: (FieldOptions<entityType, Number> | ((options: FieldOptions<entityType, Number>, remult: Remult) => void))[]): (target: any, key: any, c?: any) => void;
|
|
222
|
+
static createdAt<entityType = any>(...options: (FieldOptions<entityType, Date> | ((options: FieldOptions<entityType, Date>, remult: Remult) => void))[]): (target: any, key: any, c?: any) => void;
|
|
223
|
+
static updatedAt<entityType = any>(...options: (FieldOptions<entityType, Date> | ((options: FieldOptions<entityType, Date>, remult: Remult) => void))[]): (target: any, key: any, c?: any) => void;
|
|
223
224
|
static uuid<entityType = any>(...options: (FieldOptions<entityType, string> | ((options: FieldOptions<entityType, string>, remult: Remult) => void))[]): (target: any, key: any, c?: any) => void;
|
|
225
|
+
static cuid<entityType = any>(...options: (FieldOptions<entityType, string> | ((options: FieldOptions<entityType, string>, remult: Remult) => void))[]): (target: any, key: any, c?: any) => void;
|
|
224
226
|
static string<entityType = any>(...options: (StringFieldOptions<entityType> | ((options: StringFieldOptions<entityType>, remult: Remult) => void))[]): (target: any, key: any, c?: any) => void;
|
|
225
227
|
static boolean<entityType = any>(...options: (FieldOptions<entityType, boolean> | ((options: FieldOptions<entityType, boolean>, remult: Remult) => void))[]): (target: any, key: any, c?: any) => void;
|
|
226
228
|
}
|
|
@@ -323,4 +325,8 @@ declare class SubscribableImp implements Subscribable {
|
|
|
323
325
|
reportObserved: () => void;
|
|
324
326
|
}): Unobserve;
|
|
325
327
|
}
|
|
328
|
+
export declare function getEntityMetadata<entityType>(entity: EntityMetadataOverloads<entityType>): EntityMetadata<entityType>;
|
|
329
|
+
export declare function getRepository<entityType>(entity: RepositoryOverloads<entityType>): Repository<entityType>;
|
|
330
|
+
export declare type EntityMetadataOverloads<entityType> = Repository<entityType> | EntityMetadata<entityType> | ClassType<entityType>;
|
|
331
|
+
export declare type RepositoryOverloads<entityType> = Repository<entityType> | ClassType<entityType>;
|
|
326
332
|
export {};
|
|
@@ -3,10 +3,11 @@ import { allEntities, Remult, isBackend, queryConfig as queryConfig, setControll
|
|
|
3
3
|
import { entityFilterToJson, Filter } from "../filter/filter-interfaces";
|
|
4
4
|
import { Sort } from "../sort";
|
|
5
5
|
import { v4 as uuid } from 'uuid';
|
|
6
|
+
import cuid from 'cuid';
|
|
6
7
|
import { ValueConverters } from "../valueConverters";
|
|
7
8
|
import { filterHelper } from "../filter/filter-interfaces";
|
|
8
9
|
import { assign } from "../../assign";
|
|
9
|
-
import { remult as defaultRemult } from "../remult-proxy";
|
|
10
|
+
import { remult as defaultRemult, RemultProxy } from "../remult-proxy";
|
|
10
11
|
import { getId } from "./getId";
|
|
11
12
|
//import { remult } from "../remult-proxy";
|
|
12
13
|
let classValidatorValidate = undefined;
|
|
@@ -199,6 +200,25 @@ export class RepositoryImplementation {
|
|
|
199
200
|
}
|
|
200
201
|
}
|
|
201
202
|
}
|
|
203
|
+
liveQuery(options) {
|
|
204
|
+
if (!options)
|
|
205
|
+
options = {};
|
|
206
|
+
return {
|
|
207
|
+
subscribe: (l) => {
|
|
208
|
+
let listener = l;
|
|
209
|
+
if (typeof l === "function") {
|
|
210
|
+
listener = {
|
|
211
|
+
next: l,
|
|
212
|
+
complete: () => { },
|
|
213
|
+
error: () => { }
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
listener.error ??= () => { };
|
|
217
|
+
listener.complete ??= () => { };
|
|
218
|
+
return this.remult.liveQuerySubscriber.subscribe(this, options, listener);
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
}
|
|
202
222
|
async find(options) {
|
|
203
223
|
Remult.onFind(this._info, options);
|
|
204
224
|
if (!options)
|
|
@@ -363,6 +383,7 @@ export class RepositoryImplementation {
|
|
|
363
383
|
where: this.metadata.idMetadata.getIdFilter(id),
|
|
364
384
|
});
|
|
365
385
|
}
|
|
386
|
+
/* @internal*/
|
|
366
387
|
async translateWhereToFilter(where) {
|
|
367
388
|
if (this.metadata.options.backendPrefilter && isBackend()) {
|
|
368
389
|
let z = where;
|
|
@@ -373,7 +394,7 @@ export class RepositoryImplementation {
|
|
|
373
394
|
};
|
|
374
395
|
}
|
|
375
396
|
let r = await Filter.fromEntityFilter(this.metadata, where);
|
|
376
|
-
if (r && !this.dataProvider.
|
|
397
|
+
if (r && !this.dataProvider.supportsrawFilter) {
|
|
377
398
|
r = await Filter.translateCustomWhere(r, this.metadata, this.remult);
|
|
378
399
|
}
|
|
379
400
|
return r;
|
|
@@ -460,6 +481,12 @@ class rowHelperBase {
|
|
|
460
481
|
this._isLoading = false;
|
|
461
482
|
this.lookups = new Map();
|
|
462
483
|
this.originalValues = {};
|
|
484
|
+
{
|
|
485
|
+
let fac = remult;
|
|
486
|
+
if (fac != null && fac.remultFactory) {
|
|
487
|
+
remult = fac.remultFactory();
|
|
488
|
+
}
|
|
489
|
+
}
|
|
463
490
|
for (const col of columnsInfo) {
|
|
464
491
|
let ei = getEntitySettings(col.valueType, false);
|
|
465
492
|
if (ei && remult) {
|
|
@@ -1151,24 +1178,23 @@ export class columnDefsImpl {
|
|
|
1151
1178
|
this.inputType = this.valueConverter.inputType;
|
|
1152
1179
|
this.caption = buildCaption(settings.caption, settings.key, remult);
|
|
1153
1180
|
}
|
|
1154
|
-
getDbName() {
|
|
1155
|
-
|
|
1156
|
-
return this.dbNamePromise;
|
|
1157
|
-
this.dbNamePromise = (async () => {
|
|
1181
|
+
async getDbName() {
|
|
1182
|
+
try {
|
|
1158
1183
|
if (this.settings.sqlExpression) {
|
|
1184
|
+
let result;
|
|
1159
1185
|
if (typeof this.settings.sqlExpression === "function") {
|
|
1160
|
-
|
|
1186
|
+
result = await this.settings.sqlExpression(this.entityDefs);
|
|
1161
1187
|
}
|
|
1162
1188
|
else
|
|
1163
|
-
|
|
1189
|
+
result = this.settings.sqlExpression;
|
|
1190
|
+
if (!result)
|
|
1191
|
+
return this.settings.dbName;
|
|
1192
|
+
return result;
|
|
1164
1193
|
}
|
|
1165
1194
|
return this.settings.dbName;
|
|
1166
|
-
}
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
return this.settings.dbName;
|
|
1170
|
-
});
|
|
1171
|
-
return this.dbNamePromise;
|
|
1195
|
+
}
|
|
1196
|
+
finally {
|
|
1197
|
+
}
|
|
1172
1198
|
}
|
|
1173
1199
|
get dbName() {
|
|
1174
1200
|
let result;
|
|
@@ -1282,7 +1308,7 @@ class EntityFullInfo {
|
|
|
1282
1308
|
if (typeof this.options.sqlExpression === "string")
|
|
1283
1309
|
this.dbNamePromise = Promise.resolve(this.options.sqlExpression);
|
|
1284
1310
|
else if (typeof this.options.sqlExpression === "function") {
|
|
1285
|
-
let r = this.options.sqlExpression(this
|
|
1311
|
+
let r = this.options.sqlExpression(this);
|
|
1286
1312
|
if (r instanceof Promise)
|
|
1287
1313
|
this.dbNamePromise = r;
|
|
1288
1314
|
else if (r)
|
|
@@ -1333,10 +1359,39 @@ export class Fields {
|
|
|
1333
1359
|
static number(...options) {
|
|
1334
1360
|
return Field(() => Number, ...options);
|
|
1335
1361
|
}
|
|
1362
|
+
static createdAt(...options) {
|
|
1363
|
+
return Field(() => Date, {
|
|
1364
|
+
saving: (_, ref) => {
|
|
1365
|
+
if (getEntityRef(_).isNew())
|
|
1366
|
+
ref.value = new Date();
|
|
1367
|
+
}
|
|
1368
|
+
}, ...options);
|
|
1369
|
+
}
|
|
1370
|
+
static updatedAt(...options) {
|
|
1371
|
+
return Field(() => Date, {
|
|
1372
|
+
saving: (_, ref) => {
|
|
1373
|
+
ref.value = new Date();
|
|
1374
|
+
}
|
|
1375
|
+
}, ...options);
|
|
1376
|
+
}
|
|
1336
1377
|
static uuid(...options) {
|
|
1337
1378
|
return Field(() => String, {
|
|
1338
1379
|
allowApiUpdate: false,
|
|
1339
|
-
defaultValue: () => uuid()
|
|
1380
|
+
defaultValue: () => uuid(),
|
|
1381
|
+
saving: (_, r) => {
|
|
1382
|
+
if (!r.value)
|
|
1383
|
+
r.value = uuid();
|
|
1384
|
+
}
|
|
1385
|
+
}, ...options);
|
|
1386
|
+
}
|
|
1387
|
+
static cuid(...options) {
|
|
1388
|
+
return Field(() => String, {
|
|
1389
|
+
allowApiUpdate: false,
|
|
1390
|
+
defaultValue: () => cuid(),
|
|
1391
|
+
saving: (_, r) => {
|
|
1392
|
+
if (!r.value)
|
|
1393
|
+
r.value = cuid();
|
|
1394
|
+
}
|
|
1340
1395
|
}, ...options);
|
|
1341
1396
|
}
|
|
1342
1397
|
static string(...options) {
|
|
@@ -1353,7 +1408,7 @@ export function ValueListFieldType(...options) {
|
|
|
1353
1408
|
return (type) => {
|
|
1354
1409
|
FieldType(o => {
|
|
1355
1410
|
o.valueConverter = ValueListInfo.get(type),
|
|
1356
|
-
o.displayValue = (item, val) => val
|
|
1411
|
+
o.displayValue = (item, val) => val?.caption;
|
|
1357
1412
|
}, ...options)(type);
|
|
1358
1413
|
};
|
|
1359
1414
|
}
|
|
@@ -1613,12 +1668,12 @@ export function decorateColumnSettings(settings, remult) {
|
|
|
1613
1668
|
*/
|
|
1614
1669
|
export function Entity(key, ...options) {
|
|
1615
1670
|
return target => {
|
|
1616
|
-
for (const
|
|
1617
|
-
if (Object.prototype.hasOwnProperty.call(target,
|
|
1618
|
-
const element = target[
|
|
1619
|
-
if (element?.
|
|
1620
|
-
if (!element.
|
|
1621
|
-
element.
|
|
1671
|
+
for (const rawFilterMember in target) {
|
|
1672
|
+
if (Object.prototype.hasOwnProperty.call(target, rawFilterMember)) {
|
|
1673
|
+
const element = target[rawFilterMember];
|
|
1674
|
+
if (element?.rawFilterInfo?.rawFilterTranslator) {
|
|
1675
|
+
if (!element.rawFilterInfo.key)
|
|
1676
|
+
element.rawFilterInfo.key = rawFilterMember;
|
|
1622
1677
|
}
|
|
1623
1678
|
}
|
|
1624
1679
|
}
|
|
@@ -1687,13 +1742,6 @@ class QueryResultImpl {
|
|
|
1687
1742
|
this.options.pageSize = queryConfig.defaultPageSize;
|
|
1688
1743
|
}
|
|
1689
1744
|
}
|
|
1690
|
-
subscribe(onResult) {
|
|
1691
|
-
return this.repo.remult.liveQuerySubscriber.subscribe(this.repo, {
|
|
1692
|
-
load: this.options.load,
|
|
1693
|
-
orderBy: this.options.orderBy,
|
|
1694
|
-
where: this.options.where
|
|
1695
|
-
}, onResult);
|
|
1696
|
-
}
|
|
1697
1745
|
async getPage(page) {
|
|
1698
1746
|
if (page < 1)
|
|
1699
1747
|
page = 1;
|
|
@@ -1813,3 +1861,19 @@ class SubscribableImp {
|
|
|
1813
1861
|
return () => this._subscribers = this._subscribers.filter(x => x != list);
|
|
1814
1862
|
}
|
|
1815
1863
|
}
|
|
1864
|
+
export function getEntityMetadata(entity) {
|
|
1865
|
+
if (entity.metadata)
|
|
1866
|
+
return entity.metadata;
|
|
1867
|
+
const settings = getEntitySettings(entity, false);
|
|
1868
|
+
if (settings) {
|
|
1869
|
+
return RemultProxy.defaultRemult.repo(entity).metadata;
|
|
1870
|
+
}
|
|
1871
|
+
return entity;
|
|
1872
|
+
}
|
|
1873
|
+
export function getRepository(entity) {
|
|
1874
|
+
const settings = getEntitySettings(entity, false);
|
|
1875
|
+
if (settings) {
|
|
1876
|
+
return RemultProxy.defaultRemult.repo(entity);
|
|
1877
|
+
}
|
|
1878
|
+
return entity;
|
|
1879
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { ClassType } from "../../classType";
|
|
2
2
|
import { FieldMetadata } from "../column-interfaces";
|
|
3
3
|
import { Unobserve } from "../context";
|
|
4
|
-
import {
|
|
4
|
+
import { LiveQueryChange, SubscriptionListener, Unsubscribe } from "../live-query/SubscriptionChannel";
|
|
5
|
+
import { EntityOptions } from "../entity";
|
|
5
6
|
import { SortSegment } from "../sort";
|
|
6
7
|
import { entityEventListener } from "../__EntityValueProvider";
|
|
7
8
|
export interface EntityRef<entityType> extends Subscribable {
|
|
@@ -95,18 +96,57 @@ export interface IdMetadata<entityType = any> {
|
|
|
95
96
|
isIdField(col: FieldMetadata): boolean;
|
|
96
97
|
createIdInFilter(items: entityType[]): EntityFilter<entityType>;
|
|
97
98
|
}
|
|
99
|
+
/** Metadata for an `Entity`, this metadata can be used in the user interface to provide a richer UI experience */
|
|
98
100
|
export interface EntityMetadata<entityType = any> {
|
|
99
|
-
|
|
101
|
+
/** The Entity's key also used as it's url */
|
|
100
102
|
readonly key: string;
|
|
103
|
+
/** Metadata for the Entity's fields */
|
|
101
104
|
readonly fields: FieldsMetadata<entityType>;
|
|
105
|
+
/** A human readable caption for the entity. Can be used to achieve a consistent caption for a field throughout the app
|
|
106
|
+
* @example
|
|
107
|
+
* <h1>Create a new item in {taskRepo.metadata.caption}</h1>
|
|
108
|
+
*/
|
|
102
109
|
readonly caption: string;
|
|
110
|
+
/** The options send to the `Entity`'s decorator */
|
|
103
111
|
readonly options: EntityOptions;
|
|
112
|
+
/** The class type of the entity */
|
|
104
113
|
readonly entityType: ClassType<entityType>;
|
|
114
|
+
/** true if the current user is allowed to update an entity instance
|
|
115
|
+
* @example
|
|
116
|
+
* const taskRepo = remult.repo(Task);
|
|
117
|
+
* if (taskRepo.metadata.apiUpdateAllowed){
|
|
118
|
+
* //Allow user to edit the entity
|
|
119
|
+
* }
|
|
120
|
+
*/
|
|
105
121
|
readonly apiUpdateAllowed: boolean;
|
|
122
|
+
/** true if the current user is allowed to read from entity
|
|
123
|
+
* @example
|
|
124
|
+
* const taskRepo = remult.repo(Task);
|
|
125
|
+
* if (taskRepo.metadata.apiReadAllowed){
|
|
126
|
+
* taskRepo.find()
|
|
127
|
+
* }
|
|
128
|
+
*/
|
|
106
129
|
readonly apiReadAllowed: boolean;
|
|
130
|
+
/** true if the current user is allowed to delete an entity instance
|
|
131
|
+
* @example
|
|
132
|
+
* const taskRepo = remult.repo(Task);
|
|
133
|
+
* if (taskRepo.metadata.apiDeleteAllowed){
|
|
134
|
+
* //display delete button
|
|
135
|
+
* }
|
|
136
|
+
*/
|
|
107
137
|
readonly apiDeleteAllowed: boolean;
|
|
138
|
+
/** true if the current user is allowed to create an entity instance
|
|
139
|
+
* @example
|
|
140
|
+
* const taskRepo = remult.repo(Task);
|
|
141
|
+
* if (taskRepo.metadata.apiInsertAllowed){
|
|
142
|
+
* //display insert button
|
|
143
|
+
* }
|
|
144
|
+
*/
|
|
108
145
|
readonly apiInsertAllowed: boolean;
|
|
146
|
+
/** Returns the dbName - based on it's `dbName` option and it's `sqlExpression` option */
|
|
109
147
|
getDbName(): Promise<string>;
|
|
148
|
+
/** Metadata for the Entity's id */
|
|
149
|
+
readonly idMetadata: IdMetadata<entityType>;
|
|
110
150
|
}
|
|
111
151
|
export declare type OmitEB<T> = Omit<T, keyof import('./RepositoryImplementation').EntityBase>;
|
|
112
152
|
export declare type idType<entityType> = entityType extends {
|
|
@@ -118,6 +158,8 @@ export declare type idType<entityType> = entityType extends {
|
|
|
118
158
|
export interface Repository<entityType> {
|
|
119
159
|
/** returns a result array based on the provided options */
|
|
120
160
|
find(options?: FindOptions<entityType>): Promise<entityType[]>;
|
|
161
|
+
/** returns a result array based on the provided options */
|
|
162
|
+
liveQuery(options?: FindOptions<entityType>): LiveQuery<entityType>;
|
|
121
163
|
/** returns the first item that matchers the `where` condition
|
|
122
164
|
* @example
|
|
123
165
|
* await taskRepo.findFirst({ completed:false })
|
|
@@ -131,7 +173,7 @@ export interface Repository<entityType> {
|
|
|
131
173
|
*
|
|
132
174
|
* It also has it's own paging mechanism that can be used n paging scenarios.
|
|
133
175
|
*
|
|
134
|
-
* The `query` method doesn't return an array (as the `find` method) and instead returns an `
|
|
176
|
+
* The `query` method doesn't return an array (as the `find` method) and instead returns an `iterable` `QueryResult` object
|
|
135
177
|
* which supports iterations using the JavaScript `for await` statement.
|
|
136
178
|
* @example
|
|
137
179
|
* for await (const task of taskRepo.query()) {
|
|
@@ -181,10 +223,21 @@ export interface Repository<entityType> {
|
|
|
181
223
|
fromJson(x: any, isNew?: boolean): Promise<entityType>;
|
|
182
224
|
/** returns an `entityRef` for an item returned by `create`, `find` etc... */
|
|
183
225
|
getEntityRef(item: entityType): EntityRef<entityType>;
|
|
184
|
-
/**The metadata for the `entity`
|
|
226
|
+
/**The metadata for the `entity`
|
|
227
|
+
* @See [EntityMetadata](https://remult.dev/docs/ref_entitymetadata.html)
|
|
228
|
+
*/
|
|
185
229
|
metadata: EntityMetadata<entityType>;
|
|
186
230
|
addEventListener(listener: entityEventListener<entityType>): Unobserve;
|
|
187
231
|
}
|
|
232
|
+
export interface LiveQuery<entityType> {
|
|
233
|
+
subscribe(next: (info: LiveQueryChangeInfo<entityType>) => void): Unsubscribe;
|
|
234
|
+
subscribe(listener: Partial<SubscriptionListener<LiveQueryChangeInfo<entityType>>>): Unsubscribe;
|
|
235
|
+
}
|
|
236
|
+
export interface LiveQueryChangeInfo<entityType> {
|
|
237
|
+
items: entityType[];
|
|
238
|
+
changes: LiveQueryChange[];
|
|
239
|
+
applyChanges(prevState: entityType[]): entityType[];
|
|
240
|
+
}
|
|
188
241
|
export interface FindOptions<entityType> extends FindOptionsBase<entityType> {
|
|
189
242
|
/** Determines the number of rows returned by the request, on the browser the default is 100 rows
|
|
190
243
|
* @example
|
|
@@ -226,6 +279,8 @@ export declare type EntityFilter<entityType> = {
|
|
|
226
279
|
export declare type ValueFilter<valueType> = valueType | valueType[] | {
|
|
227
280
|
$ne?: valueType | valueType[];
|
|
228
281
|
"!="?: valueType | valueType[];
|
|
282
|
+
$in?: valueType[];
|
|
283
|
+
$nin?: valueType[];
|
|
229
284
|
};
|
|
230
285
|
export declare type ComparisonValueFilter<valueType> = ValueFilter<valueType> & {
|
|
231
286
|
$gt?: valueType;
|
|
@@ -300,8 +355,6 @@ export interface QueryResult<entityType> {
|
|
|
300
355
|
getPage(pageNumber?: number): Promise<entityType[]>;
|
|
301
356
|
/** Performs an operation on all the items matching the query criteria */
|
|
302
357
|
forEach(what: (item: entityType) => Promise<any>): Promise<number>;
|
|
303
|
-
/**@Experimental */
|
|
304
|
-
subscribe(onResult: (reducer: (prevState: entityType[]) => entityType[]) => void): VoidFunction;
|
|
305
358
|
}
|
|
306
359
|
/** An interface used to paginating using the `query` method in the `Repository` object */
|
|
307
360
|
export interface Paginator<entityType> {
|
|
@@ -21,7 +21,7 @@ export declare abstract class Action<inParam, outParam> implements ActionInterfa
|
|
|
21
21
|
}
|
|
22
22
|
export declare class ForbiddenError extends Error {
|
|
23
23
|
constructor();
|
|
24
|
-
isForbiddenError:
|
|
24
|
+
isForbiddenError: true;
|
|
25
25
|
}
|
|
26
26
|
export declare class myServerAction extends Action<inArgs, result> {
|
|
27
27
|
private types;
|
|
@@ -6,7 +6,7 @@ import { CategoriesForTesting } from "../tests/remult-3-entities";
|
|
|
6
6
|
import { Status } from "../tests/testModel/models";
|
|
7
7
|
export declare function itWithFocus(key: string, what: () => Promise<void>, focus?: boolean): void;
|
|
8
8
|
export declare function testAll(key: string, what: dbTestWhatSignature, focus?: boolean): void;
|
|
9
|
-
export declare function addDatabaseToTest(tester: dbTestMethodSignature):
|
|
9
|
+
export declare function addDatabaseToTest(tester: dbTestMethodSignature): dbTestMethodSignature;
|
|
10
10
|
export declare function testInMemory(key: string, what: dbTestWhatSignature, focus?: boolean): void;
|
|
11
11
|
export declare type dbTestWhatSignature = (db: {
|
|
12
12
|
db: DataProvider;
|
|
@@ -2,8 +2,9 @@ import { Remult } from "../context";
|
|
|
2
2
|
import { InMemoryDataProvider } from "../data-providers/in-memory-database";
|
|
3
3
|
import { Categories } from "../tests/remult-3-entities";
|
|
4
4
|
export function itWithFocus(key, what, focus = false) {
|
|
5
|
-
if (focus)
|
|
5
|
+
if (focus) {
|
|
6
6
|
fit(key, what);
|
|
7
|
+
}
|
|
7
8
|
else
|
|
8
9
|
it(key, what);
|
|
9
10
|
}
|
|
@@ -22,12 +23,12 @@ export function addDatabaseToTest(tester) {
|
|
|
22
23
|
test(tester);
|
|
23
24
|
}
|
|
24
25
|
databasesTesters.push(tester);
|
|
26
|
+
return tester;
|
|
25
27
|
}
|
|
26
28
|
export function testInMemory(key, what, focus = false) {
|
|
27
29
|
itWithFocus(key + " - in memory", async () => {
|
|
28
|
-
let remult = new Remult();
|
|
29
30
|
let db = new InMemoryDataProvider();
|
|
30
|
-
remult
|
|
31
|
+
let remult = new Remult(db);
|
|
31
32
|
await what({ db, remult, createEntity: async (x) => remult.repo(x) });
|
|
32
33
|
}, focus);
|
|
33
34
|
}
|