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.
@@ -6,5 +6,80 @@ export function initAsyncHooks() {
6
6
  if (init)
7
7
  return;
8
8
  init = true;
9
- remultStatic.asyncContext = new RemultAsyncLocalStorage(new AsyncLocalStorage());
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
  }
@@ -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
- let r;
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
  }
@@ -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)) {
@@ -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, ...options);
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
- Object.assign(r, o);
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 - should the remult api be deleteMany({id:[1,2]}) or deleteMany({where:{id:[1,2]}})
3
- //p1 - document cuid entity, usage of https://www.npmjs.com/package/@paralleldrive/cuid2
4
- //p1 - example of creating your own id column
5
- //p1 - in this video I'll use remult to turn a frontend app to a fullstack app
6
- //y1 - getFields didn't work for kobi in the home component
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
- //p1 - add section to Fields doc, explaining field type in db
10
- //p1 - add section about union type
11
- //p1 - add section about value list field type
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
@@ -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 ${Object.values(enumObj)
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,6 +1,6 @@
1
1
  {
2
2
  "name": "remult",
3
- "version": "0.25.8-exp.7",
3
+ "version": "0.25.8-exp.9",
4
4
  "description": "A CRUD framework for full-stack TypeScript",
5
5
  "homepage": "https://remult.dev",
6
6
  "repository": {
@@ -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
+ }
@@ -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 async_hooks_1.AsyncLocalStorage());
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
- * 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/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: myAsyncLocalStorage<Remult>);
12
- run(remult: Remult, callback: (remult: Remult) => void): void;
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 myAsyncLocalStorage<T> = {
16
- run<R>(store: T, callback: (...args: any[]) => R, ...args: any[]): R;
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
- if (this.remultObjectStorage)
32
- this.remultObjectStorage.run(remult, function () { return callback(remult); });
33
- else
34
- callback(remult);
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
- var r;
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] === '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,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; }], tslib_1.__read(options), false));
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
- Object.assign(r, o);
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.__assign(tslib_1.__assign({}, buildOptions(settingsOnTypeLevel, remult)), settings);
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;
@@ -1,47 +1,26 @@
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]}})
4
- //p1 - document cuid entity, usage of https://www.npmjs.com/package/@paralleldrive/cuid2
5
- //p1 - example of creating your own id column
6
- //p1 - in this video I'll use remult to turn a frontend app to a fullstack app
7
- //y1 - getFields didn't work for kobi in the home component
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
- //p1 - add section to Fields doc, explaining field type in db
11
- //p1 - add section about union type
12
- //p1 - add section about value list field type
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
@@ -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.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
- return "Value must be one of ".concat(Object.values(enumObj)
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;