remult 0.25.8-exp.6 → 0.25.8-exp.8

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.
@@ -1,5 +1,5 @@
1
1
  export function isOfType(obj, checkMethod) {
2
- return typeof obj[checkMethod] === 'function';
2
+ return typeof obj[checkMethod] !== 'undefined';
3
3
  }
4
4
  export function cast(obj, checkMethod) {
5
5
  if (isOfType(obj, checkMethod)) {
@@ -5,6 +5,7 @@ import { buildOptions } from './RepositoryImplementation.js';
5
5
  import { Validators } from '../validators.js';
6
6
  import { relationInfoMemberInOptions } from './relationInfoMember.js';
7
7
  import { remultStatic } from '../remult-static.js';
8
+ import { addValidatorKey } from './addValidatorKey.js';
8
9
  export class Fields {
9
10
  /**
10
11
  * Stored as a JSON.stringify - to store as json use Fields.json
@@ -76,6 +77,11 @@ export class Fields {
76
77
  },
77
78
  }, ...options);
78
79
  }
80
+ /**
81
+ * A CUID (Collision Resistant Unique Identifier) field.
82
+ * This id value is determined on the backend on insert, and can't be updated through the API.
83
+ * The CUID is generated using the `@paralleldrive/cuid2` npm package.
84
+ */
79
85
  static cuid(...options) {
80
86
  return Field(() => String, {
81
87
  allowApiUpdate: false,
@@ -86,6 +92,63 @@ export class Fields {
86
92
  },
87
93
  }, ...options);
88
94
  }
95
+ /**
96
+ * Defines a field that can hold a value from a specified set of string literals.
97
+ * @param {() => readonly valueType[]} optionalValues - A function that returns an array of allowed string literals.
98
+ * @returns {ClassFieldDecorator<entityType, valueType | undefined>} - A class field decorator.
99
+ *
100
+ * @example
101
+
102
+ * class MyEntity {
103
+ * .@Fields.literal(() => ['open', 'closed', 'frozen', 'in progress'] as const)
104
+ * status: 'open' | 'closed' | 'frozen' | 'in progress' = 'open';
105
+ * }
106
+
107
+ *
108
+ * // This defines a field `status` in `MyEntity` that can only hold the values 'open', 'closed', 'frozen', or 'in progress'.
109
+ *
110
+ * @example
111
+ * // For better reusability and maintainability:
112
+
113
+ * const statuses = ['open', 'closed', 'frozen', 'in progress'] as const;
114
+ * type StatusType = typeof statuses[number];
115
+ *
116
+ * class MyEntity {
117
+ * .@Fields.literal(() => statuses)
118
+ * status: StatusType = 'open';
119
+ * }
120
+
121
+ *
122
+ * // This approach allows easy management and updates of the allowed values for the `status` field.
123
+ */
124
+ static literal(optionalValues, ...options) {
125
+ return Fields.string({
126
+ //@ts-ignore
127
+ [addValidatorKey]: (entity, event) => Validators.in(optionalValues())(entity, event)
128
+ }, ...options);
129
+ }
130
+ static enum(enumType, ...options) {
131
+ let valueConverter;
132
+ return Field(() =>
133
+ //@ts-ignore
134
+ enumType(), {
135
+ [addValidatorKey]: (entity, event) => Validators.enum(enumType())(entity, event)
136
+ }, ...options, (options) => {
137
+ if (valueConverter === undefined) {
138
+ let enumObj = enumType();
139
+ let enumValues = Object.values(enumObj)
140
+ .filter((x) => typeof enumObj[x] !== 'number');
141
+ valueConverter = enumValues.find(x => typeof x === "string") ? ValueConverters.String : ValueConverters.Integer;
142
+ }
143
+ if (!options.valueConverter) {
144
+ options.valueConverter = valueConverter;
145
+ }
146
+ else if (!options.valueConverter.fieldTypeInDb) {
147
+ //@ts-ignore
148
+ options.valueConverter.fieldTypeInDb = valueConverter.fieldTypeInDb;
149
+ }
150
+ });
151
+ }
89
152
  static string(...options) {
90
153
  return Field(() => String, ...options);
91
154
  }
@@ -188,6 +251,9 @@ export function Field(valueType, ...options) {
188
251
  if (r.required) {
189
252
  r.validate = addValidator(r.validate, Validators.required);
190
253
  }
254
+ if (r[addValidatorKey]) {
255
+ r.validate = addValidator(r.validate, r[addValidatorKey]);
256
+ }
191
257
  if (r.maxLength) {
192
258
  r.validate = addValidator(r.validate, Validators.maxLength(r.maxLength));
193
259
  }
@@ -14,6 +14,7 @@ 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';
17
18
  //import { remult } from "../remult-proxy";
18
19
  let classValidatorValidate = undefined;
19
20
  // import ("class-validator".toString())
@@ -269,7 +270,7 @@ export class RepositoryImplementation {
269
270
  }
270
271
  }
271
272
  }
272
- async updateMany(where, set) {
273
+ async updateMany({ where }, set) {
273
274
  Filter.throwErrorIfFilterIsEmpty(where, 'updateMany');
274
275
  if (this._dataProvider.isProxy) {
275
276
  return this._edp.updateMany(await this._translateWhereToFilter(where), set);
@@ -636,7 +637,7 @@ export class RepositoryImplementation {
636
637
  async count(where) {
637
638
  return this._edp.count(await this._translateWhereToFilter(where));
638
639
  }
639
- async deleteMany(where) {
640
+ async deleteMany({ where }) {
640
641
  Filter.throwErrorIfFilterIsEmpty(where, 'deleteMany');
641
642
  if (this._dataProvider.isProxy) {
642
643
  return this._edp.deleteMany(await this._translateWhereToFilter(where));
@@ -1961,6 +1962,7 @@ export function ValueListFieldType(...options) {
1961
1962
  ;
1962
1963
  (o.valueConverter = ValueListInfo.get(type)),
1963
1964
  (o.displayValue = (item, val) => val?.caption);
1965
+ o.validate = (entity, ref) => Validators.in(ValueListInfo.get(type).getValues())(entity, ref);
1964
1966
  }, ...options)(type, context);
1965
1967
  };
1966
1968
  }
@@ -0,0 +1 @@
1
+ export const addValidatorKey = '__addValidator';
@@ -1,44 +1,27 @@
1
1
  export {};
2
- //p1 - should the remult api be deleteMany({id:[1,2]}) or deleteMany({where:{id:[1,2]}})
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 - Validate for NAN
5
+ //p1 - Make async hooks gracefully handle non async hooks environment
6
+ //p1 - chain validators with options
7
+ //y2 - should we validate relations
8
+ //y1 - consider replacing all errors with error classes that extend the base Error class
9
+ //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?
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
+ */
3
16
  //p1 - in this video I'll use remult to turn a frontend app to a fullstack app
4
17
  //y1 - getFields didn't work for kobi in the home component
5
- //y1 - consider replacing all errors with error classes that extend the base Error class
6
18
  //p1 - document offline support
7
- //p1 - add section to Fields doc, explaining field type in db
8
- //p1 - add section about union type
9
- //p1 - add section about value list field type
10
19
  /*y1 - Talk JYC - JYC - add some integrity checks on delete
11
20
  - soft delete
12
21
  - delete restrict (implicit, or user selected - and if so, how) (delete & update of id)
13
22
 
14
23
  */
15
- /*y1 currency.ts:10 Uncaught TypeError: Currency_1 is not a constructor
16
- // - ValueListFieldType - the decorator gives an error in react vite project - see langulage yedidya
17
- // happens in target 2022, ts<5.1.6
18
- //typescript 5.1.6
19
- @ValueListFieldType()
20
- export class Currency {
21
- constructor(
22
- public id: number,
23
- public caption: string,
24
- public symbol: string
25
- ) {}
26
- static shekel = new Currency(1, 'Shekel', '₪');
27
- static dollar = new Currency(2, 'Dollar', '$');
28
- static euro = new Currency(3, 'Euro', '€');
29
- static pound = new Currency(4, 'Pound', '£');
30
- static yen = new Currency(5, 'Yen', '¥');
31
- }
32
-
33
- */
34
- //y1 - allow api read to also support instance and filter. - problem with promise
35
- //y1 - admin url1 - pocketbase use /_ for the admin
36
24
  //y1 - consider sql expression gets a dbnames of it's own (that already has the "tableName" defined correctly) maybe also the filter translator
37
- /*y1 - talk about modules in init express with entities/controllers,initRequest,initApi
38
- - support get with backend method, with url search params as the first parameter, & url as second parameter
39
- - support returning redirect, and plain html (For sign in scenarios)
40
-
41
- */
42
25
  //y1 - tried to upgrade vitest, nuxt tests are failing with loading uuid - sounds familiar?
43
26
  //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
44
27
  //y1 - talk about the parameter issue with backend methods
@@ -53,8 +36,6 @@ export class Currency {
53
36
  })
54
37
  Description = ""*/
55
38
  //y2 - get backend methods to work when specifying types for date, and entities as poco's
56
- //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
57
- //y2 - #239 - (@JY) add a way to get from fieldMetadata back to entity repo (like we have in fieldRef)
58
39
  //y2 - constraints (@JY)
59
40
  /*p2 remult admin
60
41
 
@@ -69,11 +50,9 @@ export class Currency {
69
50
  - remult-admin didn't show a update for a table with a uniqua that is numeric
70
51
  */
71
52
  //remult
72
- //p1 - better support union types (status, etc... so it'll work also in filter etc...)
73
53
  //p1 - when a tasks table exists in a different schema - we get many errors
74
54
  //p1 - live query with include
75
55
  //p1 - adjust angular tutorial starter kit for latest angular (as is in tutorial)
76
- //p2 - allow find options preprocessor for api calls, to use for authorization
77
56
  //p2 - when subscribe is forbidden - the query still runs after the renew process
78
57
  //p2 - 'update tasks set where id = $1
79
58
  //p2 - type metadata.key - to keyof entity - based on cwis input
@@ -81,23 +60,15 @@ export class Currency {
81
60
  //p2 - when value changes for field with error, clear error - so the user will feel comfortable
82
61
  //p2 - allowApiUpdate should be false for include in api false
83
62
  //docs
84
- //p2 - make sure that internal members do not appear in the documentation - try running it on the d.ts instead of the code itself.
85
63
  //------
86
64
  //y2 - from the crm-demo(https://crm-demo.up.railway.app/deals), after editing a deal: - _updateEntityBasedOnApi
87
65
  //y2 - Backend methods are transactions, it's not intuitive and maybe should be optional / opt in
88
66
  //y2 - how to run a transaction as a user
89
- //p2 - enum column
90
- //y2 - message for invalid value
91
67
  //y2 - message for relation that is missing
92
68
  //y2 - consider multi tenancies
93
- //p2 - more column types
94
- //p2 - document validators
95
69
  //p2 - and validators to reference
96
70
  //y2 - discuss a default date formatter
97
71
  //y2 - add some api testing framework for user unit tests (will help with codesandbox based discussions)
98
72
  //[ ] V2 - what to do about for relations count?
99
73
  //[ ] V2 - condition? not to fetch if null etc....
100
- //[ ] V3 - all these fields will also appear in the where etc... in the typescript api - but we will not enforce them
101
- //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?
102
- //y2 - should enforce integer - currently we probably round / truncate it
103
74
  //p1 - adjust react tutorial to esm
@@ -26,7 +26,7 @@ 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
31
  static enum = createValueValidatorWithArgs((value, enumObj) => Object.values(enumObj).includes(value), (enumObj) => `Value must be one of ${Object.values(enumObj)
32
32
  .filter((x) => typeof enumObj[x] !== 'number')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remult",
3
- "version": "0.25.8-exp.6",
3
+ "version": "0.25.8-exp.8",
4
4
  "description": "A CRUD framework for full-stack TypeScript",
5
5
  "homepage": "https://remult.dev",
6
6
  "repository": {
@@ -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
- * readonly fieldTypeInDb = 'decimal(18,2)';
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
- * readonly inputType = 'date';
266
+ * inputType = 'date';
261
267
  */
262
268
  readonly inputType?: string;
263
269
  }
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] === 'function';
5
+ return typeof obj[checkMethod] !== 'undefined';
6
6
  }
7
7
  exports.isOfType = isOfType;
8
8
  function cast(obj, checkMethod) {
@@ -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
  }
@@ -9,6 +9,7 @@ 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 addValidatorKey_js_1 = require("./addValidatorKey.js");
12
13
  var Fields = /** @class */ (function () {
13
14
  function Fields() {
14
15
  }
@@ -127,6 +128,11 @@ var Fields = /** @class */ (function () {
127
128
  },
128
129
  }], tslib_1.__read(options), false));
129
130
  };
131
+ /**
132
+ * A CUID (Collision Resistant Unique Identifier) field.
133
+ * This id value is determined on the backend on insert, and can't be updated through the API.
134
+ * The CUID is generated using the `@paralleldrive/cuid2` npm package.
135
+ */
130
136
  Fields.cuid = function () {
131
137
  var options = [];
132
138
  for (var _i = 0; _i < arguments.length; _i++) {
@@ -142,6 +148,74 @@ var Fields = /** @class */ (function () {
142
148
  },
143
149
  }], tslib_1.__read(options), false));
144
150
  };
151
+ /**
152
+ * Defines a field that can hold a value from a specified set of string literals.
153
+ * @param {() => readonly valueType[]} optionalValues - A function that returns an array of allowed string literals.
154
+ * @returns {ClassFieldDecorator<entityType, valueType | undefined>} - A class field decorator.
155
+ *
156
+ * @example
157
+
158
+ * class MyEntity {
159
+ * .@Fields.literal(() => ['open', 'closed', 'frozen', 'in progress'] as const)
160
+ * status: 'open' | 'closed' | 'frozen' | 'in progress' = 'open';
161
+ * }
162
+
163
+ *
164
+ * // This defines a field `status` in `MyEntity` that can only hold the values 'open', 'closed', 'frozen', or 'in progress'.
165
+ *
166
+ * @example
167
+ * // For better reusability and maintainability:
168
+
169
+ * const statuses = ['open', 'closed', 'frozen', 'in progress'] as const;
170
+ * type StatusType = typeof statuses[number];
171
+ *
172
+ * class MyEntity {
173
+ * .@Fields.literal(() => statuses)
174
+ * status: StatusType = 'open';
175
+ * }
176
+
177
+ *
178
+ * // This approach allows easy management and updates of the allowed values for the `status` field.
179
+ */
180
+ Fields.literal = function (optionalValues) {
181
+ var _a;
182
+ var options = [];
183
+ for (var _i = 1; _i < arguments.length; _i++) {
184
+ options[_i - 1] = arguments[_i];
185
+ }
186
+ return Fields.string.apply(Fields, tslib_1.__spreadArray([(_a = {},
187
+ //@ts-ignore
188
+ _a[addValidatorKey_js_1.addValidatorKey] = function (entity, event) { return validators_js_1.Validators.in(optionalValues())(entity, event); },
189
+ _a)], tslib_1.__read(options), false));
190
+ };
191
+ Fields.enum = function (enumType) {
192
+ var _a;
193
+ var options = [];
194
+ for (var _i = 1; _i < arguments.length; _i++) {
195
+ options[_i - 1] = arguments[_i];
196
+ }
197
+ var valueConverter;
198
+ return Field.apply(void 0, tslib_1.__spreadArray(tslib_1.__spreadArray([function () {
199
+ //@ts-ignore
200
+ return (enumType());
201
+ }, (_a = {},
202
+ _a[addValidatorKey_js_1.addValidatorKey] = function (entity, event) { return validators_js_1.Validators.enum(enumType())(entity, event); },
203
+ _a)], tslib_1.__read(options), false), [function (options) {
204
+ if (valueConverter === undefined) {
205
+ var enumObj_1 = enumType();
206
+ var enumValues = Object.values(enumObj_1)
207
+ .filter(function (x) { return typeof enumObj_1[x] !== 'number'; });
208
+ valueConverter = enumValues.find(function (x) { return typeof x === "string"; }) ? valueConverters_js_1.ValueConverters.String : valueConverters_js_1.ValueConverters.Integer;
209
+ }
210
+ if (!options.valueConverter) {
211
+ options.valueConverter = valueConverter;
212
+ }
213
+ else if (!options.valueConverter.fieldTypeInDb) {
214
+ //@ts-ignore
215
+ options.valueConverter.fieldTypeInDb = valueConverter.fieldTypeInDb;
216
+ }
217
+ }], false));
218
+ };
145
219
  Fields.string = function () {
146
220
  var options = [];
147
221
  for (var _i = 0; _i < arguments.length; _i++) {
@@ -251,6 +325,9 @@ function Field(valueType) {
251
325
  if (r.required) {
252
326
  r.validate = addValidator(r.validate, validators_js_1.Validators.required);
253
327
  }
328
+ if (r[addValidatorKey_js_1.addValidatorKey]) {
329
+ r.validate = addValidator(r.validate, r[addValidatorKey_js_1.addValidatorKey]);
330
+ }
254
331
  if (r.maxLength) {
255
332
  r.validate = addValidator(r.validate, validators_js_1.Validators.maxLength(r.maxLength));
256
333
  }
@@ -47,7 +47,9 @@ export declare class RepositoryImplementation<entityType> implements Repository<
47
47
  insert(item: Partial<MembersOnly<entityType>>): Promise<entityType>;
48
48
  get fields(): FieldsMetadata<entityType>;
49
49
  validate(entity: Partial<MembersOnly<entityType>>, ...fields: Extract<keyof MembersOnly<entityType>, string>[]): Promise<ErrorInfo<entityType> | undefined>;
50
- updateMany(where: EntityFilter<entityType>, set: Partial<MembersOnly<entityType>>): Promise<number>;
50
+ updateMany({ where }: {
51
+ where: EntityFilter<entityType>;
52
+ }, set: Partial<MembersOnly<entityType>>): Promise<number>;
51
53
  update(id: idType<entityType>, item: Partial<MembersOnly<entityType>>): Promise<entityType>;
52
54
  update(originalItem: Partial<MembersOnly<entityType>>, item: Partial<MembersOnly<entityType>>): Promise<entityType>;
53
55
  private _getRefForExistingRow;
@@ -63,7 +65,9 @@ export declare class RepositoryImplementation<entityType> implements Repository<
63
65
  toJson(item: entityType | entityType[] | Promise<entityType> | Promise<entityType[]>): any;
64
66
  fromJson(json: any, newRow?: boolean): any;
65
67
  count(where?: EntityFilter<entityType>): Promise<number>;
66
- deleteMany(where: EntityFilter<entityType>): Promise<number>;
68
+ deleteMany({ where }: {
69
+ where: EntityFilter<entityType>;
70
+ }): Promise<number>;
67
71
  private _cache;
68
72
  findOne(options?: FindFirstOptions<entityType>, skipOrderByAndLimit?: boolean): Promise<entityType>;
69
73
  findFirst(where?: EntityFilter<entityType>, options?: FindFirstOptions<entityType>, skipOrderByAndLimit?: boolean): Promise<entityType>;
@@ -18,6 +18,7 @@ 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");
21
22
  //import { remult } from "../remult-proxy";
22
23
  var classValidatorValidate = undefined;
23
24
  // import ("class-validator".toString())
@@ -435,52 +436,53 @@ var RepositoryImplementation = /** @class */ (function () {
435
436
  });
436
437
  });
437
438
  };
438
- RepositoryImplementation.prototype.updateMany = function (where, set) {
439
- var _a, e_7, _b, _c;
439
+ RepositoryImplementation.prototype.updateMany = function (_a, set) {
440
+ var _b, e_7, _c, _d;
441
+ var where = _a.where;
440
442
  return tslib_1.__awaiter(this, void 0, void 0, function () {
441
- var _d, _e, updated, _f, _g, _h, item, e_7_1;
442
- return tslib_1.__generator(this, function (_j) {
443
- switch (_j.label) {
443
+ var _e, _f, updated, _g, _h, _j, item, e_7_1;
444
+ return tslib_1.__generator(this, function (_k) {
445
+ switch (_k.label) {
444
446
  case 0:
445
447
  filter_interfaces_js_1.Filter.throwErrorIfFilterIsEmpty(where, 'updateMany');
446
448
  if (!this._dataProvider.isProxy) return [3 /*break*/, 2];
447
- _e = (_d = this._edp).updateMany;
449
+ _f = (_e = this._edp).updateMany;
448
450
  return [4 /*yield*/, this._translateWhereToFilter(where)];
449
- case 1: return [2 /*return*/, _e.apply(_d, [_j.sent(), set])];
451
+ case 1: return [2 /*return*/, _f.apply(_e, [_k.sent(), set])];
450
452
  case 2:
451
453
  updated = 0;
452
- _j.label = 3;
454
+ _k.label = 3;
453
455
  case 3:
454
- _j.trys.push([3, 9, 10, 15]);
455
- _f = true, _g = tslib_1.__asyncValues(this.query({ where: where }));
456
- _j.label = 4;
457
- case 4: return [4 /*yield*/, _g.next()];
456
+ _k.trys.push([3, 9, 10, 15]);
457
+ _g = true, _h = tslib_1.__asyncValues(this.query({ where: where }));
458
+ _k.label = 4;
459
+ case 4: return [4 /*yield*/, _h.next()];
458
460
  case 5:
459
- if (!(_h = _j.sent(), _a = _h.done, !_a)) return [3 /*break*/, 8];
460
- _c = _h.value;
461
- _f = false;
462
- item = _c;
461
+ if (!(_j = _k.sent(), _b = _j.done, !_b)) return [3 /*break*/, 8];
462
+ _d = _j.value;
463
+ _g = false;
464
+ item = _d;
463
465
  (0, assign_js_1.assign)(item, set);
464
466
  return [4 /*yield*/, (0, getEntityRef_js_1.getEntityRef)(item).save()];
465
467
  case 6:
466
- _j.sent();
468
+ _k.sent();
467
469
  updated++;
468
- _j.label = 7;
470
+ _k.label = 7;
469
471
  case 7:
470
- _f = true;
472
+ _g = true;
471
473
  return [3 /*break*/, 4];
472
474
  case 8: return [3 /*break*/, 15];
473
475
  case 9:
474
- e_7_1 = _j.sent();
476
+ e_7_1 = _k.sent();
475
477
  e_7 = { error: e_7_1 };
476
478
  return [3 /*break*/, 15];
477
479
  case 10:
478
- _j.trys.push([10, , 13, 14]);
479
- if (!(!_f && !_a && (_b = _g.return))) return [3 /*break*/, 12];
480
- return [4 /*yield*/, _b.call(_g)];
480
+ _k.trys.push([10, , 13, 14]);
481
+ if (!(!_g && !_b && (_c = _h.return))) return [3 /*break*/, 12];
482
+ return [4 /*yield*/, _c.call(_h)];
481
483
  case 11:
482
- _j.sent();
483
- _j.label = 12;
484
+ _k.sent();
485
+ _k.label = 12;
484
486
  case 12: return [3 /*break*/, 14];
485
487
  case 13:
486
488
  if (e_7) throw e_7.error;
@@ -1087,51 +1089,52 @@ var RepositoryImplementation = /** @class */ (function () {
1087
1089
  });
1088
1090
  });
1089
1091
  };
1090
- RepositoryImplementation.prototype.deleteMany = function (where) {
1091
- var _a, e_18, _b, _c;
1092
+ RepositoryImplementation.prototype.deleteMany = function (_a) {
1093
+ var _b, e_18, _c, _d;
1094
+ var where = _a.where;
1092
1095
  return tslib_1.__awaiter(this, void 0, void 0, function () {
1093
- var _d, _e, deleted, _f, _g, _h, item, e_18_1;
1094
- return tslib_1.__generator(this, function (_j) {
1095
- switch (_j.label) {
1096
+ var _e, _f, deleted, _g, _h, _j, item, e_18_1;
1097
+ return tslib_1.__generator(this, function (_k) {
1098
+ switch (_k.label) {
1096
1099
  case 0:
1097
1100
  filter_interfaces_js_1.Filter.throwErrorIfFilterIsEmpty(where, 'deleteMany');
1098
1101
  if (!this._dataProvider.isProxy) return [3 /*break*/, 2];
1099
- _e = (_d = this._edp).deleteMany;
1102
+ _f = (_e = this._edp).deleteMany;
1100
1103
  return [4 /*yield*/, this._translateWhereToFilter(where)];
1101
- case 1: return [2 /*return*/, _e.apply(_d, [_j.sent()])];
1104
+ case 1: return [2 /*return*/, _f.apply(_e, [_k.sent()])];
1102
1105
  case 2:
1103
1106
  deleted = 0;
1104
- _j.label = 3;
1107
+ _k.label = 3;
1105
1108
  case 3:
1106
- _j.trys.push([3, 9, 10, 15]);
1107
- _f = true, _g = tslib_1.__asyncValues(this.query({ where: where }));
1108
- _j.label = 4;
1109
- case 4: return [4 /*yield*/, _g.next()];
1109
+ _k.trys.push([3, 9, 10, 15]);
1110
+ _g = true, _h = tslib_1.__asyncValues(this.query({ where: where }));
1111
+ _k.label = 4;
1112
+ case 4: return [4 /*yield*/, _h.next()];
1110
1113
  case 5:
1111
- if (!(_h = _j.sent(), _a = _h.done, !_a)) return [3 /*break*/, 8];
1112
- _c = _h.value;
1113
- _f = false;
1114
- item = _c;
1114
+ if (!(_j = _k.sent(), _b = _j.done, !_b)) return [3 /*break*/, 8];
1115
+ _d = _j.value;
1116
+ _g = false;
1117
+ item = _d;
1115
1118
  return [4 /*yield*/, (0, getEntityRef_js_1.getEntityRef)(item).delete()];
1116
1119
  case 6:
1117
- _j.sent();
1120
+ _k.sent();
1118
1121
  deleted++;
1119
- _j.label = 7;
1122
+ _k.label = 7;
1120
1123
  case 7:
1121
- _f = true;
1124
+ _g = true;
1122
1125
  return [3 /*break*/, 4];
1123
1126
  case 8: return [3 /*break*/, 15];
1124
1127
  case 9:
1125
- e_18_1 = _j.sent();
1128
+ e_18_1 = _k.sent();
1126
1129
  e_18 = { error: e_18_1 };
1127
1130
  return [3 /*break*/, 15];
1128
1131
  case 10:
1129
- _j.trys.push([10, , 13, 14]);
1130
- if (!(!_f && !_a && (_b = _g.return))) return [3 /*break*/, 12];
1131
- return [4 /*yield*/, _b.call(_g)];
1132
+ _k.trys.push([10, , 13, 14]);
1133
+ if (!(!_g && !_b && (_c = _h.return))) return [3 /*break*/, 12];
1134
+ return [4 /*yield*/, _c.call(_h)];
1132
1135
  case 11:
1133
- _j.sent();
1134
- _j.label = 12;
1136
+ _k.sent();
1137
+ _k.label = 12;
1135
1138
  case 12: return [3 /*break*/, 14];
1136
1139
  case 13:
1137
1140
  if (e_18) throw e_18.error;
@@ -3247,6 +3250,7 @@ function ValueListFieldType() {
3247
3250
  ;
3248
3251
  (o.valueConverter = ValueListInfo.get(type)),
3249
3252
  (o.displayValue = function (item, val) { return val === null || val === void 0 ? void 0 : val.caption; });
3253
+ o.validate = function (entity, ref) { return validators_js_1.Validators.in(ValueListInfo.get(type).getValues())(entity, ref); };
3250
3254
  }], tslib_1.__read(options), false))(type, context);
3251
3255
  };
3252
3256
  }
@@ -0,0 +1 @@
1
+ export declare const addValidatorKey = "__addValidator";
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.addValidatorKey = void 0;
4
+ exports.addValidatorKey = '__addValidator';
@@ -315,14 +315,18 @@ export interface Repository<entityType> {
315
315
  /**
316
316
  * Updates all items that match the `where` condition.
317
317
  */
318
- updateMany(where: EntityFilter<entityType>, item: Partial<MembersOnly<entityType>>): Promise<number>;
318
+ updateMany(options: {
319
+ where: EntityFilter<entityType>;
320
+ }, item: Partial<MembersOnly<entityType>>): Promise<number>;
319
321
  /** Deletes an Item*/
320
322
  delete(id: idType<entityType>): Promise<void>;
321
323
  delete(item: Partial<MembersOnly<entityType>>): Promise<void>;
322
324
  /**
323
325
  * Deletes all items that match the `where` condition.
324
326
  */
325
- deleteMany(where: EntityFilter<entityType>): Promise<number>;
327
+ deleteMany(options: {
328
+ where: EntityFilter<entityType>;
329
+ }): Promise<number>;
326
330
  /** Creates an instance of an item. It'll not be saved to the data source unless `save` or `insert` will be called for that item */
327
331
  create(item?: Partial<MembersOnly<entityType>>): entityType;
328
332
  toJson(item: Promise<entityType[]>): Promise<any[]>;
@@ -1,45 +1,28 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- //p1 - should the remult api be deleteMany({id:[1,2]}) or deleteMany({where:{id:[1,2]}})
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 - Validate for NAN
6
+ //p1 - Make async hooks gracefully handle non async hooks environment
7
+ //p1 - chain validators with options
8
+ //y2 - should we validate relations
9
+ //y1 - consider replacing all errors with error classes that extend the base Error class
10
+ //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?
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
+ */
4
17
  //p1 - in this video I'll use remult to turn a frontend app to a fullstack app
5
18
  //y1 - getFields didn't work for kobi in the home component
6
- //y1 - consider replacing all errors with error classes that extend the base Error class
7
19
  //p1 - document offline support
8
- //p1 - add section to Fields doc, explaining field type in db
9
- //p1 - add section about union type
10
- //p1 - add section about value list field type
11
20
  /*y1 - Talk JYC - JYC - add some integrity checks on delete
12
21
  - soft delete
13
22
  - delete restrict (implicit, or user selected - and if so, how) (delete & update of id)
14
23
 
15
24
  */
16
- /*y1 currency.ts:10 Uncaught TypeError: Currency_1 is not a constructor
17
- // - ValueListFieldType - the decorator gives an error in react vite project - see langulage yedidya
18
- // happens in target 2022, ts<5.1.6
19
- //typescript 5.1.6
20
- @ValueListFieldType()
21
- export class Currency {
22
- constructor(
23
- public id: number,
24
- public caption: string,
25
- public symbol: string
26
- ) {}
27
- static shekel = new Currency(1, 'Shekel', '₪');
28
- static dollar = new Currency(2, 'Dollar', '$');
29
- static euro = new Currency(3, 'Euro', '€');
30
- static pound = new Currency(4, 'Pound', '£');
31
- static yen = new Currency(5, 'Yen', '¥');
32
- }
33
-
34
- */
35
- //y1 - allow api read to also support instance and filter. - problem with promise
36
- //y1 - admin url1 - pocketbase use /_ for the admin
37
25
  //y1 - consider sql expression gets a dbnames of it's own (that already has the "tableName" defined correctly) maybe also the filter translator
38
- /*y1 - talk about modules in init express with entities/controllers,initRequest,initApi
39
- - support get with backend method, with url search params as the first parameter, & url as second parameter
40
- - support returning redirect, and plain html (For sign in scenarios)
41
-
42
- */
43
26
  //y1 - tried to upgrade vitest, nuxt tests are failing with loading uuid - sounds familiar?
44
27
  //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
45
28
  //y1 - talk about the parameter issue with backend methods
@@ -54,8 +37,6 @@ export class Currency {
54
37
  })
55
38
  Description = ""*/
56
39
  //y2 - get backend methods to work when specifying types for date, and entities as poco's
57
- //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
58
- //y2 - #239 - (@JY) add a way to get from fieldMetadata back to entity repo (like we have in fieldRef)
59
40
  //y2 - constraints (@JY)
60
41
  /*p2 remult admin
61
42
 
@@ -70,11 +51,9 @@ export class Currency {
70
51
  - remult-admin didn't show a update for a table with a uniqua that is numeric
71
52
  */
72
53
  //remult
73
- //p1 - better support union types (status, etc... so it'll work also in filter etc...)
74
54
  //p1 - when a tasks table exists in a different schema - we get many errors
75
55
  //p1 - live query with include
76
56
  //p1 - adjust angular tutorial starter kit for latest angular (as is in tutorial)
77
- //p2 - allow find options preprocessor for api calls, to use for authorization
78
57
  //p2 - when subscribe is forbidden - the query still runs after the renew process
79
58
  //p2 - 'update tasks set where id = $1
80
59
  //p2 - type metadata.key - to keyof entity - based on cwis input
@@ -82,23 +61,15 @@ export class Currency {
82
61
  //p2 - when value changes for field with error, clear error - so the user will feel comfortable
83
62
  //p2 - allowApiUpdate should be false for include in api false
84
63
  //docs
85
- //p2 - make sure that internal members do not appear in the documentation - try running it on the d.ts instead of the code itself.
86
64
  //------
87
65
  //y2 - from the crm-demo(https://crm-demo.up.railway.app/deals), after editing a deal: - _updateEntityBasedOnApi
88
66
  //y2 - Backend methods are transactions, it's not intuitive and maybe should be optional / opt in
89
67
  //y2 - how to run a transaction as a user
90
- //p2 - enum column
91
- //y2 - message for invalid value
92
68
  //y2 - message for relation that is missing
93
69
  //y2 - consider multi tenancies
94
- //p2 - more column types
95
- //p2 - document validators
96
70
  //p2 - and validators to reference
97
71
  //y2 - discuss a default date formatter
98
72
  //y2 - add some api testing framework for user unit tests (will help with codesandbox based discussions)
99
73
  //[ ] V2 - what to do about for relations count?
100
74
  //[ ] V2 - condition? not to fetch if null etc....
101
- //[ ] V3 - all these fields will also appear in the where etc... in the typescript api - but we will not enforce them
102
- //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?
103
- //y2 - should enforce integer - currently we probably round / truncate it
104
75
  //p1 - adjust react tutorial to esm
package/src/validators.js CHANGED
@@ -48,7 +48,9 @@ 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.join(', ')); });
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
56
  return "Value must be one of ".concat(Object.values(enumObj)