remult 0.24.2-exp.0 → 0.25.0
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 +4 -4
- package/esm/index.js +2 -2
- package/esm/remult-mongo.js +1 -1
- package/esm/remult-sql-js.js +113 -0
- package/esm/src/data-providers/array-entity-data-provider.js +1 -1
- package/esm/src/data-providers/json-data-provider.js +1 -1
- package/esm/src/data-providers/{ipfs-data-provider.js → json-entity-opfs-storage.js} +1 -1
- package/esm/src/data-providers/sql-database.js +10 -6
- package/esm/src/filter/filter-consumer-bridge-to-sql-request.js +4 -5
- package/esm/src/remult-proxy.js +4 -0
- package/esm/src/remult3/Fields.js +22 -0
- package/esm/src/remult3/RepositoryImplementation.js +109 -60
- package/esm/src/remult3/entity.js +2 -2
- package/esm/src/remult3/getEntityRef.js +5 -5
- package/esm/src/remult3/relationInfoMember.js +1 -1
- package/esm/src/remult3/remult3.js +6 -7
- package/esm/src/server-action-info.js +1 -1
- package/esm/src/server-action.js +1 -1
- package/esm/src/validators.js +120 -28
- package/index.d.ts +3 -3
- package/index.js +8 -3
- package/package.json +6 -1
- package/remult-mongo.js +1 -1
- package/remult-sql-js.d.ts +18 -0
- package/remult-sql-js.js +211 -0
- package/src/column-interfaces.d.ts +12 -6
- package/src/data-providers/array-entity-data-provider.js +9 -10
- package/src/data-providers/json-data-provider.js +1 -1
- package/src/data-providers/{ipfs-data-provider.d.ts → json-entity-opfs-storage.d.ts} +1 -1
- package/src/data-providers/{ipfs-data-provider.js → json-entity-opfs-storage.js} +7 -7
- package/src/data-providers/sql-database.js +14 -6
- package/src/filter/filter-consumer-bridge-to-sql-request.js +4 -5
- package/src/remult-proxy.js +11 -0
- package/src/remult3/Fields.js +22 -0
- package/src/remult3/RepositoryImplementation.d.ts +6 -6
- package/src/remult3/RepositoryImplementation.js +136 -78
- package/src/remult3/entity.js +2 -2
- package/src/remult3/getEntityRef.js +5 -5
- package/src/remult3/relationInfoMember.js +1 -1
- package/src/remult3/remult3.d.ts +33 -0
- package/src/remult3/remult3.js +6 -7
- package/src/server-action-info.js +1 -1
- package/src/server-action.js +1 -1
- package/src/sql-command.d.ts +1 -0
- package/src/validators.d.ts +41 -9
- package/src/validators.js +183 -54
|
@@ -619,9 +619,11 @@ export class RepositoryImplementation {
|
|
|
619
619
|
for (const col of this.fieldsOf(json)) {
|
|
620
620
|
let ei = getEntitySettings(col.valueType, false);
|
|
621
621
|
if (ei) {
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
622
|
+
let val = json[col.key];
|
|
623
|
+
if (typeof val === 'string' || typeof val === 'number')
|
|
624
|
+
result[col.key] = val;
|
|
625
|
+
else
|
|
626
|
+
result[col.key] = this.remult.repo(col.valueType).fromJson(val);
|
|
625
627
|
}
|
|
626
628
|
else {
|
|
627
629
|
if (json[col.key] !== undefined) {
|
|
@@ -650,6 +652,8 @@ export class RepositoryImplementation {
|
|
|
650
652
|
async findOne(options, skipOrderByAndLimit = false) {
|
|
651
653
|
let r;
|
|
652
654
|
let cacheInfo;
|
|
655
|
+
if (!options)
|
|
656
|
+
options = {};
|
|
653
657
|
if (options.useCache) {
|
|
654
658
|
let f = findOptionsToJson(options, this.metadata);
|
|
655
659
|
let key = JSON.stringify(f);
|
|
@@ -816,7 +820,7 @@ export function createOldEntity(entity, remult) {
|
|
|
816
820
|
return new EntityFullInfo(prepareColumnInfo(r, remult), info, remult, entity, key);
|
|
817
821
|
}
|
|
818
822
|
class rowHelperBase {
|
|
819
|
-
|
|
823
|
+
fieldsMetadata;
|
|
820
824
|
instance;
|
|
821
825
|
remult;
|
|
822
826
|
isNewRow;
|
|
@@ -829,8 +833,8 @@ class rowHelperBase {
|
|
|
829
833
|
this._error = val;
|
|
830
834
|
this._subscribers?.reportChanged();
|
|
831
835
|
}
|
|
832
|
-
constructor(
|
|
833
|
-
this.
|
|
836
|
+
constructor(fieldsMetadata, instance, remult, isNewRow) {
|
|
837
|
+
this.fieldsMetadata = fieldsMetadata;
|
|
834
838
|
this.instance = instance;
|
|
835
839
|
this.remult = remult;
|
|
836
840
|
this.isNewRow = isNewRow;
|
|
@@ -840,10 +844,10 @@ class rowHelperBase {
|
|
|
840
844
|
remult = fac.remultFactory();
|
|
841
845
|
}
|
|
842
846
|
}
|
|
843
|
-
for (const col of
|
|
847
|
+
for (const col of fieldsMetadata) {
|
|
844
848
|
let ei = getEntitySettings(col.valueType, false);
|
|
845
849
|
if (ei && remult) {
|
|
846
|
-
let lookup = new LookupColumn(remult.repo(col.valueType), Boolean(col
|
|
850
|
+
let lookup = new LookupColumn(remult.repo(col.valueType), Boolean(getRelationInfo(col.options)), col.allowNull);
|
|
847
851
|
this.lookups.set(col.key, lookup);
|
|
848
852
|
let val = instance[col.key];
|
|
849
853
|
let refImpl;
|
|
@@ -876,7 +880,7 @@ class rowHelperBase {
|
|
|
876
880
|
});
|
|
877
881
|
lookup.set(val);
|
|
878
882
|
}
|
|
879
|
-
else if (getRelationInfo(col)?.type === 'toOne') {
|
|
883
|
+
else if (getRelationInfo(col.options)?.type === 'toOne') {
|
|
880
884
|
let hasVal = instance.hasOwnProperty(col.key);
|
|
881
885
|
let val = instance[col.key];
|
|
882
886
|
if (isNewRow && !val)
|
|
@@ -889,10 +893,10 @@ class rowHelperBase {
|
|
|
889
893
|
val = newVal;
|
|
890
894
|
if (newVal === undefined)
|
|
891
895
|
return;
|
|
892
|
-
const op = col;
|
|
896
|
+
const op = col.options;
|
|
893
897
|
if (op.field) {
|
|
894
898
|
this.instance[op.field] = this.remult
|
|
895
|
-
.repo(getRelationInfo(col).toType())
|
|
899
|
+
.repo(getRelationInfo(col.options).toType())
|
|
896
900
|
.metadata.idMetadata.getId(newVal);
|
|
897
901
|
}
|
|
898
902
|
if (op.fields) {
|
|
@@ -920,7 +924,7 @@ class rowHelperBase {
|
|
|
920
924
|
initSubscribers() {
|
|
921
925
|
if (!this._subscribers) {
|
|
922
926
|
this._subscribers = new SubscribableImp();
|
|
923
|
-
for (const col of this.
|
|
927
|
+
for (const col of this.fieldsMetadata) {
|
|
924
928
|
let ei = getEntitySettings(col.valueType, false);
|
|
925
929
|
let refImpl = this.fields.find(col.key);
|
|
926
930
|
refImpl._subscribers = new SubscribableImp();
|
|
@@ -968,7 +972,7 @@ class rowHelperBase {
|
|
|
968
972
|
message: this.error,
|
|
969
973
|
};
|
|
970
974
|
if (!error.message) {
|
|
971
|
-
for (const col of this.
|
|
975
|
+
for (const col of this.fieldsMetadata) {
|
|
972
976
|
if (this.errors[col.key]) {
|
|
973
977
|
error.message =
|
|
974
978
|
this.fields[col.key].metadata.caption + ': ' + this.errors[col.key];
|
|
@@ -1021,10 +1025,10 @@ class rowHelperBase {
|
|
|
1021
1025
|
}
|
|
1022
1026
|
copyDataToObject(isNew = false) {
|
|
1023
1027
|
let d = {};
|
|
1024
|
-
for (const col of this.
|
|
1028
|
+
for (const col of this.fieldsMetadata) {
|
|
1025
1029
|
let lu = this.lookups.get(col.key);
|
|
1026
1030
|
let val = undefined;
|
|
1027
|
-
const rel = getRelationInfo(col);
|
|
1031
|
+
const rel = getRelationInfo(col.options);
|
|
1028
1032
|
if (lu)
|
|
1029
1033
|
val = lu.id;
|
|
1030
1034
|
else
|
|
@@ -1077,11 +1081,8 @@ class rowHelperBase {
|
|
|
1077
1081
|
async __performColumnAndEntityValidations() { }
|
|
1078
1082
|
toApiJson(includeRelatedEntities = false, notJustApi = false) {
|
|
1079
1083
|
let result = {};
|
|
1080
|
-
for (const col of this.
|
|
1081
|
-
if (notJustApi ||
|
|
1082
|
-
!this.remult ||
|
|
1083
|
-
col.includeInApi === undefined ||
|
|
1084
|
-
this.remult.isAllowedForInstance(this.instance, col.includeInApi)) {
|
|
1084
|
+
for (const col of this.fieldsMetadata) {
|
|
1085
|
+
if (notJustApi || !this.remult || col.includedInApi(this.instance)) {
|
|
1085
1086
|
let val;
|
|
1086
1087
|
let lu = this.lookups.get(col.key);
|
|
1087
1088
|
let disable = false;
|
|
@@ -1094,7 +1095,7 @@ class rowHelperBase {
|
|
|
1094
1095
|
else
|
|
1095
1096
|
val = lu.id;
|
|
1096
1097
|
else {
|
|
1097
|
-
if (getRelationInfo(col) && !includeRelatedEntities) {
|
|
1098
|
+
if (getRelationInfo(col.options) && !includeRelatedEntities) {
|
|
1098
1099
|
disable = true;
|
|
1099
1100
|
}
|
|
1100
1101
|
else {
|
|
@@ -1117,14 +1118,12 @@ class rowHelperBase {
|
|
|
1117
1118
|
}
|
|
1118
1119
|
async _updateEntityBasedOnApi(body, ignoreApiAllowed = false) {
|
|
1119
1120
|
let keys = Object.keys(body);
|
|
1120
|
-
for (const col of this.
|
|
1121
|
+
for (const col of this.fieldsMetadata) {
|
|
1121
1122
|
if (keys.includes(col.key))
|
|
1122
|
-
if (col.
|
|
1123
|
-
this.remult.isAllowedForInstance(this.instance, col.includeInApi)) {
|
|
1123
|
+
if (col.includedInApi(this.instance)) {
|
|
1124
1124
|
if (!this.remult ||
|
|
1125
1125
|
ignoreApiAllowed ||
|
|
1126
|
-
col.
|
|
1127
|
-
this.remult.isAllowedForInstance(this.instance, col.allowApiUpdate)) {
|
|
1126
|
+
col.apiUpdateAllowed(this.instance)) {
|
|
1128
1127
|
let lu = this.lookups.get(col.key);
|
|
1129
1128
|
if (lu)
|
|
1130
1129
|
lu.id = body[col.key];
|
|
@@ -1142,20 +1141,20 @@ export class rowHelperImplementation extends rowHelperBase {
|
|
|
1142
1141
|
edp;
|
|
1143
1142
|
_isNew;
|
|
1144
1143
|
constructor(info, instance, repository, edp, remult, _isNew) {
|
|
1145
|
-
super(info.
|
|
1144
|
+
super(info.fieldsMetadata, instance, remult, _isNew);
|
|
1146
1145
|
this.info = info;
|
|
1147
1146
|
this.repository = repository;
|
|
1148
1147
|
this.edp = edp;
|
|
1149
1148
|
this._isNew = _isNew;
|
|
1150
1149
|
this.metadata = info;
|
|
1151
1150
|
if (_isNew) {
|
|
1152
|
-
for (const col of info.
|
|
1153
|
-
if (col.defaultValue && instance[col.key] === undefined) {
|
|
1154
|
-
if (typeof col.defaultValue === 'function') {
|
|
1155
|
-
instance[col.key] = col.defaultValue(instance);
|
|
1151
|
+
for (const col of info.fieldsMetadata) {
|
|
1152
|
+
if (col.options.defaultValue && instance[col.key] === undefined) {
|
|
1153
|
+
if (typeof col.options.defaultValue === 'function') {
|
|
1154
|
+
instance[col.key] = col.options.defaultValue(instance);
|
|
1156
1155
|
}
|
|
1157
1156
|
else if (!instance[col.key])
|
|
1158
|
-
instance[col.key] = col.defaultValue;
|
|
1157
|
+
instance[col.key] = col.options.defaultValue;
|
|
1159
1158
|
}
|
|
1160
1159
|
}
|
|
1161
1160
|
}
|
|
@@ -1221,8 +1220,8 @@ export class rowHelperImplementation extends rowHelperBase {
|
|
|
1221
1220
|
[Symbol.iterator]: () => _items[Symbol.iterator](),
|
|
1222
1221
|
toArray: () => _items,
|
|
1223
1222
|
};
|
|
1224
|
-
for (const c of this.info.
|
|
1225
|
-
_items.push((r[c.key] = new FieldRefImplementation(c,
|
|
1223
|
+
for (const c of this.info.fieldsMetadata) {
|
|
1224
|
+
_items.push((r[c.key] = new FieldRefImplementation(c.options, c, this.instance, this, this)));
|
|
1226
1225
|
}
|
|
1227
1226
|
this._columns = r;
|
|
1228
1227
|
}
|
|
@@ -1389,9 +1388,9 @@ export class rowHelperImplementation extends rowHelperBase {
|
|
|
1389
1388
|
async calcServerExpression() {
|
|
1390
1389
|
if (isBackend())
|
|
1391
1390
|
//y2 should be changed to be based on data provider - consider naming
|
|
1392
|
-
for (const col of this.info.
|
|
1393
|
-
if (col.serverExpression) {
|
|
1394
|
-
this.instance[col.key] = await col.serverExpression(this.instance);
|
|
1391
|
+
for (const col of this.info.fieldsMetadata) {
|
|
1392
|
+
if (col.options.serverExpression) {
|
|
1393
|
+
this.instance[col.key] = await col.options.serverExpression(this.instance);
|
|
1395
1394
|
}
|
|
1396
1395
|
}
|
|
1397
1396
|
}
|
|
@@ -1410,9 +1409,9 @@ export class rowHelperImplementation extends rowHelperBase {
|
|
|
1410
1409
|
return false;
|
|
1411
1410
|
}
|
|
1412
1411
|
async __performColumnAndEntityValidations() {
|
|
1413
|
-
for (const c of this.
|
|
1414
|
-
if (c.validate) {
|
|
1415
|
-
let col = new FieldRefImplementation(c,
|
|
1412
|
+
for (const c of this.fieldsMetadata) {
|
|
1413
|
+
if (c.options.validate) {
|
|
1414
|
+
let col = new FieldRefImplementation(c.options, c, this.instance, this, this);
|
|
1416
1415
|
await col.__performValidation();
|
|
1417
1416
|
}
|
|
1418
1417
|
}
|
|
@@ -1426,7 +1425,7 @@ export class rowHelperImplementation extends rowHelperBase {
|
|
|
1426
1425
|
}
|
|
1427
1426
|
}
|
|
1428
1427
|
}
|
|
1429
|
-
const controllerColumns = Symbol('controllerColumns');
|
|
1428
|
+
const controllerColumns = Symbol.for('controllerColumns');
|
|
1430
1429
|
function prepareColumnInfo(r, remult) {
|
|
1431
1430
|
return r.map((x) => decorateColumnSettings(x.settings(remult), remult));
|
|
1432
1431
|
}
|
|
@@ -1450,7 +1449,7 @@ export function getControllerRef(container, remultArg) {
|
|
|
1450
1449
|
}
|
|
1451
1450
|
base = Object.getPrototypeOf(base);
|
|
1452
1451
|
}
|
|
1453
|
-
container[controllerColumns] = result = new controllerRefImpl(prepareColumnInfo(columnSettings, remultVar), container, remultVar);
|
|
1452
|
+
container[controllerColumns] = result = new controllerRefImpl(prepareColumnInfo(columnSettings, remultVar).map((x) => new columnDefsImpl(x, undefined, remultVar)), container, remultVar);
|
|
1454
1453
|
}
|
|
1455
1454
|
return result;
|
|
1456
1455
|
}
|
|
@@ -1464,7 +1463,7 @@ export class controllerRefImpl extends rowHelperBase {
|
|
|
1464
1463
|
toArray: () => _items,
|
|
1465
1464
|
};
|
|
1466
1465
|
for (const col of columnsInfo) {
|
|
1467
|
-
_items.push((r[col.key] = new FieldRefImplementation(col,
|
|
1466
|
+
_items.push((r[col.key] = new FieldRefImplementation(col.options, col, instance, undefined, this)));
|
|
1468
1467
|
}
|
|
1469
1468
|
this.fields = r;
|
|
1470
1469
|
}
|
|
@@ -1525,6 +1524,8 @@ export class FieldRefImplementation {
|
|
|
1525
1524
|
.relations(this.container)[this.metadata.key].findOne();
|
|
1526
1525
|
if (val)
|
|
1527
1526
|
this.container[this.metadata.key] = val;
|
|
1527
|
+
else
|
|
1528
|
+
return null;
|
|
1528
1529
|
}
|
|
1529
1530
|
}
|
|
1530
1531
|
else if (lu) {
|
|
@@ -1620,14 +1621,42 @@ export class FieldRefImplementation {
|
|
|
1620
1621
|
entityRef;
|
|
1621
1622
|
async __performValidation() {
|
|
1622
1623
|
try {
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1624
|
+
const processValidation = (result) => {
|
|
1625
|
+
if (result !== true && result !== undefined && !this.error) {
|
|
1626
|
+
if (typeof result === 'string' && result.length > 0)
|
|
1627
|
+
this.error = result;
|
|
1628
|
+
else
|
|
1629
|
+
this.error = 'invalid value';
|
|
1627
1630
|
}
|
|
1631
|
+
};
|
|
1632
|
+
if (this.settings.validate) {
|
|
1633
|
+
let self = this;
|
|
1634
|
+
let event = {
|
|
1635
|
+
entityRef: this.entityRef,
|
|
1636
|
+
get error() {
|
|
1637
|
+
return self.error;
|
|
1638
|
+
},
|
|
1639
|
+
set error(value) {
|
|
1640
|
+
self.error = value;
|
|
1641
|
+
},
|
|
1642
|
+
isNew: self.rowBase.isNewRow,
|
|
1643
|
+
load: () => self.load(),
|
|
1644
|
+
metadata: self.metadata,
|
|
1645
|
+
originalValue: self.originalValue,
|
|
1646
|
+
value: self.value,
|
|
1647
|
+
valueChanged: () => self.valueChanged(),
|
|
1648
|
+
originalValueIsNull: () => self.originalValueIsNull(),
|
|
1649
|
+
valueIsNull: () => self.valueIsNull(),
|
|
1650
|
+
isBackend: () => !self.rowBase?.remult?.dataProvider?.isProxy,
|
|
1651
|
+
};
|
|
1652
|
+
if (Array.isArray(this.settings.validate)) {
|
|
1653
|
+
for (const v of this.settings.validate) {
|
|
1654
|
+
processValidation(await v(this.container, event));
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
else if (typeof this.settings.validate === 'function')
|
|
1658
|
+
processValidation(await this.settings.validate(this.container, event));
|
|
1628
1659
|
}
|
|
1629
|
-
else if (typeof this.settings.validate === 'function')
|
|
1630
|
-
await this.settings.validate(this.container, this);
|
|
1631
1660
|
}
|
|
1632
1661
|
catch (error) {
|
|
1633
1662
|
if (typeof error === 'string')
|
|
@@ -1698,7 +1727,28 @@ export class columnDefsImpl {
|
|
|
1698
1727
|
this.remult = remult;
|
|
1699
1728
|
this.options = this.settings;
|
|
1700
1729
|
this.target = this.settings.target;
|
|
1701
|
-
this.valueConverter = this.settings.valueConverter
|
|
1730
|
+
this.valueConverter = new Proxy(this.settings.valueConverter, {
|
|
1731
|
+
get: (target, prop) => {
|
|
1732
|
+
let result = target[prop];
|
|
1733
|
+
if (typeof result === 'function') {
|
|
1734
|
+
return (...args) => {
|
|
1735
|
+
try {
|
|
1736
|
+
return target[prop](...args);
|
|
1737
|
+
}
|
|
1738
|
+
catch (err) {
|
|
1739
|
+
const error = `${String(prop)} failed for value ${args?.[0]}. Error: ${typeof err === 'string' ? err : err.message}`;
|
|
1740
|
+
throw {
|
|
1741
|
+
message: this.caption + ': ' + error,
|
|
1742
|
+
modelState: {
|
|
1743
|
+
[this.key]: error,
|
|
1744
|
+
},
|
|
1745
|
+
};
|
|
1746
|
+
}
|
|
1747
|
+
};
|
|
1748
|
+
}
|
|
1749
|
+
return result;
|
|
1750
|
+
},
|
|
1751
|
+
});
|
|
1702
1752
|
this.allowNull = !!this.settings.allowNull;
|
|
1703
1753
|
this.valueType = this.settings.valueType;
|
|
1704
1754
|
this.key = this.settings.key;
|
|
@@ -1754,14 +1804,13 @@ export class columnDefsImpl {
|
|
|
1754
1804
|
valueType;
|
|
1755
1805
|
}
|
|
1756
1806
|
class EntityFullInfo {
|
|
1757
|
-
columnsInfo;
|
|
1758
1807
|
entityInfo;
|
|
1759
1808
|
remult;
|
|
1760
1809
|
entityType;
|
|
1761
1810
|
key;
|
|
1762
1811
|
options;
|
|
1812
|
+
fieldsMetadata = [];
|
|
1763
1813
|
constructor(columnsInfo, entityInfo, remult, entityType, key) {
|
|
1764
|
-
this.columnsInfo = columnsInfo;
|
|
1765
1814
|
this.entityInfo = entityInfo;
|
|
1766
1815
|
this.remult = remult;
|
|
1767
1816
|
this.entityType = entityType;
|
|
@@ -1788,14 +1837,14 @@ class EntityFullInfo {
|
|
|
1788
1837
|
this.key = entityType.name;
|
|
1789
1838
|
if (!entityInfo.dbName)
|
|
1790
1839
|
entityInfo.dbName = this.key;
|
|
1791
|
-
|
|
1840
|
+
this.dbName = entityInfo.dbName;
|
|
1792
1841
|
let r = {
|
|
1793
1842
|
find: (c) => r[typeof c === 'string' ? c : c.key],
|
|
1794
|
-
[Symbol.iterator]: () =>
|
|
1795
|
-
toArray: () =>
|
|
1843
|
+
[Symbol.iterator]: () => this.fieldsMetadata[Symbol.iterator](),
|
|
1844
|
+
toArray: () => this.fieldsMetadata,
|
|
1796
1845
|
};
|
|
1797
1846
|
for (const x of columnsInfo) {
|
|
1798
|
-
|
|
1847
|
+
this.fieldsMetadata.push((r[x.key] = new columnDefsImpl(x, this, remult)));
|
|
1799
1848
|
}
|
|
1800
1849
|
this.fields = r;
|
|
1801
1850
|
this.caption = buildCaption(entityInfo.caption, this.key, remult, this);
|
|
@@ -1903,7 +1952,7 @@ export function FieldType(...options) {
|
|
|
1903
1952
|
options = [];
|
|
1904
1953
|
}
|
|
1905
1954
|
options.splice(0, 0, { valueType: target });
|
|
1906
|
-
|
|
1955
|
+
target[storableMember] = options;
|
|
1907
1956
|
return target;
|
|
1908
1957
|
};
|
|
1909
1958
|
}
|
|
@@ -1950,7 +1999,7 @@ export class ValueListInfo {
|
|
|
1950
1999
|
if (this.isNumeric) {
|
|
1951
2000
|
this.fieldTypeInDb = 'integer';
|
|
1952
2001
|
}
|
|
1953
|
-
var options =
|
|
2002
|
+
var options = this.valueListType[storableMember];
|
|
1954
2003
|
if (options) {
|
|
1955
2004
|
for (const op of options) {
|
|
1956
2005
|
if (op?.getValues) {
|
|
@@ -2020,7 +2069,7 @@ export function getValueList(type) {
|
|
|
2020
2069
|
}
|
|
2021
2070
|
return ValueListInfo.get(type).getValues();
|
|
2022
2071
|
}
|
|
2023
|
-
export const storableMember = Symbol('storableMember');
|
|
2072
|
+
export const storableMember = Symbol.for('storableMember');
|
|
2024
2073
|
export function buildOptions(options, remult) {
|
|
2025
2074
|
let r = {};
|
|
2026
2075
|
for (const o of options) {
|
|
@@ -2035,7 +2084,7 @@ export function buildOptions(options, remult) {
|
|
|
2035
2084
|
}
|
|
2036
2085
|
export function decorateColumnSettings(settings, remult) {
|
|
2037
2086
|
if (settings.valueType) {
|
|
2038
|
-
let settingsOnTypeLevel =
|
|
2087
|
+
let settingsOnTypeLevel = settings.valueType[storableMember];
|
|
2039
2088
|
if (settingsOnTypeLevel) {
|
|
2040
2089
|
settings = {
|
|
2041
2090
|
...buildOptions(settingsOnTypeLevel, remult),
|
|
@@ -62,8 +62,8 @@ export function Entity(key, ...options) {
|
|
|
62
62
|
};
|
|
63
63
|
allEntities.push(target);
|
|
64
64
|
setControllerSettings(target, { key });
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
target[entityInfo] = factory;
|
|
66
|
+
target[entityInfo_key] = key;
|
|
67
67
|
return target;
|
|
68
68
|
};
|
|
69
69
|
}
|
|
@@ -6,9 +6,9 @@ export function getEntityRef(entity, throwException = true) {
|
|
|
6
6
|
' was not initialized using a context');
|
|
7
7
|
return x;
|
|
8
8
|
}
|
|
9
|
-
export const entityMember = Symbol('entityMember');
|
|
10
|
-
export const entityInfo = Symbol('entityInfo');
|
|
11
|
-
export const entityInfo_key = Symbol('entityInfo_key');
|
|
9
|
+
export const entityMember = Symbol.for('entityMember');
|
|
10
|
+
export const entityInfo = Symbol.for('entityInfo');
|
|
11
|
+
export const entityInfo_key = Symbol.for('entityInfo_key');
|
|
12
12
|
export function getEntitySettings(entity, throwError = true) {
|
|
13
13
|
if (entity === undefined)
|
|
14
14
|
if (throwError) {
|
|
@@ -16,12 +16,12 @@ export function getEntitySettings(entity, throwError = true) {
|
|
|
16
16
|
}
|
|
17
17
|
else
|
|
18
18
|
return undefined;
|
|
19
|
-
let info =
|
|
19
|
+
let info = entity[entityInfo];
|
|
20
20
|
if (!info && throwError)
|
|
21
21
|
throw new Error(entity.prototype.constructor.name +
|
|
22
22
|
" is not a known entity, did you forget to set @Entity() or did you forget to add the '@' before the call to Entity?");
|
|
23
23
|
return info;
|
|
24
24
|
}
|
|
25
25
|
export function getEntityKey(entity) {
|
|
26
|
-
return
|
|
26
|
+
return entity[entityInfo_key];
|
|
27
27
|
}
|
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
export {};
|
|
2
|
-
|
|
3
|
-
-
|
|
4
|
-
- https://www.npmjs.com/package/@sqlite.org/sqlite-wasm
|
|
5
|
-
All I could find is correspondance around evolu :) the guy I met in prague last year
|
|
6
|
-
Indexed db was also annoying with it's upgrade process etc...
|
|
7
|
-
I'm OPFS & json for now.
|
|
2
|
+
//y1 - discuss limit and include queries
|
|
3
|
+
//y1 - tried to upgrade vitest, nuxt tests are failing with loading uuid - sounds familiar?
|
|
8
4
|
//y2 - from the crm-demo(https://crm-demo.up.railway.app/deals), after editing a deal: - _updateEntityBasedOnApi
|
|
9
|
-
|
|
10
5
|
//y2 - Backend methods are transactions, it's not intuitive and maybe should be optional / opt in
|
|
11
6
|
//y2 - how to run a transaction as a user
|
|
12
7
|
//p1 - enum column
|
|
@@ -48,6 +43,8 @@ remult.apiClient.url='localhost:3007/api
|
|
|
48
43
|
|
|
49
44
|
|
|
50
45
|
*/
|
|
46
|
+
//p2 - document validators
|
|
47
|
+
//p2 - and validators to reference
|
|
51
48
|
//y2 - discuss a default date formatter
|
|
52
49
|
//y2 - add some api testing framework for user unit tests (will help with codesandbox based discussions)
|
|
53
50
|
//y2 - live query refresh of view on table update
|
|
@@ -71,3 +68,5 @@ remult.apiClient.url='localhost:3007/api
|
|
|
71
68
|
//[ ] V2 - what to do about for relations count?
|
|
72
69
|
//[ ] V2 - condition? not to fetch if null etc....
|
|
73
70
|
//[ ] V3 - all these fields will also appear in the where etc... in the typescript api - but we will not enforce them
|
|
71
|
+
//y2 - consider if field types should include validation in them by default (string,number that it's not NaN etc...) and if so, what message?
|
|
72
|
+
//y2 - should enforce integer - currently we probably round / truncate it
|
package/esm/src/server-action.js
CHANGED
|
@@ -395,4 +395,4 @@ export async function prepareReceivedArgs(types, args, remult, ds, res) {
|
|
|
395
395
|
}
|
|
396
396
|
return args;
|
|
397
397
|
}
|
|
398
|
-
export const classBackendMethodsArray = Symbol('classBackendMethodsArray');
|
|
398
|
+
export const classBackendMethodsArray = Symbol.for('classBackendMethodsArray');
|
package/esm/src/validators.js
CHANGED
|
@@ -1,36 +1,128 @@
|
|
|
1
|
-
import { isBackend } from './context.js';
|
|
2
1
|
export class Validators {
|
|
3
|
-
static required =
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
col.error = message || Validators.required.defaultMessage;
|
|
7
|
-
}, {
|
|
8
|
-
withMessage: (message) => {
|
|
9
|
-
return (entity, col) => Validators.required(entity, col, message);
|
|
10
|
-
},
|
|
11
|
-
defaultMessage: 'Should not be empty',
|
|
12
|
-
});
|
|
13
|
-
static unique = Object.assign(async (entity, col, message = undefined) => {
|
|
14
|
-
if (!col.entityRef)
|
|
2
|
+
static required = createValidator(async (_, e) => e.value != null && e.value != undefined && e.value !== '' && e.value != 0, 'Should not be empty');
|
|
3
|
+
static unique = createValidator(async (_, e) => {
|
|
4
|
+
if (!e.entityRef)
|
|
15
5
|
throw 'unique validation may only work on columns that are attached to an entity';
|
|
16
|
-
if (
|
|
17
|
-
|
|
18
|
-
[
|
|
19
|
-
}))
|
|
20
|
-
col.error = message || Validators.unique.defaultMessage;
|
|
6
|
+
if (e.isBackend() && (e.isNew || e.valueChanged())) {
|
|
7
|
+
return ((await e.entityRef.repository.count({
|
|
8
|
+
[e.metadata.key]: e.value,
|
|
9
|
+
})) == 0);
|
|
21
10
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
11
|
+
else
|
|
12
|
+
return true;
|
|
13
|
+
}, 'already exists');
|
|
14
|
+
/**
|
|
15
|
+
* @deprecated is `unique` instead - it also runs only on the backend
|
|
16
|
+
*/
|
|
17
|
+
static uniqueOnBackend = createValidator(async (_, e) => {
|
|
18
|
+
if (e.isBackend() && (e.isNew || e.valueChanged())) {
|
|
19
|
+
return ((await e.entityRef.repository.count({
|
|
20
|
+
[e.metadata.key]: e.value,
|
|
21
|
+
})) == 0);
|
|
22
|
+
}
|
|
23
|
+
else
|
|
24
|
+
return true;
|
|
25
|
+
}, Validators.unique.defaultMessage);
|
|
26
|
+
static regex = createValueValidatorWithArgs((val, regex) => regex.test(val));
|
|
27
|
+
static email = createValueValidator((val) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val), 'Invalid Email');
|
|
28
|
+
static url = createValueValidator((val) => !!new URL(val), 'Invalid Url');
|
|
29
|
+
static in = createValueValidatorWithArgs((val, values) => values.includes(val), (values) => `Value must be one of ${values.join(', ')}`);
|
|
30
|
+
static notNull = createValueValidator((val) => val != null, 'Should not be null');
|
|
31
|
+
static enum = createValueValidatorWithArgs((value, enumObj) => Object.values(enumObj).includes(value), (enumObj) => `Value must be one of ${Object.values(enumObj)
|
|
32
|
+
.filter((x) => typeof enumObj[x] !== 'number')
|
|
33
|
+
.join(', ')}`);
|
|
34
|
+
static relationExists = createValidator(async (_, e) => {
|
|
35
|
+
if (e.valueIsNull())
|
|
36
|
+
return true;
|
|
37
|
+
if (!e.isBackend())
|
|
38
|
+
return true;
|
|
39
|
+
return Boolean(await e.load());
|
|
40
|
+
}, 'Relation value does not exist');
|
|
41
|
+
static maxLength = createValueValidatorWithArgs((val, maxLength) => val.length <= maxLength, (maxLength) => `Value must be at most ${maxLength} characters`);
|
|
42
|
+
static defaultMessage = 'Invalid value';
|
|
43
|
+
}
|
|
44
|
+
export function createValidator(validate, defaultMessage) {
|
|
45
|
+
const validation = async (entity, e, message) => {
|
|
46
|
+
const valid = await validate(entity, e);
|
|
47
|
+
if (typeof valid === 'string' && valid.length > 0)
|
|
48
|
+
e.error = valid;
|
|
49
|
+
else if (!valid)
|
|
50
|
+
e.error =
|
|
51
|
+
(typeof message === 'function' && message(entity, e, undefined)) ||
|
|
52
|
+
message ||
|
|
53
|
+
(typeof defaultMessage === 'function' &&
|
|
54
|
+
defaultMessage(entity, e, undefined)) ||
|
|
55
|
+
defaultMessage ||
|
|
56
|
+
Validators.defaultMessage;
|
|
57
|
+
};
|
|
58
|
+
const result = (entityOrMessage, e, message) => {
|
|
59
|
+
if (typeof entityOrMessage === 'string' ||
|
|
60
|
+
entityOrMessage === 'function' ||
|
|
61
|
+
(entityOrMessage === undefined && e === undefined)) {
|
|
62
|
+
return async (entity, e, message) => await validation(entity, e, entityOrMessage || message);
|
|
63
|
+
}
|
|
64
|
+
return validation(entityOrMessage, e, message);
|
|
65
|
+
};
|
|
66
|
+
//@ts-ignore
|
|
67
|
+
return Object.assign(result, {
|
|
68
|
+
withMessage: (message) => async (entity, e) => result(entity, e, message),
|
|
69
|
+
get defaultMessage() {
|
|
70
|
+
return defaultMessage;
|
|
71
|
+
},
|
|
72
|
+
set defaultMessage(val) {
|
|
73
|
+
defaultMessage = val;
|
|
74
|
+
},
|
|
75
|
+
isValid: validate,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
export function valueValidator(validate, defaultMessage) {
|
|
79
|
+
return (entity, e) => validate(e.value) || defaultMessage || false;
|
|
80
|
+
}
|
|
81
|
+
export function createValueValidator(validate, defaultMessage) {
|
|
82
|
+
return createValidator((_, e) => {
|
|
83
|
+
if (e.value === undefined || e.value === null)
|
|
84
|
+
return true;
|
|
85
|
+
return validate(e.value);
|
|
86
|
+
}, defaultMessage);
|
|
87
|
+
}
|
|
88
|
+
export function createValueValidatorWithArgs(validate, defaultMessage) {
|
|
89
|
+
const result = createValidatorWithArgs((_, e, args) => {
|
|
90
|
+
if (e.value === undefined || e.value === null)
|
|
91
|
+
return true;
|
|
92
|
+
return validate(e.value, args);
|
|
93
|
+
}, (_, e, args) => (typeof defaultMessage === 'function' && defaultMessage(args)) ||
|
|
94
|
+
defaultMessage);
|
|
95
|
+
return Object.assign((entity, e) => result(entity, e), {
|
|
96
|
+
get defaultMessage() {
|
|
97
|
+
return defaultMessage;
|
|
98
|
+
},
|
|
99
|
+
set defaultMessage(val) {
|
|
100
|
+
defaultMessage = val;
|
|
25
101
|
},
|
|
26
|
-
defaultMessage: 'already exists',
|
|
27
102
|
});
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
103
|
+
}
|
|
104
|
+
export function createValidatorWithArgs(validate, defaultMessage) {
|
|
105
|
+
const result = (args, message) => async (entity, e) => {
|
|
106
|
+
const valid = await validate(entity, e, args);
|
|
107
|
+
if (typeof valid === 'string')
|
|
108
|
+
e.error = valid;
|
|
109
|
+
else if (!valid)
|
|
110
|
+
e.error =
|
|
111
|
+
message || defaultMessage
|
|
112
|
+
? typeof defaultMessage === 'function'
|
|
113
|
+
? defaultMessage(entity, e, args)
|
|
114
|
+
: defaultMessage
|
|
115
|
+
: Validators.defaultMessage;
|
|
116
|
+
};
|
|
117
|
+
return Object.assign(result, {
|
|
118
|
+
get defaultMessage() {
|
|
119
|
+
return defaultMessage;
|
|
120
|
+
},
|
|
121
|
+
set defaultMessage(val) {
|
|
122
|
+
defaultMessage = val;
|
|
34
123
|
},
|
|
35
124
|
});
|
|
36
125
|
}
|
|
126
|
+
//y1 - talk about the 3rd parameter as message
|
|
127
|
+
//y1 - talk about the changed signature breaking existing validations and preventing the usage of unique outside the normal flow
|
|
128
|
+
//y1 - talk about the defaultMessage value now being a function that get's too many arguments, that makes it's reuse that much harder
|