remult 0.17.3 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SseSubscriptionServer.d.ts +23 -0
- package/SseSubscriptionServer.js +143 -0
- package/ably.d.ts +13 -0
- package/ably.js +59 -0
- package/esm/SseSubscriptionServer.d.ts +23 -0
- package/esm/SseSubscriptionServer.js +102 -0
- package/esm/ably.d.ts +13 -0
- package/esm/ably.js +28 -0
- package/esm/index.d.ts +7 -4
- package/esm/index.js +4 -2
- package/esm/live-query/data-provider-live-query-storage.d.ts +23 -0
- package/esm/live-query/data-provider-live-query-storage.js +78 -0
- package/esm/myLog.d.ts +1 -0
- package/esm/myLog.js +5 -0
- package/esm/postgres/postgres-data-provider.d.ts +4 -2
- package/esm/postgres/postgres-data-provider.js +18 -6
- package/esm/postgres/schema-builder.d.ts +2 -1
- package/esm/postgres/schema-builder.js +49 -29
- package/esm/remult-knex/index.d.ts +10 -32
- package/esm/remult-knex/index.js +75 -51
- 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/core.d.ts +2 -0
- package/esm/server/core.js +2 -0
- package/esm/server/expressBridge.d.ts +19 -7
- package/esm/server/expressBridge.js +135 -31
- package/esm/server/index.d.ts +2 -1
- package/esm/server/index.js +2 -1
- package/esm/server/initDataProvider.d.ts +2 -0
- package/esm/server/initDataProvider.js +14 -0
- package/esm/src/buildRestDataProvider.d.ts +31 -0
- package/esm/src/buildRestDataProvider.js +110 -0
- package/esm/src/column-interfaces.d.ts +44 -8
- package/esm/src/column.d.ts +3 -3
- package/esm/src/context.d.ts +39 -36
- package/esm/src/context.js +87 -117
- package/esm/src/data-api.d.ts +9 -3
- package/esm/src/data-api.js +130 -63
- package/esm/src/data-interfaces.d.ts +5 -1
- package/esm/src/data-providers/array-entity-data-provider.d.ts +1 -1
- package/esm/src/data-providers/array-entity-data-provider.js +2 -17
- package/esm/src/data-providers/rest-data-provider.d.ts +4 -3
- package/esm/src/data-providers/rest-data-provider.js +64 -23
- package/esm/src/data-providers/sql-database.d.ts +7 -3
- package/esm/src/data-providers/sql-database.js +60 -17
- package/esm/src/data-providers/web-sql-data-provider.d.ts +3 -0
- package/esm/src/data-providers/web-sql-data-provider.js +23 -9
- 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 -0
- package/esm/src/live-query/LiveQueryClient.js +184 -0
- package/esm/src/live-query/SseSubscriptionClient.d.ts +5 -0
- package/esm/src/live-query/SseSubscriptionClient.js +86 -0
- package/esm/src/live-query/SubscriptionChannel.d.ts +41 -0
- package/esm/src/live-query/SubscriptionChannel.js +121 -0
- package/esm/src/live-query/SubscriptionServer.d.ts +38 -0
- package/esm/src/live-query/SubscriptionServer.js +108 -0
- package/esm/src/remult-proxy.d.ts +1 -1
- package/esm/src/remult-proxy.js +52 -7
- package/esm/src/remult3/RepositoryImplementation.d.ts +14 -5
- package/esm/src/remult3/RepositoryImplementation.js +124 -44
- package/esm/src/remult3/getId.d.ts +2 -0
- package/esm/src/remult3/getId.js +7 -0
- package/esm/src/remult3/remult3.d.ts +67 -10
- package/esm/src/remult3/remult3.js +1 -0
- package/esm/src/server-action.d.ts +5 -1
- package/esm/src/server-action.js +11 -12
- package/esm/src/shared-tests/db-tests-setup.d.ts +12 -5
- package/esm/src/shared-tests/db-tests-setup.js +14 -6
- package/esm/src/shared-tests/db-tests.db-tests.js +71 -10
- package/esm/src/sort.d.ts +1 -0
- package/esm/src/sort.js +18 -0
- package/esm/src/sql-command.d.ts +5 -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 +3 -4
- package/esm/src/tests/testHelper.spec.d.ts +3 -1
- package/esm/src/tests/testHelper.spec.js +63 -60
- package/esm/src/valueConverters.js +15 -7
- package/index.d.ts +7 -4
- package/index.js +7 -3
- package/live-query/data-provider-live-query-storage.d.ts +23 -0
- package/live-query/data-provider-live-query-storage.js +236 -0
- package/myLog.d.ts +1 -0
- package/myLog.js +20 -0
- package/package.json +18 -1
- package/postgres/postgres-data-provider.d.ts +4 -2
- package/postgres/postgres-data-provider.js +54 -39
- package/postgres/schema-builder.d.ts +2 -1
- package/postgres/schema-builder.js +118 -70
- package/remult-express.js +1 -1
- package/remult-knex/index.d.ts +10 -32
- package/remult-knex/index.js +134 -87
- 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/core.d.ts +2 -0
- package/server/core.js +5 -1
- package/server/expressBridge.d.ts +19 -7
- package/server/expressBridge.js +283 -69
- package/server/index.d.ts +2 -1
- package/server/index.js +3 -1
- package/server/initDataProvider.d.ts +2 -0
- package/server/initDataProvider.js +26 -0
- package/src/buildRestDataProvider.d.ts +31 -0
- package/src/buildRestDataProvider.js +162 -0
- package/src/column-interfaces.d.ts +44 -8
- package/src/column.d.ts +3 -3
- package/src/context.d.ts +39 -36
- package/src/context.js +171 -166
- package/src/data-api.d.ts +9 -3
- package/src/data-api.js +233 -88
- package/src/data-interfaces.d.ts +5 -1
- package/src/data-providers/array-entity-data-provider.d.ts +1 -1
- package/src/data-providers/array-entity-data-provider.js +2 -17
- package/src/data-providers/rest-data-provider.d.ts +4 -3
- package/src/data-providers/rest-data-provider.js +105 -39
- package/src/data-providers/sql-database.d.ts +7 -3
- package/src/data-providers/sql-database.js +89 -16
- package/src/data-providers/web-sql-data-provider.d.ts +3 -0
- package/src/data-providers/web-sql-data-provider.js +56 -11
- 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 -0
- package/src/live-query/LiveQueryClient.js +283 -0
- package/src/live-query/SseSubscriptionClient.d.ts +5 -0
- package/src/live-query/SseSubscriptionClient.js +150 -0
- package/src/live-query/SubscriptionChannel.d.ts +41 -0
- package/src/live-query/SubscriptionChannel.js +209 -0
- package/src/live-query/SubscriptionServer.d.ts +38 -0
- package/src/live-query/SubscriptionServer.js +258 -0
- package/src/remult-proxy.d.ts +1 -1
- package/src/remult-proxy.js +153 -8
- package/src/remult3/RepositoryImplementation.d.ts +14 -5
- package/src/remult3/RepositoryImplementation.js +200 -85
- package/src/remult3/getId.d.ts +2 -0
- package/src/remult3/getId.js +11 -0
- package/src/remult3/remult3.d.ts +67 -10
- package/src/remult3/remult3.js +1 -0
- package/src/server-action.d.ts +5 -1
- package/src/server-action.js +11 -11
- package/src/shared-tests/db-tests-setup.d.ts +12 -5
- package/src/shared-tests/db-tests-setup.js +17 -8
- package/src/shared-tests/db-tests.db-tests.js +140 -8
- package/src/sort.d.ts +1 -0
- package/src/sort.js +18 -0
- package/src/sql-command.d.ts +5 -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 +5 -6
- package/src/tests/testHelper.spec.d.ts +3 -1
- package/src/tests/testHelper.spec.js +104 -100
- package/src/valueConverters.js +15 -7
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { buildRestDataProvider } from "../buildRestDataProvider";
|
|
2
|
+
import { remult } from "../remult-proxy";
|
|
3
|
+
import { actionInfo } from "../server-action";
|
|
4
|
+
import { streamUrl } from "./SubscriptionChannel";
|
|
5
|
+
export class SseSubscriptionClient {
|
|
6
|
+
openConnection(onReconnect) {
|
|
7
|
+
let connectionId;
|
|
8
|
+
const channels = new Map();
|
|
9
|
+
const provider = buildRestDataProvider(remult.apiClient.httpClient);
|
|
10
|
+
let connected = false;
|
|
11
|
+
let source;
|
|
12
|
+
const client = {
|
|
13
|
+
close() {
|
|
14
|
+
source.close();
|
|
15
|
+
},
|
|
16
|
+
async subscribe(channel, handler) {
|
|
17
|
+
let listeners = channels.get(channel);
|
|
18
|
+
if (!listeners) {
|
|
19
|
+
channels.set(channel, listeners = []);
|
|
20
|
+
await subscribeToChannel(channel);
|
|
21
|
+
}
|
|
22
|
+
listeners.push(handler);
|
|
23
|
+
return () => {
|
|
24
|
+
listeners.splice(listeners.indexOf(handler, 1));
|
|
25
|
+
if (listeners.length == 0) {
|
|
26
|
+
actionInfo.runActionWithoutBlockingUI(() => provider.post(remult.apiClient.url + '/' + streamUrl + '/unsubscribe', {
|
|
27
|
+
channel: channel,
|
|
28
|
+
clientId: connectionId
|
|
29
|
+
}));
|
|
30
|
+
channels.delete(channel);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
const createConnectionPromise = () => new Promise((res) => {
|
|
36
|
+
createConnection();
|
|
37
|
+
function createConnection() {
|
|
38
|
+
if (source)
|
|
39
|
+
source.close();
|
|
40
|
+
source = new EventSource(remult.apiClient.url + '/' + streamUrl, {
|
|
41
|
+
withCredentials: true
|
|
42
|
+
});
|
|
43
|
+
source.onmessage = e => {
|
|
44
|
+
let message = JSON.parse(e.data);
|
|
45
|
+
const listeners = channels.get(message.channel);
|
|
46
|
+
if (listeners)
|
|
47
|
+
listeners.forEach(x => x(message.data));
|
|
48
|
+
};
|
|
49
|
+
source.onerror = e => {
|
|
50
|
+
console.error("Live Query Event Source Error", e);
|
|
51
|
+
source.close();
|
|
52
|
+
setTimeout(() => {
|
|
53
|
+
createConnection();
|
|
54
|
+
}, 500);
|
|
55
|
+
};
|
|
56
|
+
source.addEventListener("connectionId", async (e) => {
|
|
57
|
+
//@ts-ignore
|
|
58
|
+
connectionId = e.data;
|
|
59
|
+
if (connected) {
|
|
60
|
+
for (const channel of channels.keys()) {
|
|
61
|
+
await subscribeToChannel(channel);
|
|
62
|
+
}
|
|
63
|
+
onReconnect();
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
connected = true;
|
|
67
|
+
res(client);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
return createConnectionPromise();
|
|
73
|
+
async function subscribeToChannel(channel) {
|
|
74
|
+
const result = await actionInfo.runActionWithoutBlockingUI(() => {
|
|
75
|
+
return provider.post(remult.apiClient.url + '/' + streamUrl + '/subscribe', {
|
|
76
|
+
channel: channel,
|
|
77
|
+
clientId: connectionId
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
if (result === ConnectionNotFoundError) {
|
|
81
|
+
await createConnectionPromise();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export const ConnectionNotFoundError = "client connection not found";
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Remult } from '../../index';
|
|
2
|
+
export declare const streamUrl = "stream";
|
|
3
|
+
export interface SubscriptionListener<type> {
|
|
4
|
+
next(message: type): void;
|
|
5
|
+
error(err: any): void;
|
|
6
|
+
complete(): void;
|
|
7
|
+
}
|
|
8
|
+
export declare type Unsubscribe = VoidFunction;
|
|
9
|
+
export interface SubscriptionClientConnection {
|
|
10
|
+
subscribe(channel: string, onMessage: (message: any) => void, onError: (err: any) => void): Promise<Unsubscribe>;
|
|
11
|
+
close(): void;
|
|
12
|
+
}
|
|
13
|
+
export interface SubscriptionClient {
|
|
14
|
+
openConnection(onReconnect: VoidFunction): Promise<SubscriptionClientConnection>;
|
|
15
|
+
}
|
|
16
|
+
export declare const liveQueryKeepAliveRoute = "_liveQueryKeepAlive";
|
|
17
|
+
export declare type LiveQueryChange = {
|
|
18
|
+
type: "all";
|
|
19
|
+
data: any[];
|
|
20
|
+
} | {
|
|
21
|
+
type: "add";
|
|
22
|
+
data: any;
|
|
23
|
+
} | {
|
|
24
|
+
type: 'replace';
|
|
25
|
+
data: {
|
|
26
|
+
oldId: any;
|
|
27
|
+
item: any;
|
|
28
|
+
};
|
|
29
|
+
} | {
|
|
30
|
+
type: "remove";
|
|
31
|
+
data: {
|
|
32
|
+
id: any;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
export declare class SubscriptionChannel<messageType> {
|
|
36
|
+
channelKey: string;
|
|
37
|
+
constructor(channelKey: string);
|
|
38
|
+
publish(message: messageType, remult?: Remult): void;
|
|
39
|
+
subscribe(next: (message: messageType) => void, remult?: Remult): any;
|
|
40
|
+
subscribe(listener: Partial<SubscriptionListener<messageType>>): any;
|
|
41
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { remult as defaultRemult, Sort } from '../../index';
|
|
2
|
+
import { getId } from '../remult3/getId';
|
|
3
|
+
import { v4 as uuid } from 'uuid';
|
|
4
|
+
import { getLiveQueryChannel } from '../data-api';
|
|
5
|
+
export const streamUrl = 'stream';
|
|
6
|
+
//@internal
|
|
7
|
+
export class LiveQuerySubscriber {
|
|
8
|
+
constructor(repo, query, userId) {
|
|
9
|
+
this.repo = repo;
|
|
10
|
+
this.query = query;
|
|
11
|
+
this.unsubscribe = () => { };
|
|
12
|
+
this.defaultQueryState = [];
|
|
13
|
+
this.listeners = [];
|
|
14
|
+
this.id = uuid();
|
|
15
|
+
this.queryChannel = getLiveQueryChannel(this.id, userId);
|
|
16
|
+
}
|
|
17
|
+
sendDefaultState(onResult) {
|
|
18
|
+
onResult(this.createReducerType(() => [...this.defaultQueryState], this.allItemsMessage(this.defaultQueryState)));
|
|
19
|
+
}
|
|
20
|
+
async setAllItems(result) {
|
|
21
|
+
const items = await Promise.all(result.map(item => this.repo.fromJson(item)));
|
|
22
|
+
this.forListeners(listener => {
|
|
23
|
+
listener(x => {
|
|
24
|
+
return items;
|
|
25
|
+
});
|
|
26
|
+
}, this.allItemsMessage(items));
|
|
27
|
+
}
|
|
28
|
+
allItemsMessage(items) {
|
|
29
|
+
return [
|
|
30
|
+
{
|
|
31
|
+
type: "all",
|
|
32
|
+
data: items
|
|
33
|
+
}
|
|
34
|
+
];
|
|
35
|
+
}
|
|
36
|
+
forListeners(what, changes) {
|
|
37
|
+
what(reducer => {
|
|
38
|
+
this.defaultQueryState = reducer(this.defaultQueryState);
|
|
39
|
+
if (changes.find(c => c.type === "add" || c.type === "replace")) {
|
|
40
|
+
if (this.query.orderBy) {
|
|
41
|
+
const o = Sort.translateOrderByToSort(this.repo.metadata, this.query.orderBy);
|
|
42
|
+
this.defaultQueryState.sort((a, b) => o.compare(a, b));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
for (const l of this.listeners) {
|
|
47
|
+
what(reducer => {
|
|
48
|
+
l.next(this.createReducerType(reducer, changes));
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
createReducerType(applyChanges, changes) {
|
|
53
|
+
return {
|
|
54
|
+
applyChanges,
|
|
55
|
+
changes,
|
|
56
|
+
items: this.defaultQueryState
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
async handle(messages) {
|
|
60
|
+
for (const m of messages) {
|
|
61
|
+
switch (m.type) {
|
|
62
|
+
case "add":
|
|
63
|
+
case "replace":
|
|
64
|
+
m.data.item = await this.repo.fromJson(m.data.item);
|
|
65
|
+
break;
|
|
66
|
+
case "all":
|
|
67
|
+
this.setAllItems(m.data);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
this.forListeners(listener => {
|
|
71
|
+
listener(items => {
|
|
72
|
+
if (!items)
|
|
73
|
+
items = [];
|
|
74
|
+
for (const message of messages) {
|
|
75
|
+
switch (message.type) {
|
|
76
|
+
case "all":
|
|
77
|
+
this.setAllItems(message.data);
|
|
78
|
+
break;
|
|
79
|
+
case "replace": {
|
|
80
|
+
items = items.map(x => getId(this.repo.metadata, x) === message.data.oldId ? message.data.item : x);
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
case "add":
|
|
84
|
+
items = items.filter(x => getId(this.repo.metadata, x) !== getId(this.repo.metadata, message.data.item));
|
|
85
|
+
items.push(message.data.item);
|
|
86
|
+
break;
|
|
87
|
+
case "remove":
|
|
88
|
+
items = items.filter(x => getId(this.repo.metadata, x) !== message.data.id);
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
;
|
|
92
|
+
}
|
|
93
|
+
return items;
|
|
94
|
+
});
|
|
95
|
+
}, messages);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
export const liveQueryKeepAliveRoute = '_liveQueryKeepAlive';
|
|
99
|
+
export class SubscriptionChannel {
|
|
100
|
+
constructor(channelKey) {
|
|
101
|
+
this.channelKey = channelKey;
|
|
102
|
+
}
|
|
103
|
+
publish(message, remult) {
|
|
104
|
+
remult = remult || defaultRemult;
|
|
105
|
+
remult.subscriptionServer.publishMessage(this.channelKey, message);
|
|
106
|
+
}
|
|
107
|
+
//@internal
|
|
108
|
+
subscribe(next, remult) {
|
|
109
|
+
remult = remult || defaultRemult;
|
|
110
|
+
let listener = next;
|
|
111
|
+
if (typeof (next) === "function") {
|
|
112
|
+
listener = {
|
|
113
|
+
next
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
listener.error ??= () => { };
|
|
117
|
+
listener.complete ??= () => { };
|
|
118
|
+
return remult.liveQuerySubscriber.subscribeChannel(this.channelKey, listener);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
//TODO2 - consider moving the queued job mechanism into this.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export interface SubscriptionServer {
|
|
2
|
+
publishMessage<T>(channel: string, message: T): Promise<void>;
|
|
3
|
+
}
|
|
4
|
+
export interface LiveQueryStorage {
|
|
5
|
+
add(query: StoredQuery): Promise<void>;
|
|
6
|
+
remove(queryId: string): Promise<void>;
|
|
7
|
+
forEach(entityKey: string, callback: (args: {
|
|
8
|
+
query: StoredQuery;
|
|
9
|
+
setData(data: any): Promise<void>;
|
|
10
|
+
}) => Promise<void>): Promise<void>;
|
|
11
|
+
keepAliveAndReturnUnknownQueryIds(queryIds: string[]): Promise<string[]>;
|
|
12
|
+
}
|
|
13
|
+
export declare class InMemoryLiveQueryStorage implements LiveQueryStorage {
|
|
14
|
+
debugFileSaver: (x: any) => void;
|
|
15
|
+
debug(): void;
|
|
16
|
+
keepAliveAndReturnUnknownQueryIds(ids: string[]): Promise<string[]>;
|
|
17
|
+
queries: (StoredQuery & {
|
|
18
|
+
lastUsed: string;
|
|
19
|
+
})[];
|
|
20
|
+
constructor();
|
|
21
|
+
add(query: StoredQuery): Promise<void>;
|
|
22
|
+
removeCountForTesting: number;
|
|
23
|
+
remove(id: any): Promise<void>;
|
|
24
|
+
forEach(entityKey: string, handle: (args: {
|
|
25
|
+
query: StoredQuery;
|
|
26
|
+
setData(data: any): Promise<void>;
|
|
27
|
+
}) => Promise<void>): Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
export interface StoredQuery {
|
|
30
|
+
entityKey: string;
|
|
31
|
+
id: string;
|
|
32
|
+
data: any;
|
|
33
|
+
}
|
|
34
|
+
export interface QueryData {
|
|
35
|
+
findOptionsJson: any;
|
|
36
|
+
requestJson: any;
|
|
37
|
+
lastIds: any[];
|
|
38
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { findOptionsFromJson } from '../data-providers/rest-data-provider';
|
|
2
|
+
/* @internal*/
|
|
3
|
+
export class LiveQueryPublisher {
|
|
4
|
+
constructor(subscriptionServer, liveQueryStorage, performWithRequest) {
|
|
5
|
+
this.subscriptionServer = subscriptionServer;
|
|
6
|
+
this.liveQueryStorage = liveQueryStorage;
|
|
7
|
+
this.performWithRequest = performWithRequest;
|
|
8
|
+
this.debugFileSaver = (x) => { };
|
|
9
|
+
}
|
|
10
|
+
runPromise(p) { }
|
|
11
|
+
async itemChanged(entityKey, changes) {
|
|
12
|
+
//TODO 2 - optimize so that the user will get their messages first. Based on user id
|
|
13
|
+
await (this.liveQueryStorage().forEach(entityKey, async ({ query: q, setData }) => {
|
|
14
|
+
let query = { ...q.data };
|
|
15
|
+
await this.performWithRequest(query.requestJson, entityKey, async (repo) => {
|
|
16
|
+
const messages = [];
|
|
17
|
+
const currentItems = await repo.find(findOptionsFromJson(query.findOptionsJson, repo.metadata));
|
|
18
|
+
const currentIds = currentItems.map(x => repo.getEntityRef(x).getId());
|
|
19
|
+
for (const id of query.lastIds.filter(y => !currentIds.includes(y))) {
|
|
20
|
+
let c = changes.find(c => c.oldId == id);
|
|
21
|
+
if (c === undefined || id != c.oldId || !currentIds.includes(c.id))
|
|
22
|
+
messages.push({
|
|
23
|
+
type: "remove",
|
|
24
|
+
data: {
|
|
25
|
+
id: id
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
for (const item of currentItems) {
|
|
30
|
+
const itemRef = repo.getEntityRef(item);
|
|
31
|
+
let c = changes.find(c => c.id == itemRef.getId());
|
|
32
|
+
if (c !== undefined && query.lastIds.includes(c.oldId)) {
|
|
33
|
+
messages.push({
|
|
34
|
+
type: "replace",
|
|
35
|
+
data: {
|
|
36
|
+
oldId: c.oldId,
|
|
37
|
+
item: itemRef.toApiJson()
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
else if (!query.lastIds.includes(itemRef.getId())) {
|
|
42
|
+
messages.push({
|
|
43
|
+
type: "add",
|
|
44
|
+
data: { item: itemRef.toApiJson() }
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
this.debugFileSaver({
|
|
49
|
+
query: q.id,
|
|
50
|
+
currentIds,
|
|
51
|
+
changes,
|
|
52
|
+
lastIds: query.lastIds,
|
|
53
|
+
messages
|
|
54
|
+
});
|
|
55
|
+
query.lastIds = currentIds;
|
|
56
|
+
await setData(query);
|
|
57
|
+
if (messages.length > 0)
|
|
58
|
+
this.subscriptionServer().publishMessage(q.id, messages);
|
|
59
|
+
});
|
|
60
|
+
}));
|
|
61
|
+
}
|
|
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
|
+
async add(query) {
|
|
86
|
+
this.queries.push({ ...query, lastUsed: new Date().toISOString() });
|
|
87
|
+
this.debug();
|
|
88
|
+
}
|
|
89
|
+
async 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
|
+
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Remult } from "./context";
|
|
1
|
+
import type { Remult } from "./context";
|
|
2
2
|
export declare const remult: Remult;
|
package/esm/src/remult-proxy.js
CHANGED
|
@@ -1,12 +1,51 @@
|
|
|
1
|
-
import { Remult } from "./context";
|
|
2
|
-
import { RepositoryImplementation } from "./remult3";
|
|
3
|
-
let defaultRemult = new Remult();
|
|
4
1
|
/*@internal*/
|
|
5
2
|
export class RemultProxy {
|
|
6
3
|
constructor() {
|
|
7
4
|
/*@internal*/
|
|
8
|
-
this.remultFactory = () => defaultRemult;
|
|
9
|
-
|
|
5
|
+
this.remultFactory = () => RemultProxy.defaultRemult;
|
|
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
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/* @internal*/
|
|
31
|
+
get liveQuerySubscriber() { return this.remultFactory().liveQuerySubscriber; }
|
|
32
|
+
;
|
|
33
|
+
/* @internal*/
|
|
34
|
+
set liveQuerySubscriber(val) { this.remultFactory().liveQuerySubscriber = val; }
|
|
35
|
+
;
|
|
36
|
+
/* @internal*/
|
|
37
|
+
get liveQueryStorage() { return this.remultFactory().liveQueryStorage; }
|
|
38
|
+
;
|
|
39
|
+
/* @internal*/
|
|
40
|
+
set liveQueryStorage(val) { this.remultFactory().liveQueryStorage = val; }
|
|
41
|
+
;
|
|
42
|
+
/* @internal*/
|
|
43
|
+
get liveQueryPublisher() {
|
|
44
|
+
return this.remultFactory().liveQueryPublisher;
|
|
45
|
+
}
|
|
46
|
+
/* @internal*/
|
|
47
|
+
set liveQueryPublisher(val) {
|
|
48
|
+
this.remultFactory().liveQueryPublisher = val;
|
|
10
49
|
}
|
|
11
50
|
call(backendMethod, self, ...args) {
|
|
12
51
|
return this.remultFactory().call(backendMethod, self, ...args);
|
|
@@ -17,6 +56,7 @@ export class RemultProxy {
|
|
|
17
56
|
get dataProvider() { return this.remultFactory().dataProvider; }
|
|
18
57
|
;
|
|
19
58
|
set dataProvider(provider) { this.remultFactory().dataProvider = provider; }
|
|
59
|
+
/*@internal*/
|
|
20
60
|
get repCache() { return this.remultFactory().repCache; }
|
|
21
61
|
;
|
|
22
62
|
authenticated() {
|
|
@@ -33,7 +73,7 @@ export class RemultProxy {
|
|
|
33
73
|
}
|
|
34
74
|
/*@internal*/
|
|
35
75
|
resetFactory() {
|
|
36
|
-
this.remultFactory = () => defaultRemult;
|
|
76
|
+
this.remultFactory = () => RemultProxy.defaultRemult;
|
|
37
77
|
}
|
|
38
78
|
get user() {
|
|
39
79
|
return this.remultFactory().user;
|
|
@@ -47,6 +87,11 @@ export class RemultProxy {
|
|
|
47
87
|
set apiClient(client) {
|
|
48
88
|
this.remultFactory().apiClient = client;
|
|
49
89
|
}
|
|
90
|
+
get subscriptionServer() {
|
|
91
|
+
return this.remultFactory().subscriptionServer;
|
|
92
|
+
}
|
|
93
|
+
set subscriptionServer(value) {
|
|
94
|
+
this.remultFactory().subscriptionServer = value;
|
|
95
|
+
}
|
|
50
96
|
}
|
|
51
97
|
export const remult = new RemultProxy();
|
|
52
|
-
RepositoryImplementation.defaultRemult = remult;
|
|
@@ -5,13 +5,12 @@ import { EntityMetadata, FieldRef, FieldsRef, EntityFilter, FindOptions, Reposit
|
|
|
5
5
|
import { ClassType } from "../../classType";
|
|
6
6
|
import { Remult, Unobserve } from "../context";
|
|
7
7
|
import { entityEventListener } from "../__EntityValueProvider";
|
|
8
|
-
import { DataProvider, EntityDataProvider } from "../data-interfaces";
|
|
8
|
+
import { DataProvider, EntityDataProvider, EntityDataProviderFindOptions } from "../data-interfaces";
|
|
9
9
|
import { RefSubscriber } from ".";
|
|
10
10
|
export declare class RepositoryImplementation<entityType> implements Repository<entityType> {
|
|
11
11
|
private entity;
|
|
12
|
-
|
|
12
|
+
remult: Remult;
|
|
13
13
|
private dataProvider;
|
|
14
|
-
static defaultRemult: Remult;
|
|
15
14
|
createAfterFilter(orderBy: EntityOrderBy<entityType>, lastRow: entityType): Promise<EntityFilter<entityType>>;
|
|
16
15
|
private _info;
|
|
17
16
|
private __edp;
|
|
@@ -42,7 +41,9 @@ export declare class RepositoryImplementation<entityType> implements Repository<
|
|
|
42
41
|
private getRefForExistingRow;
|
|
43
42
|
save(item: Partial<OmitEB<entityType>>[]): Promise<entityType[]>;
|
|
44
43
|
save(item: Partial<OmitEB<entityType>>): Promise<entityType>;
|
|
44
|
+
liveQuery(options?: FindOptions<entityType>): any;
|
|
45
45
|
find(options: FindOptions<entityType>): Promise<entityType[]>;
|
|
46
|
+
buildEntityDataProviderFindOptions(options: FindOptions<entityType>): Promise<EntityDataProviderFindOptions>;
|
|
46
47
|
private mapRawDataToResult;
|
|
47
48
|
count(where?: EntityFilter<entityType>): Promise<number>;
|
|
48
49
|
private cache;
|
|
@@ -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;
|
|
@@ -89,6 +89,7 @@ declare abstract class rowHelperBase<T> {
|
|
|
89
89
|
copyDataToObject(): any;
|
|
90
90
|
originalValues: any;
|
|
91
91
|
saveOriginalData(): void;
|
|
92
|
+
saveMoreOriginalData(): void;
|
|
92
93
|
validate(): Promise<boolean>;
|
|
93
94
|
__validateEntity(): Promise<void>;
|
|
94
95
|
__performColumnAndEntityValidations(): Promise<void>;
|
|
@@ -106,6 +107,7 @@ export declare class rowHelperImplementation<T> extends rowHelperBase<T> impleme
|
|
|
106
107
|
get apiInsertAllowed(): boolean;
|
|
107
108
|
metadata: EntityMetadata<T>;
|
|
108
109
|
getId(): any;
|
|
110
|
+
saveMoreOriginalData(): void;
|
|
109
111
|
private _wasDeleted;
|
|
110
112
|
wasDeleted(): boolean;
|
|
111
113
|
undoChanges(): void;
|
|
@@ -118,6 +120,7 @@ export declare class rowHelperImplementation<T> extends rowHelperBase<T> impleme
|
|
|
118
120
|
delete(): Promise<void>;
|
|
119
121
|
loadDataFrom(data: any, loadItems?: FieldMetadata[]): Promise<void>;
|
|
120
122
|
id: any;
|
|
123
|
+
originalId: any;
|
|
121
124
|
getOriginalId(): any;
|
|
122
125
|
private calcServerExpression;
|
|
123
126
|
isNew(): boolean;
|
|
@@ -175,7 +178,6 @@ export declare class columnDefsImpl implements FieldMetadata {
|
|
|
175
178
|
private settings;
|
|
176
179
|
private entityDefs;
|
|
177
180
|
constructor(settings: FieldOptions, entityDefs: EntityFullInfo<any>, remult: Remult);
|
|
178
|
-
dbNamePromise: Promise<string>;
|
|
179
181
|
getDbName(): Promise<string>;
|
|
180
182
|
options: FieldOptions<any, any>;
|
|
181
183
|
target: ClassType<any>;
|
|
@@ -217,7 +219,10 @@ export declare class Fields {
|
|
|
217
219
|
static integer<entityType = any>(...options: (FieldOptions<entityType, Number> | ((options: FieldOptions<entityType, Number>, remult: Remult) => void))[]): (target: any, key: any, c?: any) => void;
|
|
218
220
|
static autoIncrement<entityType = any>(...options: (FieldOptions<entityType, Number> | ((options: FieldOptions<entityType, Number>, remult: Remult) => void))[]): (target: any, key: any, c?: any) => void;
|
|
219
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;
|
|
220
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;
|
|
221
226
|
static string<entityType = any>(...options: (StringFieldOptions<entityType> | ((options: StringFieldOptions<entityType>, remult: Remult) => void))[]): (target: any, key: any, c?: any) => void;
|
|
222
227
|
static boolean<entityType = any>(...options: (FieldOptions<entityType, boolean> | ((options: FieldOptions<entityType, boolean>, remult: Remult) => void))[]): (target: any, key: any, c?: any) => void;
|
|
223
228
|
}
|
|
@@ -320,4 +325,8 @@ declare class SubscribableImp implements Subscribable {
|
|
|
320
325
|
reportObserved: () => void;
|
|
321
326
|
}): Unobserve;
|
|
322
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>;
|
|
323
332
|
export {};
|