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