remult 0.16.3-exp.3 → 0.16.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 (84) hide show
  1. package/README.md +27 -5
  2. package/esm/index.d.ts +2 -1
  3. package/esm/index.js +2 -1
  4. package/esm/postgres/postgres-data-provider.d.ts +1 -3
  5. package/esm/postgres/postgres-data-provider.js +1 -11
  6. package/esm/postgres/postgresColumnSyntax.d.ts +2 -0
  7. package/esm/postgres/postgresColumnSyntax.js +30 -0
  8. package/esm/postgres/schema-builder.js +20 -20
  9. package/esm/remult-express.js +2 -0
  10. package/esm/remult-fastify.js +2 -0
  11. package/esm/remult-knex/index.d.ts +28 -4
  12. package/esm/remult-knex/index.js +33 -46
  13. package/esm/remult-middleware.d.ts +2 -0
  14. package/esm/remult-middleware.js +65 -0
  15. package/esm/remult-mongo.d.ts +2 -8
  16. package/esm/remult-mongo.js +8 -22
  17. package/esm/server/expressBridge.d.ts +9 -0
  18. package/esm/server/expressBridge.js +18 -24
  19. package/esm/server/initAsyncHooks.d.ts +1 -0
  20. package/esm/server/initAsyncHooks.js +9 -0
  21. package/esm/server/register-actions-on-server.d.ts +3 -0
  22. package/esm/server/register-actions-on-server.js +24 -0
  23. package/esm/server/register-entities-on-server.d.ts +3 -0
  24. package/esm/server/register-entities-on-server.js +15 -0
  25. package/esm/src/backend-tests/my-test.backend-spec.js +48 -64
  26. package/esm/src/data-providers/sql-database.d.ts +0 -2
  27. package/esm/src/data-providers/sql-database.js +15 -26
  28. package/esm/src/data-providers/web-sql-data-provider.d.ts +0 -3
  29. package/esm/src/data-providers/web-sql-data-provider.js +8 -16
  30. package/esm/src/filter/filter-consumer-bridge-to-sql-request.d.ts +14 -13
  31. package/esm/src/filter/filter-consumer-bridge-to-sql-request.js +19 -52
  32. package/esm/src/filter/filter-interfaces.d.ts +3 -3
  33. package/esm/src/filter/filter-interfaces.js +1 -1
  34. package/esm/src/remult-proxy.js +4 -0
  35. package/esm/src/remult3/RepositoryImplementation.d.ts +2 -1
  36. package/esm/src/remult3/RepositoryImplementation.js +12 -17
  37. package/esm/src/shared-tests/db-tests-setup.js +2 -1
  38. package/esm/src/tests/entityForCustomFilter.js +7 -6
  39. package/esm/src/tests/frontend-database-tests-setup.spec.js +2 -1
  40. package/esm/src/tests/testHelper.spec.js +1 -0
  41. package/esm/valueConverters.d.ts +12 -0
  42. package/esm/valueConverters.js +169 -0
  43. package/index.d.ts +2 -1
  44. package/index.js +3 -3
  45. package/package.json +1 -1
  46. package/postgres/postgres-data-provider.d.ts +1 -3
  47. package/postgres/postgres-data-provider.js +1 -11
  48. package/postgres/postgresColumnSyntax.d.ts +2 -0
  49. package/postgres/postgresColumnSyntax.js +34 -0
  50. package/postgres/schema-builder.js +19 -19
  51. package/remult-express.js +2 -0
  52. package/remult-fastify.js +2 -0
  53. package/remult-knex/index.d.ts +28 -4
  54. package/remult-knex/index.js +38 -68
  55. package/remult-middleware.d.ts +2 -0
  56. package/remult-middleware.js +70 -0
  57. package/remult-mongo.d.ts +2 -8
  58. package/remult-mongo.js +8 -39
  59. package/server/expressBridge.d.ts +9 -0
  60. package/server/expressBridge.js +19 -34
  61. package/server/initAsyncHooks.d.ts +1 -0
  62. package/server/initAsyncHooks.js +13 -0
  63. package/server/register-actions-on-server.d.ts +3 -0
  64. package/server/register-actions-on-server.js +50 -0
  65. package/server/register-entities-on-server.d.ts +3 -0
  66. package/server/register-entities-on-server.js +19 -0
  67. package/src/backend-tests/my-test.backend-spec.js +55 -210
  68. package/src/data-providers/sql-database.d.ts +0 -2
  69. package/src/data-providers/sql-database.js +14 -25
  70. package/src/data-providers/web-sql-data-provider.d.ts +0 -3
  71. package/src/data-providers/web-sql-data-provider.js +7 -15
  72. package/src/filter/filter-consumer-bridge-to-sql-request.d.ts +14 -13
  73. package/src/filter/filter-consumer-bridge-to-sql-request.js +46 -95
  74. package/src/filter/filter-interfaces.d.ts +3 -3
  75. package/src/filter/filter-interfaces.js +1 -1
  76. package/src/remult-proxy.js +4 -0
  77. package/src/remult3/RepositoryImplementation.d.ts +2 -1
  78. package/src/remult3/RepositoryImplementation.js +16 -31
  79. package/src/shared-tests/db-tests-setup.js +3 -2
  80. package/src/tests/entityForCustomFilter.js +7 -6
  81. package/src/tests/frontend-database-tests-setup.spec.js +5 -4
  82. package/src/tests/testHelper.spec.js +4 -3
  83. package/valueConverters.d.ts +12 -0
  84. package/valueConverters.js +168 -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,28 @@
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
+ <br/>
31
+
32
+ <div align="center">
33
+ <a href="#getting-started">Getting Started</a> |
34
+ <a href="#documentation">Documentation</a> |
35
+ <a href="#example-apps">Example Apps</a>
36
+ </div>
37
+
31
38
  <hr/>
39
+ <br/>
32
40
 
33
- [Getting Started](#getting-started) | [Documentation](#documentation) |
34
- [Example Apps](#example-apps)
41
+ <a href="https://www.youtube.com/watch?v=rEoScmSVNUE" target="_blank">
42
+ <p align="center">
43
+ <img src="https://github.com/remult/remult/raw/master/static/images/video-thumbnail.jpg" alt="Video thumbnail" width="320" />
44
+ </p>
45
+ </a>
46
+
47
+ <a href="https://www.youtube.com/watch?v=rEoScmSVNUE" target="_blank">
48
+ <p align="center">Watch code demo on YouTube here</p>
49
+ </a>
35
50
 
36
51
  ## What is Remult?
37
52
 
@@ -39,6 +54,13 @@
39
54
  as a single source of truth for your API, frontend type-safe API client and
40
55
  backend ORM**.
41
56
 
57
+ * :zap: Zero-boilerplate CRUD API routes with paging, sorting, and filtering for Express / Fastify / Next.js / NestJS / Koa / others...
58
+ * :ok_hand: Fullstack type-safety for API queries, mutations and RPC, without code generation
59
+ * :sparkles: Input validation, defined once, runs both on the backend and on the frontend for best UX
60
+ * :lock: Fine-grained code-based API authorization
61
+ * :relieved: Incrementally adoptable
62
+ * :rocket: Production ready
63
+
42
64
  ### Status
43
65
 
44
66
  Remult is **production-ready** and, in fact, used in production apps since 2018.
package/esm/index.d.ts CHANGED
@@ -10,7 +10,7 @@ export { InMemoryDataProvider } from './src/data-providers/in-memory-database';
10
10
  export { ArrayEntityDataProvider } from './src/data-providers/array-entity-data-provider';
11
11
  export { WebSqlDataProvider } from './src/data-providers/web-sql-data-provider';
12
12
  export { SqlDatabase, } from './src/data-providers/sql-database';
13
- export { CustomSqlFilterObject, CustomSqlFilterBuilder, dbNamesOf, sqlCondition } from './src/filter/filter-consumer-bridge-to-sql-request';
13
+ export { CustomSqlFilterObject, CustomSqlFilterBuilder } from './src/filter/filter-consumer-bridge-to-sql-request';
14
14
  export { JsonDataProvider, JsonEntityStorage } from './src/data-providers/json-data-provider';
15
15
  export { Controller, BackendMethodOptions, BackendMethod, ProgressListener } from './src/server-action';
16
16
  export { Allowed, Allow, Remult, RemultContext, ApiClient, isBackend, AllowedForInstance, EventDispatcher, EventSource, ExternalHttpProvider, Unobserve, UserInfo } from './src/context';
@@ -18,6 +18,7 @@ export { IdEntity } from './src/id-entity';
18
18
  export { SortSegment, Sort } from './src/sort';
19
19
  export { OneToMany, CompoundIdField } from './src/column';
20
20
  export { Filter } from './src/filter/filter-interfaces';
21
+ export { FilterConsumerBridgeToSqlRequest } from './src/filter/filter-consumer-bridge-to-sql-request';
21
22
  export { UrlBuilder } from './urlBuilder';
22
23
  export { Validators } from './src/validators';
23
24
  export { ValueConverters } from './src/valueConverters';
package/esm/index.js CHANGED
@@ -8,7 +8,7 @@ export { InMemoryDataProvider } from './src/data-providers/in-memory-database';
8
8
  export { ArrayEntityDataProvider } from './src/data-providers/array-entity-data-provider'; //V
9
9
  export { WebSqlDataProvider } from './src/data-providers/web-sql-data-provider'; //V
10
10
  export { SqlDatabase, } from './src/data-providers/sql-database'; //V
11
- export { CustomSqlFilterBuilder, dbNamesOf, sqlCondition } from './src/filter/filter-consumer-bridge-to-sql-request';
11
+ export { CustomSqlFilterBuilder } from './src/filter/filter-consumer-bridge-to-sql-request';
12
12
  export { JsonDataProvider } from './src/data-providers/json-data-provider'; //V
13
13
  //export * from './src/data-api'; //reconsider if to make internal
14
14
  export { Controller, BackendMethod, ProgressListener } from './src/server-action';
@@ -17,6 +17,7 @@ export { IdEntity } from './src/id-entity';
17
17
  export { Sort } from './src/sort';
18
18
  export { OneToMany, CompoundIdField } from './src/column';
19
19
  export { Filter } from './src/filter/filter-interfaces';
20
+ export { FilterConsumerBridgeToSqlRequest } from './src/filter/filter-consumer-bridge-to-sql-request';
20
21
  export { UrlBuilder } from './urlBuilder';
21
22
  export { Validators } from './src/validators';
22
23
  export { ValueConverters } from './src/valueConverters';
@@ -1,5 +1,4 @@
1
- import { ClientBase, PoolConfig, QueryResult } from 'pg';
2
- import { Remult } from '../src/context';
1
+ import { PoolConfig, QueryResult } from 'pg';
3
2
  import { EntityMetadata } from '../src/remult3';
4
3
  import { SqlCommand, SqlImplementation } from '../src/sql-command';
5
4
  import { SqlDatabase } from '../src/data-providers/sql-database';
@@ -11,7 +10,6 @@ export interface PostgresClient extends PostgresCommandSource {
11
10
  }
12
11
  export declare class PostgresDataProvider implements SqlImplementation {
13
12
  private pool;
14
- static getDb(remult?: Remult): ClientBase;
15
13
  entityIsUsedForTheFirstTime(entity: EntityMetadata): Promise<void>;
16
14
  getLimitSqlSyntax(limit: number, offset: number): string;
17
15
  createCommand(): SqlCommand;
@@ -6,14 +6,6 @@ export class PostgresDataProvider {
6
6
  constructor(pool) {
7
7
  this.pool = pool;
8
8
  }
9
- static getDb(remult) {
10
- const sql = SqlDatabase.getDb(remult);
11
- const me = sql._getSourceSql();
12
- if (!me.pool) {
13
- throw "the data provider is not a PostgresDataProvider";
14
- }
15
- return me.pool;
16
- }
17
9
  async entityIsUsedForTheFirstTime(entity) {
18
10
  }
19
11
  getLimitSqlSyntax(limit, offset) {
@@ -30,9 +22,7 @@ export class PostgresDataProvider {
30
22
  createCommand: () => new PostgresBridgeToSQLCommand(client),
31
23
  entityIsUsedForTheFirstTime: this.entityIsUsedForTheFirstTime,
32
24
  transaction: () => { throw "nested transactions not allowed"; },
33
- getLimitSqlSyntax: this.getLimitSqlSyntax,
34
- //@ts-ignore
35
- pool: client
25
+ getLimitSqlSyntax: this.getLimitSqlSyntax
36
26
  });
37
27
  await client.query('COMMIT');
38
28
  }
@@ -0,0 +1,2 @@
1
+ import { FieldMetadata } from '../src/column-interfaces';
2
+ export declare function postgresColumnSyntax(x: FieldMetadata, dbName: string): string;
@@ -0,0 +1,30 @@
1
+ import { ValueConverters } from '../valueConverters';
2
+ export function postgresColumnSyntax(x, dbName) {
3
+ let result = dbName;
4
+ if (x.valueType == Number) {
5
+ if (!x.valueConverter.fieldTypeInDb)
6
+ result += " numeric" + (x.allowNull ? "" : " default 0 not null");
7
+ else
8
+ result += " " + x.valueConverter.fieldTypeInDb + (x.allowNull ? "" : " default 0 not null");
9
+ }
10
+ else if (x.valueType == Date) {
11
+ if (!x.valueConverter.fieldTypeInDb)
12
+ if (x.valueConverter == ValueConverters.DateOnly)
13
+ result += " date";
14
+ else
15
+ result += " timestamp";
16
+ else
17
+ result += " " + x.valueConverter.fieldTypeInDb;
18
+ }
19
+ else if (x.valueType == Boolean)
20
+ result += " boolean" + (x.allowNull ? "" : " default false not null");
21
+ else if (x.valueConverter.fieldTypeInDb) {
22
+ result += " " + x.valueConverter.fieldTypeInDb;
23
+ if (!x.allowNull && x.valueConverter.fieldTypeInDb == 'integer') {
24
+ result += " default 0 not null";
25
+ }
26
+ }
27
+ else
28
+ result += " varchar" + (x.allowNull ? "" : " default '' not null");
29
+ return result;
30
+ }
@@ -1,5 +1,5 @@
1
1
  import { allEntities } from '../src/context';
2
- import { dbNamesOf, isDbReadonly } from '../src/filter/filter-consumer-bridge-to-sql-request';
2
+ import { getDbNameProvider } from '../src/filter/filter-consumer-bridge-to-sql-request';
3
3
  import { isAutoIncrement } from '../src/remult3';
4
4
  import { ValueConverters } from '../src/valueConverters';
5
5
  export function postgresColumnSyntax(x, dbName) {
@@ -46,57 +46,57 @@ export class PostgresSchemaBuilder {
46
46
  console.log("start verify structure");
47
47
  for (const entityClass of allEntities) {
48
48
  let entity = remult.repo(entityClass).metadata;
49
- let e = await dbNamesOf(entity);
49
+ let e = await getDbNameProvider(entity);
50
50
  try {
51
51
  if (!entity.options.sqlExpression) {
52
- if ((await e.$entityName).toLowerCase().indexOf('from ') < 0) {
52
+ if ((await e.entityName).toLowerCase().indexOf('from ') < 0) {
53
53
  await this.createIfNotExist(entity);
54
54
  await this.verifyAllColumns(entity);
55
55
  }
56
56
  }
57
57
  }
58
58
  catch (err) {
59
- console.log("failed verify structure of " + e.$entityName + " ", err);
59
+ console.log("failed verify structure of " + e.entityName + " ", err);
60
60
  }
61
61
  }
62
62
  }
63
63
  async createIfNotExist(entity) {
64
64
  var c = this.pool.createCommand();
65
- let e = await dbNamesOf(entity);
66
- await c.execute("select 1 from information_Schema.tables where table_name=" + c.addParameterAndReturnSqlToken((e.$entityName).toLowerCase()) + this.additionalWhere).then(async (r) => {
65
+ let e = await getDbNameProvider(entity);
66
+ await c.execute("select 1 from information_Schema.tables where table_name=" + c.addParameterAndReturnSqlToken((e.entityName).toLowerCase()) + this.additionalWhere).then(async (r) => {
67
67
  if (r.rows.length == 0) {
68
68
  let result = '';
69
69
  for (const x of entity.fields) {
70
- if (!isDbReadonly(x, e) || isAutoIncrement(x)) {
70
+ if (!e.isDbReadonly(x) || isAutoIncrement(x)) {
71
71
  if (result.length != 0)
72
72
  result += ',';
73
73
  result += '\r\n ';
74
74
  if (isAutoIncrement(x))
75
- result += e.dbNameOf(x) + ' serial';
75
+ result += e.nameOf(x) + ' serial';
76
76
  else {
77
- result += postgresColumnSyntax(x, e.dbNameOf(x));
77
+ result += postgresColumnSyntax(x, e.nameOf(x));
78
78
  if (x == entity.idMetadata.field)
79
79
  result += ' primary key';
80
80
  }
81
81
  }
82
82
  }
83
- let sql = 'create table ' + e.$entityName + ' (' + result + '\r\n)';
83
+ let sql = 'create table ' + e.entityName + ' (' + result + '\r\n)';
84
84
  //console.log(sql);
85
85
  await this.pool.execute(sql);
86
86
  }
87
87
  });
88
88
  }
89
89
  async addColumnIfNotExist(entity, c) {
90
- let e = await dbNamesOf(entity);
91
- if (isDbReadonly(c(entity), e))
90
+ let e = await getDbNameProvider(entity);
91
+ if (e.isDbReadonly(c(entity)))
92
92
  return;
93
93
  try {
94
94
  let cmd = this.pool.createCommand();
95
- const colName = e.dbNameOf(c(entity));
95
+ const colName = e.nameOf(c(entity));
96
96
  if ((await cmd.execute(`select 1
97
97
  FROM information_schema.columns
98
- WHERE table_name=${cmd.addParameterAndReturnSqlToken((e.$entityName).toLocaleLowerCase())} and column_name=${cmd.addParameterAndReturnSqlToken((colName).toLocaleLowerCase())}` + this.additionalWhere)).rows.length == 0) {
99
- let sql = `alter table ${e.$entityName} add column ${postgresColumnSyntax(c(entity), colName)}`;
98
+ WHERE table_name=${cmd.addParameterAndReturnSqlToken((e.entityName).toLocaleLowerCase())} and column_name=${cmd.addParameterAndReturnSqlToken((colName).toLocaleLowerCase())}` + this.additionalWhere)).rows.length == 0) {
99
+ let sql = `alter table ${e.entityName} add column ${postgresColumnSyntax(c(entity), colName)}`;
100
100
  //console.log(sql);
101
101
  await this.pool.execute(sql);
102
102
  }
@@ -108,14 +108,14 @@ export class PostgresSchemaBuilder {
108
108
  async verifyAllColumns(entity) {
109
109
  try {
110
110
  let cmd = this.pool.createCommand();
111
- let e = await dbNamesOf(entity);
111
+ let e = await getDbNameProvider(entity);
112
112
  let cols = (await cmd.execute(`select column_name
113
113
  FROM information_schema.columns
114
- WHERE table_name=${cmd.addParameterAndReturnSqlToken((e.$entityName).toLocaleLowerCase())} ` + this.additionalWhere)).rows.map(x => x.column_name);
114
+ WHERE table_name=${cmd.addParameterAndReturnSqlToken((e.entityName).toLocaleLowerCase())} ` + this.additionalWhere)).rows.map(x => x.column_name);
115
115
  for (const col of entity.fields) {
116
- if (!isDbReadonly(col, e))
117
- if (!cols.includes(e.dbNameOf(col).toLocaleLowerCase())) {
118
- let sql = `alter table ${e.$entityName} add column ${postgresColumnSyntax(col, e.dbNameOf(col))}`;
116
+ if (!e.isDbReadonly(col))
117
+ if (!cols.includes(e.nameOf(col).toLocaleLowerCase())) {
118
+ let sql = `alter table ${e.entityName} add column ${postgresColumnSyntax(col, e.nameOf(col))}`;
119
119
  //console.log(sql);
120
120
  await this.pool.execute(sql);
121
121
  }
@@ -1,5 +1,6 @@
1
1
  import * as express from 'express';
2
2
  import { createRemultServer } from './server/expressBridge';
3
+ import { initAsyncHooks } from './server/initAsyncHooks';
3
4
  export function remultExpress(options) {
4
5
  let app = express.Router();
5
6
  if (!options) {
@@ -12,6 +13,7 @@ export function remultExpress(options) {
12
13
  app.use(express.json({ limit: options.bodySizeLimit }));
13
14
  app.use(express.urlencoded({ extended: true, limit: options.bodySizeLimit }));
14
15
  }
16
+ initAsyncHooks();
15
17
  const server = createRemultServer(options);
16
18
  server.registerRouter(app);
17
19
  return Object.assign(app, {
@@ -1,4 +1,5 @@
1
1
  import { createRemultServer } from './server/expressBridge';
2
+ import { initAsyncHooks } from './server/initAsyncHooks';
2
3
  export function remultFastify(options) {
3
4
  function fastifyHandler(handler) {
4
5
  const response = (req, res) => {
@@ -18,6 +19,7 @@ export function remultFastify(options) {
18
19
  };
19
20
  return response;
20
21
  }
22
+ initAsyncHooks();
21
23
  const api = createRemultServer(options);
22
24
  const pluginFunction = async (instance, op) => {
23
25
  //@ts-ignore
@@ -1,19 +1,43 @@
1
- import type { Knex } from 'knex';
1
+ import { Knex } from 'knex';
2
+ import { Filter, FilterConsumer } from "../src/filter/filter-interfaces";
3
+ import { dbNameProvider } from "../src/filter/filter-consumer-bridge-to-sql-request";
2
4
  import { Remult } from "../src/context";
3
- import { EntityFilter, EntityMetadata, Repository } from "../src/remult3";
5
+ import { EntityFilter, EntityMetadata } from "../src/remult3";
4
6
  import { DataProvider, EntityDataProvider } from '../src/data-interfaces';
5
7
  import { FieldMetadata } from '../src/column-interfaces';
6
8
  export declare class KnexDataProvider implements DataProvider {
7
9
  knex: Knex;
8
10
  constructor(knex: Knex);
9
- static getDb(remult?: Remult): Knex<any, Record<string, any>[]>;
10
11
  getEntityDataProvider(entity: EntityMetadata<any>): EntityDataProvider;
11
12
  transaction(action: (dataProvider: DataProvider) => Promise<void>): Promise<void>;
12
13
  static customFilter(build: CustomKnexFilterBuilderFunction): EntityFilter<any>;
13
14
  supportsCustomFilter?: boolean;
14
15
  }
15
16
  export declare type CustomKnexFilterBuilderFunction = () => Promise<(builder: Knex.QueryBuilder) => void>;
16
- export declare function knexCondition<entityType>(repo: Repository<entityType>, condition: EntityFilter<entityType>): Promise<(knex: any) => void>;
17
+ export declare class FilterConsumerBridgeToKnexRequest implements FilterConsumer {
18
+ private nameProvider;
19
+ _addWhere: boolean;
20
+ promises: Promise<void>[];
21
+ result: ((builder: Knex.QueryBuilder) => void)[];
22
+ resolveWhere(): Promise<((builder: Knex.QueryBuilder<any, any>) => void)[]>;
23
+ constructor(nameProvider: dbNameProvider);
24
+ custom(key: string, customItem: any): void;
25
+ or(orElements: Filter[]): void;
26
+ isNull(col: FieldMetadata): void;
27
+ isNotNull(col: FieldMetadata): void;
28
+ isIn(col: FieldMetadata, val: any[]): void;
29
+ isEqualTo(col: FieldMetadata, val: any): void;
30
+ isDifferentFrom(col: FieldMetadata, val: any): void;
31
+ isGreaterOrEqualTo(col: FieldMetadata, val: any): void;
32
+ isGreaterThan(col: FieldMetadata, val: any): void;
33
+ isLessOrEqualTo(col: FieldMetadata, val: any): void;
34
+ isLessThan(col: FieldMetadata, val: any): void;
35
+ containsCaseInsensitive(col: FieldMetadata, val: any): void;
36
+ private add;
37
+ databaseCustom(databaseCustom: {
38
+ buildKnex: CustomKnexFilterBuilderFunction;
39
+ }): void;
40
+ }
17
41
  export declare class KnexSchemaBuilder {
18
42
  private knex;
19
43
  verifyStructureOfAllEntities(remult: Remult): Promise<void>;
@@ -1,21 +1,15 @@
1
+ import knex from 'knex';
1
2
  import { customDatabaseFilterToken, Filter } from "../src/filter/filter-interfaces";
2
- import { dbNamesOf, isDbReadonly } from "../src/filter/filter-consumer-bridge-to-sql-request";
3
+ import { getDbNameProvider } from "../src/filter/filter-consumer-bridge-to-sql-request";
3
4
  import { allEntities, Remult } from "../src/context";
4
5
  import { isAutoIncrement } from "../src/remult3";
5
6
  import { ValueConverters } from "../src/valueConverters";
6
7
  import { CompoundIdField } from '../src/column';
7
8
  import { Sort } from '../src/sort';
8
- import { remult as remultContext } from '../src/remult-proxy';
9
9
  export class KnexDataProvider {
10
10
  constructor(knex) {
11
11
  this.knex = knex;
12
12
  }
13
- static getDb(remult) {
14
- const r = (remult || remultContext).dataProvider;
15
- if (!r.knex)
16
- throw "the data provider is not an KnexDataProvider";
17
- return r.knex;
18
- }
19
13
  getEntityDataProvider(entity) {
20
14
  return new KnexEntityDataProvider(entity, this.knex);
21
15
  }
@@ -48,7 +42,7 @@ class KnexEntityDataProvider {
48
42
  const br = new FilterConsumerBridgeToKnexRequest(e);
49
43
  where.__applyToConsumer(br);
50
44
  let r = await br.resolveWhere();
51
- const result = (await this.knex(e.$entityName)
45
+ const result = (await this.knex(e.entityName)
52
46
  .count().where(b => r.forEach(w => w(b))));
53
47
  var row = result[0];
54
48
  for (const key in row) {
@@ -66,11 +60,11 @@ class KnexEntityDataProvider {
66
60
  if (x.isServerExpression) {
67
61
  }
68
62
  else {
69
- cols.push(e.dbNameOf(x));
63
+ cols.push(e.nameOf(x));
70
64
  colKeys.push(x);
71
65
  }
72
66
  }
73
- let query = this.knex(e.$entityName).select(cols);
67
+ let query = this.knex(e.entityName).select(cols);
74
68
  if (options?.where) {
75
69
  const br = new FilterConsumerBridgeToKnexRequest(e);
76
70
  options.where.__applyToConsumer(br);
@@ -82,7 +76,7 @@ class KnexEntityDataProvider {
82
76
  }
83
77
  if (options.orderBy) {
84
78
  query = query.orderBy(options.orderBy.Segments.map(s => ({
85
- column: e.dbNameOf(s.field),
79
+ column: e.nameOf(s.field),
86
80
  order: s.isDescending ? "desc" : "asc"
87
81
  })));
88
82
  }
@@ -108,7 +102,7 @@ class KnexEntityDataProvider {
108
102
  });
109
103
  }
110
104
  async init() {
111
- return await dbNamesOf(this.entity);
105
+ return await getDbNameProvider(this.entity);
112
106
  }
113
107
  async update(id, data) {
114
108
  const e = await this.init();
@@ -119,17 +113,17 @@ class KnexEntityDataProvider {
119
113
  resultFilter = this.entity.idMetadata.getIdFilter(data.id);
120
114
  let updateObject = {};
121
115
  for (const x of this.entity.fields) {
122
- if (isDbReadonly(x, e)) { }
116
+ if (e.isDbReadonly(x)) { }
123
117
  else if (data[x.key] !== undefined) {
124
118
  let v = x.valueConverter.toDb(data[x.key]);
125
119
  if (v !== undefined) {
126
- let key = await e.dbNameOf(x);
120
+ let key = await e.nameOf(x);
127
121
  updateObject[key] = v;
128
122
  }
129
123
  }
130
124
  }
131
125
  let where = await f.resolveWhere();
132
- await this.knex(e.$entityName).update(updateObject).where(b => where.forEach(w => w(b)));
126
+ await this.knex(e.entityName).update(updateObject).where(b => where.forEach(w => w(b)));
133
127
  return this.find({ where: Filter.fromEntityFilter(this.entity, resultFilter) }).then(y => y[0]);
134
128
  }
135
129
  async delete(id) {
@@ -137,7 +131,7 @@ class KnexEntityDataProvider {
137
131
  let f = new FilterConsumerBridgeToKnexRequest(e);
138
132
  Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(id)).__applyToConsumer(f);
139
133
  let where = await f.resolveWhere();
140
- await this.knex(e.$entityName).delete().where(b => where.forEach(w => w(b)));
134
+ await this.knex(e.entityName).delete().where(b => where.forEach(w => w(b)));
141
135
  }
142
136
  async insert(data) {
143
137
  const e = await this.init();
@@ -148,16 +142,16 @@ class KnexEntityDataProvider {
148
142
  resultFilter = Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(data[this.entity.idMetadata.field.key]));
149
143
  let insertObject = {};
150
144
  for (const x of this.entity.fields) {
151
- if (isDbReadonly(x, e)) { }
145
+ if (e.isDbReadonly(x)) { }
152
146
  else {
153
147
  let v = x.valueConverter.toDb(data[x.key]);
154
148
  if (v != undefined) {
155
- let key = await e.dbNameOf(x);
149
+ let key = await e.nameOf(x);
156
150
  insertObject[key] = v;
157
151
  }
158
152
  }
159
153
  }
160
- let insert = this.knex(e.$entityName).insert(insertObject);
154
+ let insert = this.knex(e.entityName).insert(insertObject);
161
155
  if (isAutoIncrement(this.entity.idMetadata.field)) {
162
156
  let result = await insert.returning(this.entity.idMetadata.field.key);
163
157
  let newId = result[0].id;
@@ -170,7 +164,7 @@ class KnexEntityDataProvider {
170
164
  });
171
165
  }
172
166
  }
173
- class FilterConsumerBridgeToKnexRequest {
167
+ export class FilterConsumerBridgeToKnexRequest {
174
168
  constructor(nameProvider) {
175
169
  this.nameProvider = nameProvider;
176
170
  this._addWhere = true;
@@ -214,13 +208,13 @@ class FilterConsumerBridgeToKnexRequest {
214
208
  })());
215
209
  }
216
210
  isNull(col) {
217
- this.result.push(b => b.whereNull(this.nameProvider.dbNameOf(col)));
211
+ this.result.push(b => b.whereNull(this.nameProvider.nameOf(col)));
218
212
  }
219
213
  isNotNull(col) {
220
- this.result.push(b => b.whereNotNull(this.nameProvider.dbNameOf(col)));
214
+ this.result.push(b => b.whereNotNull(this.nameProvider.nameOf(col)));
221
215
  }
222
216
  isIn(col, val) {
223
- this.result.push(knex => knex.whereIn(this.nameProvider.dbNameOf(col), val.map(x => col.valueConverter.toDb(x))));
217
+ this.result.push(knex => knex.whereIn(this.nameProvider.nameOf(col), val.map(x => col.valueConverter.toDb(x))));
224
218
  }
225
219
  isEqualTo(col, val) {
226
220
  this.add(col, val, "=");
@@ -241,12 +235,12 @@ class FilterConsumerBridgeToKnexRequest {
241
235
  this.add(col, val, "<");
242
236
  }
243
237
  containsCaseInsensitive(col, val) {
244
- this.result.push(b => b.whereRaw('lower (' + this.nameProvider.dbNameOf(col) + ") like lower ('%" + val.replace(/'/g, '\'\'') + "%')"));
238
+ this.result.push(b => b.whereRaw('lower (' + this.nameProvider.nameOf(col) + ") like lower ('%" + val.replace(/'/g, '\'\'') + "%')"));
245
239
  this.promises.push((async () => {
246
240
  })());
247
241
  }
248
242
  add(col, val, operator) {
249
- this.result.push(b => b.where(this.nameProvider.dbNameOf(col), operator, col.valueConverter.toDb(val)));
243
+ this.result.push(b => b.where(this.nameProvider.nameOf(col), operator, col.valueConverter.toDb(val)));
250
244
  }
251
245
  databaseCustom(databaseCustom) {
252
246
  this.promises.push((async () => {
@@ -256,13 +250,6 @@ class FilterConsumerBridgeToKnexRequest {
256
250
  })());
257
251
  }
258
252
  }
259
- export async function knexCondition(repo, condition) {
260
- var b = new FilterConsumerBridgeToKnexRequest(await dbNamesOf(repo.metadata));
261
- b._addWhere = false;
262
- await (await (repo.translateWhereToFilter(condition))).__applyToConsumer(b);
263
- let r = await b.resolveWhere();
264
- return knex => r.forEach(y => y(knex));
265
- }
266
253
  export class KnexSchemaBuilder {
267
254
  constructor(knex) {
268
255
  this.knex = knex;
@@ -286,16 +273,16 @@ export class KnexSchemaBuilder {
286
273
  }
287
274
  }
288
275
  async createIfNotExist(entity) {
289
- const e = await dbNamesOf(entity);
290
- if (!await this.knex.schema.hasTable(e.$entityName)) {
276
+ const e = await getDbNameProvider(entity);
277
+ if (!await this.knex.schema.hasTable(e.entityName)) {
291
278
  let cols = new Map();
292
279
  for (const f of entity.fields) {
293
280
  cols.set(f, {
294
- name: e.dbNameOf(f),
295
- readonly: isDbReadonly(f, e)
281
+ name: e.nameOf(f),
282
+ readonly: e.isDbReadonly(f)
296
283
  });
297
284
  }
298
- await logSql(this.knex.schema.createTable(e.$entityName, b => {
285
+ await logSql(this.knex.schema.createTable(e.entityName, b => {
299
286
  for (const x of entity.fields) {
300
287
  if (!cols.get(x).readonly || isAutoIncrement(x)) {
301
288
  if (isAutoIncrement(x))
@@ -311,22 +298,22 @@ export class KnexSchemaBuilder {
311
298
  }
312
299
  }
313
300
  async addColumnIfNotExist(entity, c) {
314
- let e = await dbNamesOf(entity);
315
- if (isDbReadonly(c(entity), e))
301
+ let e = await getDbNameProvider(entity);
302
+ if (e.isDbReadonly(c(entity)))
316
303
  return;
317
304
  let col = c(entity);
318
- let colName = e.dbNameOf(col);
319
- if (!await this.knex.schema.hasColumn(e.$entityName, colName)) {
320
- await this.knex.schema.alterTable(e.$entityName, b => {
305
+ let colName = e.nameOf(col);
306
+ if (!await this.knex.schema.hasColumn(e.entityName, colName)) {
307
+ await this.knex.schema.alterTable(e.entityName, b => {
321
308
  buildColumn(col, colName, b);
322
309
  });
323
310
  }
324
311
  }
325
312
  async verifyAllColumns(entity) {
326
- let e = await dbNamesOf(entity);
313
+ let e = await getDbNameProvider(entity);
327
314
  try {
328
315
  for (const col of entity.fields) {
329
- if (isDbReadonly(col, e)) {
316
+ if (!e.isDbReadonly(col)) {
330
317
  await this.addColumnIfNotExist(entity, () => col);
331
318
  }
332
319
  }
@@ -390,7 +377,7 @@ function logSql(who) {
390
377
  return who;
391
378
  }
392
379
  export async function createKnexDataProvider(config, autoCreateTables = true) {
393
- let k = (await import('knex')).default(config);
380
+ let k = knex(config);
394
381
  let result = new KnexDataProvider(k);
395
382
  if (autoCreateTables)
396
383
  await new KnexSchemaBuilder(k).verifyStructureOfAllEntities(new Remult(result));
@@ -0,0 +1,2 @@
1
+ import { RemultMiddlewareOptions } from './server/expressBridge';
2
+ export declare function remultMiddleware(options?: RemultMiddlewareOptions): import("./server/expressBridge").RemultExpressBridge;
@@ -0,0 +1,65 @@
1
+ import { remultMiddlewareBase } from './server/expressBridge';
2
+ export function remultMiddleware(options) {
3
+ const m = new middleware();
4
+ let app = Object.assign((req, res, next) => m.handleRequest(req, res, next), {
5
+ route: (path) => m.route(path)
6
+ });
7
+ return remultMiddlewareBase(app, options);
8
+ }
9
+ class middleware {
10
+ constructor() {
11
+ this.map = new Map();
12
+ }
13
+ route(path) {
14
+ //consider using:
15
+ //* https://raw.githubusercontent.com/cmorten/opine/main/src/utils/pathToRegex.ts
16
+ //* https://github.com/pillarjs/path-to-regexp
17
+ let r = path.toLowerCase();
18
+ let m = new Map();
19
+ this.map.set(r, m);
20
+ const route = {
21
+ get: (h) => {
22
+ m.set("get", h);
23
+ return route;
24
+ },
25
+ put: (h) => {
26
+ m.set("put", h);
27
+ return route;
28
+ },
29
+ post: (h) => {
30
+ m.set("post", h);
31
+ return route;
32
+ },
33
+ delete: (h) => {
34
+ m.set("delete", h);
35
+ return route;
36
+ }
37
+ };
38
+ return route;
39
+ }
40
+ handleRequest(req, res, next) {
41
+ let lowerPath = req.path.toLowerCase();
42
+ let m = this.map.get(lowerPath);
43
+ if (m) {
44
+ let h = m.get(req.method.toLowerCase());
45
+ if (h) {
46
+ h(req, res, next);
47
+ return;
48
+ }
49
+ }
50
+ let idPosition = req.path.lastIndexOf('/');
51
+ if (idPosition >= 0) {
52
+ lowerPath = req.path.substring(0, idPosition) + '/:id';
53
+ m = this.map.get(lowerPath);
54
+ if (m) {
55
+ let h = m.get(req.method.toLowerCase());
56
+ if (h) {
57
+ req.params.id = req.path.substring(idPosition + 1);
58
+ h(req, res, next);
59
+ return;
60
+ }
61
+ }
62
+ }
63
+ next();
64
+ }
65
+ }