remult 0.27.21-next.9 → 0.27.21

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.
@@ -0,0 +1,125 @@
1
+ import { dbNamesOf, } from '../filter/filter-consumer-bridge-to-sql-request.js';
2
+ import { getRelationFieldInfo } from '../remult3/relationInfoMember.js';
3
+ import { SqlDatabase } from './sql-database.js';
4
+ import { remult } from '../remult-proxy.js';
5
+ export function sqlRelations(forEntity) {
6
+ return new Proxy({}, {
7
+ get: (target, relationField) => new Proxy(new SqlRelationTools(forEntity, relationField), {
8
+ get: (target, prop) => {
9
+ if (prop == '$count')
10
+ return target.$count;
11
+ if (prop == '$subQuery')
12
+ return target.$subQuery;
13
+ if (prop == '$relations')
14
+ return target.___relations();
15
+ if (prop == '$first')
16
+ return target.$first;
17
+ return target.$fields[prop];
18
+ },
19
+ }),
20
+ });
21
+ }
22
+ class SqlRelationTools {
23
+ myEntity;
24
+ relationField;
25
+ constructor(myEntity, relationField) {
26
+ this.myEntity = myEntity;
27
+ this.relationField = relationField;
28
+ }
29
+ $count = (where) => {
30
+ return this.$subQuery(() => 'count(*)', { where });
31
+ };
32
+ $first = (options) => {
33
+ return new Proxy(this, {
34
+ get: (target, prop) => {
35
+ if (prop == '$subQuery')
36
+ return (what) => target.$subQuery(what, { ...options, first: true });
37
+ if (prop == '$relations')
38
+ return this.___relations({ ...options, first: true });
39
+ return target.$subQuery((fieldNamesOfToEntity) => fieldNamesOfToEntity.$dbNameOf(prop), { ...options, first: true });
40
+ },
41
+ });
42
+ };
43
+ $fields = new Proxy(this, {
44
+ get: (target, field) => {
45
+ return target.$subQuery((fieldNamesOfToEntity) => fieldNamesOfToEntity.$dbNameOf(field));
46
+ },
47
+ });
48
+ $subQuery = async (what, options) => {
49
+ const rel = getRelationFieldInfo(remult.repo(this.myEntity).fields.find(this.relationField));
50
+ if (!rel)
51
+ throw new Error(`${this.relationField} is not a relation`);
52
+ const relFields = rel.getFields();
53
+ const filters = [];
54
+ const namesOfOtherTable = await dbNamesOf(rel.toEntity, {
55
+ tableName: true,
56
+ });
57
+ const namesOfMyTable = await dbNamesOf(this.myEntity, { tableName: true });
58
+ for (const key in relFields.fields) {
59
+ if (Object.prototype.hasOwnProperty.call(relFields.fields, key)) {
60
+ filters.push(`${namesOfOtherTable.$dbNameOf(key)} = ${namesOfMyTable.$dbNameOf(relFields.fields[key])}`);
61
+ }
62
+ }
63
+ const otherTableFilter = await SqlDatabase.filterToRaw(remult.repo(rel.toEntity),
64
+ // eslint-disable-next-line
65
+ options?.where, undefined, namesOfOtherTable);
66
+ if (otherTableFilter)
67
+ filters.push(otherTableFilter);
68
+ let result = `
69
+ ( SELECT ${await what(namesOfOtherTable)}
70
+ FROM ${namesOfOtherTable}
71
+ WHERE ${filters.join(' and ')}`;
72
+ if (options?.orderBy) {
73
+ result += `
74
+ ORDER BY ${Object.keys(options.orderBy)
75
+ .map((key) => `${namesOfOtherTable.$dbNameOf(key)} ${options.orderBy[key]}`)
76
+ .join(', ')}`;
77
+ }
78
+ if (options?.first) {
79
+ result += `
80
+ LIMIT 1`;
81
+ }
82
+ return (result +
83
+ `
84
+ )`);
85
+ };
86
+ ___relations(options) {
87
+ return new Proxy(this, {
88
+ get: (target, field) => {
89
+ const rel1 = getRelationFieldInfo(remult.repo(this.myEntity).fields.find(this.relationField));
90
+ return new Proxy(this, {
91
+ get: (target, field1) => {
92
+ return this.$subQuery(() => indent(sqlRelations(rel1.toEntity)[field][field1]), options);
93
+ },
94
+ });
95
+ },
96
+ });
97
+ }
98
+ }
99
+ export function sqlRelationsFilter(forEntity) {
100
+ return new Proxy({}, {
101
+ get: (target, relationField) => new SqlRelationFilter(forEntity, relationField),
102
+ });
103
+ }
104
+ export class SqlRelationFilter {
105
+ _tools;
106
+ constructor(myEntity, relationField) {
107
+ this._tools = new SqlRelationTools(myEntity, relationField);
108
+ }
109
+ some(where) {
110
+ //many orms use some, every, none
111
+ return SqlDatabase.rawFilter(async (c) => {
112
+ return ('exists ' +
113
+ (await this._tools.$subQuery(() => '1', {
114
+ where,
115
+ c,
116
+ })));
117
+ });
118
+ }
119
+ }
120
+ function indent(s) {
121
+ return s.then((s) => s
122
+ .split('\n')
123
+ .map((x) => ' ' + x)
124
+ .join('\n'));
125
+ }
@@ -18,6 +18,7 @@ let x = {
18
18
  close: () => { },
19
19
  }),
20
20
  },
21
+ fieldOptionsEnricher: undefined,
21
22
  captionTransformer: undefined,
22
23
  defaultDataProvider: () => undefined,
23
24
  };
@@ -1,6 +1,6 @@
1
1
  import { LookupColumn, makeTitle } from '../column.js';
2
2
  import { CompoundIdField } from '../CompoundIdField.js';
3
- import { Remult, isBackend } from '../context.js';
3
+ import { Remult } from '../context.js';
4
4
  import { Filter } from '../filter/filter-interfaces.js';
5
5
  import { Sort } from '../sort.js';
6
6
  import { GroupByForApiKey, } from './remult3.js';
@@ -640,50 +640,69 @@ export class RepositoryImplementation {
640
640
  repo._addToCache(r);
641
641
  }
642
642
  }
643
- let result = await promiseAll(rawRows, async (r) => await this._mapRawDataToResult(r, loadFields));
643
+ const excludeRelationMembers = new Set(this.fields
644
+ .toArray()
645
+ .map((f) => {
646
+ const i = this.__getRelationAndInclude(f, loadOptions);
647
+ if (i.rel && !i.incl)
648
+ return f.key;
649
+ return undefined;
650
+ })
651
+ .filter((x) => x !== undefined));
652
+ let result = await promiseAll(rawRows, async (r) => await this._mapRawDataToResult(r, loadFields, excludeRelationMembers));
644
653
  const fields = this.metadata.fields.toArray();
645
654
  this._populateRelationsForFields(fields, loadOptions, result, loader);
646
655
  return result;
647
656
  }
648
657
  _populateRelationsForFields(fields, loadOptions, result, loader) {
649
658
  for (const col of fields) {
650
- let rel = getRelationFieldInfo(col);
651
- let incl = col.options
652
- .defaultIncluded;
653
- const include = loadOptions?.include?.[col.key];
654
- if (include !== undefined) {
655
- incl = include;
656
- }
657
- if (rel && incl) {
658
- const otherRepo = rel.toRepo;
659
- for (const row of result) {
660
- let { findOptions, returnNull } = this._findOptionsBasedOnRelation(rel, col, incl, row, otherRepo);
661
- const colKey = col.key;
662
- if (returnNull)
663
- row[colKey] = null;
664
- else {
665
- const entityType = rel.toEntity;
666
- const toRepo = otherRepo;
667
- loader
668
- .load({
669
- entityType,
670
- find: (options) => toRepo._rawFind(options, false, loader, (o) => toRepo._edp.find(o)),
671
- metadata: toRepo.metadata,
672
- }, findOptions)
673
- .then((result) => {
674
- if (result.length == 0 && rel.type == 'toOne')
675
- return;
676
- row[colKey] =
677
- rel.type !== 'toMany'
678
- ? result.length == 0
679
- ? null
680
- : result[0]
681
- : result;
682
- });
659
+ let { rel, incl } = this.__getRelationAndInclude(col, loadOptions);
660
+ if (rel)
661
+ if (incl) {
662
+ const otherRepo = rel.toRepo;
663
+ for (const row of result) {
664
+ let { findOptions, returnNull } = this._findOptionsBasedOnRelation(rel, col, incl, row, otherRepo);
665
+ const colKey = col.key;
666
+ if (returnNull)
667
+ row[colKey] = null;
668
+ else {
669
+ const entityType = rel.toEntity;
670
+ const toRepo = otherRepo;
671
+ loader
672
+ .load({
673
+ entityType,
674
+ find: (options) => toRepo._rawFind(options, false, loader, (o) => toRepo._edp.find(o)),
675
+ metadata: toRepo.metadata,
676
+ }, findOptions)
677
+ .then((result) => {
678
+ if (result.length == 0 && rel.type == 'toOne')
679
+ return;
680
+ row[colKey] =
681
+ rel.type !== 'toMany'
682
+ ? result.length == 0
683
+ ? null
684
+ : result[0]
685
+ : result;
686
+ });
687
+ }
683
688
  }
684
689
  }
685
- }
690
+ else {
691
+ for (const row of result) {
692
+ Reflect.deleteProperty(row, col.key);
693
+ }
694
+ }
695
+ }
696
+ }
697
+ __getRelationAndInclude(col, loadOptions) {
698
+ let rel = getRelationFieldInfo(col);
699
+ let incl = col.options
700
+ .defaultIncluded;
701
+ const include = loadOptions?.include?.[col.key];
702
+ if (include !== undefined) {
703
+ incl = include;
686
704
  }
705
+ return { rel, incl };
687
706
  }
688
707
  /*@internal */
689
708
  _findOptionsBasedOnRelation(rel, field, moreFindOptions, row, otherRepo) {
@@ -750,9 +769,9 @@ export class RepositoryImplementation {
750
769
  findOptions.limit = 1;
751
770
  return { findOptions, returnNull, returnUndefined };
752
771
  }
753
- async _mapRawDataToResult(r, loadFields) {
772
+ async _mapRawDataToResult(r, loadFields, excludeRelationMembers) {
754
773
  let x = new this._entity(this._remult);
755
- let helper = new rowHelperImplementation(this._info, x, this, this._edp, this._remult, false);
774
+ let helper = new rowHelperImplementation(this._info, x, this, this._edp, this._remult, false, excludeRelationMembers);
756
775
  Object.defineProperty(x, entityMember, {
757
776
  //I've used define property to hide this member from console.lo g
758
777
  get: () => helper,
@@ -1021,7 +1040,7 @@ class rowHelperBase {
1021
1040
  this._error = val;
1022
1041
  this._subscribers?.reportChanged();
1023
1042
  }
1024
- constructor(fieldsMetadata, instance, remult, isNewRow) {
1043
+ constructor(fieldsMetadata, instance, remult, isNewRow, excludeRelationMembers) {
1025
1044
  this.fieldsMetadata = fieldsMetadata;
1026
1045
  this.instance = instance;
1027
1046
  this.remult = remult;
@@ -1064,7 +1083,7 @@ class rowHelperBase {
1064
1083
  }
1065
1084
  refImpl._subscribers.reportChanged();
1066
1085
  },
1067
- enumerable: true,
1086
+ enumerable: !excludeRelationMembers?.has(col.key),
1068
1087
  });
1069
1088
  lookup.set(val);
1070
1089
  }
@@ -1098,7 +1117,7 @@ class rowHelperBase {
1098
1117
  }
1099
1118
  }
1100
1119
  },
1101
- enumerable: true,
1120
+ enumerable: !excludeRelationMembers?.has(col.key),
1102
1121
  });
1103
1122
  if (hasVal)
1104
1123
  instance[col.key] = val;
@@ -1333,8 +1352,8 @@ export class rowHelperImplementation extends rowHelperBase {
1333
1352
  repo;
1334
1353
  edp;
1335
1354
  _isNew;
1336
- constructor(info, instance, repo, edp, remult, _isNew) {
1337
- super(info.fieldsMetadata, instance, remult, _isNew);
1355
+ constructor(info, instance, repo, edp, remult, _isNew, excludeRelationMembers) {
1356
+ super(info.fieldsMetadata, instance, remult, _isNew, excludeRelationMembers);
1338
1357
  this.info = info;
1339
1358
  this.repo = repo;
1340
1359
  this.edp = edp;
@@ -1616,7 +1635,10 @@ export class rowHelperImplementation extends rowHelperBase {
1616
1635
  }
1617
1636
  }
1618
1637
  else if (!getRelationFieldInfo(col))
1619
- this.instance[col.key] = data[col.key];
1638
+ if (data[col.key] === undefined)
1639
+ delete this.instance[col.key];
1640
+ else
1641
+ this.instance[col.key] = data[col.key];
1620
1642
  }
1621
1643
  await this.calcServerExpression();
1622
1644
  this.id = this.getId();
@@ -1627,12 +1649,13 @@ export class rowHelperImplementation extends rowHelperBase {
1627
1649
  return this.originalId;
1628
1650
  }
1629
1651
  async calcServerExpression() {
1630
- if (isBackend())
1652
+ if (!this.repo._dataProvider.isProxy)
1631
1653
  //y2 should be changed to be based on data provider - consider naming
1632
1654
  for (const col of this.info.fieldsMetadata) {
1633
1655
  if (col.options.serverExpression) {
1634
- this.instance[col.key] =
1635
- (await col.options.serverExpression(this.instance));
1656
+ const result = (await col.options.serverExpression(this.instance));
1657
+ if (result !== undefined)
1658
+ this.instance[col.key] = result;
1636
1659
  }
1637
1660
  }
1638
1661
  }
@@ -1924,6 +1947,9 @@ export class FieldRefImplementation {
1924
1947
  let tempCaptionTransformer = {
1925
1948
  transformCaption: (remult, key, caption, entityMetaData) => caption,
1926
1949
  };
1950
+ export const fieldOptionsEnricher = remultStatic.fieldOptionsEnricher || {
1951
+ enrichFieldOptions: (options) => options,
1952
+ };
1927
1953
  export const CaptionTransformer = remultStatic.captionTransformer ||
1928
1954
  (remultStatic.captionTransformer = tempCaptionTransformer);
1929
1955
  export function buildCaption(caption, key, remult, metaData) {
@@ -2335,6 +2361,7 @@ export function buildOptions(options, remult) {
2335
2361
  }
2336
2362
  }
2337
2363
  }
2364
+ fieldOptionsEnricher.enrichFieldOptions?.(r);
2338
2365
  return r;
2339
2366
  }
2340
2367
  export function decorateColumnSettings(settings, remult) {
package/index.d.ts CHANGED
@@ -2,7 +2,7 @@ import type { ClassType } from './classType.js';
2
2
  import type { DataProvider } from './src/data-interfaces.js';
3
3
  export { ArrayEntityDataProvider } from './src/data-providers/array-entity-data-provider.js';
4
4
  export type { ClassType } from './classType.js';
5
- export { ValidateFieldEvent, MembersOnly, NumericKeys, FieldsMetadata, FieldRef, IdFieldRef, FieldsRef, EntityMetadata, EntityOrderBy, EntityFilter, FindOptions, FindOptionsBase, QueryResult, QueryOptions, Repository, GroupByOptions, GroupByResult, FindFirstOptions, ComparisonValueFilter, ValueFilter, IdFilter, ContainsStringValueFilter, EntityRef, SortSegments, Paginator, LiveQuery, LiveQueryChangeInfo, Subscribable, RefSubscriber, RefSubscriberBase, RelationOptions, ObjectMembersOnly, MembersToInclude, RepositoryRelations, EntityIdFields, ClassFieldDecorator, ClassFieldDecoratorContextStub, //n1 consider removing in ts5
5
+ export { ValidateFieldEvent, MembersOnly, NumericKeys, FieldsMetadata, FieldRef, IdFieldRef, FieldsRef, EntityMetadata, EntityOrderBy, EntityFilter, FindOptions, FindOptionsBase, QueryResult, QueryOptions, UpsertOptions, Repository, GroupByOptions, GroupByResult, FindFirstOptions, ComparisonValueFilter, ValueFilter, IdFilter, ContainsStringValueFilter, EntityRef, SortSegments, Paginator, LiveQuery, LiveQueryChangeInfo, Subscribable, RefSubscriber, RefSubscriberBase, RelationOptions, ObjectMembersOnly, MembersToInclude, RepositoryRelations, EntityIdFields, ClassFieldDecorator, ClassFieldDecoratorContextStub, //n1 consider removing in ts5
6
6
  LifecycleEvent, FieldsRefBase, FieldsRefForEntityBase, RepositoryRelationsForEntityBase, ControllerRefForControllerBase, ControllerRefBase, ControllerRef, EntityRefForEntityBase, IdMetadata, FindFirstOptionsBase, } from './src/remult3/remult3.js';
7
7
  export { EntityBase, ControllerBase, FieldType, getFields, ValueListFieldType, getValueList, ValueListFieldOptions, ValueListInfo, CaptionTransformer, } from './src/remult3/RepositoryImplementation.js';
8
8
  export { Entity } from './src/remult3/entity.js';
package/internals.d.ts CHANGED
@@ -1,5 +1,6 @@
1
+ export { sqlRelations, sqlRelationsFilter, SqlRelations, SqlRelationFilter, } from './src/data-providers/sql-relations.js';
1
2
  export { getRelationInfo, getRelationFieldInfo, type RelationFieldInfo, type RelationInfo, type RelationFields, } from './src/remult3/relationInfoMember.js';
2
- export { decorateColumnSettings, controllerRefImpl, getControllerRef, } from './src/remult3/RepositoryImplementation.js';
3
+ export { decorateColumnSettings, controllerRefImpl, getControllerRef, fieldOptionsEnricher, } from './src/remult3/RepositoryImplementation.js';
3
4
  export { getEntitySettings } from './src/remult3/getEntityRef.js';
4
5
  export { __updateEntityBasedOnWhere } from './src/filter/filter-interfaces.js';
5
6
  export declare const actionInfo: {
package/internals.js CHANGED
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.flags = exports.actionInfo = exports.__updateEntityBasedOnWhere = exports.getEntitySettings = exports.getControllerRef = exports.controllerRefImpl = exports.decorateColumnSettings = exports.getRelationFieldInfo = exports.getRelationInfo = void 0;
3
+ exports.flags = exports.actionInfo = exports.__updateEntityBasedOnWhere = exports.getEntitySettings = exports.fieldOptionsEnricher = exports.getControllerRef = exports.controllerRefImpl = exports.decorateColumnSettings = exports.getRelationFieldInfo = exports.getRelationInfo = exports.SqlRelationFilter = exports.sqlRelationsFilter = exports.sqlRelations = void 0;
4
4
  var remult_static_js_1 = require("./src/remult-static.js");
5
+ var sql_relations_js_1 = require("./src/data-providers/sql-relations.js");
6
+ Object.defineProperty(exports, "sqlRelations", { enumerable: true, get: function () { return sql_relations_js_1.sqlRelations; } });
7
+ Object.defineProperty(exports, "sqlRelationsFilter", { enumerable: true, get: function () { return sql_relations_js_1.sqlRelationsFilter; } });
8
+ Object.defineProperty(exports, "SqlRelationFilter", { enumerable: true, get: function () { return sql_relations_js_1.SqlRelationFilter; } });
5
9
  var relationInfoMember_js_1 = require("./src/remult3/relationInfoMember.js");
6
10
  Object.defineProperty(exports, "getRelationInfo", { enumerable: true, get: function () { return relationInfoMember_js_1.getRelationInfo; } });
7
11
  Object.defineProperty(exports, "getRelationFieldInfo", { enumerable: true, get: function () { return relationInfoMember_js_1.getRelationFieldInfo; } });
@@ -9,6 +13,7 @@ var RepositoryImplementation_js_1 = require("./src/remult3/RepositoryImplementat
9
13
  Object.defineProperty(exports, "decorateColumnSettings", { enumerable: true, get: function () { return RepositoryImplementation_js_1.decorateColumnSettings; } });
10
14
  Object.defineProperty(exports, "controllerRefImpl", { enumerable: true, get: function () { return RepositoryImplementation_js_1.controllerRefImpl; } });
11
15
  Object.defineProperty(exports, "getControllerRef", { enumerable: true, get: function () { return RepositoryImplementation_js_1.getControllerRef; } });
16
+ Object.defineProperty(exports, "fieldOptionsEnricher", { enumerable: true, get: function () { return RepositoryImplementation_js_1.fieldOptionsEnricher; } });
12
17
  var getEntityRef_js_1 = require("./src/remult3/getEntityRef.js");
13
18
  Object.defineProperty(exports, "getEntitySettings", { enumerable: true, get: function () { return getEntityRef_js_1.getEntitySettings; } });
14
19
  var filter_interfaces_js_1 = require("./src/filter/filter-interfaces.js");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remult",
3
- "version": "0.27.21-next.9",
3
+ "version": "0.27.21",
4
4
  "description": "A CRUD framework for full-stack TypeScript",
5
5
  "homepage": "https://remult.dev",
6
6
  "repository": {
package/remult-sqlite3.js CHANGED
@@ -7,14 +7,14 @@ var index_js_1 = require("./index.js");
7
7
  var remult_sqlite_core_js_1 = require("./remult-sqlite-core.js");
8
8
  function createSqlite3DataProvider() {
9
9
  return tslib_1.__awaiter(this, arguments, void 0, function (fileName) {
10
- var Database;
10
+ var sqlite3;
11
11
  if (fileName === void 0) { fileName = ':memory:'; }
12
12
  return tslib_1.__generator(this, function (_a) {
13
13
  switch (_a.label) {
14
14
  case 0: return [4 /*yield*/, Promise.resolve().then(function () { return tslib_1.__importStar(require('sqlite3')); })];
15
15
  case 1:
16
- Database = (_a.sent()).Database;
17
- return [2 /*return*/, new index_js_1.SqlDatabase(new Sqlite3DataProvider(new Database(fileName)))];
16
+ sqlite3 = _a.sent();
17
+ return [2 /*return*/, new index_js_1.SqlDatabase(new Sqlite3DataProvider(new sqlite3.default.Database(fileName)))];
18
18
  }
19
19
  });
20
20
  });