remult 0.18.1-exp.2 → 0.18.1-exp.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -5
- package/esm/live-query/ably.d.ts +6 -6
- package/esm/live-query/ably.js +3 -3
- package/esm/live-query/index.d.ts +2 -3
- package/esm/live-query/index.js +1 -2
- package/esm/remult-express.d.ts +5 -4
- package/esm/remult-express.js +11 -11
- package/esm/remult-fastify.js +1 -1
- package/esm/remult-fresh.js +2 -2
- package/esm/remult-knex/index.js +9 -2
- package/esm/server/expressBridge.d.ts +13 -45
- package/esm/server/expressBridge.js +30 -26
- package/esm/server/index.d.ts +3 -1
- package/esm/server/index.js +6 -1
- package/esm/src/context.d.ts +5 -13
- package/esm/src/context.js +11 -36
- package/esm/src/data-api.js +12 -2
- package/esm/src/data-providers/rest-data-provider.d.ts +9 -4
- package/esm/src/data-providers/rest-data-provider.js +32 -21
- package/esm/src/live-query/EventSourceLiveQueryProvider.d.ts +3 -3
- package/esm/src/live-query/EventSourceLiveQueryProvider.js +3 -3
- package/esm/src/live-query/LiveQueryClient.d.ts +1 -19
- package/esm/src/live-query/LiveQueryClient.js +29 -38
- package/esm/src/live-query/LiveQueryPublisher.d.ts +9 -23
- package/esm/src/live-query/LiveQueryPublisher.js +9 -27
- package/esm/src/live-query/LiveQuerySubscriber.d.ts +11 -9
- package/esm/src/live-query/LiveQuerySubscriber.js +16 -6
- package/esm/src/remult-proxy.js +20 -0
- package/esm/src/remult3/RepositoryImplementation.d.ts +1 -0
- package/esm/src/remult3/RepositoryImplementation.js +7 -7
- package/esm/src/remult3/remult3.d.ts +11 -2
- package/esm/src/shared-tests/db-tests-setup.d.ts +1 -1
- package/esm/src/shared-tests/db-tests-setup.js +3 -1
- package/esm/src/shared-tests/db-tests.db-tests.js +1 -1
- package/esm/src/transactionLiveQueryPublisher.d.ts +15 -0
- package/esm/src/transactionLiveQueryPublisher.js +45 -0
- package/esm/src/valueConverters.js +2 -0
- package/live-query/ably.d.ts +6 -6
- package/live-query/ably.js +3 -3
- package/live-query/index.d.ts +2 -3
- package/live-query/index.js +1 -4
- package/package.json +9 -1
- package/remult-express.d.ts +5 -4
- package/remult-express.js +12 -12
- package/remult-fastify.js +2 -2
- package/remult-fresh.js +1 -1
- package/remult-knex/index.js +16 -8
- package/server/expressBridge.d.ts +13 -45
- package/server/expressBridge.js +37 -39
- package/server/index.d.ts +3 -1
- package/server/index.js +7 -2
- package/src/context.d.ts +5 -13
- package/src/context.js +10 -43
- package/src/data-api.js +13 -3
- package/src/data-providers/rest-data-provider.d.ts +9 -4
- package/src/data-providers/rest-data-provider.js +41 -22
- package/src/live-query/EventSourceLiveQueryProvider.d.ts +3 -3
- package/src/live-query/EventSourceLiveQueryProvider.js +7 -7
- package/src/live-query/LiveQueryClient.d.ts +1 -19
- package/src/live-query/LiveQueryClient.js +29 -38
- package/src/live-query/LiveQueryPublisher.d.ts +9 -23
- package/src/live-query/LiveQueryPublisher.js +8 -26
- package/src/live-query/LiveQuerySubscriber.d.ts +11 -9
- package/src/live-query/LiveQuerySubscriber.js +21 -8
- package/src/remult-proxy.js +28 -0
- package/src/remult3/RepositoryImplementation.d.ts +1 -0
- package/src/remult3/RepositoryImplementation.js +10 -7
- package/src/remult3/remult3.d.ts +11 -2
- package/src/shared-tests/db-tests-setup.d.ts +1 -1
- package/src/shared-tests/db-tests-setup.js +3 -1
- package/src/shared-tests/db-tests.db-tests.js +1 -1
- package/src/transactionLiveQueryPublisher.d.ts +15 -0
- package/src/transactionLiveQueryPublisher.js +76 -0
- package/src/valueConverters.js +2 -0
package/README.md
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
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>
|
|
6
7
|
<a href="https://circleci.com/gh/remult/remult/tree/master" rel="nofollow">
|
|
7
8
|
<img alt="CircleCI" src="https://circleci.com/gh/remult/remult/tree/master.svg?style=shield">
|
|
8
9
|
</a>
|
|
@@ -24,14 +25,29 @@
|
|
|
24
25
|
<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">
|
|
25
26
|
<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">
|
|
26
27
|
</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>
|
|
30
28
|
</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
|
+
|
|
31
39
|
<hr/>
|
|
40
|
+
<br/>
|
|
32
41
|
|
|
33
|
-
|
|
34
|
-
|
|
42
|
+
<a href="https://www.youtube.com/watch?v=rEoScmSVNUE" target="_blank">
|
|
43
|
+
<p align="center">
|
|
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>
|
|
35
51
|
|
|
36
52
|
## What is Remult?
|
|
37
53
|
|
|
@@ -39,6 +55,13 @@
|
|
|
39
55
|
as a single source of truth for your API, frontend type-safe API client and
|
|
40
56
|
backend ORM**.
|
|
41
57
|
|
|
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
|
+
|
|
42
65
|
### Status
|
|
43
66
|
|
|
44
67
|
Remult is **production-ready** and, in fact, used in production apps since 2018.
|
package/esm/live-query/ably.d.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import type * as Ably from 'ably';
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
4
|
-
export declare class AblyLiveQueryProvider implements
|
|
2
|
+
import type { SubscriptionServer } from '../src/live-query/LiveQueryPublisher';
|
|
3
|
+
import type { SubscriptionClient, SubscriptionClientConnection } from '../src/live-query/LiveQuerySubscriber';
|
|
4
|
+
export declare class AblyLiveQueryProvider implements SubscriptionClient {
|
|
5
5
|
private ably;
|
|
6
6
|
constructor(ably: Ably.Types.RealtimePromise);
|
|
7
|
-
|
|
7
|
+
openConnection(onReconnect: VoidFunction): Promise<SubscriptionClientConnection>;
|
|
8
8
|
}
|
|
9
|
-
export declare class AblyServerEventDispatcher implements
|
|
9
|
+
export declare class AblyServerEventDispatcher implements SubscriptionServer {
|
|
10
10
|
private ably;
|
|
11
11
|
constructor(ably: Ably.Types.RealtimePromise);
|
|
12
|
-
|
|
12
|
+
publishMessage<T>(channel: string, message: T): void;
|
|
13
13
|
}
|
package/esm/live-query/ably.js
CHANGED
|
@@ -2,9 +2,9 @@ export class AblyLiveQueryProvider {
|
|
|
2
2
|
constructor(ably) {
|
|
3
3
|
this.ably = ably;
|
|
4
4
|
}
|
|
5
|
-
async
|
|
5
|
+
async openConnection(onReconnect) {
|
|
6
6
|
return {
|
|
7
|
-
|
|
7
|
+
close: () => {
|
|
8
8
|
this.ably.connection.close();
|
|
9
9
|
},
|
|
10
10
|
subscribe: (channel, handler) => {
|
|
@@ -19,7 +19,7 @@ export class AblyServerEventDispatcher {
|
|
|
19
19
|
constructor(ably) {
|
|
20
20
|
this.ably = ably;
|
|
21
21
|
}
|
|
22
|
-
|
|
22
|
+
publishMessage(channel, message) {
|
|
23
23
|
this.ably.channels.get(channel).publish({ data: message });
|
|
24
24
|
}
|
|
25
25
|
}
|
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
3
|
-
export { LiveQueryClient } from '../src/live-query/LiveQueryClient';
|
|
1
|
+
export { SubscriptionServer as MessagePublisher, LiveQueryStorage, LiveQueryStorageInMemoryImplementation } from "../src/live-query/LiveQueryPublisher";
|
|
2
|
+
export { SubscriptionClientConnection as SubClientConnection, SubscriptionClient as SubClient, AMessageChannel } from '../src/live-query/LiveQuerySubscriber';
|
package/esm/live-query/index.js
CHANGED
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { LiveQueryStorageInMemoryImplementation } from "../src/live-query/LiveQueryPublisher";
|
|
2
2
|
export { AMessageChannel } from '../src/live-query/LiveQuerySubscriber';
|
|
3
|
-
export { LiveQueryClient } from '../src/live-query/LiveQueryClient';
|
package/esm/remult-express.d.ts
CHANGED
|
@@ -2,18 +2,19 @@ import * as express from 'express';
|
|
|
2
2
|
import { RemultServer, RemultServerOptions } from './server/expressBridge';
|
|
3
3
|
import { Remult } from './src/context';
|
|
4
4
|
import { ServerEventChannelSubscribeDTO } from './src/live-query/LiveQuerySubscriber';
|
|
5
|
-
import {
|
|
5
|
+
import { SubscriptionServer } from './src/live-query/LiveQueryPublisher';
|
|
6
|
+
import { MessagePublisher } from './live-query';
|
|
6
7
|
export declare function remultExpress(options?: RemultServerOptions<express.Request> & {
|
|
7
8
|
bodyParser?: boolean;
|
|
8
9
|
bodySizeLimit?: string;
|
|
9
|
-
|
|
10
|
+
subscriptionServer?: (router: express.Router, server: RemultServer) => MessagePublisher;
|
|
10
11
|
}): express.RequestHandler & RemultServer;
|
|
11
|
-
export declare class ServerEventsController implements
|
|
12
|
+
export declare class ServerEventsController implements SubscriptionServer {
|
|
12
13
|
private canUserConnectToChannel?;
|
|
13
14
|
subscribeToChannel({ channel, clientId }: ServerEventChannelSubscribeDTO, res: import('express').Response, remult: Remult, remove?: boolean): void;
|
|
14
15
|
connections: clientConnection[];
|
|
15
16
|
constructor(canUserConnectToChannel?: (channel: string, remult: Remult) => boolean);
|
|
16
|
-
|
|
17
|
+
publishMessage<T>(channel: string, message: any): void;
|
|
17
18
|
debugMessageFileSaver: (id: any, channel: any, message: any) => void;
|
|
18
19
|
openHttpServerStream(req: import('express').Request, res: import('express').Response): clientConnection;
|
|
19
20
|
debug(): void;
|
package/esm/remult-express.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import * as express from 'express';
|
|
2
|
-
import { createRemultServer } from './server/
|
|
2
|
+
import { createRemultServer } from './server/index';
|
|
3
3
|
import { liveQueryKeepAliveRoute, streamUrl } from './src/live-query/LiveQuerySubscriber';
|
|
4
|
-
import { LiveQueryPublisher, LiveQueryStorageInMemoryImplementation } from './src/live-query/LiveQueryPublisher';
|
|
5
4
|
import { v4 as uuid } from 'uuid';
|
|
6
5
|
import { remult } from './src/remult-proxy';
|
|
7
6
|
import { getEntityKey } from './src/remult3';
|
|
@@ -19,15 +18,15 @@ export function remultExpress(options) {
|
|
|
19
18
|
}
|
|
20
19
|
const server = createRemultServer(options);
|
|
21
20
|
server.registerRouter(app);
|
|
22
|
-
if (!options.
|
|
23
|
-
options.
|
|
21
|
+
if (!options.subscriptionServer) {
|
|
22
|
+
options.subscriptionServer = (router, server) => {
|
|
24
23
|
return buildHttpServerEventDispatcher(router, options.rootPath, server);
|
|
25
24
|
};
|
|
26
25
|
}
|
|
27
26
|
app.post(options.rootPath + liveQueryKeepAliveRoute, (r, res, n) => server.withRemult(r, res, n), async (req, res) => {
|
|
28
|
-
res.send(await remult.
|
|
27
|
+
res.send(await remult.liveQueryStorage.keepAliveAndReturnUnknownIds(req.body));
|
|
29
28
|
});
|
|
30
|
-
server.
|
|
29
|
+
server.runWithRequest = async (req, entityKey, what) => {
|
|
31
30
|
for (const e of options.entities) {
|
|
32
31
|
let key = getEntityKey(e);
|
|
33
32
|
if (key === entityKey) {
|
|
@@ -41,7 +40,8 @@ export function remultExpress(options) {
|
|
|
41
40
|
}
|
|
42
41
|
}
|
|
43
42
|
throw new Error("Couldn't find entity " + entityKey);
|
|
44
|
-
}
|
|
43
|
+
};
|
|
44
|
+
server.subscriptionServer = options.subscriptionServer(app, server);
|
|
45
45
|
return Object.assign(app, {
|
|
46
46
|
getRemult: (req) => server.getRemult(req),
|
|
47
47
|
openApiDoc: (options) => server.openApiDoc(options),
|
|
@@ -80,7 +80,7 @@ export class ServerEventsController {
|
|
|
80
80
|
}
|
|
81
81
|
res.sendStatus(404);
|
|
82
82
|
}
|
|
83
|
-
|
|
83
|
+
publishMessage(channel, message) {
|
|
84
84
|
const data = JSON.stringify({ channel, data: message });
|
|
85
85
|
for (const sc of this.connections) {
|
|
86
86
|
if (sc.channels[channel]) {
|
|
@@ -147,13 +147,13 @@ function buildHttpServerEventDispatcher(router, apiPath, server) {
|
|
|
147
147
|
const streamPath = apiPath + '/' + streamUrl;
|
|
148
148
|
let httpServerEvents = new ServerEventsController();
|
|
149
149
|
router.get(streamPath, (r, res, next) => server.withRemult(r, res, next), (req, res) => {
|
|
150
|
-
remult.
|
|
150
|
+
remult.subscriptionServer.openHttpServerStream(req, res);
|
|
151
151
|
});
|
|
152
152
|
router.post(streamPath + '/subscribe', (r, res, next) => server.withRemult(r, res, next), (req, res) => {
|
|
153
|
-
remult.
|
|
153
|
+
remult.subscriptionServer.subscribeToChannel(req.body, res, remult);
|
|
154
154
|
});
|
|
155
155
|
router.post(streamPath + '/unsubscribe', (r, res, next) => server.withRemult(r, res, next), (req, res) => {
|
|
156
|
-
remult.
|
|
156
|
+
remult.subscriptionServer.subscribeToChannel(req.body, res, remult, true);
|
|
157
157
|
});
|
|
158
158
|
return httpServerEvents;
|
|
159
159
|
}
|
package/esm/remult-fastify.js
CHANGED
package/esm/remult-fresh.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createRemultServerCore } from "./server/expressBridge";
|
|
2
2
|
export function remultFresh(options, response) {
|
|
3
|
-
const server =
|
|
3
|
+
const server = createRemultServerCore(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,8 +153,15 @@ 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
|
-
|
|
156
|
+
let newId;
|
|
157
|
+
if (this.knex.client.config.client === 'mysql2') {
|
|
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
|
+
}
|
|
158
165
|
resultFilter = new Filter(x => x.isEqualTo(this.entity.idMetadata.field, newId));
|
|
159
166
|
}
|
|
160
167
|
else
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { queuedJobInfoResponse } from '../src/server-action';
|
|
2
2
|
import { DataProvider } from '../src/data-interfaces';
|
|
3
|
-
import {
|
|
4
|
-
import { AllowedForInstance, Remult, UserInfo } from '../src/context';
|
|
3
|
+
import { Remult, UserInfo } from '../src/context';
|
|
5
4
|
import { ClassType } from '../classType';
|
|
6
5
|
import { Repository } from '../src/remult3';
|
|
7
6
|
import { IdEntity } from '../src/id-entity';
|
|
8
|
-
import {
|
|
7
|
+
import { LiveQueryStorage } from '../src/live-query/LiveQueryPublisher';
|
|
9
8
|
export interface RemultServerOptions<RequestType extends GenericRequest> {
|
|
10
9
|
/** Sets a database connection for Remult.
|
|
11
10
|
*
|
|
@@ -13,6 +12,7 @@ export interface RemultServerOptions<RequestType extends GenericRequest> {
|
|
|
13
12
|
*/
|
|
14
13
|
dataProvider?: DataProvider | Promise<DataProvider> | (() => Promise<DataProvider | undefined>);
|
|
15
14
|
queueStorage?: QueueStorage;
|
|
15
|
+
liveQueryStorageForRequest?: (remult?: Remult) => LiveQueryStorage | Promise<LiveQueryStorage>;
|
|
16
16
|
initRequest?: (remult: Remult, origReq: RequestType) => Promise<void>;
|
|
17
17
|
requestSerializer?: {
|
|
18
18
|
toJson: (request: RequestType) => any;
|
|
@@ -26,7 +26,7 @@ export interface RemultServerOptions<RequestType extends GenericRequest> {
|
|
|
26
26
|
controllers?: ClassType<any>[];
|
|
27
27
|
rootPath?: string;
|
|
28
28
|
}
|
|
29
|
-
export declare function
|
|
29
|
+
export declare function createRemultServerCore<RequestType extends GenericRequest = GenericRequest>(options?: RemultServerOptions<RequestType>): RemultServer;
|
|
30
30
|
export declare type GenericRequestHandler = (req: GenericRequest, res: GenericResponse, next: VoidFunction) => void;
|
|
31
31
|
export interface ServerHandleResponse {
|
|
32
32
|
data?: any;
|
|
@@ -63,39 +63,14 @@ export interface GenericResponse {
|
|
|
63
63
|
status(statusCode: number): GenericResponse;
|
|
64
64
|
end(): any;
|
|
65
65
|
}
|
|
66
|
-
export declare class
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
constructor(
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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>;
|
|
66
|
+
export declare class RemultAsyncLocalStorage {
|
|
67
|
+
private readonly remultObjectStorage;
|
|
68
|
+
static enable(): void;
|
|
69
|
+
static disable(): void;
|
|
70
|
+
constructor(remultObjectStorage: import('async_hooks').AsyncLocalStorage<Remult>);
|
|
71
|
+
run(remult: Remult, callback: VoidFunction): void;
|
|
72
|
+
getRemult(): Remult;
|
|
73
|
+
static instance: RemultAsyncLocalStorage;
|
|
99
74
|
}
|
|
100
75
|
export interface queuedJobInfo {
|
|
101
76
|
info: queuedJobInfoResponse;
|
|
@@ -121,12 +96,6 @@ export declare class EntityQueueStorage implements QueueStorage {
|
|
|
121
96
|
getJobInfo(queuedJobId: string): Promise<queuedJobInfo>;
|
|
122
97
|
createJob(url: string, userId: string): Promise<string>;
|
|
123
98
|
}
|
|
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
|
-
}
|
|
130
99
|
export declare class JobsInQueueEntity extends IdEntity {
|
|
131
100
|
userId: string;
|
|
132
101
|
url: string;
|
|
@@ -137,4 +106,3 @@ export declare class JobsInQueueEntity extends IdEntity {
|
|
|
137
106
|
error: boolean;
|
|
138
107
|
progress: number;
|
|
139
108
|
}
|
|
140
|
-
export {};
|
|
@@ -5,8 +5,9 @@ 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, LiveQueryStorageInMemoryImplementation } from '../src/live-query/LiveQueryPublisher';
|
|
8
9
|
;
|
|
9
|
-
export function
|
|
10
|
+
export function createRemultServerCore(options) {
|
|
10
11
|
if (!options) {
|
|
11
12
|
options = {};
|
|
12
13
|
}
|
|
@@ -30,30 +31,12 @@ export function createRemultServer(options) {
|
|
|
30
31
|
return dp;
|
|
31
32
|
return new (await import('./JsonEntityFileStorage')).JsonFileDataProvider('./db');
|
|
32
33
|
});
|
|
33
|
-
|
|
34
|
-
dataProvider = dataProvider.then(async (dp) => {
|
|
35
|
-
try {
|
|
36
|
-
const asyncHooks = await import('async_hooks');
|
|
37
|
-
remultObjectStorage = new RemultAsyncLocalStorage(new asyncHooks.AsyncLocalStorage());
|
|
38
|
-
}
|
|
39
|
-
catch {
|
|
40
|
-
remultObjectStorage = new RemultAsyncLocalStorage(undefined);
|
|
41
|
-
}
|
|
42
|
-
remult.remultFactory = () => {
|
|
43
|
-
const r = remultObjectStorage.getRemult();
|
|
44
|
-
if (r)
|
|
45
|
-
return r;
|
|
46
|
-
else
|
|
47
|
-
throw new Error("remult object was requested outside of a valid context, try running it within initApi or a remult request cycle");
|
|
48
|
-
};
|
|
49
|
-
return dp;
|
|
50
|
-
});
|
|
51
|
-
}
|
|
34
|
+
RemultAsyncLocalStorage.enable();
|
|
52
35
|
if (options.initApi) {
|
|
53
36
|
dataProvider = dataProvider.then(async (dp) => {
|
|
54
37
|
var remult = new Remult(dp);
|
|
55
38
|
await new Promise((res) => {
|
|
56
|
-
|
|
39
|
+
RemultAsyncLocalStorage.instance.run(remult, async () => {
|
|
57
40
|
await options.initApi(remult);
|
|
58
41
|
res({});
|
|
59
42
|
});
|
|
@@ -95,10 +78,22 @@ export function createRemultServer(options) {
|
|
|
95
78
|
return bridge;
|
|
96
79
|
}
|
|
97
80
|
;
|
|
98
|
-
class RemultAsyncLocalStorage {
|
|
81
|
+
export class RemultAsyncLocalStorage {
|
|
99
82
|
constructor(remultObjectStorage) {
|
|
100
83
|
this.remultObjectStorage = remultObjectStorage;
|
|
101
84
|
}
|
|
85
|
+
static enable() {
|
|
86
|
+
remult.remultFactory = () => {
|
|
87
|
+
const r = RemultAsyncLocalStorage.instance.getRemult();
|
|
88
|
+
if (r)
|
|
89
|
+
return r;
|
|
90
|
+
else
|
|
91
|
+
throw new Error("remult object was requested outside of a valid context, try running it within initApi or a remult request cycle");
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
static disable() {
|
|
95
|
+
remult.resetFactory();
|
|
96
|
+
}
|
|
102
97
|
run(remult, callback) {
|
|
103
98
|
if (this.remultObjectStorage)
|
|
104
99
|
this.remultObjectStorage.run(remult, callback);
|
|
@@ -107,17 +102,19 @@ class RemultAsyncLocalStorage {
|
|
|
107
102
|
}
|
|
108
103
|
getRemult() {
|
|
109
104
|
if (!this.remultObjectStorage) {
|
|
110
|
-
throw "can't use static remult in this environment, `async_hooks` were not
|
|
105
|
+
throw new Error("can't use static remult in this environment, `async_hooks` were not initialized");
|
|
111
106
|
}
|
|
112
107
|
return this.remultObjectStorage.getStore();
|
|
113
108
|
}
|
|
114
109
|
}
|
|
115
|
-
|
|
110
|
+
RemultAsyncLocalStorage.instance = new RemultAsyncLocalStorage(undefined);
|
|
111
|
+
/* @internal*/
|
|
116
112
|
export class RemultServerImplementation {
|
|
117
113
|
constructor(queue, options, dataProvider) {
|
|
118
114
|
this.queue = queue;
|
|
119
115
|
this.options = options;
|
|
120
116
|
this.dataProvider = dataProvider;
|
|
117
|
+
this.liveQueryStorage = new LiveQueryStorageInMemoryImplementation();
|
|
121
118
|
this.registeredRouter = false;
|
|
122
119
|
this.hasQueue = false;
|
|
123
120
|
/* internal */
|
|
@@ -197,10 +194,12 @@ export class RemultServerImplementation {
|
|
|
197
194
|
let myReq = new ExpressRequestBridgeToDataApiRequest(req);
|
|
198
195
|
let myRes = new ExpressResponseBridgeToDataApiResponse(res, req);
|
|
199
196
|
let remult = new Remult();
|
|
200
|
-
remult.liveQueryPublisher = this.
|
|
197
|
+
remult.liveQueryPublisher = new LiveQueryPublisher(() => remult.subscriptionServer, () => remult.liveQueryStorage, this.runWithRequest);
|
|
201
198
|
remult.dataProvider = (await this.dataProvider);
|
|
199
|
+
remult.subscriptionServer = this.subscriptionServer;
|
|
200
|
+
remult.liveQueryStorage = this.liveQueryStorage;
|
|
202
201
|
await new Promise(res => {
|
|
203
|
-
|
|
202
|
+
RemultAsyncLocalStorage.instance.run(remult, async () => {
|
|
204
203
|
if (req) {
|
|
205
204
|
let user;
|
|
206
205
|
if (this.options.getUser)
|
|
@@ -216,6 +215,11 @@ export class RemultServerImplementation {
|
|
|
216
215
|
if (this.options.initRequest) {
|
|
217
216
|
await this.options.initRequest(remult, req);
|
|
218
217
|
}
|
|
218
|
+
if (this.options.liveQueryStorageForRequest) {
|
|
219
|
+
const r = await this.options.liveQueryStorageForRequest(remult);
|
|
220
|
+
if (r)
|
|
221
|
+
remult.liveQueryStorage = r;
|
|
222
|
+
}
|
|
219
223
|
await what(remult, myReq, myRes, req);
|
|
220
224
|
res({});
|
|
221
225
|
});
|
package/esm/server/index.d.ts
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import { GenericRequest, RemultServer, RemultServerOptions } from './expressBridge';
|
|
2
|
+
export { queuedJobInfo, QueueStorage, GenericRequest, GenericRequestHandler, GenericResponse, GenericRouter, RemultServerOptions, SpecificRoute, RemultServer } from './expressBridge';
|
|
2
3
|
export { JsonEntityFileStorage, JsonFileDataProvider } from './JsonEntityFileStorage';
|
|
4
|
+
export declare function createRemultServer<RequestType extends GenericRequest = GenericRequest>(options?: RemultServerOptions<RequestType>): RemultServer;
|
package/esm/server/index.js
CHANGED
|
@@ -1,2 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import { createRemultServerCore } from './expressBridge';
|
|
2
|
+
import { initAsyncHooks } from './initAsyncHooks';
|
|
2
3
|
export { JsonEntityFileStorage, JsonFileDataProvider } from './JsonEntityFileStorage';
|
|
4
|
+
export function createRemultServer(options) {
|
|
5
|
+
initAsyncHooks();
|
|
6
|
+
return createRemultServerCore(options);
|
|
7
|
+
}
|
package/esm/src/context.d.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { DataProvider } from "./data-interfaces";
|
|
2
2
|
import { EntityMetadata, EntityRef, FindOptions, Repository } from "./remult3";
|
|
3
3
|
import { ClassType } from "../classType";
|
|
4
|
-
import {
|
|
5
|
-
import type { ServerEventDispatcher } from "../live-query";
|
|
6
|
-
import { LiveQueryStorage } from "./live-query/LiveQueryPublisher";
|
|
4
|
+
import type { MessagePublisher } from "../live-query";
|
|
7
5
|
import { ExternalHttpProvider } from "./buildRestDataProvider";
|
|
6
|
+
import { SubscriptionClient } from "./live-query/LiveQuerySubscriber";
|
|
8
7
|
export declare function isBackend(): boolean;
|
|
9
8
|
export declare class Remult {
|
|
10
9
|
/**Return's a `Repository` of the specific entity type
|
|
@@ -38,11 +37,10 @@ export declare class Remult {
|
|
|
38
37
|
constructor(http: ExternalHttpProvider | typeof fetch | ApiClient);
|
|
39
38
|
constructor(p: DataProvider);
|
|
40
39
|
constructor();
|
|
41
|
-
|
|
40
|
+
subscriptionServer?: MessagePublisher;
|
|
42
41
|
call<T extends ((...args: any[]) => Promise<any>)>(backendMethod: T, classInstance?: any, ...args: GetArguments<T>): ReturnType<T>;
|
|
43
42
|
/** The current data provider */
|
|
44
43
|
dataProvider: DataProvider;
|
|
45
|
-
liveQuerySubscriber: LiveQueryClient;
|
|
46
44
|
/** A helper callback that can be used to debug and trace all find operations. Useful in debugging scenarios */
|
|
47
45
|
static onFind: (metadata: EntityMetadata, options: FindOptions<any>) => void;
|
|
48
46
|
clearAllCache(): any;
|
|
@@ -57,6 +55,8 @@ export interface RemultContext {
|
|
|
57
55
|
export interface ApiClient {
|
|
58
56
|
httpClient?: ExternalHttpProvider | typeof fetch;
|
|
59
57
|
url?: string;
|
|
58
|
+
subscriptionClient?: SubscriptionClient;
|
|
59
|
+
wrapMessageHandling?: (x: VoidFunction) => void;
|
|
60
60
|
}
|
|
61
61
|
export declare const allEntities: ClassType<any>[];
|
|
62
62
|
export interface ControllerOptions {
|
|
@@ -90,14 +90,6 @@ export declare class EventSource {
|
|
|
90
90
|
fire(): Promise<void>;
|
|
91
91
|
dispatcher: EventDispatcher;
|
|
92
92
|
}
|
|
93
|
-
export interface LiveQueryPublisherInterface {
|
|
94
|
-
stopLiveQuery(id: any): void;
|
|
95
|
-
sendChannelMessage<messageType>(channel: string, message: messageType): void;
|
|
96
|
-
defineLiveQueryChannel(serializeRequest: () => any, entityKey: string, options: FindOptions<any>, ids: any[], userId: string, repo: Repository<any>): string;
|
|
97
|
-
itemChanged(entityKey: string, changes: itemChange[]): void;
|
|
98
|
-
dispatcher: ServerEventDispatcher;
|
|
99
|
-
storage: LiveQueryStorage;
|
|
100
|
-
}
|
|
101
93
|
export interface itemChange {
|
|
102
94
|
id: any;
|
|
103
95
|
oldId: any;
|
package/esm/src/context.js
CHANGED
|
@@ -2,7 +2,7 @@ import { actionInfo, serverActionField } from './server-action';
|
|
|
2
2
|
import { RestDataProvider } from './data-providers/rest-data-provider';
|
|
3
3
|
import { RepositoryImplementation } from "./remult3/RepositoryImplementation";
|
|
4
4
|
import { LiveQueryClient } from "./live-query/LiveQueryClient";
|
|
5
|
-
import {
|
|
5
|
+
import { EventSourceSubClient } from "./live-query/EventSourceLiveQueryProvider";
|
|
6
6
|
import { RemultProxy } from "./remult-proxy";
|
|
7
7
|
import { buildRestDataProvider, isExternalHttpProvider } from "./buildRestDataProvider";
|
|
8
8
|
export function isBackend() {
|
|
@@ -12,24 +12,18 @@ export class Remult {
|
|
|
12
12
|
constructor(provider) {
|
|
13
13
|
/* @internal */
|
|
14
14
|
this.repCache = new Map();
|
|
15
|
+
/* @internal*/
|
|
15
16
|
this.liveQueryPublisher = {
|
|
16
|
-
itemChanged: () => { }
|
|
17
|
-
defineLiveQueryChannel: () => "",
|
|
18
|
-
dispatcher: undefined,
|
|
19
|
-
sendChannelMessage: function (arg0, what) {
|
|
20
|
-
throw new Error("invalid publisher.");
|
|
21
|
-
},
|
|
22
|
-
stopLiveQuery: function (id) {
|
|
23
|
-
throw new Error("Function not implemented.");
|
|
24
|
-
},
|
|
25
|
-
storage: undefined
|
|
17
|
+
itemChanged: () => { }
|
|
26
18
|
};
|
|
27
19
|
/** The current data provider */
|
|
28
20
|
this.dataProvider = new RestDataProvider(() => this.apiClient);
|
|
29
|
-
|
|
21
|
+
/* @internal*/
|
|
22
|
+
this.liveQuerySubscriber = new LiveQueryClient(() => this.apiClient);
|
|
30
23
|
this.context = {};
|
|
31
24
|
this.apiClient = {
|
|
32
|
-
url: '/api'
|
|
25
|
+
url: '/api',
|
|
26
|
+
subscriptionClient: new EventSourceSubClient()
|
|
33
27
|
};
|
|
34
28
|
if (provider && provider.getEntityDataProvider) {
|
|
35
29
|
this.dataProvider = provider;
|
|
@@ -46,6 +40,10 @@ export class Remult {
|
|
|
46
40
|
this.apiClient.httpClient = apiClient.httpClient;
|
|
47
41
|
if (apiClient.url)
|
|
48
42
|
this.apiClient.url = apiClient.url;
|
|
43
|
+
if (apiClient.subscriptionClient)
|
|
44
|
+
this.apiClient.subscriptionClient = apiClient.subscriptionClient;
|
|
45
|
+
if (apiClient.wrapMessageHandling)
|
|
46
|
+
this.apiClient.wrapMessageHandling = apiClient.wrapMessageHandling;
|
|
49
47
|
}
|
|
50
48
|
}
|
|
51
49
|
/**Return's a `Repository` of the specific entity type
|
|
@@ -188,22 +186,11 @@ export async function doTransaction(remult, what) {
|
|
|
188
186
|
trans.flush();
|
|
189
187
|
});
|
|
190
188
|
}
|
|
191
|
-
// TODO - talk about message size limit in ably - something that we may reach if we group the messages
|
|
192
189
|
class transactionLiveQueryPublisher {
|
|
193
190
|
constructor(orig) {
|
|
194
191
|
this.orig = orig;
|
|
195
192
|
this.transactionItems = new Map();
|
|
196
193
|
}
|
|
197
|
-
get storage() {
|
|
198
|
-
return this.orig.storage;
|
|
199
|
-
}
|
|
200
|
-
;
|
|
201
|
-
set storage(value) {
|
|
202
|
-
this.orig.storage = value;
|
|
203
|
-
}
|
|
204
|
-
stopLiveQuery(id) {
|
|
205
|
-
this.orig.stopLiveQuery(id);
|
|
206
|
-
}
|
|
207
194
|
itemChanged(entityKey, changes) {
|
|
208
195
|
let items = this.transactionItems.get(entityKey);
|
|
209
196
|
if (!items) {
|
|
@@ -230,16 +217,4 @@ class transactionLiveQueryPublisher {
|
|
|
230
217
|
this.orig.itemChanged(key, this.transactionItems.get(key));
|
|
231
218
|
}
|
|
232
219
|
}
|
|
233
|
-
sendChannelMessage(channel, message) {
|
|
234
|
-
this.orig.sendChannelMessage(channel, message);
|
|
235
|
-
}
|
|
236
|
-
defineLiveQueryChannel(serializeRequest, entityKey, options, ids, userId, repo) {
|
|
237
|
-
return this.orig.defineLiveQueryChannel(serializeRequest, entityKey, options, ids, userId, repo);
|
|
238
|
-
}
|
|
239
|
-
get dispatcher() {
|
|
240
|
-
return this.orig.dispatcher;
|
|
241
|
-
}
|
|
242
|
-
set dispatcher(value) {
|
|
243
|
-
this.orig.dispatcher = value;
|
|
244
|
-
}
|
|
245
220
|
}
|
package/esm/src/data-api.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { v4 as uuid } from 'uuid';
|
|
1
2
|
import { customUrlToken, buildFilterFromRequestParameters } from './filter/filter-interfaces';
|
|
2
3
|
import { doTransaction } from './context';
|
|
3
4
|
import { Filter } from './filter/filter-interfaces';
|
|
4
5
|
import { ForbiddenError } from './server-action';
|
|
5
6
|
import { getId } from './remult3/getId';
|
|
7
|
+
import { findOptionsToJson } from './data-providers/rest-data-provider';
|
|
6
8
|
export class DataApi {
|
|
7
9
|
constructor(repository, remult) {
|
|
8
10
|
this.repository = repository;
|
|
@@ -29,7 +31,7 @@ export class DataApi {
|
|
|
29
31
|
case "count":
|
|
30
32
|
return this.count(res, req, body);
|
|
31
33
|
case "endLiveQuery":
|
|
32
|
-
this.remult.
|
|
34
|
+
this.remult.liveQueryStorage.remove(body.id);
|
|
33
35
|
res.success("ok");
|
|
34
36
|
return;
|
|
35
37
|
default:
|
|
@@ -127,8 +129,16 @@ export class DataApi {
|
|
|
127
129
|
}
|
|
128
130
|
try {
|
|
129
131
|
const r = await this.getArrayImpl(response, request, filterBody);
|
|
132
|
+
const queryChannel = `users:${this.remult.user?.id}:queries:${uuid()}`;
|
|
133
|
+
this.remult.liveQueryStorage.store({
|
|
134
|
+
requestJson: serializeRequest(),
|
|
135
|
+
entityKey: this.repository.metadata.key,
|
|
136
|
+
id: queryChannel,
|
|
137
|
+
findOptionsJson: findOptionsToJson(r.findOptions, this.repository.metadata),
|
|
138
|
+
lastIds: r.r.map(y => getId(this.repository.metadata, y))
|
|
139
|
+
});
|
|
130
140
|
response.success({
|
|
131
|
-
queryChannel
|
|
141
|
+
queryChannel,
|
|
132
142
|
result: r.r
|
|
133
143
|
});
|
|
134
144
|
}
|