remult 0.18.1-exp.35 → 0.18.1-exp.37

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 CHANGED
@@ -61,6 +61,7 @@ backend ORM**.
61
61
  * :lock: Fine-grained code-based API authorization
62
62
  * :relieved: Incrementally adoptable
63
63
  * :rocket: Production ready
64
+ * :mega: **NEW - Zero-boilerplate realtime live-queries**
64
65
 
65
66
  ### Status
66
67
 
@@ -6,32 +6,48 @@ import { Repository } from '../src/remult3';
6
6
  import { IdEntity } from '../src/id-entity';
7
7
  import { LiveQueryStorage, SubscriptionServer } from '../src/live-query/SubscriptionServer';
8
8
  export interface RemultServerOptions<RequestType extends GenericRequest> {
9
- /** Sets a database connection for Remult.
9
+ /**Entities to use for the api */
10
+ entities?: ClassType<any>[];
11
+ /**Controller to use for the api */
12
+ controllers?: ClassType<any>[];
13
+ /** Will be called to get the current user based on the current request */
14
+ getUser?: (request: RequestType) => Promise<UserInfo | undefined>;
15
+ /** Will be called for each request and can be used for configuration */
16
+ initRequest?: (request: RequestType, options: InitRequestOptions) => Promise<void>;
17
+ /** Will be called once the server is loaded and the data provider is ready */
18
+ initApi?: (remult: Remult) => void | Promise<void>;
19
+ /** Data Provider to use for the api.
10
20
  *
11
21
  * @see [Connecting to a Database](https://remult.dev/docs/databases.html).
12
22
  */
13
23
  dataProvider?: DataProvider | Promise<DataProvider> | (() => Promise<DataProvider | undefined>);
14
- queueStorage?: QueueStorage;
15
- liveQueryStorage?: LiveQueryStorage;
16
- subscriptionServer?: SubscriptionServer;
17
- initRequest?: (request: RequestType, options: InitRequestOptions) => Promise<void>;
18
- requestSerializer?: {
19
- toJson: (request: RequestType) => any;
20
- fromJson: (request: any) => RequestType;
21
- };
22
24
  /** Will create tables and columns in supporting databases. default: true
23
25
  *
24
26
  * @description
25
27
  * when set to true, it'll create entities that do not exist, and add columns that are missing.
26
28
  */
27
29
  ensureSchema?: boolean;
28
- getUser?: (request: RequestType) => Promise<UserInfo | undefined>;
29
- initApi?: (remult: Remult) => void | Promise<void>;
30
- logApiEndPoints?: boolean;
31
- defaultGetLimit?: number;
32
- entities?: ClassType<any>[];
33
- controllers?: ClassType<any>[];
30
+ /** The path to use for the api, default:/api
31
+ *
32
+ * @description
33
+ * If you want to use a different api path adjust this field
34
+ */
34
35
  rootPath?: string;
36
+ /** The default limit to use for find requests that did not specify a limit */
37
+ defaultGetLimit?: number;
38
+ /** When set to true (default) it'll console log each api endpoint that is created */
39
+ logApiEndPoints?: boolean;
40
+ /** A subscription server to use for live query and message channels */
41
+ subscriptionServer?: SubscriptionServer;
42
+ /** A storage to use to store live queries, relevant mostly for serverless scenarios or larger scales */
43
+ liveQueryStorage?: LiveQueryStorage;
44
+ /** Used to store the relevent request info for re running a live query */
45
+ requestSerializer?: {
46
+ toJson: (request: RequestType) => any;
47
+ fromJson: (request: any) => RequestType;
48
+ };
49
+ /** Storage to use for backend methods that use queue */
50
+ queueStorage?: QueueStorage;
35
51
  }
36
52
  export interface InitRequestOptions {
37
53
  liveQueryStorage: LiveQueryStorage;
@@ -2,4 +2,5 @@ import { GenericRequest, RemultServer, RemultServerOptions } from './expressBrid
2
2
  export { queuedJobInfo, QueueStorage, GenericRequest, GenericRequestHandler, GenericResponse, GenericRouter, RemultServerOptions, SpecificRoute, RemultServer, InitRequestOptions } from './expressBridge';
3
3
  export { JsonEntityFileStorage, JsonFileDataProvider } from './JsonEntityFileStorage';
4
4
  export { DataProviderLiveQueryStorage } from '../live-query/data-provider-live-query-storage';
5
+ export { SseSubscriptionServer } from '../SseSubscriptionServer';
5
6
  export declare function createRemultServer<RequestType extends GenericRequest = GenericRequest>(options?: RemultServerOptions<RequestType>): RemultServer;
@@ -2,6 +2,7 @@ import { createRemultServerCore, } from './expressBridge';
2
2
  import { initAsyncHooks } from './initAsyncHooks';
3
3
  export { JsonEntityFileStorage, JsonFileDataProvider } from './JsonEntityFileStorage';
4
4
  export { DataProviderLiveQueryStorage } from '../live-query/data-provider-live-query-storage';
5
+ export { SseSubscriptionServer } from '../SseSubscriptionServer';
5
6
  export function createRemultServer(options) {
6
7
  initAsyncHooks();
7
8
  return createRemultServerCore(options);
@@ -305,3 +305,26 @@ class myDummySQLCommand {
305
305
  return val.toString();
306
306
  }
307
307
  }
308
+ async function bulkInsert(array, db) {
309
+ if (array.length == 0)
310
+ return;
311
+ const chunkSize = 250;
312
+ for (let i = 0; i < array.length; i += chunkSize) {
313
+ const items = array.slice(i, i + chunkSize);
314
+ // do whatever
315
+ const c = db.createCommand();
316
+ let sql = 'insert into ' +
317
+ (await items[0]._.metadata.getDbName()) +
318
+ ' (' +
319
+ (await Promise.all(items[0]._.metadata.fields.toArray().map((f) => f.getDbName()))).join(',') +
320
+ ') values ';
321
+ sql += items
322
+ .map((row) => '(' +
323
+ row.$.toArray()
324
+ .map((f) => c.addParameterAndReturnSqlToken(f.metadata.valueConverter.toDb(f.value)))
325
+ .join(', ') +
326
+ ')')
327
+ .join(',');
328
+ await c.execute(sql);
329
+ }
330
+ }
@@ -36,6 +36,6 @@ export declare class SubscriptionChannel<messageType> {
36
36
  channelKey: string;
37
37
  constructor(channelKey: string);
38
38
  publish(message: messageType, remult?: Remult): void;
39
- subscribe(next: (message: messageType) => void, remult?: Remult): any;
40
- subscribe(listener: Partial<SubscriptionListener<messageType>>): any;
39
+ subscribe(next: (message: messageType) => void, remult?: Remult): Promise<Unsubscribe>;
40
+ subscribe(listener: Partial<SubscriptionListener<messageType>>): Promise<Unsubscribe>;
41
41
  }
@@ -115,7 +115,7 @@ export interface EntityMetadata<entityType = any> {
115
115
  * @example
116
116
  * const taskRepo = remult.repo(Task);
117
117
  * if (taskRepo.metadata.apiUpdateAllowed){
118
- * //Allow user to edit the entity
118
+ * // Allow user to edit the entity
119
119
  * }
120
120
  */
121
121
  readonly apiUpdateAllowed: boolean;
@@ -123,7 +123,7 @@ export interface EntityMetadata<entityType = any> {
123
123
  * @example
124
124
  * const taskRepo = remult.repo(Task);
125
125
  * if (taskRepo.metadata.apiReadAllowed){
126
- * taskRepo.find()
126
+ * await taskRepo.find()
127
127
  * }
128
128
  */
129
129
  readonly apiReadAllowed: boolean;
@@ -131,7 +131,7 @@ export interface EntityMetadata<entityType = any> {
131
131
  * @example
132
132
  * const taskRepo = remult.repo(Task);
133
133
  * if (taskRepo.metadata.apiDeleteAllowed){
134
- * //display delete button
134
+ * // display delete button
135
135
  * }
136
136
  */
137
137
  readonly apiDeleteAllowed: boolean;
@@ -139,7 +139,7 @@ export interface EntityMetadata<entityType = any> {
139
139
  * @example
140
140
  * const taskRepo = remult.repo(Task);
141
141
  * if (taskRepo.metadata.apiInsertAllowed){
142
- * //display insert button
142
+ * // display insert button
143
143
  * }
144
144
  */
145
145
  readonly apiInsertAllowed: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remult",
3
- "version": "0.18.1-exp.35",
3
+ "version": "0.18.1-exp.37",
4
4
  "description": "A CRUD framework for full-stack TypeScript",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -6,32 +6,48 @@ import { Repository } from '../src/remult3';
6
6
  import { IdEntity } from '../src/id-entity';
7
7
  import { LiveQueryStorage, SubscriptionServer } from '../src/live-query/SubscriptionServer';
8
8
  export interface RemultServerOptions<RequestType extends GenericRequest> {
9
- /** Sets a database connection for Remult.
9
+ /**Entities to use for the api */
10
+ entities?: ClassType<any>[];
11
+ /**Controller to use for the api */
12
+ controllers?: ClassType<any>[];
13
+ /** Will be called to get the current user based on the current request */
14
+ getUser?: (request: RequestType) => Promise<UserInfo | undefined>;
15
+ /** Will be called for each request and can be used for configuration */
16
+ initRequest?: (request: RequestType, options: InitRequestOptions) => Promise<void>;
17
+ /** Will be called once the server is loaded and the data provider is ready */
18
+ initApi?: (remult: Remult) => void | Promise<void>;
19
+ /** Data Provider to use for the api.
10
20
  *
11
21
  * @see [Connecting to a Database](https://remult.dev/docs/databases.html).
12
22
  */
13
23
  dataProvider?: DataProvider | Promise<DataProvider> | (() => Promise<DataProvider | undefined>);
14
- queueStorage?: QueueStorage;
15
- liveQueryStorage?: LiveQueryStorage;
16
- subscriptionServer?: SubscriptionServer;
17
- initRequest?: (request: RequestType, options: InitRequestOptions) => Promise<void>;
18
- requestSerializer?: {
19
- toJson: (request: RequestType) => any;
20
- fromJson: (request: any) => RequestType;
21
- };
22
24
  /** Will create tables and columns in supporting databases. default: true
23
25
  *
24
26
  * @description
25
27
  * when set to true, it'll create entities that do not exist, and add columns that are missing.
26
28
  */
27
29
  ensureSchema?: boolean;
28
- getUser?: (request: RequestType) => Promise<UserInfo | undefined>;
29
- initApi?: (remult: Remult) => void | Promise<void>;
30
- logApiEndPoints?: boolean;
31
- defaultGetLimit?: number;
32
- entities?: ClassType<any>[];
33
- controllers?: ClassType<any>[];
30
+ /** The path to use for the api, default:/api
31
+ *
32
+ * @description
33
+ * If you want to use a different api path adjust this field
34
+ */
34
35
  rootPath?: string;
36
+ /** The default limit to use for find requests that did not specify a limit */
37
+ defaultGetLimit?: number;
38
+ /** When set to true (default) it'll console log each api endpoint that is created */
39
+ logApiEndPoints?: boolean;
40
+ /** A subscription server to use for live query and message channels */
41
+ subscriptionServer?: SubscriptionServer;
42
+ /** A storage to use to store live queries, relevant mostly for serverless scenarios or larger scales */
43
+ liveQueryStorage?: LiveQueryStorage;
44
+ /** Used to store the relevent request info for re running a live query */
45
+ requestSerializer?: {
46
+ toJson: (request: RequestType) => any;
47
+ fromJson: (request: any) => RequestType;
48
+ };
49
+ /** Storage to use for backend methods that use queue */
50
+ queueStorage?: QueueStorage;
35
51
  }
36
52
  export interface InitRequestOptions {
37
53
  liveQueryStorage: LiveQueryStorage;
package/server/index.d.ts CHANGED
@@ -2,4 +2,5 @@ import { GenericRequest, RemultServer, RemultServerOptions } from './expressBrid
2
2
  export { queuedJobInfo, QueueStorage, GenericRequest, GenericRequestHandler, GenericResponse, GenericRouter, RemultServerOptions, SpecificRoute, RemultServer, InitRequestOptions } from './expressBridge';
3
3
  export { JsonEntityFileStorage, JsonFileDataProvider } from './JsonEntityFileStorage';
4
4
  export { DataProviderLiveQueryStorage } from '../live-query/data-provider-live-query-storage';
5
+ export { SseSubscriptionServer } from '../SseSubscriptionServer';
5
6
  export declare function createRemultServer<RequestType extends GenericRequest = GenericRequest>(options?: RemultServerOptions<RequestType>): RemultServer;
package/server/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createRemultServer = exports.DataProviderLiveQueryStorage = exports.JsonFileDataProvider = exports.JsonEntityFileStorage = void 0;
3
+ exports.createRemultServer = exports.SseSubscriptionServer = exports.DataProviderLiveQueryStorage = exports.JsonFileDataProvider = exports.JsonEntityFileStorage = void 0;
4
4
  var expressBridge_1 = require("./expressBridge");
5
5
  var initAsyncHooks_1 = require("./initAsyncHooks");
6
6
  var JsonEntityFileStorage_1 = require("./JsonEntityFileStorage");
@@ -8,6 +8,8 @@ Object.defineProperty(exports, "JsonEntityFileStorage", { enumerable: true, get:
8
8
  Object.defineProperty(exports, "JsonFileDataProvider", { enumerable: true, get: function () { return JsonEntityFileStorage_1.JsonFileDataProvider; } });
9
9
  var data_provider_live_query_storage_1 = require("../live-query/data-provider-live-query-storage");
10
10
  Object.defineProperty(exports, "DataProviderLiveQueryStorage", { enumerable: true, get: function () { return data_provider_live_query_storage_1.DataProviderLiveQueryStorage; } });
11
+ var SseSubscriptionServer_1 = require("../SseSubscriptionServer");
12
+ Object.defineProperty(exports, "SseSubscriptionServer", { enumerable: true, get: function () { return SseSubscriptionServer_1.SseSubscriptionServer; } });
11
13
  function createRemultServer(options) {
12
14
  initAsyncHooks_1.initAsyncHooks();
13
15
  return expressBridge_1.createRemultServerCore(options);
@@ -515,3 +515,64 @@ var myDummySQLCommand = /** @class */ (function () {
515
515
  };
516
516
  return myDummySQLCommand;
517
517
  }());
518
+ function bulkInsert(array, db) {
519
+ return tslib_1.__awaiter(this, void 0, void 0, function () {
520
+ var chunkSize, _loop_2, i;
521
+ return tslib_1.__generator(this, function (_a) {
522
+ switch (_a.label) {
523
+ case 0:
524
+ if (array.length == 0)
525
+ return [2 /*return*/];
526
+ chunkSize = 250;
527
+ _loop_2 = function (i) {
528
+ var items, c, sql, _a, _b;
529
+ return tslib_1.__generator(this, function (_c) {
530
+ switch (_c.label) {
531
+ case 0:
532
+ items = array.slice(i, i + chunkSize);
533
+ c = db.createCommand();
534
+ _a = 'insert into ';
535
+ return [4 /*yield*/, items[0]._.metadata.getDbName()];
536
+ case 1:
537
+ _b = _a +
538
+ (_c.sent()) +
539
+ ' (';
540
+ return [4 /*yield*/, Promise.all(items[0]._.metadata.fields.toArray().map(function (f) { return f.getDbName(); }))];
541
+ case 2:
542
+ sql = _b +
543
+ (_c.sent()).join(',') +
544
+ ') values ';
545
+ sql += items
546
+ .map(function (row) {
547
+ return '(' +
548
+ row.$.toArray()
549
+ .map(function (f) {
550
+ return c.addParameterAndReturnSqlToken(f.metadata.valueConverter.toDb(f.value));
551
+ })
552
+ .join(', ') +
553
+ ')';
554
+ })
555
+ .join(',');
556
+ return [4 /*yield*/, c.execute(sql)];
557
+ case 3:
558
+ _c.sent();
559
+ return [2 /*return*/];
560
+ }
561
+ });
562
+ };
563
+ i = 0;
564
+ _a.label = 1;
565
+ case 1:
566
+ if (!(i < array.length)) return [3 /*break*/, 4];
567
+ return [5 /*yield**/, _loop_2(i)];
568
+ case 2:
569
+ _a.sent();
570
+ _a.label = 3;
571
+ case 3:
572
+ i += chunkSize;
573
+ return [3 /*break*/, 1];
574
+ case 4: return [2 /*return*/];
575
+ }
576
+ });
577
+ });
578
+ }
@@ -36,6 +36,6 @@ export declare class SubscriptionChannel<messageType> {
36
36
  channelKey: string;
37
37
  constructor(channelKey: string);
38
38
  publish(message: messageType, remult?: Remult): void;
39
- subscribe(next: (message: messageType) => void, remult?: Remult): any;
40
- subscribe(listener: Partial<SubscriptionListener<messageType>>): any;
39
+ subscribe(next: (message: messageType) => void, remult?: Remult): Promise<Unsubscribe>;
40
+ subscribe(listener: Partial<SubscriptionListener<messageType>>): Promise<Unsubscribe>;
41
41
  }
@@ -115,7 +115,7 @@ export interface EntityMetadata<entityType = any> {
115
115
  * @example
116
116
  * const taskRepo = remult.repo(Task);
117
117
  * if (taskRepo.metadata.apiUpdateAllowed){
118
- * //Allow user to edit the entity
118
+ * // Allow user to edit the entity
119
119
  * }
120
120
  */
121
121
  readonly apiUpdateAllowed: boolean;
@@ -123,7 +123,7 @@ export interface EntityMetadata<entityType = any> {
123
123
  * @example
124
124
  * const taskRepo = remult.repo(Task);
125
125
  * if (taskRepo.metadata.apiReadAllowed){
126
- * taskRepo.find()
126
+ * await taskRepo.find()
127
127
  * }
128
128
  */
129
129
  readonly apiReadAllowed: boolean;
@@ -131,7 +131,7 @@ export interface EntityMetadata<entityType = any> {
131
131
  * @example
132
132
  * const taskRepo = remult.repo(Task);
133
133
  * if (taskRepo.metadata.apiDeleteAllowed){
134
- * //display delete button
134
+ * // display delete button
135
135
  * }
136
136
  */
137
137
  readonly apiDeleteAllowed: boolean;
@@ -139,7 +139,7 @@ export interface EntityMetadata<entityType = any> {
139
139
  * @example
140
140
  * const taskRepo = remult.repo(Task);
141
141
  * if (taskRepo.metadata.apiInsertAllowed){
142
- * //display insert button
142
+ * // display insert button
143
143
  * }
144
144
  */
145
145
  readonly apiInsertAllowed: boolean;
@@ -1,13 +0,0 @@
1
- import type * as Ably from 'ably';
2
- import type { SubscriptionServer } from '../src/live-query/SubscriptionServer';
3
- import type { SubscriptionClient, SubscriptionClientConnection } from '../src/live-query/SubscriptionChannel';
4
- export declare class AblySubscriptionClient implements SubscriptionClient {
5
- private ably;
6
- constructor(ably: Ably.Types.RealtimePromise);
7
- openConnection(onReconnect: VoidFunction): Promise<SubscriptionClientConnection>;
8
- }
9
- export declare class AblySubscriptionServer implements SubscriptionServer {
10
- private ably;
11
- constructor(ably: Ably.Types.RestPromise);
12
- publishMessage<T>(channel: string, message: T): Promise<void>;
13
- }
@@ -1,28 +0,0 @@
1
- export class AblySubscriptionClient {
2
- constructor(ably) {
3
- this.ably = ably;
4
- }
5
- async openConnection(onReconnect) {
6
- return {
7
- close: () => {
8
- // Since we did not open the connection, we do not close it
9
- },
10
- subscribe: async (channel, handler) => {
11
- let myHandler = (y) => handler(y.data);
12
- await this.ably.channels.get(channel).subscribe(y => myHandler(y));
13
- return () => {
14
- myHandler = () => { };
15
- this.ably.channels.get(channel).unsubscribe();
16
- };
17
- }
18
- };
19
- }
20
- }
21
- export class AblySubscriptionServer {
22
- constructor(ably) {
23
- this.ably = ably;
24
- }
25
- async publishMessage(channel, message) {
26
- await this.ably.channels.get(channel).publish({ data: message });
27
- }
28
- }
@@ -1,13 +0,0 @@
1
- import type * as Ably from 'ably';
2
- import type { SubscriptionServer } from '../src/live-query/SubscriptionServer';
3
- import type { SubscriptionClient, SubscriptionClientConnection } from '../src/live-query/SubscriptionChannel';
4
- export declare class AblySubscriptionClient implements SubscriptionClient {
5
- private ably;
6
- constructor(ably: Ably.Types.RealtimePromise);
7
- openConnection(onReconnect: VoidFunction): Promise<SubscriptionClientConnection>;
8
- }
9
- export declare class AblySubscriptionServer implements SubscriptionServer {
10
- private ably;
11
- constructor(ably: Ably.Types.RestPromise);
12
- publishMessage<T>(channel: string, message: T): Promise<void>;
13
- }
@@ -1,59 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AblySubscriptionServer = exports.AblySubscriptionClient = void 0;
4
- var tslib_1 = require("tslib");
5
- var AblySubscriptionClient = /** @class */ (function () {
6
- function AblySubscriptionClient(ably) {
7
- this.ably = ably;
8
- }
9
- AblySubscriptionClient.prototype.openConnection = function (onReconnect) {
10
- return tslib_1.__awaiter(this, void 0, void 0, function () {
11
- var _this = this;
12
- return tslib_1.__generator(this, function (_a) {
13
- return [2 /*return*/, {
14
- close: function () {
15
- // Since we did not open the connection, we do not close it
16
- },
17
- subscribe: function (channel, handler) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
18
- var myHandler;
19
- var _this = this;
20
- return tslib_1.__generator(this, function (_a) {
21
- switch (_a.label) {
22
- case 0:
23
- myHandler = function (y) { return handler(y.data); };
24
- return [4 /*yield*/, this.ably.channels.get(channel).subscribe(function (y) { return myHandler(y); })];
25
- case 1:
26
- _a.sent();
27
- return [2 /*return*/, function () {
28
- myHandler = function () { };
29
- _this.ably.channels.get(channel).unsubscribe();
30
- }];
31
- }
32
- });
33
- }); }
34
- }];
35
- });
36
- });
37
- };
38
- return AblySubscriptionClient;
39
- }());
40
- exports.AblySubscriptionClient = AblySubscriptionClient;
41
- var AblySubscriptionServer = /** @class */ (function () {
42
- function AblySubscriptionServer(ably) {
43
- this.ably = ably;
44
- }
45
- AblySubscriptionServer.prototype.publishMessage = function (channel, message) {
46
- return tslib_1.__awaiter(this, void 0, void 0, function () {
47
- return tslib_1.__generator(this, function (_a) {
48
- switch (_a.label) {
49
- case 0: return [4 /*yield*/, this.ably.channels.get(channel).publish({ data: message })];
50
- case 1:
51
- _a.sent();
52
- return [2 /*return*/];
53
- }
54
- });
55
- });
56
- };
57
- return AblySubscriptionServer;
58
- }());
59
- exports.AblySubscriptionServer = AblySubscriptionServer;