remult 0.18.1-exp.1 → 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.
Files changed (74) hide show
  1. package/README.md +28 -5
  2. package/esm/live-query/ably.d.ts +6 -6
  3. package/esm/live-query/ably.js +3 -3
  4. package/esm/live-query/index.d.ts +2 -3
  5. package/esm/live-query/index.js +1 -2
  6. package/esm/remult-express.d.ts +5 -4
  7. package/esm/remult-express.js +11 -11
  8. package/esm/remult-fastify.js +1 -1
  9. package/esm/remult-fresh.js +2 -2
  10. package/esm/remult-knex/index.js +9 -2
  11. package/esm/server/expressBridge.d.ts +13 -45
  12. package/esm/server/expressBridge.js +30 -26
  13. package/esm/server/index.d.ts +3 -1
  14. package/esm/server/index.js +6 -1
  15. package/esm/src/context.d.ts +5 -13
  16. package/esm/src/context.js +14 -42
  17. package/esm/src/data-api.js +12 -2
  18. package/esm/src/data-providers/rest-data-provider.d.ts +9 -4
  19. package/esm/src/data-providers/rest-data-provider.js +32 -21
  20. package/esm/src/live-query/EventSourceLiveQueryProvider.d.ts +3 -3
  21. package/esm/src/live-query/EventSourceLiveQueryProvider.js +3 -3
  22. package/esm/src/live-query/LiveQueryClient.d.ts +1 -19
  23. package/esm/src/live-query/LiveQueryClient.js +30 -38
  24. package/esm/src/live-query/LiveQueryPublisher.d.ts +9 -22
  25. package/esm/src/live-query/LiveQueryPublisher.js +17 -26
  26. package/esm/src/live-query/LiveQuerySubscriber.d.ts +11 -9
  27. package/esm/src/live-query/LiveQuerySubscriber.js +17 -6
  28. package/esm/src/remult-proxy.js +20 -0
  29. package/esm/src/remult3/RepositoryImplementation.d.ts +1 -0
  30. package/esm/src/remult3/RepositoryImplementation.js +7 -7
  31. package/esm/src/remult3/remult3.d.ts +11 -2
  32. package/esm/src/shared-tests/db-tests-setup.d.ts +1 -1
  33. package/esm/src/shared-tests/db-tests-setup.js +3 -1
  34. package/esm/src/shared-tests/db-tests.db-tests.js +1 -1
  35. package/esm/src/transactionLiveQueryPublisher.d.ts +15 -0
  36. package/esm/src/transactionLiveQueryPublisher.js +45 -0
  37. package/esm/src/valueConverters.js +2 -0
  38. package/live-query/ably.d.ts +6 -6
  39. package/live-query/ably.js +3 -3
  40. package/live-query/index.d.ts +2 -3
  41. package/live-query/index.js +1 -4
  42. package/package.json +9 -1
  43. package/remult-express.d.ts +5 -4
  44. package/remult-express.js +12 -12
  45. package/remult-fastify.js +2 -2
  46. package/remult-fresh.js +1 -1
  47. package/remult-knex/index.js +16 -8
  48. package/server/expressBridge.d.ts +13 -45
  49. package/server/expressBridge.js +37 -39
  50. package/server/index.d.ts +3 -1
  51. package/server/index.js +7 -2
  52. package/src/context.d.ts +5 -13
  53. package/src/context.js +22 -59
  54. package/src/data-api.js +13 -3
  55. package/src/data-providers/rest-data-provider.d.ts +9 -4
  56. package/src/data-providers/rest-data-provider.js +41 -22
  57. package/src/live-query/EventSourceLiveQueryProvider.d.ts +3 -3
  58. package/src/live-query/EventSourceLiveQueryProvider.js +7 -7
  59. package/src/live-query/LiveQueryClient.d.ts +1 -19
  60. package/src/live-query/LiveQueryClient.js +30 -38
  61. package/src/live-query/LiveQueryPublisher.d.ts +9 -22
  62. package/src/live-query/LiveQueryPublisher.js +16 -25
  63. package/src/live-query/LiveQuerySubscriber.d.ts +11 -9
  64. package/src/live-query/LiveQuerySubscriber.js +22 -8
  65. package/src/remult-proxy.js +28 -0
  66. package/src/remult3/RepositoryImplementation.d.ts +1 -0
  67. package/src/remult3/RepositoryImplementation.js +10 -7
  68. package/src/remult3/remult3.d.ts +11 -2
  69. package/src/shared-tests/db-tests-setup.d.ts +1 -1
  70. package/src/shared-tests/db-tests-setup.js +3 -1
  71. package/src/shared-tests/db-tests.db-tests.js +1 -1
  72. package/src/transactionLiveQueryPublisher.d.ts +15 -0
  73. package/src/transactionLiveQueryPublisher.js +76 -0
  74. 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
- [Getting Started](#getting-started) | [Documentation](#documentation) |
34
- [Example Apps](#example-apps)
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.
@@ -1,13 +1,13 @@
1
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 {
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
- openStreamAndReturnCloseFunction(onReconnect: VoidFunction): Promise<PubSubClient>;
7
+ openConnection(onReconnect: VoidFunction): Promise<SubscriptionClientConnection>;
8
8
  }
9
- export declare class AblyServerEventDispatcher implements ServerEventDispatcher {
9
+ export declare class AblyServerEventDispatcher implements SubscriptionServer {
10
10
  private ably;
11
11
  constructor(ably: Ably.Types.RealtimePromise);
12
- sendChannelMessage<T>(channel: string, message: T): void;
12
+ publishMessage<T>(channel: string, message: T): void;
13
13
  }
@@ -2,9 +2,9 @@ export class AblyLiveQueryProvider {
2
2
  constructor(ably) {
3
3
  this.ably = ably;
4
4
  }
5
- async openStreamAndReturnCloseFunction(onReconnect) {
5
+ async openConnection(onReconnect) {
6
6
  return {
7
- disconnect: () => {
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
- sendChannelMessage(channel, message) {
22
+ publishMessage(channel, message) {
23
23
  this.ably.channels.get(channel).publish({ data: message });
24
24
  }
25
25
  }
@@ -1,3 +1,2 @@
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';
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';
@@ -1,3 +1,2 @@
1
- export { LiveQueryPublisher, LiveQueryStorageInMemoryImplementation } from "../src/live-query/LiveQueryPublisher";
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';
@@ -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 { ServerEventDispatcher } from './src/live-query/LiveQueryPublisher';
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
- serverEventDispatcher?: (router: express.Router, server: RemultServer) => ServerEventDispatcher;
10
+ subscriptionServer?: (router: express.Router, server: RemultServer) => MessagePublisher;
10
11
  }): express.RequestHandler & RemultServer;
11
- export declare class ServerEventsController implements ServerEventDispatcher {
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
- sendChannelMessage<T>(channel: string, message: any): void;
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;
@@ -1,7 +1,6 @@
1
1
  import * as express from 'express';
2
- import { createRemultServer } from './server/expressBridge';
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.serverEventDispatcher) {
23
- options.serverEventDispatcher = (router, server) => {
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.liveQueryPublisher.storage.keepAliveAndReturnUnknownIds(req.body));
27
+ res.send(await remult.liveQueryStorage.keepAliveAndReturnUnknownIds(req.body));
29
28
  });
30
- server.liveQueryManager = new LiveQueryPublisher(options.serverEventDispatcher(app, server), new LiveQueryStorageInMemoryImplementation(), async (req, entityKey, what) => {
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
- sendChannelMessage(channel, message) {
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.liveQueryPublisher.dispatcher.openHttpServerStream(req, res);
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.liveQueryPublisher.dispatcher.subscribeToChannel(req.body, res, 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.liveQueryPublisher.dispatcher.subscribeToChannel(req.body, res, remult, true);
156
+ remult.subscriptionServer.subscribeToChannel(req.body, res, remult, true);
157
157
  });
158
158
  return httpServerEvents;
159
159
  }
@@ -1,4 +1,4 @@
1
- import { createRemultServer } from './server/expressBridge';
1
+ import { createRemultServer } from './server/index';
2
2
  export function remultFastify(options) {
3
3
  function fastifyHandler(handler) {
4
4
  const response = (req, res) => {
@@ -1,6 +1,6 @@
1
- import { createRemultServer } from "./server/expressBridge";
1
+ import { createRemultServerCore } from "./server/expressBridge";
2
2
  export function remultFresh(options, response) {
3
- const server = createRemultServer(options);
3
+ const server = createRemultServerCore(options);
4
4
  return {
5
5
  getRemult: r => server.getRemult(r),
6
6
  openApiDoc: x => server.openApiDoc(x),
@@ -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 result = await insert.returning(this.entity.idMetadata.field.key);
157
- let newId = result[0].id;
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 { ActionInterface, queuedJobInfoResponse } from '../src/server-action';
1
+ import { queuedJobInfoResponse } from '../src/server-action';
2
2
  import { DataProvider } from '../src/data-interfaces';
3
- import { DataApi, DataApiRequest, DataApiResponse } from '../src/data-api';
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 { LiveQueryPublisher } from '../src/live-query/LiveQueryPublisher';
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 createRemultServer<RequestType extends GenericRequest = GenericRequest>(options?: RemultServerOptions<RequestType>): RemultServer;
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 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>;
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 createRemultServer(options) {
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
- 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
+ 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
- remultObjectStorage.run(remult, async () => {
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 found";
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
- let remultObjectStorage;
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.liveQueryManager;
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
- remultObjectStorage.run(remult, async () => {
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
  });
@@ -1,2 +1,4 @@
1
- export { queuedJobInfo, QueueStorage, GenericRequest, GenericRequestHandler, GenericResponse, GenericRouter, createRemultServer, RemultServerOptions, SpecificRoute, RemultServer } from './expressBridge';
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;
@@ -1,2 +1,7 @@
1
- export { createRemultServer } from './expressBridge';
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
+ }
@@ -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 { LiveQueryClient } from "./live-query/LiveQueryClient";
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
- liveQueryPublisher: LiveQueryPublisherInterface;
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;
@@ -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 { EventSourceLiveQueryProvider } from "./live-query/EventSourceLiveQueryProvider";
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
- this.liveQuerySubscriber = new LiveQueryClient(new EventSourceLiveQueryProvider());
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) {
@@ -226,23 +213,8 @@ class transactionLiveQueryPublisher {
226
213
  }
227
214
  }
228
215
  flush() {
229
- //TODO - figure out and resolve
230
- setTimeout(() => {
231
- for (const key of this.transactionItems.keys()) {
232
- this.orig.itemChanged(key, this.transactionItems.get(key));
233
- }
234
- }, 100);
235
- }
236
- sendChannelMessage(channel, message) {
237
- this.orig.sendChannelMessage(channel, message);
238
- }
239
- defineLiveQueryChannel(serializeRequest, entityKey, options, ids, userId, repo) {
240
- return this.orig.defineLiveQueryChannel(serializeRequest, entityKey, options, ids, userId, repo);
241
- }
242
- get dispatcher() {
243
- return this.orig.dispatcher;
244
- }
245
- set dispatcher(value) {
246
- this.orig.dispatcher = value;
216
+ for (const key of this.transactionItems.keys()) {
217
+ this.orig.itemChanged(key, this.transactionItems.get(key));
218
+ }
247
219
  }
248
220
  }