remult 0.25.8-exp.7 → 0.25.8-exp.9
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/esm/server/initAsyncHooks.js +76 -1
- package/esm/src/context.js +6 -16
- package/esm/src/isOfType.js +1 -1
- package/esm/src/remult3/Fields.js +70 -18
- package/esm/src/remult3/RepositoryImplementation.js +9 -6
- package/esm/src/remult3/addValidator.js +15 -0
- package/esm/src/remult3/remult3.js +14 -47
- package/esm/src/validators.js +6 -3
- package/package.json +1 -1
- package/server/initAsyncHooks.d.ts +16 -0
- package/server/initAsyncHooks.js +129 -2
- package/src/column-interfaces.d.ts +9 -3
- package/src/context.d.ts +5 -5
- package/src/context.js +11 -28
- package/src/isOfType.js +1 -1
- package/src/remult3/Fields.d.ts +36 -0
- package/src/remult3/Fields.js +81 -19
- package/src/remult3/RepositoryImplementation.js +9 -3
- package/src/remult3/addValidator.d.ts +2 -0
- package/src/remult3/addValidator.js +20 -0
- package/src/remult3/remult3.js +14 -47
- package/src/validators.d.ts +1 -0
- package/src/validators.js +10 -4
|
@@ -6,5 +6,80 @@ export function initAsyncHooks() {
|
|
|
6
6
|
if (init)
|
|
7
7
|
return;
|
|
8
8
|
init = true;
|
|
9
|
-
remultStatic.asyncContext = new RemultAsyncLocalStorage(new
|
|
9
|
+
remultStatic.asyncContext = new RemultAsyncLocalStorage(new AsyncLocalStorageBridgeToRemultAsyncLocalStorageCore());
|
|
10
|
+
let test = new AsyncLocalStorage();
|
|
11
|
+
test.run(1, async () => {
|
|
12
|
+
await Promise.resolve();
|
|
13
|
+
if (test.getStore() === undefined) {
|
|
14
|
+
console.log("async_hooks.AsyncLocalStorage not working, using sequential implementation (You're probably running on stackblitz, this will work on a normal nodejs environment");
|
|
15
|
+
remultStatic.asyncContext = new RemultAsyncLocalStorage(new SequentialRemultAsyncLocalStorageCore());
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
export class AsyncLocalStorageBridgeToRemultAsyncLocalStorageCoreImpl {
|
|
20
|
+
asyncLocalStorage = new AsyncLocalStorage();
|
|
21
|
+
wasImplemented;
|
|
22
|
+
run(store, callback) {
|
|
23
|
+
let r;
|
|
24
|
+
this.asyncLocalStorage.run(store, () => {
|
|
25
|
+
r = new Promise(async (res, rej) => {
|
|
26
|
+
try {
|
|
27
|
+
res(await callback());
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
rej(err);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
return r;
|
|
35
|
+
}
|
|
36
|
+
getStore() {
|
|
37
|
+
return this.asyncLocalStorage.getStore();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const ignoreInStack = ["AsyncLocalStorageBridgeToRemultAsyncLocalStorageCore.run", "RemultAsyncLocalStorage.run", "Module.withRemult", "RemultServerImplementation.runWithRemult"];
|
|
41
|
+
export class SequentialRemultAsyncLocalStorageCore {
|
|
42
|
+
wasImplemented;
|
|
43
|
+
async run(store, callback) {
|
|
44
|
+
let log = (msg) => {
|
|
45
|
+
console.log(msg);
|
|
46
|
+
};
|
|
47
|
+
if (false) {
|
|
48
|
+
let stack = new Error().stack;
|
|
49
|
+
let y = stack.split('\n');
|
|
50
|
+
while (y.length > 0 && (y[0].trim().startsWith("Error") || ignoreInStack.includes(y[0].trim().split(' ')[1]))) {
|
|
51
|
+
y.splice(0, 1);
|
|
52
|
+
}
|
|
53
|
+
stack = y.join('\n');
|
|
54
|
+
log = (msg) => {
|
|
55
|
+
console.log(msg, stack);
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
log('waiting on ');
|
|
59
|
+
const nextPromise = this.lastPromise.then(async () => {
|
|
60
|
+
log('executing ');
|
|
61
|
+
const previousValue = this.currentValue;
|
|
62
|
+
this.currentValue = store;
|
|
63
|
+
const result = await callback();
|
|
64
|
+
this.currentValue = previousValue;
|
|
65
|
+
return result;
|
|
66
|
+
});
|
|
67
|
+
this.lastPromise = nextPromise.catch(() => {
|
|
68
|
+
log("Error on ");
|
|
69
|
+
return undefined;
|
|
70
|
+
});
|
|
71
|
+
try {
|
|
72
|
+
return await nextPromise;
|
|
73
|
+
}
|
|
74
|
+
finally {
|
|
75
|
+
log('completed ');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
getStore() {
|
|
79
|
+
return this.currentValue;
|
|
80
|
+
}
|
|
81
|
+
lastPromise = Promise.resolve(undefined);
|
|
82
|
+
currentValue;
|
|
83
|
+
}
|
|
84
|
+
export class AsyncLocalStorageBridgeToRemultAsyncLocalStorageCore extends AsyncLocalStorageBridgeToRemultAsyncLocalStorageCoreImpl {
|
|
10
85
|
}
|
package/esm/src/context.js
CHANGED
|
@@ -24,11 +24,12 @@ export class RemultAsyncLocalStorage {
|
|
|
24
24
|
constructor(remultObjectStorage) {
|
|
25
25
|
this.remultObjectStorage = remultObjectStorage;
|
|
26
26
|
}
|
|
27
|
-
run(remult, callback) {
|
|
28
|
-
if (this.remultObjectStorage)
|
|
29
|
-
this.remultObjectStorage.run(remult, () => callback(remult));
|
|
27
|
+
async run(remult, callback) {
|
|
28
|
+
if (this.remultObjectStorage) {
|
|
29
|
+
return this.remultObjectStorage.run(remult, () => callback(remult));
|
|
30
|
+
}
|
|
30
31
|
else
|
|
31
|
-
callback(remult);
|
|
32
|
+
return callback(remult);
|
|
32
33
|
}
|
|
33
34
|
getRemult() {
|
|
34
35
|
if (!this.remultObjectStorage) {
|
|
@@ -285,16 +286,5 @@ export function withRemult(callback, options) {
|
|
|
285
286
|
const remult = new Remult();
|
|
286
287
|
if (options?.dataProvider)
|
|
287
288
|
remult.dataProvider = options.dataProvider;
|
|
288
|
-
|
|
289
|
-
remultStatic.asyncContext.run(remult, () => {
|
|
290
|
-
r = new Promise(async (res, rej) => {
|
|
291
|
-
try {
|
|
292
|
-
res(await callback(remult));
|
|
293
|
-
}
|
|
294
|
-
catch (err) {
|
|
295
|
-
rej(err);
|
|
296
|
-
}
|
|
297
|
-
});
|
|
298
|
-
});
|
|
299
|
-
return r;
|
|
289
|
+
return remultStatic.asyncContext.run(remult, r => callback(r));
|
|
300
290
|
}
|
package/esm/src/isOfType.js
CHANGED
|
@@ -2,9 +2,13 @@ import { createId } from '@paralleldrive/cuid2';
|
|
|
2
2
|
import { v4 as uuid } from 'uuid';
|
|
3
3
|
import { ValueConverters } from '../valueConverters.js';
|
|
4
4
|
import { buildOptions } from './RepositoryImplementation.js';
|
|
5
|
-
import { Validators } from '../validators.js';
|
|
5
|
+
import { Validators, createValueValidator, getEnumValues } from '../validators.js';
|
|
6
6
|
import { relationInfoMemberInOptions } from './relationInfoMember.js';
|
|
7
7
|
import { remultStatic } from '../remult-static.js';
|
|
8
|
+
import { addValidator } from './addValidator.js';
|
|
9
|
+
const validateNumber = createValueValidator((x) => {
|
|
10
|
+
return !isNaN(x) && isFinite(x);
|
|
11
|
+
});
|
|
8
12
|
export class Fields {
|
|
9
13
|
/**
|
|
10
14
|
* Stored as a JSON.stringify - to store as json use Fields.json
|
|
@@ -34,6 +38,7 @@ export class Fields {
|
|
|
34
38
|
static integer(...options) {
|
|
35
39
|
return Field(() => Number, {
|
|
36
40
|
valueConverter: ValueConverters.Integer,
|
|
41
|
+
validate: validateNumber,
|
|
37
42
|
}, ...options);
|
|
38
43
|
}
|
|
39
44
|
static autoIncrement(...options) {
|
|
@@ -47,7 +52,9 @@ export class Fields {
|
|
|
47
52
|
}, ...options);
|
|
48
53
|
}
|
|
49
54
|
static number(...options) {
|
|
50
|
-
return Field(() => Number,
|
|
55
|
+
return Field(() => Number, {
|
|
56
|
+
validate: validateNumber,
|
|
57
|
+
}, ...options);
|
|
51
58
|
}
|
|
52
59
|
static createdAt(...options) {
|
|
53
60
|
return Field(() => Date, {
|
|
@@ -76,6 +83,11 @@ export class Fields {
|
|
|
76
83
|
},
|
|
77
84
|
}, ...options);
|
|
78
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* A CUID (Collision Resistant Unique Identifier) field.
|
|
88
|
+
* This id value is determined on the backend on insert, and can't be updated through the API.
|
|
89
|
+
* The CUID is generated using the `@paralleldrive/cuid2` npm package.
|
|
90
|
+
*/
|
|
79
91
|
static cuid(...options) {
|
|
80
92
|
return Field(() => String, {
|
|
81
93
|
allowApiUpdate: false,
|
|
@@ -86,6 +98,61 @@ export class Fields {
|
|
|
86
98
|
},
|
|
87
99
|
}, ...options);
|
|
88
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* Defines a field that can hold a value from a specified set of string literals.
|
|
103
|
+
* @param {() => readonly valueType[]} optionalValues - A function that returns an array of allowed string literals.
|
|
104
|
+
* @returns {ClassFieldDecorator<entityType, valueType | undefined>} - A class field decorator.
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
|
|
108
|
+
* class MyEntity {
|
|
109
|
+
* .@Fields.literal(() => ['open', 'closed', 'frozen', 'in progress'] as const)
|
|
110
|
+
* status: 'open' | 'closed' | 'frozen' | 'in progress' = 'open';
|
|
111
|
+
* }
|
|
112
|
+
|
|
113
|
+
*
|
|
114
|
+
* // This defines a field `status` in `MyEntity` that can only hold the values 'open', 'closed', 'frozen', or 'in progress'.
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* // For better reusability and maintainability:
|
|
118
|
+
|
|
119
|
+
* const statuses = ['open', 'closed', 'frozen', 'in progress'] as const;
|
|
120
|
+
* type StatusType = typeof statuses[number];
|
|
121
|
+
*
|
|
122
|
+
* class MyEntity {
|
|
123
|
+
* .@Fields.literal(() => statuses)
|
|
124
|
+
* status: StatusType = 'open';
|
|
125
|
+
* }
|
|
126
|
+
|
|
127
|
+
*
|
|
128
|
+
* // This approach allows easy management and updates of the allowed values for the `status` field.
|
|
129
|
+
*/
|
|
130
|
+
static literal(optionalValues, ...options) {
|
|
131
|
+
return Fields.string({
|
|
132
|
+
validate: (entity, event) => Validators.in(optionalValues())(entity, event)
|
|
133
|
+
}, ...options);
|
|
134
|
+
}
|
|
135
|
+
static enum(enumType, ...options) {
|
|
136
|
+
let valueConverter;
|
|
137
|
+
return Field(() =>
|
|
138
|
+
//@ts-ignore
|
|
139
|
+
enumType(), {
|
|
140
|
+
validate: (entity, event) => Validators.enum(enumType())(entity, event)
|
|
141
|
+
}, ...options, (options) => {
|
|
142
|
+
if (valueConverter === undefined) {
|
|
143
|
+
let enumObj = enumType();
|
|
144
|
+
let enumValues = getEnumValues(enumObj);
|
|
145
|
+
valueConverter = enumValues.find(x => typeof x === "string") ? ValueConverters.String : ValueConverters.Integer;
|
|
146
|
+
}
|
|
147
|
+
if (!options.valueConverter) {
|
|
148
|
+
options.valueConverter = valueConverter;
|
|
149
|
+
}
|
|
150
|
+
else if (!options.valueConverter.fieldTypeInDb) {
|
|
151
|
+
//@ts-ignore
|
|
152
|
+
options.valueConverter.fieldTypeInDb = valueConverter.fieldTypeInDb;
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
}
|
|
89
156
|
static string(...options) {
|
|
90
157
|
return Field(() => String, ...options);
|
|
91
158
|
}
|
|
@@ -186,7 +253,7 @@ export function Field(valueType, ...options) {
|
|
|
186
253
|
let factory = (remult) => {
|
|
187
254
|
let r = buildOptions(options, remult);
|
|
188
255
|
if (r.required) {
|
|
189
|
-
r.validate = addValidator(r.validate, Validators.required);
|
|
256
|
+
r.validate = addValidator(r.validate, Validators.required, true);
|
|
190
257
|
}
|
|
191
258
|
if (r.maxLength) {
|
|
192
259
|
r.validate = addValidator(r.validate, Validators.maxLength(r.maxLength));
|
|
@@ -240,18 +307,3 @@ export function checkTarget(target) {
|
|
|
240
307
|
if (!target)
|
|
241
308
|
throw new Error("Set the 'experimentalDecorators:true' option in your 'tsconfig' or 'jsconfig' (target undefined)");
|
|
242
309
|
}
|
|
243
|
-
function addValidator(validators, newValidator, atStart = false) {
|
|
244
|
-
if (!newValidator)
|
|
245
|
-
return;
|
|
246
|
-
const newValidators = Array.isArray(newValidator)
|
|
247
|
-
? newValidator
|
|
248
|
-
: [newValidator];
|
|
249
|
-
const validatorsArray = Array.isArray(validators)
|
|
250
|
-
? validators
|
|
251
|
-
: validators
|
|
252
|
-
? [validators]
|
|
253
|
-
: [];
|
|
254
|
-
return atStart
|
|
255
|
-
? [...newValidators, ...validatorsArray]
|
|
256
|
-
: [...validatorsArray, ...newValidators];
|
|
257
|
-
}
|
|
@@ -14,6 +14,8 @@ import { RelationLoader } from './relation-loader.js';
|
|
|
14
14
|
import { getInternalKey, } from './repository-internals.js';
|
|
15
15
|
import { entityDbName, fieldDbName, } from '../filter/filter-consumer-bridge-to-sql-request.js';
|
|
16
16
|
import { remultStatic } from '../remult-static.js';
|
|
17
|
+
import { Validators } from '../validators.js';
|
|
18
|
+
import { addValidator } from './addValidator.js';
|
|
17
19
|
//import { remult } from "../remult-proxy";
|
|
18
20
|
let classValidatorValidate = undefined;
|
|
19
21
|
// import ("class-validator".toString())
|
|
@@ -1961,6 +1963,7 @@ export function ValueListFieldType(...options) {
|
|
|
1961
1963
|
;
|
|
1962
1964
|
(o.valueConverter = ValueListInfo.get(type)),
|
|
1963
1965
|
(o.displayValue = (item, val) => val?.caption);
|
|
1966
|
+
o.validate = (entity, ref) => Validators.in(ValueListInfo.get(type).getValues())(entity, ref);
|
|
1964
1967
|
}, ...options)(type, context);
|
|
1965
1968
|
};
|
|
1966
1969
|
}
|
|
@@ -2072,8 +2075,11 @@ export function buildOptions(options, remult) {
|
|
|
2072
2075
|
if (o) {
|
|
2073
2076
|
if (typeof o === 'function')
|
|
2074
2077
|
o(r, remult);
|
|
2075
|
-
else
|
|
2076
|
-
|
|
2078
|
+
else {
|
|
2079
|
+
const { validate, ...otherOptions } = o;
|
|
2080
|
+
r.validate = addValidator(r.validate, validate);
|
|
2081
|
+
Object.assign(r, otherOptions);
|
|
2082
|
+
}
|
|
2077
2083
|
}
|
|
2078
2084
|
}
|
|
2079
2085
|
return r;
|
|
@@ -2082,10 +2088,7 @@ export function decorateColumnSettings(settings, remult) {
|
|
|
2082
2088
|
if (settings.valueType) {
|
|
2083
2089
|
let settingsOnTypeLevel = settings.valueType[storableMember];
|
|
2084
2090
|
if (settingsOnTypeLevel) {
|
|
2085
|
-
settings =
|
|
2086
|
-
...buildOptions(settingsOnTypeLevel, remult),
|
|
2087
|
-
...settings,
|
|
2088
|
-
};
|
|
2091
|
+
settings = buildOptions([...settingsOnTypeLevel, settings], remult);
|
|
2089
2092
|
}
|
|
2090
2093
|
}
|
|
2091
2094
|
if (settings.valueType == String) {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function addValidator(validators, newValidator, atStart = false) {
|
|
2
|
+
if (!newValidator)
|
|
3
|
+
return validators;
|
|
4
|
+
const newValidators = Array.isArray(newValidator)
|
|
5
|
+
? newValidator
|
|
6
|
+
: [newValidator];
|
|
7
|
+
const validatorsArray = Array.isArray(validators)
|
|
8
|
+
? validators
|
|
9
|
+
: validators
|
|
10
|
+
? [validators]
|
|
11
|
+
: [];
|
|
12
|
+
return atStart
|
|
13
|
+
? [...newValidators, ...validatorsArray]
|
|
14
|
+
: [...validatorsArray, ...newValidators];
|
|
15
|
+
}
|
|
@@ -1,46 +1,25 @@
|
|
|
1
1
|
export {};
|
|
2
|
-
//p1 -
|
|
3
|
-
//p1 -
|
|
4
|
-
//p1 -
|
|
5
|
-
//p1 -
|
|
6
|
-
//
|
|
7
|
-
//y1 - consider replacing all errors with error classes that extend the base Error class
|
|
2
|
+
//p1 - `preprocessApiFindOptions`, naming - allow find options preprocessor for api calls, to use for authorization
|
|
3
|
+
//p1 - admin url1 - pocketbase use /_ for the admin //{allowed?:Allowed,url?:string}
|
|
4
|
+
//p1 - Make async hooks gracefully handle non async hooks environment
|
|
5
|
+
//p1 - implement generic sqlite support better-sqlite3/ bun sqlite
|
|
6
|
+
//p1 - article auth.js with express
|
|
8
7
|
//p1 - document offline support
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
8
|
+
//y2 - should we validate relations
|
|
9
|
+
//y1 - consider replacing all errors with error classes that extend the base Error class
|
|
10
|
+
//y2 - should enforce integer - currently we probably round / truncate it
|
|
11
|
+
/*y1 - talk about modules in init express with entities/controllers,initRequest,initApi
|
|
12
|
+
- support get with backend method, with url search params as the first parameter, & url as second parameter
|
|
13
|
+
- support returning redirect, and plain html (For sign in scenarios)
|
|
14
|
+
|
|
15
|
+
*/
|
|
16
|
+
//p1 - in this video I'll use remult to turn a frontend app to a fullstack app
|
|
12
17
|
/*y1 - Talk JYC - JYC - add some integrity checks on delete
|
|
13
18
|
- soft delete
|
|
14
19
|
- delete restrict (implicit, or user selected - and if so, how) (delete & update of id)
|
|
15
20
|
|
|
16
21
|
*/
|
|
17
|
-
/*y1 currency.ts:10 Uncaught TypeError: Currency_1 is not a constructor
|
|
18
|
-
// - ValueListFieldType - the decorator gives an error in react vite project - see langulage yedidya
|
|
19
|
-
// happens in target 2022, ts<5.1.6
|
|
20
|
-
//typescript 5.1.6
|
|
21
|
-
@ValueListFieldType()
|
|
22
|
-
export class Currency {
|
|
23
|
-
constructor(
|
|
24
|
-
public id: number,
|
|
25
|
-
public caption: string,
|
|
26
|
-
public symbol: string
|
|
27
|
-
) {}
|
|
28
|
-
static shekel = new Currency(1, 'Shekel', '₪');
|
|
29
|
-
static dollar = new Currency(2, 'Dollar', '$');
|
|
30
|
-
static euro = new Currency(3, 'Euro', '€');
|
|
31
|
-
static pound = new Currency(4, 'Pound', '£');
|
|
32
|
-
static yen = new Currency(5, 'Yen', '¥');
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
*/
|
|
36
|
-
//y1 - allow api read to also support instance and filter. - problem with promise
|
|
37
|
-
//y1 - admin url1 - pocketbase use /_ for the admin
|
|
38
22
|
//y1 - consider sql expression gets a dbnames of it's own (that already has the "tableName" defined correctly) maybe also the filter translator
|
|
39
|
-
/*y1 - talk about modules in init express with entities/controllers,initRequest,initApi
|
|
40
|
-
- support get with backend method, with url search params as the first parameter, & url as second parameter
|
|
41
|
-
- support returning redirect, and plain html (For sign in scenarios)
|
|
42
|
-
|
|
43
|
-
*/
|
|
44
23
|
//y1 - tried to upgrade vitest, nuxt tests are failing with loading uuid - sounds familiar?
|
|
45
24
|
//y1 - I think that the tests you've setup don't cover next app router - I added to the setup, but not sure where else
|
|
46
25
|
//y1 - talk about the parameter issue with backend methods
|
|
@@ -55,8 +34,6 @@ export class Currency {
|
|
|
55
34
|
})
|
|
56
35
|
Description = ""*/
|
|
57
36
|
//y2 - get backend methods to work when specifying types for date, and entities as poco's
|
|
58
|
-
//y2 - conside law-q db based on schema issue - I think that while running the dataProvider function, we should have a valid remult - maybe even have a valid remult, that will be valid until api is run
|
|
59
|
-
//y2 - #239 - (@JY) add a way to get from fieldMetadata back to entity repo (like we have in fieldRef)
|
|
60
37
|
//y2 - constraints (@JY)
|
|
61
38
|
/*p2 remult admin
|
|
62
39
|
|
|
@@ -71,11 +48,9 @@ export class Currency {
|
|
|
71
48
|
- remult-admin didn't show a update for a table with a uniqua that is numeric
|
|
72
49
|
*/
|
|
73
50
|
//remult
|
|
74
|
-
//p1 - better support union types (status, etc... so it'll work also in filter etc...)
|
|
75
51
|
//p1 - when a tasks table exists in a different schema - we get many errors
|
|
76
52
|
//p1 - live query with include
|
|
77
53
|
//p1 - adjust angular tutorial starter kit for latest angular (as is in tutorial)
|
|
78
|
-
//p2 - allow find options preprocessor for api calls, to use for authorization
|
|
79
54
|
//p2 - when subscribe is forbidden - the query still runs after the renew process
|
|
80
55
|
//p2 - 'update tasks set where id = $1
|
|
81
56
|
//p2 - type metadata.key - to keyof entity - based on cwis input
|
|
@@ -83,23 +58,15 @@ export class Currency {
|
|
|
83
58
|
//p2 - when value changes for field with error, clear error - so the user will feel comfortable
|
|
84
59
|
//p2 - allowApiUpdate should be false for include in api false
|
|
85
60
|
//docs
|
|
86
|
-
//p2 - make sure that internal members do not appear in the documentation - try running it on the d.ts instead of the code itself.
|
|
87
61
|
//------
|
|
88
62
|
//y2 - from the crm-demo(https://crm-demo.up.railway.app/deals), after editing a deal: - _updateEntityBasedOnApi
|
|
89
63
|
//y2 - Backend methods are transactions, it's not intuitive and maybe should be optional / opt in
|
|
90
64
|
//y2 - how to run a transaction as a user
|
|
91
|
-
//p2 - enum column
|
|
92
|
-
//y2 - message for invalid value
|
|
93
65
|
//y2 - message for relation that is missing
|
|
94
66
|
//y2 - consider multi tenancies
|
|
95
|
-
//p2 - more column types
|
|
96
|
-
//p2 - document validators
|
|
97
67
|
//p2 - and validators to reference
|
|
98
68
|
//y2 - discuss a default date formatter
|
|
99
69
|
//y2 - add some api testing framework for user unit tests (will help with codesandbox based discussions)
|
|
100
70
|
//[ ] V2 - what to do about for relations count?
|
|
101
71
|
//[ ] V2 - condition? not to fetch if null etc....
|
|
102
|
-
//[ ] V3 - all these fields will also appear in the where etc... in the typescript api - but we will not enforce them
|
|
103
|
-
//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?
|
|
104
|
-
//y2 - should enforce integer - currently we probably round / truncate it
|
|
105
72
|
//p1 - adjust react tutorial to esm
|
package/esm/src/validators.js
CHANGED
|
@@ -26,10 +26,9 @@ export class Validators {
|
|
|
26
26
|
static regex = createValueValidatorWithArgs((val, regex) => regex.test(val));
|
|
27
27
|
static email = createValueValidator((val) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val), 'Invalid Email');
|
|
28
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(', ')}`);
|
|
29
|
+
static in = createValueValidatorWithArgs((val, values) => values.includes(val), (values) => `Value must be one of ${values.map(y => typeof y === "object" ? y['id'] !== undefined ? y['id'] : y.toString() : y).join(', ')}`);
|
|
30
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 ${
|
|
32
|
-
.filter((x) => typeof enumObj[x] !== 'number')
|
|
31
|
+
static enum = createValueValidatorWithArgs((value, enumObj) => Object.values(enumObj).includes(value), (enumObj) => `Value must be one of ${getEnumValues(enumObj)
|
|
33
32
|
.join(', ')}`);
|
|
34
33
|
static relationExists = createValidator(async (_, e) => {
|
|
35
34
|
if (e.valueIsNull())
|
|
@@ -134,3 +133,7 @@ function createValidatorWithArgsInternal(validate, defaultMessage, isValueValida
|
|
|
134
133
|
},
|
|
135
134
|
});
|
|
136
135
|
}
|
|
136
|
+
export function getEnumValues(enumObj) {
|
|
137
|
+
return Object.values(enumObj)
|
|
138
|
+
.filter((x) => typeof enumObj[x] !== 'number');
|
|
139
|
+
}
|
package/package.json
CHANGED
|
@@ -1 +1,17 @@
|
|
|
1
|
+
import { type RemultAsyncLocalStorageCore } from '../src/context.js';
|
|
1
2
|
export declare function initAsyncHooks(): void;
|
|
3
|
+
export declare class AsyncLocalStorageBridgeToRemultAsyncLocalStorageCoreImpl<T> implements RemultAsyncLocalStorageCore<T> {
|
|
4
|
+
private asyncLocalStorage;
|
|
5
|
+
wasImplemented: 'yes';
|
|
6
|
+
run<R>(store: T, callback: () => Promise<R>): Promise<R>;
|
|
7
|
+
getStore(): T;
|
|
8
|
+
}
|
|
9
|
+
export declare class SequentialRemultAsyncLocalStorageCore<T> implements RemultAsyncLocalStorageCore<T> {
|
|
10
|
+
wasImplemented: 'yes';
|
|
11
|
+
run<R>(store: T, callback: () => Promise<R>): Promise<R>;
|
|
12
|
+
getStore(): T;
|
|
13
|
+
lastPromise: Promise<T>;
|
|
14
|
+
currentValue: T;
|
|
15
|
+
}
|
|
16
|
+
export declare class AsyncLocalStorageBridgeToRemultAsyncLocalStorageCore<T> extends AsyncLocalStorageBridgeToRemultAsyncLocalStorageCoreImpl<T> {
|
|
17
|
+
}
|
package/server/initAsyncHooks.js
CHANGED
|
@@ -1,14 +1,141 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.initAsyncHooks = void 0;
|
|
3
|
+
exports.AsyncLocalStorageBridgeToRemultAsyncLocalStorageCore = exports.SequentialRemultAsyncLocalStorageCore = exports.AsyncLocalStorageBridgeToRemultAsyncLocalStorageCoreImpl = exports.initAsyncHooks = void 0;
|
|
4
|
+
var tslib_1 = require("tslib");
|
|
4
5
|
var async_hooks_1 = require("async_hooks");
|
|
5
6
|
var context_js_1 = require("../src/context.js");
|
|
6
7
|
var remult_static_js_1 = require("../src/remult-static.js");
|
|
7
8
|
var init = false;
|
|
8
9
|
function initAsyncHooks() {
|
|
10
|
+
var _this = this;
|
|
9
11
|
if (init)
|
|
10
12
|
return;
|
|
11
13
|
init = true;
|
|
12
|
-
remult_static_js_1.remultStatic.asyncContext = new context_js_1.RemultAsyncLocalStorage(new
|
|
14
|
+
remult_static_js_1.remultStatic.asyncContext = new context_js_1.RemultAsyncLocalStorage(new AsyncLocalStorageBridgeToRemultAsyncLocalStorageCore());
|
|
15
|
+
var test = new async_hooks_1.AsyncLocalStorage();
|
|
16
|
+
test.run(1, function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
|
17
|
+
return tslib_1.__generator(this, function (_a) {
|
|
18
|
+
switch (_a.label) {
|
|
19
|
+
case 0: return [4 /*yield*/, Promise.resolve()];
|
|
20
|
+
case 1:
|
|
21
|
+
_a.sent();
|
|
22
|
+
if (test.getStore() === undefined) {
|
|
23
|
+
console.log("async_hooks.AsyncLocalStorage not working, using sequential implementation (You're probably running on stackblitz, this will work on a normal nodejs environment");
|
|
24
|
+
remult_static_js_1.remultStatic.asyncContext = new context_js_1.RemultAsyncLocalStorage(new SequentialRemultAsyncLocalStorageCore());
|
|
25
|
+
}
|
|
26
|
+
return [2 /*return*/];
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}); });
|
|
13
30
|
}
|
|
14
31
|
exports.initAsyncHooks = initAsyncHooks;
|
|
32
|
+
var AsyncLocalStorageBridgeToRemultAsyncLocalStorageCoreImpl = /** @class */ (function () {
|
|
33
|
+
function AsyncLocalStorageBridgeToRemultAsyncLocalStorageCoreImpl() {
|
|
34
|
+
this.asyncLocalStorage = new async_hooks_1.AsyncLocalStorage();
|
|
35
|
+
}
|
|
36
|
+
AsyncLocalStorageBridgeToRemultAsyncLocalStorageCoreImpl.prototype.run = function (store, callback) {
|
|
37
|
+
var _this = this;
|
|
38
|
+
var r;
|
|
39
|
+
this.asyncLocalStorage.run(store, function () {
|
|
40
|
+
r = new Promise(function (res, rej) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
|
41
|
+
var _a, err_1;
|
|
42
|
+
return tslib_1.__generator(this, function (_b) {
|
|
43
|
+
switch (_b.label) {
|
|
44
|
+
case 0:
|
|
45
|
+
_b.trys.push([0, 2, , 3]);
|
|
46
|
+
_a = res;
|
|
47
|
+
return [4 /*yield*/, callback()];
|
|
48
|
+
case 1:
|
|
49
|
+
_a.apply(void 0, [_b.sent()]);
|
|
50
|
+
return [3 /*break*/, 3];
|
|
51
|
+
case 2:
|
|
52
|
+
err_1 = _b.sent();
|
|
53
|
+
rej(err_1);
|
|
54
|
+
return [3 /*break*/, 3];
|
|
55
|
+
case 3: return [2 /*return*/];
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}); });
|
|
59
|
+
});
|
|
60
|
+
return r;
|
|
61
|
+
};
|
|
62
|
+
AsyncLocalStorageBridgeToRemultAsyncLocalStorageCoreImpl.prototype.getStore = function () {
|
|
63
|
+
return this.asyncLocalStorage.getStore();
|
|
64
|
+
};
|
|
65
|
+
return AsyncLocalStorageBridgeToRemultAsyncLocalStorageCoreImpl;
|
|
66
|
+
}());
|
|
67
|
+
exports.AsyncLocalStorageBridgeToRemultAsyncLocalStorageCoreImpl = AsyncLocalStorageBridgeToRemultAsyncLocalStorageCoreImpl;
|
|
68
|
+
var ignoreInStack = ["AsyncLocalStorageBridgeToRemultAsyncLocalStorageCore.run", "RemultAsyncLocalStorage.run", "Module.withRemult", "RemultServerImplementation.runWithRemult"];
|
|
69
|
+
var SequentialRemultAsyncLocalStorageCore = /** @class */ (function () {
|
|
70
|
+
function SequentialRemultAsyncLocalStorageCore() {
|
|
71
|
+
this.lastPromise = Promise.resolve(undefined);
|
|
72
|
+
}
|
|
73
|
+
SequentialRemultAsyncLocalStorageCore.prototype.run = function (store, callback) {
|
|
74
|
+
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
|
75
|
+
var log, stack_1, y, nextPromise;
|
|
76
|
+
var _this = this;
|
|
77
|
+
return tslib_1.__generator(this, function (_a) {
|
|
78
|
+
switch (_a.label) {
|
|
79
|
+
case 0:
|
|
80
|
+
log = function (msg) {
|
|
81
|
+
console.log(msg);
|
|
82
|
+
};
|
|
83
|
+
if (false) {
|
|
84
|
+
stack_1 = new Error().stack;
|
|
85
|
+
y = stack_1.split('\n');
|
|
86
|
+
while (y.length > 0 && (y[0].trim().startsWith("Error") || ignoreInStack.includes(y[0].trim().split(' ')[1]))) {
|
|
87
|
+
y.splice(0, 1);
|
|
88
|
+
}
|
|
89
|
+
stack_1 = y.join('\n');
|
|
90
|
+
log = function (msg) {
|
|
91
|
+
console.log(msg, stack_1);
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
log('waiting on ');
|
|
95
|
+
nextPromise = this.lastPromise.then(function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
|
96
|
+
var previousValue, result;
|
|
97
|
+
return tslib_1.__generator(this, function (_a) {
|
|
98
|
+
switch (_a.label) {
|
|
99
|
+
case 0:
|
|
100
|
+
log('executing ');
|
|
101
|
+
previousValue = this.currentValue;
|
|
102
|
+
this.currentValue = store;
|
|
103
|
+
return [4 /*yield*/, callback()];
|
|
104
|
+
case 1:
|
|
105
|
+
result = _a.sent();
|
|
106
|
+
this.currentValue = previousValue;
|
|
107
|
+
return [2 /*return*/, result];
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}); });
|
|
111
|
+
this.lastPromise = nextPromise.catch(function () {
|
|
112
|
+
log("Error on ");
|
|
113
|
+
return undefined;
|
|
114
|
+
});
|
|
115
|
+
_a.label = 1;
|
|
116
|
+
case 1:
|
|
117
|
+
_a.trys.push([1, , 3, 4]);
|
|
118
|
+
return [4 /*yield*/, nextPromise];
|
|
119
|
+
case 2: return [2 /*return*/, _a.sent()];
|
|
120
|
+
case 3:
|
|
121
|
+
log('completed ');
|
|
122
|
+
return [7 /*endfinally*/];
|
|
123
|
+
case 4: return [2 /*return*/];
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
};
|
|
128
|
+
SequentialRemultAsyncLocalStorageCore.prototype.getStore = function () {
|
|
129
|
+
return this.currentValue;
|
|
130
|
+
};
|
|
131
|
+
return SequentialRemultAsyncLocalStorageCore;
|
|
132
|
+
}());
|
|
133
|
+
exports.SequentialRemultAsyncLocalStorageCore = SequentialRemultAsyncLocalStorageCore;
|
|
134
|
+
var AsyncLocalStorageBridgeToRemultAsyncLocalStorageCore = /** @class */ (function (_super) {
|
|
135
|
+
tslib_1.__extends(AsyncLocalStorageBridgeToRemultAsyncLocalStorageCore, _super);
|
|
136
|
+
function AsyncLocalStorageBridgeToRemultAsyncLocalStorageCore() {
|
|
137
|
+
return _super !== null && _super.apply(this, arguments) || this;
|
|
138
|
+
}
|
|
139
|
+
return AsyncLocalStorageBridgeToRemultAsyncLocalStorageCore;
|
|
140
|
+
}(AsyncLocalStorageBridgeToRemultAsyncLocalStorageCoreImpl));
|
|
141
|
+
exports.AsyncLocalStorageBridgeToRemultAsyncLocalStorageCore = AsyncLocalStorageBridgeToRemultAsyncLocalStorageCore;
|
|
@@ -247,17 +247,23 @@ export interface ValueConverter<valueType> {
|
|
|
247
247
|
*/
|
|
248
248
|
displayValue?(val: valueType): string;
|
|
249
249
|
/**
|
|
250
|
-
* Specifies the storage type used in the database for this field.
|
|
250
|
+
* Specifies the storage type used in the database for this field. This can be used to explicitly define the data type and precision of the field in the database.
|
|
251
251
|
*
|
|
252
252
|
* @example
|
|
253
|
-
*
|
|
253
|
+
* // Define a field with a specific decimal precision in the database
|
|
254
|
+
* @Fields.number({
|
|
255
|
+
* valueConverter: {
|
|
256
|
+
* fieldTypeInDb: 'decimal(18,8)'
|
|
257
|
+
* }
|
|
258
|
+
* })
|
|
259
|
+
* price=0;
|
|
254
260
|
*/
|
|
255
261
|
readonly fieldTypeInDb?: string;
|
|
256
262
|
/**
|
|
257
263
|
* Specifies the type of HTML input element suitable for values of valueType.
|
|
258
264
|
*
|
|
259
265
|
* @example
|
|
260
|
-
*
|
|
266
|
+
* inputType = 'date';
|
|
261
267
|
*/
|
|
262
268
|
readonly inputType?: string;
|
|
263
269
|
}
|
package/src/context.d.ts
CHANGED
|
@@ -8,13 +8,14 @@ export declare class RemultAsyncLocalStorage {
|
|
|
8
8
|
private readonly remultObjectStorage;
|
|
9
9
|
static enable(): void;
|
|
10
10
|
static disable(): void;
|
|
11
|
-
constructor(remultObjectStorage:
|
|
12
|
-
run(remult: Remult, callback: (remult: Remult) =>
|
|
11
|
+
constructor(remultObjectStorage: RemultAsyncLocalStorageCore<Remult>);
|
|
12
|
+
run<T>(remult: Remult, callback: (remult: Remult) => Promise<T>): Promise<T>;
|
|
13
13
|
getRemult(): Remult;
|
|
14
14
|
}
|
|
15
|
-
type
|
|
16
|
-
run<R>(store: T, callback: (
|
|
15
|
+
export type RemultAsyncLocalStorageCore<T> = {
|
|
16
|
+
run<R>(store: T, callback: () => Promise<R>): Promise<R>;
|
|
17
17
|
getStore(): T | undefined;
|
|
18
|
+
wasImplemented: 'yes';
|
|
18
19
|
};
|
|
19
20
|
export declare function isBackend(): boolean;
|
|
20
21
|
export declare class Remult {
|
|
@@ -189,4 +190,3 @@ export declare function doTransaction(remult: Remult, what: (dp: DataProvider) =
|
|
|
189
190
|
export declare function withRemult<T>(callback: (remult: any) => Promise<T>, options?: {
|
|
190
191
|
dataProvider?: DataProvider;
|
|
191
192
|
}): Promise<T>;
|
|
192
|
-
export {};
|
package/src/context.js
CHANGED
|
@@ -28,10 +28,16 @@ var RemultAsyncLocalStorage = /** @class */ (function () {
|
|
|
28
28
|
(0, remult_static_js_1.resetFactory)();
|
|
29
29
|
};
|
|
30
30
|
RemultAsyncLocalStorage.prototype.run = function (remult, callback) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
|
32
|
+
return tslib_1.__generator(this, function (_a) {
|
|
33
|
+
if (this.remultObjectStorage) {
|
|
34
|
+
return [2 /*return*/, this.remultObjectStorage.run(remult, function () { return callback(remult); })];
|
|
35
|
+
}
|
|
36
|
+
else
|
|
37
|
+
return [2 /*return*/, callback(remult)];
|
|
38
|
+
return [2 /*return*/];
|
|
39
|
+
});
|
|
40
|
+
});
|
|
35
41
|
};
|
|
36
42
|
RemultAsyncLocalStorage.prototype.getRemult = function () {
|
|
37
43
|
if (!this.remultObjectStorage) {
|
|
@@ -449,32 +455,9 @@ var transactionLiveQueryPublisher = /** @class */ (function () {
|
|
|
449
455
|
return transactionLiveQueryPublisher;
|
|
450
456
|
}());
|
|
451
457
|
function withRemult(callback, options) {
|
|
452
|
-
var _this = this;
|
|
453
458
|
var remult = new Remult();
|
|
454
459
|
if (options === null || options === void 0 ? void 0 : options.dataProvider)
|
|
455
460
|
remult.dataProvider = options.dataProvider;
|
|
456
|
-
|
|
457
|
-
remult_static_js_1.remultStatic.asyncContext.run(remult, function () {
|
|
458
|
-
r = new Promise(function (res, rej) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
|
459
|
-
var _a, err_1;
|
|
460
|
-
return tslib_1.__generator(this, function (_b) {
|
|
461
|
-
switch (_b.label) {
|
|
462
|
-
case 0:
|
|
463
|
-
_b.trys.push([0, 2, , 3]);
|
|
464
|
-
_a = res;
|
|
465
|
-
return [4 /*yield*/, callback(remult)];
|
|
466
|
-
case 1:
|
|
467
|
-
_a.apply(void 0, [_b.sent()]);
|
|
468
|
-
return [3 /*break*/, 3];
|
|
469
|
-
case 2:
|
|
470
|
-
err_1 = _b.sent();
|
|
471
|
-
rej(err_1);
|
|
472
|
-
return [3 /*break*/, 3];
|
|
473
|
-
case 3: return [2 /*return*/];
|
|
474
|
-
}
|
|
475
|
-
});
|
|
476
|
-
}); });
|
|
477
|
-
});
|
|
478
|
-
return r;
|
|
461
|
+
return remult_static_js_1.remultStatic.asyncContext.run(remult, function (r) { return callback(r); });
|
|
479
462
|
}
|
|
480
463
|
exports.withRemult = withRemult;
|
package/src/isOfType.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.cast = exports.isOfType = void 0;
|
|
4
4
|
function isOfType(obj, checkMethod) {
|
|
5
|
-
return typeof obj[checkMethod]
|
|
5
|
+
return typeof obj[checkMethod] !== 'undefined';
|
|
6
6
|
}
|
|
7
7
|
exports.isOfType = isOfType;
|
|
8
8
|
function cast(obj, checkMethod) {
|
package/src/remult3/Fields.d.ts
CHANGED
|
@@ -16,7 +16,43 @@ export declare class Fields {
|
|
|
16
16
|
static createdAt<entityType = any>(...options: (FieldOptions<entityType, Date> | ((options: FieldOptions<entityType, Date>, remult: Remult) => void))[]): ClassFieldDecorator<entityType, Date | undefined>;
|
|
17
17
|
static updatedAt<entityType = any>(...options: (FieldOptions<entityType, Date> | ((options: FieldOptions<entityType, Date>, remult: Remult) => void))[]): ClassFieldDecorator<entityType, Date | undefined>;
|
|
18
18
|
static uuid<entityType = any>(...options: (FieldOptions<entityType, string> | ((options: FieldOptions<entityType, string>, remult: Remult) => void))[]): ClassFieldDecorator<entityType, string | undefined>;
|
|
19
|
+
/**
|
|
20
|
+
* A CUID (Collision Resistant Unique Identifier) field.
|
|
21
|
+
* This id value is determined on the backend on insert, and can't be updated through the API.
|
|
22
|
+
* The CUID is generated using the `@paralleldrive/cuid2` npm package.
|
|
23
|
+
*/
|
|
19
24
|
static cuid<entityType = any>(...options: (FieldOptions<entityType, string> | ((options: FieldOptions<entityType, string>, remult: Remult) => void))[]): ClassFieldDecorator<entityType, string | undefined>;
|
|
25
|
+
/**
|
|
26
|
+
* Defines a field that can hold a value from a specified set of string literals.
|
|
27
|
+
* @param {() => readonly valueType[]} optionalValues - A function that returns an array of allowed string literals.
|
|
28
|
+
* @returns {ClassFieldDecorator<entityType, valueType | undefined>} - A class field decorator.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
|
|
32
|
+
* class MyEntity {
|
|
33
|
+
* .@Fields.literal(() => ['open', 'closed', 'frozen', 'in progress'] as const)
|
|
34
|
+
* status: 'open' | 'closed' | 'frozen' | 'in progress' = 'open';
|
|
35
|
+
* }
|
|
36
|
+
|
|
37
|
+
*
|
|
38
|
+
* // This defines a field `status` in `MyEntity` that can only hold the values 'open', 'closed', 'frozen', or 'in progress'.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* // For better reusability and maintainability:
|
|
42
|
+
|
|
43
|
+
* const statuses = ['open', 'closed', 'frozen', 'in progress'] as const;
|
|
44
|
+
* type StatusType = typeof statuses[number];
|
|
45
|
+
*
|
|
46
|
+
* class MyEntity {
|
|
47
|
+
* .@Fields.literal(() => statuses)
|
|
48
|
+
* status: StatusType = 'open';
|
|
49
|
+
* }
|
|
50
|
+
|
|
51
|
+
*
|
|
52
|
+
* // This approach allows easy management and updates of the allowed values for the `status` field.
|
|
53
|
+
*/
|
|
54
|
+
static literal<entityType = any, valueType extends string = any>(optionalValues: () => readonly valueType[], ...options: (StringFieldOptions<entityType, valueType> | ((options: StringFieldOptions<entityType, valueType>, remult: Remult) => void))[]): ClassFieldDecorator<entityType, valueType | undefined>;
|
|
55
|
+
static enum<entityType = any, theEnum = any>(enumType: () => theEnum, ...options: (FieldOptions<entityType, theEnum[keyof theEnum]> | ((options: FieldOptions<entityType, theEnum[keyof theEnum]>, remult: Remult) => void))[]): ClassFieldDecorator<entityType, theEnum[keyof theEnum] | undefined>;
|
|
20
56
|
static string<entityType = any, valueType = string>(...options: (StringFieldOptions<entityType, valueType> | ((options: StringFieldOptions<entityType, valueType>, remult: Remult) => void))[]): ClassFieldDecorator<entityType, valueType | undefined>;
|
|
21
57
|
static boolean<entityType = any>(...options: (FieldOptions<entityType, boolean> | ((options: FieldOptions<entityType, boolean>, remult: Remult) => void))[]): ClassFieldDecorator<entityType, boolean | undefined>;
|
|
22
58
|
}
|
package/src/remult3/Fields.js
CHANGED
|
@@ -9,6 +9,10 @@ var RepositoryImplementation_js_1 = require("./RepositoryImplementation.js");
|
|
|
9
9
|
var validators_js_1 = require("../validators.js");
|
|
10
10
|
var relationInfoMember_js_1 = require("./relationInfoMember.js");
|
|
11
11
|
var remult_static_js_1 = require("../remult-static.js");
|
|
12
|
+
var addValidator_js_1 = require("./addValidator.js");
|
|
13
|
+
var validateNumber = (0, validators_js_1.createValueValidator)(function (x) {
|
|
14
|
+
return !isNaN(x) && isFinite(x);
|
|
15
|
+
});
|
|
12
16
|
var Fields = /** @class */ (function () {
|
|
13
17
|
function Fields() {
|
|
14
18
|
}
|
|
@@ -63,6 +67,7 @@ var Fields = /** @class */ (function () {
|
|
|
63
67
|
return Field.apply(void 0, tslib_1.__spreadArray([function () { return Number; },
|
|
64
68
|
{
|
|
65
69
|
valueConverter: valueConverters_js_1.ValueConverters.Integer,
|
|
70
|
+
validate: validateNumber,
|
|
66
71
|
}], tslib_1.__read(options), false));
|
|
67
72
|
};
|
|
68
73
|
Fields.autoIncrement = function () {
|
|
@@ -82,7 +87,9 @@ var Fields = /** @class */ (function () {
|
|
|
82
87
|
for (var _i = 0; _i < arguments.length; _i++) {
|
|
83
88
|
options[_i] = arguments[_i];
|
|
84
89
|
}
|
|
85
|
-
return Field.apply(void 0, tslib_1.__spreadArray([function () { return Number; }
|
|
90
|
+
return Field.apply(void 0, tslib_1.__spreadArray([function () { return Number; }, {
|
|
91
|
+
validate: validateNumber,
|
|
92
|
+
}], tslib_1.__read(options), false));
|
|
86
93
|
};
|
|
87
94
|
Fields.createdAt = function () {
|
|
88
95
|
var options = [];
|
|
@@ -127,6 +134,11 @@ var Fields = /** @class */ (function () {
|
|
|
127
134
|
},
|
|
128
135
|
}], tslib_1.__read(options), false));
|
|
129
136
|
};
|
|
137
|
+
/**
|
|
138
|
+
* A CUID (Collision Resistant Unique Identifier) field.
|
|
139
|
+
* This id value is determined on the backend on insert, and can't be updated through the API.
|
|
140
|
+
* The CUID is generated using the `@paralleldrive/cuid2` npm package.
|
|
141
|
+
*/
|
|
130
142
|
Fields.cuid = function () {
|
|
131
143
|
var options = [];
|
|
132
144
|
for (var _i = 0; _i < arguments.length; _i++) {
|
|
@@ -142,6 +154,71 @@ var Fields = /** @class */ (function () {
|
|
|
142
154
|
},
|
|
143
155
|
}], tslib_1.__read(options), false));
|
|
144
156
|
};
|
|
157
|
+
/**
|
|
158
|
+
* Defines a field that can hold a value from a specified set of string literals.
|
|
159
|
+
* @param {() => readonly valueType[]} optionalValues - A function that returns an array of allowed string literals.
|
|
160
|
+
* @returns {ClassFieldDecorator<entityType, valueType | undefined>} - A class field decorator.
|
|
161
|
+
*
|
|
162
|
+
* @example
|
|
163
|
+
|
|
164
|
+
* class MyEntity {
|
|
165
|
+
* .@Fields.literal(() => ['open', 'closed', 'frozen', 'in progress'] as const)
|
|
166
|
+
* status: 'open' | 'closed' | 'frozen' | 'in progress' = 'open';
|
|
167
|
+
* }
|
|
168
|
+
|
|
169
|
+
*
|
|
170
|
+
* // This defines a field `status` in `MyEntity` that can only hold the values 'open', 'closed', 'frozen', or 'in progress'.
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* // For better reusability and maintainability:
|
|
174
|
+
|
|
175
|
+
* const statuses = ['open', 'closed', 'frozen', 'in progress'] as const;
|
|
176
|
+
* type StatusType = typeof statuses[number];
|
|
177
|
+
*
|
|
178
|
+
* class MyEntity {
|
|
179
|
+
* .@Fields.literal(() => statuses)
|
|
180
|
+
* status: StatusType = 'open';
|
|
181
|
+
* }
|
|
182
|
+
|
|
183
|
+
*
|
|
184
|
+
* // This approach allows easy management and updates of the allowed values for the `status` field.
|
|
185
|
+
*/
|
|
186
|
+
Fields.literal = function (optionalValues) {
|
|
187
|
+
var options = [];
|
|
188
|
+
for (var _i = 1; _i < arguments.length; _i++) {
|
|
189
|
+
options[_i - 1] = arguments[_i];
|
|
190
|
+
}
|
|
191
|
+
return Fields.string.apply(Fields, tslib_1.__spreadArray([{
|
|
192
|
+
validate: function (entity, event) { return validators_js_1.Validators.in(optionalValues())(entity, event); }
|
|
193
|
+
}], tslib_1.__read(options), false));
|
|
194
|
+
};
|
|
195
|
+
Fields.enum = function (enumType) {
|
|
196
|
+
var options = [];
|
|
197
|
+
for (var _i = 1; _i < arguments.length; _i++) {
|
|
198
|
+
options[_i - 1] = arguments[_i];
|
|
199
|
+
}
|
|
200
|
+
var valueConverter;
|
|
201
|
+
return Field.apply(void 0, tslib_1.__spreadArray(tslib_1.__spreadArray([function () {
|
|
202
|
+
//@ts-ignore
|
|
203
|
+
return (enumType());
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
validate: function (entity, event) { return validators_js_1.Validators.enum(enumType())(entity, event); }
|
|
207
|
+
}], tslib_1.__read(options), false), [function (options) {
|
|
208
|
+
if (valueConverter === undefined) {
|
|
209
|
+
var enumObj = enumType();
|
|
210
|
+
var enumValues = (0, validators_js_1.getEnumValues)(enumObj);
|
|
211
|
+
valueConverter = enumValues.find(function (x) { return typeof x === "string"; }) ? valueConverters_js_1.ValueConverters.String : valueConverters_js_1.ValueConverters.Integer;
|
|
212
|
+
}
|
|
213
|
+
if (!options.valueConverter) {
|
|
214
|
+
options.valueConverter = valueConverter;
|
|
215
|
+
}
|
|
216
|
+
else if (!options.valueConverter.fieldTypeInDb) {
|
|
217
|
+
//@ts-ignore
|
|
218
|
+
options.valueConverter.fieldTypeInDb = valueConverter.fieldTypeInDb;
|
|
219
|
+
}
|
|
220
|
+
}], false));
|
|
221
|
+
};
|
|
145
222
|
Fields.string = function () {
|
|
146
223
|
var options = [];
|
|
147
224
|
for (var _i = 0; _i < arguments.length; _i++) {
|
|
@@ -249,13 +326,13 @@ function Field(valueType) {
|
|
|
249
326
|
var factory = function (remult) {
|
|
250
327
|
var r = (0, RepositoryImplementation_js_1.buildOptions)(options, remult);
|
|
251
328
|
if (r.required) {
|
|
252
|
-
r.validate = addValidator(r.validate, validators_js_1.Validators.required);
|
|
329
|
+
r.validate = (0, addValidator_js_1.addValidator)(r.validate, validators_js_1.Validators.required, true);
|
|
253
330
|
}
|
|
254
331
|
if (r.maxLength) {
|
|
255
|
-
r.validate = addValidator(r.validate, validators_js_1.Validators.maxLength(r.maxLength));
|
|
332
|
+
r.validate = (0, addValidator_js_1.addValidator)(r.validate, validators_js_1.Validators.maxLength(r.maxLength));
|
|
256
333
|
}
|
|
257
334
|
if (r.minLength) {
|
|
258
|
-
r.validate = addValidator(r.validate, validators_js_1.Validators.minLength(r.minLength));
|
|
335
|
+
r.validate = (0, addValidator_js_1.addValidator)(r.validate, validators_js_1.Validators.minLength(r.minLength));
|
|
259
336
|
}
|
|
260
337
|
if (!r.valueType && valueType) {
|
|
261
338
|
r.valueType = valueType();
|
|
@@ -305,18 +382,3 @@ function checkTarget(target) {
|
|
|
305
382
|
throw new Error("Set the 'experimentalDecorators:true' option in your 'tsconfig' or 'jsconfig' (target undefined)");
|
|
306
383
|
}
|
|
307
384
|
exports.checkTarget = checkTarget;
|
|
308
|
-
function addValidator(validators, newValidator, atStart) {
|
|
309
|
-
if (atStart === void 0) { atStart = false; }
|
|
310
|
-
if (!newValidator)
|
|
311
|
-
return;
|
|
312
|
-
var newValidators = Array.isArray(newValidator)
|
|
313
|
-
? newValidator
|
|
314
|
-
: [newValidator];
|
|
315
|
-
var validatorsArray = Array.isArray(validators)
|
|
316
|
-
? validators
|
|
317
|
-
: validators
|
|
318
|
-
? [validators]
|
|
319
|
-
: [];
|
|
320
|
-
return atStart
|
|
321
|
-
? tslib_1.__spreadArray(tslib_1.__spreadArray([], tslib_1.__read(newValidators), false), tslib_1.__read(validatorsArray), false) : tslib_1.__spreadArray(tslib_1.__spreadArray([], tslib_1.__read(validatorsArray), false), tslib_1.__read(newValidators), false);
|
|
322
|
-
}
|
|
@@ -18,6 +18,8 @@ var relation_loader_js_1 = require("./relation-loader.js");
|
|
|
18
18
|
var repository_internals_js_1 = require("./repository-internals.js");
|
|
19
19
|
var filter_consumer_bridge_to_sql_request_js_1 = require("../filter/filter-consumer-bridge-to-sql-request.js");
|
|
20
20
|
var remult_static_js_1 = require("../remult-static.js");
|
|
21
|
+
var validators_js_1 = require("../validators.js");
|
|
22
|
+
var addValidator_js_1 = require("./addValidator.js");
|
|
21
23
|
//import { remult } from "../remult-proxy";
|
|
22
24
|
var classValidatorValidate = undefined;
|
|
23
25
|
// import ("class-validator".toString())
|
|
@@ -3249,6 +3251,7 @@ function ValueListFieldType() {
|
|
|
3249
3251
|
;
|
|
3250
3252
|
(o.valueConverter = ValueListInfo.get(type)),
|
|
3251
3253
|
(o.displayValue = function (item, val) { return val === null || val === void 0 ? void 0 : val.caption; });
|
|
3254
|
+
o.validate = function (entity, ref) { return validators_js_1.Validators.in(ValueListInfo.get(type).getValues())(entity, ref); };
|
|
3252
3255
|
}], tslib_1.__read(options), false))(type, context);
|
|
3253
3256
|
};
|
|
3254
3257
|
}
|
|
@@ -3377,8 +3380,11 @@ function buildOptions(options, remult) {
|
|
|
3377
3380
|
if (o) {
|
|
3378
3381
|
if (typeof o === 'function')
|
|
3379
3382
|
o(r, remult);
|
|
3380
|
-
else
|
|
3381
|
-
|
|
3383
|
+
else {
|
|
3384
|
+
var validate = o.validate, otherOptions = tslib_1.__rest(o, ["validate"]);
|
|
3385
|
+
r.validate = (0, addValidator_js_1.addValidator)(r.validate, validate);
|
|
3386
|
+
Object.assign(r, otherOptions);
|
|
3387
|
+
}
|
|
3382
3388
|
}
|
|
3383
3389
|
}
|
|
3384
3390
|
}
|
|
@@ -3396,7 +3402,7 @@ function decorateColumnSettings(settings, remult) {
|
|
|
3396
3402
|
if (settings.valueType) {
|
|
3397
3403
|
var settingsOnTypeLevel = settings.valueType[exports.storableMember];
|
|
3398
3404
|
if (settingsOnTypeLevel) {
|
|
3399
|
-
settings = tslib_1.
|
|
3405
|
+
settings = buildOptions(tslib_1.__spreadArray(tslib_1.__spreadArray([], tslib_1.__read(settingsOnTypeLevel), false), [settings], false), remult);
|
|
3400
3406
|
}
|
|
3401
3407
|
}
|
|
3402
3408
|
if (settings.valueType == String) {
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import type { FieldOptions } from '../column-interfaces.js';
|
|
2
|
+
export declare function addValidator(validators: FieldOptions['validate'], newValidator: FieldOptions['validate'], atStart?: boolean): import("../column-interfaces.js").FieldValidator<any, any> | import("../column-interfaces.js").FieldValidator<any, any>[];
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.addValidator = void 0;
|
|
4
|
+
var tslib_1 = require("tslib");
|
|
5
|
+
function addValidator(validators, newValidator, atStart) {
|
|
6
|
+
if (atStart === void 0) { atStart = false; }
|
|
7
|
+
if (!newValidator)
|
|
8
|
+
return validators;
|
|
9
|
+
var newValidators = Array.isArray(newValidator)
|
|
10
|
+
? newValidator
|
|
11
|
+
: [newValidator];
|
|
12
|
+
var validatorsArray = Array.isArray(validators)
|
|
13
|
+
? validators
|
|
14
|
+
: validators
|
|
15
|
+
? [validators]
|
|
16
|
+
: [];
|
|
17
|
+
return atStart
|
|
18
|
+
? tslib_1.__spreadArray(tslib_1.__spreadArray([], tslib_1.__read(newValidators), false), tslib_1.__read(validatorsArray), false) : tslib_1.__spreadArray(tslib_1.__spreadArray([], tslib_1.__read(validatorsArray), false), tslib_1.__read(newValidators), false);
|
|
19
|
+
}
|
|
20
|
+
exports.addValidator = addValidator;
|
package/src/remult3/remult3.js
CHANGED
|
@@ -1,47 +1,26 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
//p1 -
|
|
4
|
-
//p1 -
|
|
5
|
-
//p1 -
|
|
6
|
-
//p1 -
|
|
7
|
-
//
|
|
8
|
-
//y1 - consider replacing all errors with error classes that extend the base Error class
|
|
3
|
+
//p1 - `preprocessApiFindOptions`, naming - allow find options preprocessor for api calls, to use for authorization
|
|
4
|
+
//p1 - admin url1 - pocketbase use /_ for the admin //{allowed?:Allowed,url?:string}
|
|
5
|
+
//p1 - Make async hooks gracefully handle non async hooks environment
|
|
6
|
+
//p1 - implement generic sqlite support better-sqlite3/ bun sqlite
|
|
7
|
+
//p1 - article auth.js with express
|
|
9
8
|
//p1 - document offline support
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
9
|
+
//y2 - should we validate relations
|
|
10
|
+
//y1 - consider replacing all errors with error classes that extend the base Error class
|
|
11
|
+
//y2 - should enforce integer - currently we probably round / truncate it
|
|
12
|
+
/*y1 - talk about modules in init express with entities/controllers,initRequest,initApi
|
|
13
|
+
- support get with backend method, with url search params as the first parameter, & url as second parameter
|
|
14
|
+
- support returning redirect, and plain html (For sign in scenarios)
|
|
15
|
+
|
|
16
|
+
*/
|
|
17
|
+
//p1 - in this video I'll use remult to turn a frontend app to a fullstack app
|
|
13
18
|
/*y1 - Talk JYC - JYC - add some integrity checks on delete
|
|
14
19
|
- soft delete
|
|
15
20
|
- delete restrict (implicit, or user selected - and if so, how) (delete & update of id)
|
|
16
21
|
|
|
17
22
|
*/
|
|
18
|
-
/*y1 currency.ts:10 Uncaught TypeError: Currency_1 is not a constructor
|
|
19
|
-
// - ValueListFieldType - the decorator gives an error in react vite project - see langulage yedidya
|
|
20
|
-
// happens in target 2022, ts<5.1.6
|
|
21
|
-
//typescript 5.1.6
|
|
22
|
-
@ValueListFieldType()
|
|
23
|
-
export class Currency {
|
|
24
|
-
constructor(
|
|
25
|
-
public id: number,
|
|
26
|
-
public caption: string,
|
|
27
|
-
public symbol: string
|
|
28
|
-
) {}
|
|
29
|
-
static shekel = new Currency(1, 'Shekel', '₪');
|
|
30
|
-
static dollar = new Currency(2, 'Dollar', '$');
|
|
31
|
-
static euro = new Currency(3, 'Euro', '€');
|
|
32
|
-
static pound = new Currency(4, 'Pound', '£');
|
|
33
|
-
static yen = new Currency(5, 'Yen', '¥');
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
*/
|
|
37
|
-
//y1 - allow api read to also support instance and filter. - problem with promise
|
|
38
|
-
//y1 - admin url1 - pocketbase use /_ for the admin
|
|
39
23
|
//y1 - consider sql expression gets a dbnames of it's own (that already has the "tableName" defined correctly) maybe also the filter translator
|
|
40
|
-
/*y1 - talk about modules in init express with entities/controllers,initRequest,initApi
|
|
41
|
-
- support get with backend method, with url search params as the first parameter, & url as second parameter
|
|
42
|
-
- support returning redirect, and plain html (For sign in scenarios)
|
|
43
|
-
|
|
44
|
-
*/
|
|
45
24
|
//y1 - tried to upgrade vitest, nuxt tests are failing with loading uuid - sounds familiar?
|
|
46
25
|
//y1 - I think that the tests you've setup don't cover next app router - I added to the setup, but not sure where else
|
|
47
26
|
//y1 - talk about the parameter issue with backend methods
|
|
@@ -56,8 +35,6 @@ export class Currency {
|
|
|
56
35
|
})
|
|
57
36
|
Description = ""*/
|
|
58
37
|
//y2 - get backend methods to work when specifying types for date, and entities as poco's
|
|
59
|
-
//y2 - conside law-q db based on schema issue - I think that while running the dataProvider function, we should have a valid remult - maybe even have a valid remult, that will be valid until api is run
|
|
60
|
-
//y2 - #239 - (@JY) add a way to get from fieldMetadata back to entity repo (like we have in fieldRef)
|
|
61
38
|
//y2 - constraints (@JY)
|
|
62
39
|
/*p2 remult admin
|
|
63
40
|
|
|
@@ -72,11 +49,9 @@ export class Currency {
|
|
|
72
49
|
- remult-admin didn't show a update for a table with a uniqua that is numeric
|
|
73
50
|
*/
|
|
74
51
|
//remult
|
|
75
|
-
//p1 - better support union types (status, etc... so it'll work also in filter etc...)
|
|
76
52
|
//p1 - when a tasks table exists in a different schema - we get many errors
|
|
77
53
|
//p1 - live query with include
|
|
78
54
|
//p1 - adjust angular tutorial starter kit for latest angular (as is in tutorial)
|
|
79
|
-
//p2 - allow find options preprocessor for api calls, to use for authorization
|
|
80
55
|
//p2 - when subscribe is forbidden - the query still runs after the renew process
|
|
81
56
|
//p2 - 'update tasks set where id = $1
|
|
82
57
|
//p2 - type metadata.key - to keyof entity - based on cwis input
|
|
@@ -84,23 +59,15 @@ export class Currency {
|
|
|
84
59
|
//p2 - when value changes for field with error, clear error - so the user will feel comfortable
|
|
85
60
|
//p2 - allowApiUpdate should be false for include in api false
|
|
86
61
|
//docs
|
|
87
|
-
//p2 - make sure that internal members do not appear in the documentation - try running it on the d.ts instead of the code itself.
|
|
88
62
|
//------
|
|
89
63
|
//y2 - from the crm-demo(https://crm-demo.up.railway.app/deals), after editing a deal: - _updateEntityBasedOnApi
|
|
90
64
|
//y2 - Backend methods are transactions, it's not intuitive and maybe should be optional / opt in
|
|
91
65
|
//y2 - how to run a transaction as a user
|
|
92
|
-
//p2 - enum column
|
|
93
|
-
//y2 - message for invalid value
|
|
94
66
|
//y2 - message for relation that is missing
|
|
95
67
|
//y2 - consider multi tenancies
|
|
96
|
-
//p2 - more column types
|
|
97
|
-
//p2 - document validators
|
|
98
68
|
//p2 - and validators to reference
|
|
99
69
|
//y2 - discuss a default date formatter
|
|
100
70
|
//y2 - add some api testing framework for user unit tests (will help with codesandbox based discussions)
|
|
101
71
|
//[ ] V2 - what to do about for relations count?
|
|
102
72
|
//[ ] V2 - condition? not to fetch if null etc....
|
|
103
|
-
//[ ] V3 - all these fields will also appear in the where etc... in the typescript api - but we will not enforce them
|
|
104
|
-
//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?
|
|
105
|
-
//y2 - should enforce integer - currently we probably round / truncate it
|
|
106
73
|
//p1 - adjust react tutorial to esm
|
package/src/validators.d.ts
CHANGED
|
@@ -47,3 +47,4 @@ export type ValidatorWithArgs<valueType, argsType> = (args: argsType, message?:
|
|
|
47
47
|
export declare function createValidatorWithArgs<valueType, argsType>(validate: (entity: any, e: ValidateFieldEvent<any, valueType>, args: argsType) => Promise<boolean | string> | boolean | string, defaultMessage: ValidationMessage<valueType, argsType>): ValidatorWithArgs<valueType, argsType> & {
|
|
48
48
|
defaultMessage: ValidationMessage<valueType, argsType>;
|
|
49
49
|
};
|
|
50
|
+
export declare function getEnumValues<theEnum = any>(enumObj: theEnum): any[];
|
package/src/validators.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createValidatorWithArgs = exports.createValueValidatorWithArgs = exports.createValueValidator = exports.valueValidator = exports.createValidator = exports.Validators = void 0;
|
|
3
|
+
exports.getEnumValues = exports.createValidatorWithArgs = exports.createValueValidatorWithArgs = exports.createValueValidator = exports.valueValidator = exports.createValidator = exports.Validators = void 0;
|
|
4
4
|
var tslib_1 = require("tslib");
|
|
5
5
|
var Validators = /** @class */ (function () {
|
|
6
6
|
function Validators() {
|
|
@@ -48,11 +48,12 @@ var Validators = /** @class */ (function () {
|
|
|
48
48
|
});
|
|
49
49
|
Validators.email = createValueValidator(function (val) { return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val); }, 'Invalid Email');
|
|
50
50
|
Validators.url = createValueValidator(function (val) { return !!new URL(val); }, 'Invalid Url');
|
|
51
|
-
Validators.in = createValueValidatorWithArgs(function (val, values) { return values.includes(val); }, function (values) { return "Value must be one of ".concat(values.
|
|
51
|
+
Validators.in = createValueValidatorWithArgs(function (val, values) { return values.includes(val); }, function (values) { return "Value must be one of ".concat(values.map(function (y) {
|
|
52
|
+
return typeof y === "object" ? y['id'] !== undefined ? y['id'] : y.toString() : y;
|
|
53
|
+
}).join(', ')); });
|
|
52
54
|
Validators.notNull = createValueValidator(function (val) { return val != null; }, 'Should not be null');
|
|
53
55
|
Validators.enum = createValueValidatorWithArgs(function (value, enumObj) { return Object.values(enumObj).includes(value); }, function (enumObj) {
|
|
54
|
-
return "Value must be one of ".concat(
|
|
55
|
-
.filter(function (x) { return typeof enumObj[x] !== 'number'; })
|
|
56
|
+
return "Value must be one of ".concat(getEnumValues(enumObj)
|
|
56
57
|
.join(', '));
|
|
57
58
|
});
|
|
58
59
|
Validators.relationExists = createValidator(function (_, e) { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
|
|
@@ -207,3 +208,8 @@ function createValidatorWithArgsInternal(validate, defaultMessage, isValueValida
|
|
|
207
208
|
},
|
|
208
209
|
});
|
|
209
210
|
}
|
|
211
|
+
function getEnumValues(enumObj) {
|
|
212
|
+
return Object.values(enumObj)
|
|
213
|
+
.filter(function (x) { return typeof enumObj[x] !== 'number'; });
|
|
214
|
+
}
|
|
215
|
+
exports.getEnumValues = getEnumValues;
|