remult 0.17.0 → 0.18.1-exp.1
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 +5 -28
- package/esm/index.d.ts +4 -2
- package/esm/live-query/ably.d.ts +13 -0
- package/esm/live-query/ably.js +25 -0
- package/esm/live-query/index.d.ts +3 -0
- package/esm/live-query/index.js +3 -0
- package/esm/postgres/schema-builder.js +4 -4
- package/esm/remult-express.d.ts +26 -0
- package/esm/remult-express.js +137 -1
- package/esm/remult-fastify.js +1 -1
- package/esm/remult-fresh.js +2 -2
- package/esm/remult-knex/index.js +4 -11
- package/esm/server/expressBridge.d.ts +49 -11
- package/esm/server/expressBridge.js +48 -24
- package/esm/server/index.d.ts +1 -3
- package/esm/server/index.js +1 -6
- package/esm/src/backend-tests/backend-database-test-setup.backend-spec.d.ts +5 -0
- package/esm/src/backend-tests/backend-database-test-setup.backend-spec.js +113 -0
- package/esm/src/backend-tests/my-test.backend-spec.d.ts +1 -0
- package/esm/src/backend-tests/my-test.backend-spec.js +59 -0
- package/esm/src/backend-tests/test-http-server.backend-spec.d.ts +6 -0
- package/esm/src/backend-tests/test-http-server.backend-spec.js +171 -0
- package/esm/src/buildRestDataProvider.d.ts +31 -0
- package/esm/src/buildRestDataProvider.js +110 -0
- package/esm/src/column.d.ts +3 -3
- package/esm/src/context.d.ts +21 -30
- package/esm/src/context.js +87 -110
- package/esm/src/data-api.d.ts +8 -3
- package/esm/src/data-api.js +117 -61
- package/esm/src/data-providers/array-entity-data-provider.js +1 -16
- package/esm/src/data-providers/rest-data-provider.d.ts +12 -1
- package/esm/src/data-providers/rest-data-provider.js +34 -9
- package/esm/src/data-providers/sql-database.js +1 -1
- package/esm/src/data-providers/web-sql-data-provider.js +1 -1
- package/esm/src/live-query/EventSourceLiveQueryProvider.d.ts +4 -0
- package/esm/src/live-query/EventSourceLiveQueryProvider.js +78 -0
- package/esm/src/live-query/LiveQueryClient.d.ts +19 -0
- package/esm/src/live-query/LiveQueryClient.js +147 -0
- package/esm/src/live-query/LiveQueryPublisher.d.ts +48 -0
- package/esm/src/live-query/LiveQueryPublisher.js +112 -0
- package/esm/src/live-query/LiveQuerySubscriber.d.ts +67 -0
- package/esm/src/live-query/LiveQuerySubscriber.js +99 -0
- package/esm/src/remult-proxy.d.ts +1 -1
- package/esm/src/remult-proxy.js +11 -9
- package/esm/src/remult3/RepositoryImplementation.d.ts +6 -3
- package/esm/src/remult3/RepositoryImplementation.js +38 -22
- package/esm/src/remult3/getId.d.ts +2 -0
- package/esm/src/remult3/getId.js +7 -0
- package/esm/src/remult3/remult3.d.ts +10 -6
- package/esm/src/server-action.d.ts +4 -0
- package/esm/src/server-action.js +11 -12
- package/esm/src/shared-tests/db-tests-setup.d.ts +1 -1
- package/esm/src/shared-tests/db-tests-setup.js +1 -3
- package/esm/src/sort.d.ts +1 -0
- package/esm/src/sort.js +18 -0
- package/esm/src/tests/testHelper.spec.d.ts +2 -0
- package/esm/src/tests/testHelper.spec.js +62 -58
- package/esm/src/valueConverters.js +1 -3
- package/index.d.ts +4 -2
- package/live-query/ably.d.ts +13 -0
- package/live-query/ably.js +38 -0
- package/live-query/index.d.ts +3 -0
- package/live-query/index.js +10 -0
- package/package.json +17 -9
- package/postgres/schema-builder.js +4 -4
- package/remult-express.d.ts +26 -0
- package/remult-express.js +231 -3
- package/remult-fastify.js +2 -2
- package/remult-fresh.js +1 -1
- package/remult-knex/index.js +10 -18
- package/server/expressBridge.d.ts +49 -11
- package/server/expressBridge.js +91 -43
- package/server/index.d.ts +1 -3
- package/server/index.js +2 -7
- package/src/backend-tests/backend-database-test-setup.backend-spec.d.ts +5 -0
- package/src/backend-tests/backend-database-test-setup.backend-spec.js +231 -0
- package/src/backend-tests/my-test.backend-spec.d.ts +1 -0
- package/src/backend-tests/my-test.backend-spec.js +160 -0
- package/src/backend-tests/test-http-server.backend-spec.d.ts +6 -0
- package/src/backend-tests/test-http-server.backend-spec.js +342 -0
- package/src/buildRestDataProvider.d.ts +31 -0
- package/src/buildRestDataProvider.js +162 -0
- package/src/column.d.ts +3 -3
- package/src/context.d.ts +21 -30
- package/src/context.js +142 -159
- package/src/data-api.d.ts +8 -3
- package/src/data-api.js +198 -79
- package/src/data-providers/array-entity-data-provider.js +1 -16
- package/src/data-providers/rest-data-provider.d.ts +12 -1
- package/src/data-providers/rest-data-provider.js +62 -24
- package/src/data-providers/sql-database.js +1 -1
- package/src/data-providers/web-sql-data-provider.js +1 -1
- package/src/live-query/EventSourceLiveQueryProvider.d.ts +4 -0
- package/src/live-query/EventSourceLiveQueryProvider.js +119 -0
- package/src/live-query/LiveQueryClient.d.ts +19 -0
- package/src/live-query/LiveQueryClient.js +208 -0
- package/src/live-query/LiveQueryPublisher.d.ts +48 -0
- package/src/live-query/LiveQueryPublisher.js +239 -0
- package/src/live-query/LiveQuerySubscriber.d.ts +67 -0
- package/src/live-query/LiveQuerySubscriber.js +201 -0
- package/src/remult-proxy.d.ts +1 -1
- package/src/remult-proxy.js +19 -9
- package/src/remult3/RepositoryImplementation.d.ts +6 -3
- package/src/remult3/RepositoryImplementation.js +68 -42
- package/src/remult3/getId.d.ts +2 -0
- package/src/remult3/getId.js +11 -0
- package/src/remult3/remult3.d.ts +10 -6
- package/src/server-action.d.ts +4 -0
- package/src/server-action.js +11 -11
- package/src/shared-tests/db-tests-setup.d.ts +1 -1
- package/src/shared-tests/db-tests-setup.js +1 -3
- package/src/sort.d.ts +1 -0
- package/src/sort.js +18 -0
- package/src/tests/testHelper.spec.d.ts +2 -0
- package/src/tests/testHelper.spec.js +100 -95
- package/src/valueConverters.js +1 -3
package/README.md
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
<img src="https://github.com/remult/remult/raw/master/docs/.vuepress/public/logo.png" width="140" height="140">
|
|
4
4
|
</a>
|
|
5
5
|
<h1>Remult</h1>
|
|
6
|
-
<p>A CRUD framework for full-stack TypeScript</p>
|
|
7
6
|
<a href="https://circleci.com/gh/remult/remult/tree/master" rel="nofollow">
|
|
8
7
|
<img alt="CircleCI" src="https://circleci.com/gh/remult/remult/tree/master.svg?style=shield">
|
|
9
8
|
</a>
|
|
@@ -25,29 +24,14 @@
|
|
|
25
24
|
<a href="https://twitter.com/intent/tweet?text=Remult%20-%20open-source%20CRUD%20framework%20for%20full-stack%20TypeScript%0A%0A%40remultjs%0A&url=https%3A%2F%2Fgithub.com%2Fremult%2Fremult">
|
|
26
25
|
<img alt="Twitter URL" src="https://img.shields.io/twitter/url?style=social&url=https%3A%2F%2Ftwitter.com%2Fintent%2Ftweet%3Ftext%3DRemult%2520-%2520open-source%2520CRUD%2520framework%2520for%2520full-stack%2520TypeScript%250A%250A%2540remultjs%250A%26url%3Dhttps%253A%252F%252Fgithub.com%252Fremult%252Fremult">
|
|
27
26
|
</a>
|
|
27
|
+
<a href="https://twitter.com/remultjs" rel="nofollow">
|
|
28
|
+
<img alt="Twitter Follow" src="https://img.shields.io/twitter/url.svg?label=Follow%20%40remultjs&style=social&url=https%3A%2F%2Ftwitter.com%2FRemultJs">
|
|
29
|
+
</a>
|
|
28
30
|
</div>
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
<br/>
|
|
32
|
-
|
|
33
|
-
<div align="center">
|
|
34
|
-
<a href="#getting-started">Getting Started</a> |
|
|
35
|
-
<a href="#documentation">Documentation</a> |
|
|
36
|
-
<a href="#example-apps">Example Apps</a>
|
|
37
|
-
</div>
|
|
38
|
-
|
|
39
31
|
<hr/>
|
|
40
|
-
<br/>
|
|
41
32
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
<img src="https://github.com/remult/remult/raw/master/static/images/video-thumbnail.jpg" alt="Video thumbnail" />
|
|
45
|
-
</p>
|
|
46
|
-
</a>
|
|
47
|
-
|
|
48
|
-
<a href="https://www.youtube.com/watch?v=rEoScmSVNUE" target="_blank">
|
|
49
|
-
<p align="center">Watch code demo on YouTube here (14 mins)</p>
|
|
50
|
-
</a>
|
|
33
|
+
[Getting Started](#getting-started) | [Documentation](#documentation) |
|
|
34
|
+
[Example Apps](#example-apps)
|
|
51
35
|
|
|
52
36
|
## What is Remult?
|
|
53
37
|
|
|
@@ -55,13 +39,6 @@
|
|
|
55
39
|
as a single source of truth for your API, frontend type-safe API client and
|
|
56
40
|
backend ORM**.
|
|
57
41
|
|
|
58
|
-
* :zap: Zero-boilerplate CRUD API routes with paging, sorting, and filtering for Express / Fastify / Next.js / NestJS / Koa / others...
|
|
59
|
-
* :ok_hand: Fullstack type-safety for API queries, mutations and RPC, without code generation
|
|
60
|
-
* :sparkles: Input validation, defined once, runs both on the backend and on the frontend for best UX
|
|
61
|
-
* :lock: Fine-grained code-based API authorization
|
|
62
|
-
* :relieved: Incrementally adoptable
|
|
63
|
-
* :rocket: Production ready
|
|
64
|
-
|
|
65
42
|
### Status
|
|
66
43
|
|
|
67
44
|
Remult is **production-ready** and, in fact, used in production apps since 2018.
|
package/esm/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export { Field, Fields,
|
|
1
|
+
export { Field, Fields, FieldsMetadata, Entity, EntityBase, ControllerBase, FieldRef, IdFieldRef, FieldsRef, EntityMetadata, EntityOrderBy, EntityFilter, FindOptions, QueryResult, QueryOptions, Repository, FieldType, FindFirstOptions, ComparisonValueFilter, ValueFilter, IdFilter, ContainsStringValueFilter, getFields, EntityRef, getEntityRef, SortSegments, ValueListFieldType, getValueList, ValueListFieldOptions, ValueListInfo, OmitEB, Paginator, CaptionTransformer } from './src/remult3';
|
|
2
|
+
export { StringFieldOptions } from './src/remult3/RepositoryImplementation';
|
|
2
3
|
export { describeClass } from './src/remult3/DecoratorReplacer';
|
|
3
4
|
export { EntityOptions } from './src/entity';
|
|
4
5
|
export { DataProvider, EntityDataProvider, EntityDataProviderFindOptions, ErrorInfo, RestDataProviderHttpProvider } from './src/data-interfaces';
|
|
@@ -13,7 +14,8 @@ export { SqlDatabase, } from './src/data-providers/sql-database';
|
|
|
13
14
|
export { CustomSqlFilterObject, CustomSqlFilterBuilder } from './src/filter/filter-consumer-bridge-to-sql-request';
|
|
14
15
|
export { JsonDataProvider, JsonEntityStorage } from './src/data-providers/json-data-provider';
|
|
15
16
|
export { Controller, BackendMethodOptions, BackendMethod, ProgressListener } from './src/server-action';
|
|
16
|
-
export { Allowed, Allow, Remult, RemultContext, ApiClient, isBackend, AllowedForInstance, EventDispatcher, EventSource,
|
|
17
|
+
export { Allowed, Allow, Remult, RemultContext, ApiClient, isBackend, AllowedForInstance, EventDispatcher, EventSource, Unobserve, UserInfo } from './src/context';
|
|
18
|
+
export { ExternalHttpProvider } from './src/buildRestDataProvider';
|
|
17
19
|
export { IdEntity } from './src/id-entity';
|
|
18
20
|
export { SortSegment, Sort } from './src/sort';
|
|
19
21
|
export { OneToMany, CompoundIdField } from './src/column';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type * as Ably from 'ably';
|
|
2
|
+
import type { ServerEventDispatcher } from '../src/live-query/LiveQueryPublisher';
|
|
3
|
+
import type { LiveQueryProvider, PubSubClient } from '../src/live-query/LiveQuerySubscriber';
|
|
4
|
+
export declare class AblyLiveQueryProvider implements LiveQueryProvider {
|
|
5
|
+
private ably;
|
|
6
|
+
constructor(ably: Ably.Types.RealtimePromise);
|
|
7
|
+
openStreamAndReturnCloseFunction(onReconnect: VoidFunction): Promise<PubSubClient>;
|
|
8
|
+
}
|
|
9
|
+
export declare class AblyServerEventDispatcher implements ServerEventDispatcher {
|
|
10
|
+
private ably;
|
|
11
|
+
constructor(ably: Ably.Types.RealtimePromise);
|
|
12
|
+
sendChannelMessage<T>(channel: string, message: T): void;
|
|
13
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export class AblyLiveQueryProvider {
|
|
2
|
+
constructor(ably) {
|
|
3
|
+
this.ably = ably;
|
|
4
|
+
}
|
|
5
|
+
async openStreamAndReturnCloseFunction(onReconnect) {
|
|
6
|
+
return {
|
|
7
|
+
disconnect: () => {
|
|
8
|
+
this.ably.connection.close();
|
|
9
|
+
},
|
|
10
|
+
subscribe: (channel, handler) => {
|
|
11
|
+
let myHandler = (y) => handler(y.data);
|
|
12
|
+
this.ably.channels.get(channel).subscribe(y => myHandler(y));
|
|
13
|
+
return () => { myHandler = () => { }; };
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export class AblyServerEventDispatcher {
|
|
19
|
+
constructor(ably) {
|
|
20
|
+
this.ably = ably;
|
|
21
|
+
}
|
|
22
|
+
sendChannelMessage(channel, message) {
|
|
23
|
+
this.ably.channels.get(channel).publish({ data: message });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { LiveQueryPublisher, ServerEventDispatcher, LiveQueryStorage, LiveQueryStorageInMemoryImplementation } from "../src/live-query/LiveQueryPublisher";
|
|
2
|
+
export { PubSubClient, LiveQueryProvider, AMessageChannel } from '../src/live-query/LiveQuerySubscriber';
|
|
3
|
+
export { LiveQueryClient } from '../src/live-query/LiveQueryClient';
|
|
@@ -43,7 +43,7 @@ export class PostgresSchemaBuilder {
|
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
async verifyStructureOfAllEntities(remult) {
|
|
46
|
-
console.log("start verify structure");
|
|
46
|
+
console.log("start verify structure"); //keep me
|
|
47
47
|
for (const entityClass of allEntities) {
|
|
48
48
|
let entity = remult.repo(entityClass).metadata;
|
|
49
49
|
let e = await getDbNameProvider(entity);
|
|
@@ -56,7 +56,7 @@ export class PostgresSchemaBuilder {
|
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
catch (err) {
|
|
59
|
-
console.log("failed verify structure of " + e.entityName + " ", err);
|
|
59
|
+
/*keep me */ console.log("failed verify structure of " + e.entityName + " ", err);
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
}
|
|
@@ -102,7 +102,7 @@ export class PostgresSchemaBuilder {
|
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
catch (err) {
|
|
105
|
-
console.
|
|
105
|
+
console.error(err);
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
async verifyAllColumns(entity) {
|
|
@@ -122,7 +122,7 @@ export class PostgresSchemaBuilder {
|
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
124
|
catch (err) {
|
|
125
|
-
console.
|
|
125
|
+
console.error(err);
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
}
|
package/esm/remult-express.d.ts
CHANGED
|
@@ -1,6 +1,32 @@
|
|
|
1
1
|
import * as express from 'express';
|
|
2
2
|
import { RemultServer, RemultServerOptions } from './server/expressBridge';
|
|
3
|
+
import { Remult } from './src/context';
|
|
4
|
+
import { ServerEventChannelSubscribeDTO } from './src/live-query/LiveQuerySubscriber';
|
|
5
|
+
import { ServerEventDispatcher } from './src/live-query/LiveQueryPublisher';
|
|
3
6
|
export declare function remultExpress(options?: RemultServerOptions<express.Request> & {
|
|
4
7
|
bodyParser?: boolean;
|
|
5
8
|
bodySizeLimit?: string;
|
|
9
|
+
serverEventDispatcher?: (router: express.Router, server: RemultServer) => ServerEventDispatcher;
|
|
6
10
|
}): express.RequestHandler & RemultServer;
|
|
11
|
+
export declare class ServerEventsController implements ServerEventDispatcher {
|
|
12
|
+
private canUserConnectToChannel?;
|
|
13
|
+
subscribeToChannel({ channel, clientId }: ServerEventChannelSubscribeDTO, res: import('express').Response, remult: Remult, remove?: boolean): void;
|
|
14
|
+
connections: clientConnection[];
|
|
15
|
+
constructor(canUserConnectToChannel?: (channel: string, remult: Remult) => boolean);
|
|
16
|
+
sendChannelMessage<T>(channel: string, message: any): void;
|
|
17
|
+
debugMessageFileSaver: (id: any, channel: any, message: any) => void;
|
|
18
|
+
openHttpServerStream(req: import('express').Request, res: import('express').Response): clientConnection;
|
|
19
|
+
debug(): void;
|
|
20
|
+
debugFileSaver: (x: any) => void;
|
|
21
|
+
}
|
|
22
|
+
declare class clientConnection {
|
|
23
|
+
response: import('express').Response;
|
|
24
|
+
channels: Record<string, boolean>;
|
|
25
|
+
close(): void;
|
|
26
|
+
closed: boolean;
|
|
27
|
+
write(eventData: string, eventType?: string): void;
|
|
28
|
+
connectionId: any;
|
|
29
|
+
constructor(response: import('express').Response);
|
|
30
|
+
sendLiveMessage(): void;
|
|
31
|
+
}
|
|
32
|
+
export {};
|
package/esm/remult-express.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import * as express from 'express';
|
|
2
|
-
import { createRemultServer } from './server/
|
|
2
|
+
import { createRemultServer } from './server/expressBridge';
|
|
3
|
+
import { liveQueryKeepAliveRoute, streamUrl } from './src/live-query/LiveQuerySubscriber';
|
|
4
|
+
import { LiveQueryPublisher, LiveQueryStorageInMemoryImplementation } from './src/live-query/LiveQueryPublisher';
|
|
5
|
+
import { v4 as uuid } from 'uuid';
|
|
6
|
+
import { remult } from './src/remult-proxy';
|
|
7
|
+
import { getEntityKey } from './src/remult3';
|
|
3
8
|
export function remultExpress(options) {
|
|
4
9
|
let app = express.Router();
|
|
5
10
|
if (!options) {
|
|
@@ -14,6 +19,29 @@ export function remultExpress(options) {
|
|
|
14
19
|
}
|
|
15
20
|
const server = createRemultServer(options);
|
|
16
21
|
server.registerRouter(app);
|
|
22
|
+
if (!options.serverEventDispatcher) {
|
|
23
|
+
options.serverEventDispatcher = (router, server) => {
|
|
24
|
+
return buildHttpServerEventDispatcher(router, options.rootPath, server);
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
app.post(options.rootPath + liveQueryKeepAliveRoute, (r, res, n) => server.withRemult(r, res, n), async (req, res) => {
|
|
28
|
+
res.send(await remult.liveQueryPublisher.storage.keepAliveAndReturnUnknownIds(req.body));
|
|
29
|
+
});
|
|
30
|
+
server.liveQueryManager = new LiveQueryPublisher(options.serverEventDispatcher(app, server), new LiveQueryStorageInMemoryImplementation(), async (req, entityKey, what) => {
|
|
31
|
+
for (const e of options.entities) {
|
|
32
|
+
let key = getEntityKey(e);
|
|
33
|
+
if (key === entityKey) {
|
|
34
|
+
await new Promise((result) => {
|
|
35
|
+
server.withRemult(options.requestSerializer.fromJson(req), undefined, async () => {
|
|
36
|
+
await what(remult.repo(e));
|
|
37
|
+
result({});
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
throw new Error("Couldn't find entity " + entityKey);
|
|
44
|
+
});
|
|
17
45
|
return Object.assign(app, {
|
|
18
46
|
getRemult: (req) => server.getRemult(req),
|
|
19
47
|
openApiDoc: (options) => server.openApiDoc(options),
|
|
@@ -21,3 +49,111 @@ export function remultExpress(options) {
|
|
|
21
49
|
withRemult: (...args) => server.withRemult(...args)
|
|
22
50
|
});
|
|
23
51
|
}
|
|
52
|
+
export class ServerEventsController {
|
|
53
|
+
constructor(canUserConnectToChannel) {
|
|
54
|
+
this.canUserConnectToChannel = canUserConnectToChannel;
|
|
55
|
+
this.connections = [];
|
|
56
|
+
this.debugMessageFileSaver = (id, channel, message) => { };
|
|
57
|
+
this.debugFileSaver = () => { };
|
|
58
|
+
if (!this.canUserConnectToChannel) {
|
|
59
|
+
this.canUserConnectToChannel = () => true;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
subscribeToChannel({ channel, clientId }, res, remult, remove = false) {
|
|
63
|
+
for (const c of this.connections) {
|
|
64
|
+
if (c.connectionId === clientId) {
|
|
65
|
+
if (this.canUserConnectToChannel(channel, remult)) {
|
|
66
|
+
if (remove)
|
|
67
|
+
delete c.channels[channel];
|
|
68
|
+
else
|
|
69
|
+
c.channels[channel] = true;
|
|
70
|
+
res.json("ok");
|
|
71
|
+
this.debug();
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
res.sendStatus(403);
|
|
76
|
+
this.debug();
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
res.sendStatus(404);
|
|
82
|
+
}
|
|
83
|
+
sendChannelMessage(channel, message) {
|
|
84
|
+
const data = JSON.stringify({ channel, data: message });
|
|
85
|
+
for (const sc of this.connections) {
|
|
86
|
+
if (sc.channels[channel]) {
|
|
87
|
+
this.debugMessageFileSaver(sc.connectionId, channel, message);
|
|
88
|
+
sc.write(data);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
openHttpServerStream(req, res) {
|
|
93
|
+
res.writeHead(200, {
|
|
94
|
+
'Content-Type': 'text/event-stream',
|
|
95
|
+
'Cache-Control': 'no-cache',
|
|
96
|
+
'Connection': 'keep-alive'
|
|
97
|
+
});
|
|
98
|
+
const cc = new clientConnection(res);
|
|
99
|
+
//const lastEventId = req.headers['last-event-id'];
|
|
100
|
+
this.connections.push(cc);
|
|
101
|
+
this.debug();
|
|
102
|
+
req.on("close", () => {
|
|
103
|
+
cc.close();
|
|
104
|
+
this.connections = this.connections.filter(s => s !== cc);
|
|
105
|
+
this.debug();
|
|
106
|
+
});
|
|
107
|
+
return cc;
|
|
108
|
+
}
|
|
109
|
+
debug() {
|
|
110
|
+
this.debugFileSaver(this.connections.map(x => ({
|
|
111
|
+
id: x.connectionId,
|
|
112
|
+
channels: x.channels
|
|
113
|
+
})));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
class clientConnection {
|
|
117
|
+
constructor(response) {
|
|
118
|
+
this.response = response;
|
|
119
|
+
this.channels = {};
|
|
120
|
+
this.closed = false;
|
|
121
|
+
this.connectionId = uuid();
|
|
122
|
+
this.write(this.connectionId, "connectionId");
|
|
123
|
+
this.sendLiveMessage();
|
|
124
|
+
}
|
|
125
|
+
close() {
|
|
126
|
+
this.closed = true;
|
|
127
|
+
}
|
|
128
|
+
write(eventData, eventType = "message") {
|
|
129
|
+
let event = "event:" + eventType;
|
|
130
|
+
// if (id != undefined)
|
|
131
|
+
// event += "\nid:" + id;
|
|
132
|
+
this.response.write(event + "\ndata:" + eventData + "\n\n");
|
|
133
|
+
let r = this.response;
|
|
134
|
+
if (r.flush)
|
|
135
|
+
r.flush();
|
|
136
|
+
}
|
|
137
|
+
sendLiveMessage() {
|
|
138
|
+
if (this.closed)
|
|
139
|
+
return;
|
|
140
|
+
this.write("", "keep-alive");
|
|
141
|
+
setTimeout(() => {
|
|
142
|
+
this.sendLiveMessage();
|
|
143
|
+
}, 45000);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function buildHttpServerEventDispatcher(router, apiPath, server) {
|
|
147
|
+
const streamPath = apiPath + '/' + streamUrl;
|
|
148
|
+
let httpServerEvents = new ServerEventsController();
|
|
149
|
+
router.get(streamPath, (r, res, next) => server.withRemult(r, res, next), (req, res) => {
|
|
150
|
+
remult.liveQueryPublisher.dispatcher.openHttpServerStream(req, res);
|
|
151
|
+
});
|
|
152
|
+
router.post(streamPath + '/subscribe', (r, res, next) => server.withRemult(r, res, next), (req, res) => {
|
|
153
|
+
remult.liveQueryPublisher.dispatcher.subscribeToChannel(req.body, res, remult);
|
|
154
|
+
});
|
|
155
|
+
router.post(streamPath + '/unsubscribe', (r, res, next) => server.withRemult(r, res, next), (req, res) => {
|
|
156
|
+
remult.liveQueryPublisher.dispatcher.subscribeToChannel(req.body, res, remult, true);
|
|
157
|
+
});
|
|
158
|
+
return httpServerEvents;
|
|
159
|
+
}
|
package/esm/remult-fastify.js
CHANGED
package/esm/remult-fresh.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createRemultServer } from "./server/expressBridge";
|
|
2
2
|
export function remultFresh(options, response) {
|
|
3
|
-
const server =
|
|
3
|
+
const server = createRemultServer(options);
|
|
4
4
|
return {
|
|
5
5
|
getRemult: r => server.getRemult(r),
|
|
6
6
|
openApiDoc: x => server.openApiDoc(x),
|
package/esm/remult-knex/index.js
CHANGED
|
@@ -153,15 +153,8 @@ class KnexEntityDataProvider {
|
|
|
153
153
|
}
|
|
154
154
|
let insert = this.knex(e.entityName).insert(insertObject);
|
|
155
155
|
if (isAutoIncrement(this.entity.idMetadata.field)) {
|
|
156
|
-
let
|
|
157
|
-
|
|
158
|
-
let result = await insert;
|
|
159
|
-
newId = result[0];
|
|
160
|
-
}
|
|
161
|
-
else {
|
|
162
|
-
let result = await insert.returning(this.entity.idMetadata.field.key);
|
|
163
|
-
newId = result[0].id;
|
|
164
|
-
}
|
|
156
|
+
let result = await insert.returning(this.entity.idMetadata.field.key);
|
|
157
|
+
let newId = result[0].id;
|
|
165
158
|
resultFilter = new Filter(x => x.isEqualTo(this.entity.idMetadata.field, newId));
|
|
166
159
|
}
|
|
167
160
|
else
|
|
@@ -263,7 +256,7 @@ export class KnexSchemaBuilder {
|
|
|
263
256
|
this.additionalWhere = '';
|
|
264
257
|
}
|
|
265
258
|
async verifyStructureOfAllEntities(remult) {
|
|
266
|
-
console.
|
|
259
|
+
console.info("start verify structure");
|
|
267
260
|
for (const entity of allEntities) {
|
|
268
261
|
let metadata = remult.repo(entity).metadata;
|
|
269
262
|
try {
|
|
@@ -326,7 +319,7 @@ export class KnexSchemaBuilder {
|
|
|
326
319
|
}
|
|
327
320
|
}
|
|
328
321
|
catch (err) {
|
|
329
|
-
console.
|
|
322
|
+
console.error(err);
|
|
330
323
|
}
|
|
331
324
|
}
|
|
332
325
|
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { queuedJobInfoResponse } from '../src/server-action';
|
|
1
|
+
import { ActionInterface, queuedJobInfoResponse } from '../src/server-action';
|
|
2
2
|
import { DataProvider } from '../src/data-interfaces';
|
|
3
|
-
import {
|
|
3
|
+
import { DataApi, DataApiRequest, DataApiResponse } from '../src/data-api';
|
|
4
|
+
import { AllowedForInstance, Remult, UserInfo } from '../src/context';
|
|
4
5
|
import { ClassType } from '../classType';
|
|
5
6
|
import { Repository } from '../src/remult3';
|
|
6
7
|
import { IdEntity } from '../src/id-entity';
|
|
8
|
+
import { LiveQueryPublisher } from '../src/live-query/LiveQueryPublisher';
|
|
7
9
|
export interface RemultServerOptions<RequestType extends GenericRequest> {
|
|
8
10
|
/** Sets a database connection for Remult.
|
|
9
11
|
*
|
|
@@ -12,6 +14,10 @@ export interface RemultServerOptions<RequestType extends GenericRequest> {
|
|
|
12
14
|
dataProvider?: DataProvider | Promise<DataProvider> | (() => Promise<DataProvider | undefined>);
|
|
13
15
|
queueStorage?: QueueStorage;
|
|
14
16
|
initRequest?: (remult: Remult, origReq: RequestType) => Promise<void>;
|
|
17
|
+
requestSerializer?: {
|
|
18
|
+
toJson: (request: RequestType) => any;
|
|
19
|
+
fromJson: (request: any) => RequestType;
|
|
20
|
+
};
|
|
15
21
|
getUser?: (request: RequestType) => Promise<UserInfo>;
|
|
16
22
|
initApi?: (remult: Remult) => void | Promise<void>;
|
|
17
23
|
logApiEndPoints?: boolean;
|
|
@@ -20,7 +26,7 @@ export interface RemultServerOptions<RequestType extends GenericRequest> {
|
|
|
20
26
|
controllers?: ClassType<any>[];
|
|
21
27
|
rootPath?: string;
|
|
22
28
|
}
|
|
23
|
-
export declare function
|
|
29
|
+
export declare function createRemultServer<RequestType extends GenericRequest = GenericRequest>(options?: RemultServerOptions<RequestType>): RemultServer;
|
|
24
30
|
export declare type GenericRequestHandler = (req: GenericRequest, res: GenericResponse, next: VoidFunction) => void;
|
|
25
31
|
export interface ServerHandleResponse {
|
|
26
32
|
data?: any;
|
|
@@ -57,14 +63,39 @@ export interface GenericResponse {
|
|
|
57
63
|
status(statusCode: number): GenericResponse;
|
|
58
64
|
end(): any;
|
|
59
65
|
}
|
|
60
|
-
export declare class
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
constructor(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
export declare class RemultServerImplementation implements RemultServer {
|
|
67
|
+
queue: inProcessQueueHandler;
|
|
68
|
+
options: RemultServerOptions<GenericRequest>;
|
|
69
|
+
dataProvider: DataProvider | Promise<DataProvider>;
|
|
70
|
+
constructor(queue: inProcessQueueHandler, options: RemultServerOptions<GenericRequest>, dataProvider: DataProvider | Promise<DataProvider>);
|
|
71
|
+
liveQueryManager: LiveQueryPublisher;
|
|
72
|
+
withRemult<T>(req: GenericRequest, res: GenericResponse, next: VoidFunction): void;
|
|
73
|
+
routeImpl: RouteImplementation;
|
|
74
|
+
handle(req: GenericRequest, gRes?: GenericResponse): Promise<ServerHandleResponse>;
|
|
75
|
+
registeredRouter: boolean;
|
|
76
|
+
registerRouter(r: GenericRouter): void;
|
|
77
|
+
add(key: string, dataApiFactory: ((req: Remult) => DataApi), r: GenericRouter): void;
|
|
78
|
+
process(what: (remult: Remult, myReq: DataApiRequest, myRes: DataApiResponse, origReq: GenericRequest) => Promise<void>): (req: GenericRequest, res: GenericResponse) => Promise<void>;
|
|
79
|
+
getRemult(req: GenericRequest): Promise<Remult>;
|
|
80
|
+
hasQueue: boolean;
|
|
81
|
+
addAction(action: ActionInterface, r: GenericRouter): void;
|
|
82
|
+
openApiDoc(options: {
|
|
83
|
+
title: string;
|
|
84
|
+
version?: string;
|
|
85
|
+
}): any;
|
|
86
|
+
backendMethodsOpenApi: {
|
|
87
|
+
path: string;
|
|
88
|
+
allowed: AllowedForInstance<any>;
|
|
89
|
+
tag: string;
|
|
90
|
+
}[];
|
|
91
|
+
}
|
|
92
|
+
declare class inProcessQueueHandler {
|
|
93
|
+
private storage;
|
|
94
|
+
constructor(storage: QueueStorage);
|
|
95
|
+
submitJob(url: string, req: Remult, body: any): Promise<string>;
|
|
96
|
+
mapQueuedAction(url: string, what: (data: any, r: Remult, res: ApiActionResponse) => void): void;
|
|
97
|
+
actions: Map<string, (data: any, r: Remult, res: ApiActionResponse) => void>;
|
|
98
|
+
getJobInfo(queuedJobId: string): Promise<queuedJobInfo>;
|
|
68
99
|
}
|
|
69
100
|
export interface queuedJobInfo {
|
|
70
101
|
info: queuedJobInfoResponse;
|
|
@@ -90,6 +121,12 @@ export declare class EntityQueueStorage implements QueueStorage {
|
|
|
90
121
|
getJobInfo(queuedJobId: string): Promise<queuedJobInfo>;
|
|
91
122
|
createJob(url: string, userId: string): Promise<string>;
|
|
92
123
|
}
|
|
124
|
+
declare class RouteImplementation {
|
|
125
|
+
map: Map<string, Map<string, GenericRequestHandler>>;
|
|
126
|
+
route(path: string): SpecificRoute;
|
|
127
|
+
handle(req: GenericRequest, gRes?: GenericResponse): Promise<ServerHandleResponse | undefined>;
|
|
128
|
+
middleware(req: GenericRequest, res: GenericResponse, next: VoidFunction): void;
|
|
129
|
+
}
|
|
93
130
|
export declare class JobsInQueueEntity extends IdEntity {
|
|
94
131
|
userId: string;
|
|
95
132
|
url: string;
|
|
@@ -100,3 +137,4 @@ export declare class JobsInQueueEntity extends IdEntity {
|
|
|
100
137
|
error: boolean;
|
|
101
138
|
progress: number;
|
|
102
139
|
}
|
|
140
|
+
export {};
|
|
@@ -6,7 +6,7 @@ import { Entity, Fields, getEntityKey } from '../src/remult3';
|
|
|
6
6
|
import { IdEntity } from '../src/id-entity';
|
|
7
7
|
import { remult } from '../src/remult-proxy';
|
|
8
8
|
;
|
|
9
|
-
export function
|
|
9
|
+
export function createRemultServer(options) {
|
|
10
10
|
if (!options) {
|
|
11
11
|
options = {};
|
|
12
12
|
}
|
|
@@ -30,12 +30,30 @@ export function createRemultServerCore(options) {
|
|
|
30
30
|
return dp;
|
|
31
31
|
return new (await import('./JsonEntityFileStorage')).JsonFileDataProvider('./db');
|
|
32
32
|
});
|
|
33
|
-
|
|
33
|
+
if (!remultObjectStorage) {
|
|
34
|
+
dataProvider = dataProvider.then(async (dp) => {
|
|
35
|
+
try {
|
|
36
|
+
const asyncHooks = await import('async_hooks');
|
|
37
|
+
remultObjectStorage = new RemultAsyncLocalStorage(new asyncHooks.AsyncLocalStorage());
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
remultObjectStorage = new RemultAsyncLocalStorage(undefined);
|
|
41
|
+
}
|
|
42
|
+
remult.remultFactory = () => {
|
|
43
|
+
const r = remultObjectStorage.getRemult();
|
|
44
|
+
if (r)
|
|
45
|
+
return r;
|
|
46
|
+
else
|
|
47
|
+
throw new Error("remult object was requested outside of a valid context, try running it within initApi or a remult request cycle");
|
|
48
|
+
};
|
|
49
|
+
return dp;
|
|
50
|
+
});
|
|
51
|
+
}
|
|
34
52
|
if (options.initApi) {
|
|
35
53
|
dataProvider = dataProvider.then(async (dp) => {
|
|
36
54
|
var remult = new Remult(dp);
|
|
37
55
|
await new Promise((res) => {
|
|
38
|
-
|
|
56
|
+
remultObjectStorage.run(remult, async () => {
|
|
39
57
|
await options.initApi(remult);
|
|
40
58
|
res({});
|
|
41
59
|
});
|
|
@@ -43,6 +61,23 @@ export function createRemultServerCore(options) {
|
|
|
43
61
|
return dp;
|
|
44
62
|
});
|
|
45
63
|
}
|
|
64
|
+
if (!options.requestSerializer) {
|
|
65
|
+
options.requestSerializer = {
|
|
66
|
+
toJson: x => {
|
|
67
|
+
const r = {
|
|
68
|
+
url: x.url
|
|
69
|
+
};
|
|
70
|
+
for (const key of ['session', 'auth', 'user']) {
|
|
71
|
+
if (x[key])
|
|
72
|
+
r[key] = x[key];
|
|
73
|
+
}
|
|
74
|
+
return r;
|
|
75
|
+
},
|
|
76
|
+
fromJson: x => {
|
|
77
|
+
return x;
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
}
|
|
46
81
|
{
|
|
47
82
|
let allControllers = [];
|
|
48
83
|
if (!options.entities)
|
|
@@ -60,22 +95,10 @@ export function createRemultServerCore(options) {
|
|
|
60
95
|
return bridge;
|
|
61
96
|
}
|
|
62
97
|
;
|
|
63
|
-
|
|
98
|
+
class RemultAsyncLocalStorage {
|
|
64
99
|
constructor(remultObjectStorage) {
|
|
65
100
|
this.remultObjectStorage = remultObjectStorage;
|
|
66
101
|
}
|
|
67
|
-
static enable() {
|
|
68
|
-
remult.remultFactory = () => {
|
|
69
|
-
const r = RemultAsyncLocalStorage.instance.getRemult();
|
|
70
|
-
if (r)
|
|
71
|
-
return r;
|
|
72
|
-
else
|
|
73
|
-
throw new Error("remult object was requested outside of a valid context, try running it within initApi or a remult request cycle");
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
static disable() {
|
|
77
|
-
remult.resetFactory();
|
|
78
|
-
}
|
|
79
102
|
run(remult, callback) {
|
|
80
103
|
if (this.remultObjectStorage)
|
|
81
104
|
this.remultObjectStorage.run(remult, callback);
|
|
@@ -84,13 +107,13 @@ export class RemultAsyncLocalStorage {
|
|
|
84
107
|
}
|
|
85
108
|
getRemult() {
|
|
86
109
|
if (!this.remultObjectStorage) {
|
|
87
|
-
throw
|
|
110
|
+
throw "can't use static remult in this environment, `async_hooks` were not found";
|
|
88
111
|
}
|
|
89
112
|
return this.remultObjectStorage.getStore();
|
|
90
113
|
}
|
|
91
114
|
}
|
|
92
|
-
|
|
93
|
-
class RemultServerImplementation {
|
|
115
|
+
let remultObjectStorage;
|
|
116
|
+
export class RemultServerImplementation {
|
|
94
117
|
constructor(queue, options, dataProvider) {
|
|
95
118
|
this.queue = queue;
|
|
96
119
|
this.options = options;
|
|
@@ -156,11 +179,11 @@ class RemultServerImplementation {
|
|
|
156
179
|
add(key, dataApiFactory, r) {
|
|
157
180
|
let myRoute = this.options.rootPath + '/' + key;
|
|
158
181
|
if (this.options.logApiEndPoints)
|
|
159
|
-
console.
|
|
182
|
+
console.info("[remult] " + myRoute);
|
|
160
183
|
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)))
|
|
184
|
+
.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
185
|
.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)));
|
|
186
|
+
.post(this.process(async (c, req, res, orig) => dataApiFactory(c).httpPost(res, req, orig.body, () => this.options.requestSerializer.toJson(orig))));
|
|
164
187
|
r.route(myRoute + '/:id')
|
|
165
188
|
//@ts-ignore
|
|
166
189
|
.get(this.process(async (c, req, res, orig) => dataApiFactory(c).get(res, orig.params.id)))
|
|
@@ -174,9 +197,10 @@ class RemultServerImplementation {
|
|
|
174
197
|
let myReq = new ExpressRequestBridgeToDataApiRequest(req);
|
|
175
198
|
let myRes = new ExpressResponseBridgeToDataApiResponse(res, req);
|
|
176
199
|
let remult = new Remult();
|
|
200
|
+
remult.liveQueryPublisher = this.liveQueryManager;
|
|
177
201
|
remult.dataProvider = (await this.dataProvider);
|
|
178
202
|
await new Promise(res => {
|
|
179
|
-
|
|
203
|
+
remultObjectStorage.run(remult, async () => {
|
|
180
204
|
if (req) {
|
|
181
205
|
let user;
|
|
182
206
|
if (this.options.getUser)
|
|
@@ -218,7 +242,7 @@ class RemultServerImplementation {
|
|
|
218
242
|
})();
|
|
219
243
|
this.backendMethodsOpenApi.push({ path: myUrl, allowed, tag });
|
|
220
244
|
if (this.options.logApiEndPoints)
|
|
221
|
-
console.
|
|
245
|
+
console.info("[remult] " + myUrl);
|
|
222
246
|
if (queue) {
|
|
223
247
|
this.hasQueue = true;
|
|
224
248
|
this.queue.mapQueuedAction(myUrl, what);
|
package/esm/server/index.d.ts
CHANGED
|
@@ -1,4 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
export { queuedJobInfo, QueueStorage, GenericRequest, GenericRequestHandler, GenericResponse, GenericRouter, RemultServerOptions, SpecificRoute, RemultServer } from './expressBridge';
|
|
1
|
+
export { queuedJobInfo, QueueStorage, GenericRequest, GenericRequestHandler, GenericResponse, GenericRouter, createRemultServer, RemultServerOptions, SpecificRoute, RemultServer } from './expressBridge';
|
|
3
2
|
export { JsonEntityFileStorage, JsonFileDataProvider } from './JsonEntityFileStorage';
|
|
4
|
-
export declare function createRemultServer<RequestType extends GenericRequest = GenericRequest>(options?: RemultServerOptions<RequestType>): RemultServer;
|
package/esm/server/index.js
CHANGED
|
@@ -1,7 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
import { initAsyncHooks } from './initAsyncHooks';
|
|
1
|
+
export { createRemultServer } from './expressBridge';
|
|
3
2
|
export { JsonEntityFileStorage, JsonFileDataProvider } from './JsonEntityFileStorage';
|
|
4
|
-
export function createRemultServer(options) {
|
|
5
|
-
initAsyncHooks();
|
|
6
|
-
return createRemultServerCore(options);
|
|
7
|
-
}
|