remult 0.27.5 → 0.27.7

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.
@@ -473,7 +473,7 @@ function fieldDbName(f_1, meta_1) {
473
473
  if (!f.options.sqlExpression) return [3 /*break*/, 7];
474
474
  if (!(typeof f.options.sqlExpression === 'function')) return [3 /*break*/, 5];
475
475
  if (f[sqlExpressionInProgressKey] && !forceSqlExpression) {
476
- return [2 /*return*/, "recursive sqlExpression call for field '" + f.key + "'. "];
476
+ return [2 /*return*/, "recursive sqlExpression call for field '" + f.key + "'. \0"];
477
477
  }
478
478
  _a.label = 1;
479
479
  case 1:
@@ -483,7 +483,8 @@ function fieldDbName(f_1, meta_1) {
483
483
  return [4 /*yield*/, f.options.sqlExpression(meta)];
484
484
  case 2:
485
485
  result_1 = _a.sent();
486
- f.options.sqlExpression = function () { return result_1; };
486
+ if (!result_1.includes('\0'))
487
+ f.options.sqlExpression = function () { return result_1; };
487
488
  return [3 /*break*/, 4];
488
489
  case 3:
489
490
  delete f[sqlExpressionInProgressKey];
@@ -17,7 +17,7 @@ var LiveQueryPublisher = /** @class */ (function () {
17
17
  var _this = this;
18
18
  return tslib_1.__generator(this, function (_a) {
19
19
  switch (_a.label) {
20
- case 0: return [4 /*yield*/, this.liveQueryStorage().forEach(entityKey, function (_a) { return tslib_1.__awaiter(_this, [_a], void 0, function (_b) {
20
+ case 0: return [4 /*yield*/, this.runPromise(this.liveQueryStorage().forEach(entityKey, function (_a) { return tslib_1.__awaiter(_this, [_a], void 0, function (_b) {
21
21
  var query;
22
22
  var _this = this;
23
23
  var q = _b.query, setData = _b.setData;
@@ -117,7 +117,7 @@ var LiveQueryPublisher = /** @class */ (function () {
117
117
  return [2 /*return*/];
118
118
  }
119
119
  });
120
- }); })];
120
+ }); }))];
121
121
  case 1:
122
122
  _a.sent();
123
123
  return [2 /*return*/];
@@ -52,7 +52,7 @@ export declare class Fields {
52
52
  * // This approach allows easy management and updates of the allowed values for the `status` field.
53
53
  */
54
54
  static literal<entityType = unknown, valueType extends string = string>(optionalValues: () => readonly valueType[], ...options: (StringFieldOptions<entityType, valueType> | ((options: StringFieldOptions<entityType, valueType>, remult: Remult) => void))[]): ClassFieldDecorator<entityType, valueType | undefined>;
55
- static enum<entityType = unknown, theEnum = object>(enumType: () => theEnum, ...options: (FieldOptions<entityType, theEnum[keyof theEnum]> | ((options: FieldOptions<entityType, theEnum[keyof theEnum]>, remult: Remult) => void))[]): ClassFieldDecorator<entityType, theEnum[keyof theEnum] | undefined>;
55
+ static enum<entityType = unknown, theEnum = unknown>(enumType: () => theEnum, ...options: (FieldOptions<entityType, theEnum[keyof theEnum]> | ((options: FieldOptions<entityType, theEnum[keyof theEnum]>, remult: Remult) => void))[]): ClassFieldDecorator<entityType, theEnum[keyof theEnum] | undefined>;
56
56
  static string<entityType = unknown, valueType = string>(...options: (StringFieldOptions<entityType, valueType> | ((options: StringFieldOptions<entityType, valueType>, remult: Remult) => void))[]): ClassFieldDecorator<entityType, valueType | undefined>;
57
57
  static boolean<entityType = unknown>(...options: (FieldOptions<entityType, boolean> | ((options: FieldOptions<entityType, boolean>, remult: Remult) => void))[]): ClassFieldDecorator<entityType, boolean | undefined>;
58
58
  }
@@ -2100,7 +2100,7 @@ var rowHelperImplementation = /** @class */ (function (_super) {
2100
2100
  return tslib_1.__generator(this, function (_t) {
2101
2101
  switch (_t.label) {
2102
2102
  case 0:
2103
- _t.trys.push([0, , 41, 42]);
2103
+ _t.trys.push([0, , 40, 41]);
2104
2104
  if (this._saving)
2105
2105
  throw new Error('cannot save while entity is already saving');
2106
2106
  this._saving = true;
@@ -2181,7 +2181,7 @@ var rowHelperImplementation = /** @class */ (function (_super) {
2181
2181
  isNew = this.isNew();
2182
2182
  _t.label = 13;
2183
2183
  case 13:
2184
- _t.trys.push([13, 38, , 40]);
2184
+ _t.trys.push([13, 37, , 39]);
2185
2185
  (_r = this._subscribers) === null || _r === void 0 ? void 0 : _r.reportChanged();
2186
2186
  if (!this.isNew()) return [3 /*break*/, 19];
2187
2187
  if (!doNotSave_1) return [3 /*break*/, 16];
@@ -2267,23 +2267,22 @@ var rowHelperImplementation = /** @class */ (function (_super) {
2267
2267
  }
2268
2268
  finally { if (e_33) throw e_33.error; }
2269
2269
  return [7 /*endfinally*/];
2270
- case 36: return [4 /*yield*/, this.repo._remult.liveQueryPublisher.itemChanged(this.repo.metadata.key, [{ id: this.getId(), oldId: this.getOriginalId(), deleted: false }])];
2271
- case 37:
2272
- _t.sent();
2270
+ case 36:
2271
+ this.repo._remult.liveQueryPublisher.itemChanged(this.repo.metadata.key, [{ id: this.getId(), oldId: this.getOriginalId(), deleted: false }]);
2273
2272
  this.saveOriginalData();
2274
2273
  this._isNew = false;
2275
2274
  return [2 /*return*/, this.instance];
2276
- case 38:
2275
+ case 37:
2277
2276
  err_1 = _t.sent();
2278
2277
  return [4 /*yield*/, this.catchSaveErrors(err_1)];
2279
- case 39: throw _t.sent();
2280
- case 40: return [3 /*break*/, 42];
2281
- case 41:
2278
+ case 38: throw _t.sent();
2279
+ case 39: return [3 /*break*/, 41];
2280
+ case 40:
2282
2281
  this.isLoading = false;
2283
2282
  this._reportChangedToEntityAndFields();
2284
2283
  this._saving = false;
2285
2284
  return [7 /*endfinally*/];
2286
- case 42: return [2 /*return*/];
2285
+ case 41: return [2 /*return*/];
2287
2286
  }
2288
2287
  });
2289
2288
  });
@@ -2382,7 +2381,7 @@ var rowHelperImplementation = /** @class */ (function (_super) {
2382
2381
  this.__assertValidity();
2383
2382
  _e.label = 3;
2384
2383
  case 3:
2385
- _e.trys.push([3, 17, , 19]);
2384
+ _e.trys.push([3, 16, , 18]);
2386
2385
  if (!doDelete) return [3 /*break*/, 5];
2387
2386
  return [4 /*yield*/, this.edp.delete(this.id)];
2388
2387
  case 4:
@@ -2423,16 +2422,17 @@ var rowHelperImplementation = /** @class */ (function (_super) {
2423
2422
  }
2424
2423
  finally { if (e_36) throw e_36.error; }
2425
2424
  return [7 /*endfinally*/];
2426
- case 15: return [4 /*yield*/, this.repo._remult.liveQueryPublisher.itemChanged(this.repo.metadata.key, [{ id: this.getId(), oldId: this.getOriginalId(), deleted: true }])];
2427
- case 16:
2428
- _e.sent();
2425
+ case 15:
2426
+ this.repo._remult.liveQueryPublisher.itemChanged(this.repo.metadata.key, [
2427
+ { id: this.getId(), oldId: this.getOriginalId(), deleted: true },
2428
+ ]);
2429
2429
  this._wasDeleted = true;
2430
- return [3 /*break*/, 19];
2431
- case 17:
2430
+ return [3 /*break*/, 18];
2431
+ case 16:
2432
2432
  err_2 = _e.sent();
2433
2433
  return [4 /*yield*/, this.catchSaveErrors(err_2)];
2434
- case 18: throw _e.sent();
2435
- case 19: return [2 /*return*/];
2434
+ case 17: throw _e.sent();
2435
+ case 18: return [2 /*return*/];
2436
2436
  }
2437
2437
  });
2438
2438
  });
@@ -4,51 +4,37 @@ exports.flags = void 0;
4
4
  exports.flags = {
5
5
  error500RetryCount: 4,
6
6
  };
7
- //y1 - Hacker News for the tutorial?
7
+ //p1 - add transactional option to backend method
8
+ //p1 - add parameter all to deleteMany, and updateMany
9
+ //p1 filter.apply ApiPreFilter
10
+ //p1 - min, max, avg, sum, countDistinct - noam come up with an api
11
+ /*p1 - add id and use uuid by default, but allow changes with Fields.id.defaultIdProvider NO but defaultProvider yes???
12
+ //p1 - replace uuid with crypto.randomUUID and allow custom fallback NO
13
+ //p1 - Add example for luid
14
+ //p1 - add example for nanoid
15
+ //p1 - explain the benefits of changing the default provider for testing in docs.
16
+ */
17
+ //p1 - consider upsert (where,set)
18
+ //p1 TODO - consider id to also support keyof (id:['company','index']) - had problem with | (keyof Partial<entityType>)[] & `entity`
19
+ //p2 - add some kind of options handler that will help with translation etc... like in hagai - something that runs at the stage where options are being built
8
20
  /*y1 - https://github.com/remult/remult/discussions/438
9
21
  - https://github.com/remult/remult/blob/query-argumets/projects/tests/dbs/test-sql-database.spec.ts#L100-L128
10
- //p2 - allow preprocess to replace filter values - for example replace $contains on a specific field, with specific other sql - useful for full text search and other scenarios
11
22
  //y1 - consider sql expression gets a dbnames of it's own (that already has the "tableName" defined correctly) maybe also the filter translator
23
+ //p2 - allow preprocess to replace filter values - for example replace $contains on a specific field, with specific other sql - useful for full text search and other scenarios
12
24
  //y2 - soft-delete-discussion https://discord.com/channels/975754286384418847/1230386433093533698/1230386433093533698
13
25
  */
14
- //y1 - add parameter all to deleteMany, and updateMany
15
- //p1 - fix query docs to also explain how it can be used for infinite scroll and pagination.
16
- //p1 - when like doesn't have a string - don't send it to the db
17
- //y1 - 'test expression columns without aliases'
18
- //y1 - live query update waits for last
19
- //p1 - add luid - it's cool for order by. - NO
20
- //p1 - replace uuid with crypto.randomUUID and allow custom fallback NO
21
- //p1 - add nanoid - NO
22
- //p1 - add id and use luid by default, but allow changes with Fields.id.defaultIdProvider NO but defaultProvider yes
23
- /*p1
24
- * field, fieldType ,ValueListFieldType, - return optional parameter for decorators
25
- * options to remultExpress tc... should be mandatory
26
- * return value converter to previous typing
27
- * add breaking change notification about findOne and findFirst and findId
28
- */
29
- //p1 - fix query docs to also explain how it can be used for infinite scroll and pagination.
30
- //y1 - 'test expression columns without aliases'
31
- //y1 - ts issue with fields - when a field is optional, it's $ is also optional and thats a problem
26
+ //p2 - fix query docs to also explain how it can be used for infinite scroll and pagination.
27
+ //p2 - when like doesn't have a string - don't send it to the db
32
28
  //p1 - vite 5.3 supports ts5 decorators - check and adapt.
33
- //p1 - see if I can fix the no alias problem in selects
34
- //p1 - add luid - it's cool for order by.
35
- //p1 - replace uuid with crypto.randomUUID and allow custom fallback
36
- //p1 - add nanoid
37
- //p1 - add id and use luid by default, but allow changes with Fields.id.defaultIdProvider
38
- //p1 - explain the benefits of changing the default provider for testing in docs.
29
+ //p1 - tutorial about controller - and mutable controller
30
+ //p1 - docs abount subscription channel
31
+ //p1 - Consider enforcing serial operations on JSON database to prevent data loss
39
32
  //p1 - fix sqlite to support alter table when adding a column with ensure schema = on
40
- //p1 - fix app custom filters example for multiple filters.
41
- //p1 - add not!!!!
42
33
  //p1 - add LifecycleEvent to documentation
43
- //p1 - Fix hagai and starter project to latest ts
44
34
  //p1 - fix chaining of saving and saved in multiple entity options args
45
35
  //p1 - review starter and examples and separate remult * auth from the routes
46
- //p1 - min, max, avg, sum
47
- //p1 - consider upsert (where,set)
48
36
  //y1 - live query with count #436
49
37
  //y1 TODO - In the esm version of our tutorial - the imports are automatically .ts and not .js in react and not in vue
50
- //y1 TODO - consider id to also support keyof (id:['company','index']) - had problem with | (keyof Partial<entityType>)[] & `entity`
51
- //y1 TODO - readonly key: keyof MembersOnly<entityType> & string
52
38
  //y1 TODO - fix remult admin not to load the html into memory until used
53
39
  //y2 - talk about insert / update / delete with relations
54
40
  /*
@@ -66,15 +52,10 @@ const result = await repo.batch(x=>({
66
52
  }))
67
53
  */
68
54
  //y1 - wait a second to close stream -see pr
69
- //p1 - Consider enforcing serial operations on JSON database to prevent data loss
70
55
  //p1 - prepare the createEntity discussion
71
- //p1 - return error on connection error
72
56
  //p1 - article on displayValue including it's definition for entities that is used in relations
73
57
  //p1 - article auth.js with express - played with it, requires type="module" and a few more configurations - https://github.com/noam-honig/express-auth
74
- //y2 - currently for string fields we default for 255 in knex (which is their default) why not just use text (varchar max) - and only use exact length when one is provided?
75
58
  //p2 - create foreign key constraints in user code - https://codesandbox.io/p/devbox/fk-validator-tdshcs
76
- //p2 - admin url1 - pocketbase use /_ for the admin //{allowed?:Allowed,url?:string} also checkout the admin* as it should probably be something else and didn't work in hono
77
- //y2 - fix validators cache
78
59
  //y2 - should we validate relations
79
60
  //y1 - dependency of live query tables live query refresh of view on table update
80
61
  //p1 - see why mongo tests do not run anymore
@@ -94,7 +75,6 @@ const result = await repo.batch(x=>({
94
75
  - delete restrict (implicit, or user selected - and if so, how) (delete & update of id)
95
76
 
96
77
  */
97
- //y1 - tried to upgrade vitest, nuxt tests are failing with loading uuid - sounds familiar?
98
78
  //y1 - talk about the parameter issue with backend methods
99
79
  //y2 - livequery for findfirst (@JY)
100
80
  /*y2 -
@@ -111,7 +91,6 @@ const result = await repo.batch(x=>({
111
91
  //y2 - Fix problem with promise all in sql expression recurssion - when using PromiseAll in row relation loading, some sql expressions appear is recursion call even if they are not
112
92
  //p2 - when subscribe is forbidden - the query still runs after the renew process
113
93
  //p2 - 'update tasks set where id = $1
114
- //p2 - type metadata.key - to keyof entity - based on cwis input
115
94
  //y2 - remove __dirname from tutorials
116
95
  //p2 - when value changes for field with error, clear error - so the user will feel comfortable
117
96
  //p2 - allowApiUpdate should be false for include in api false
@@ -39,6 +39,13 @@ export interface BackendMethodOptions<type> {
39
39
  * {allowed:true, apiPrefix:'someFolder/'}
40
40
  */
41
41
  apiPrefix?: string;
42
+ /**
43
+ * Controls whether this `BackendMethod` runs within a database transaction. If set to `true`, the method will either complete entirely or fail without making any partial changes. If set to `false`, the method will not be transactional and may result in partial changes if it fails.
44
+ * @default true
45
+ * @example
46
+ * {allowed: true, transactional: false}
47
+ */
48
+ transactional?: boolean;
42
49
  /** EXPERIMENTAL: Determines if this method should be queued for later execution */
43
50
  queue?: boolean;
44
51
  /** EXPERIMENTAL: Determines if the user should be blocked while this `BackendMethod` is running*/
@@ -53,7 +60,24 @@ export interface ClassMethodDecoratorContextStub<This = unknown, Value extends (
53
60
  has(object: This): boolean;
54
61
  };
55
62
  }
56
- /** Indicates that the decorated methods runs on the backend. See: [Backend Methods](https://remult.dev/docs/backendMethods.html) */
63
+ /**
64
+ * Decorator indicating that the decorated method runs on the backend.
65
+ * It allows the method to be invoked from the frontend while ensuring that the execution happens on the server side.
66
+ * By default, the method runs within a database transaction, meaning it will either complete entirely or fail without making any partial changes.
67
+ * This behavior can be controlled using the `transactional` option in the `BackendMethodOptions`.
68
+ *
69
+ * For more details, see: [Backend Methods](https://remult.dev/docs/backendMethods.html).
70
+ *
71
+ * @param options - Configuration options for the backend method, including permissions, routing, and transactional behavior.
72
+ *
73
+ * @example
74
+ * ```typescript
75
+ * @BackendMethod({ allowed: true })
76
+ * async someBackendMethod() {
77
+ * // method logic here
78
+ * }
79
+ * ```
80
+ */
57
81
  export declare function BackendMethod<type = unknown>(options: BackendMethodOptions<type>): (target: any, context: ClassMethodDecoratorContextStub<type> | string, descriptor?: any) => any;
58
82
  export interface jobWasQueuedResult {
59
83
  queuedJobId?: string;
@@ -143,7 +143,7 @@ var myServerAction = /** @class */ (function (_super) {
143
143
  case 0:
144
144
  result = { data: {} };
145
145
  ds = remult.dataProvider;
146
- return [4 /*yield*/, (0, context_js_1.doTransaction)(remult, function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
146
+ return [4 /*yield*/, decideTransaction(remult, this.options, function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
147
147
  var _a, _b, err_2;
148
148
  return tslib_1.__generator(this, function (_c) {
149
149
  switch (_c.label) {
@@ -188,7 +188,24 @@ function Controller(key) {
188
188
  return target;
189
189
  };
190
190
  }
191
- /** Indicates that the decorated methods runs on the backend. See: [Backend Methods](https://remult.dev/docs/backendMethods.html) */
191
+ /**
192
+ * Decorator indicating that the decorated method runs on the backend.
193
+ * It allows the method to be invoked from the frontend while ensuring that the execution happens on the server side.
194
+ * By default, the method runs within a database transaction, meaning it will either complete entirely or fail without making any partial changes.
195
+ * This behavior can be controlled using the `transactional` option in the `BackendMethodOptions`.
196
+ *
197
+ * For more details, see: [Backend Methods](https://remult.dev/docs/backendMethods.html).
198
+ *
199
+ * @param options - Configuration options for the backend method, including permissions, routing, and transactional behavior.
200
+ *
201
+ * @example
202
+ * ```typescript
203
+ * @BackendMethod({ allowed: true })
204
+ * async someBackendMethod() {
205
+ * // method logic here
206
+ * }
207
+ * ```
208
+ */
192
209
  function BackendMethod(options) {
193
210
  var _this = this;
194
211
  return function (target, context, descriptor) {
@@ -242,7 +259,7 @@ function BackendMethod(options) {
242
259
  return [4 /*yield*/, serverAction_1.doWork(args, undefined)];
243
260
  case 1: return [2 /*return*/, _a.sent()];
244
261
  case 2: return [4 /*yield*/, originalMethod.apply(
245
- //@ts-expect-error I specifically referred to the this of the original function - so it'll be sent inside
262
+ //@ts-ignore
246
263
  this, args)];
247
264
  case 3: return [2 /*return*/, _a.sent()];
248
265
  }
@@ -289,7 +306,7 @@ function BackendMethod(options) {
289
306
  case 1:
290
307
  _a.trys.push([1, 3, , 4]);
291
308
  remult_1 = req;
292
- return [4 /*yield*/, (0, context_js_1.doTransaction)(remult_1, function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
309
+ return [4 /*yield*/, decideTransaction(remult_1, options, function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
293
310
  var _a, repo, y, rowInfo, rowHelper, rows, defs, err_4, y, controllerRef, err_5;
294
311
  var _b, _c, _d;
295
312
  var _e;
@@ -645,3 +662,21 @@ function prepareReceivedArgs(types, args, remult, ds, res) {
645
662
  });
646
663
  }
647
664
  exports.classBackendMethodsArray = Symbol.for('classBackendMethodsArray');
665
+ //
666
+ function decideTransaction(remult, options, what) {
667
+ return tslib_1.__awaiter(this, void 0, void 0, function () {
668
+ return tslib_1.__generator(this, function (_a) {
669
+ switch (_a.label) {
670
+ case 0:
671
+ if (!(options.transactional === undefined || options.transactional === true)) return [3 /*break*/, 2];
672
+ return [4 /*yield*/, (0, context_js_1.doTransaction)(remult, what)];
673
+ case 1: return [2 /*return*/, _a.sent()];
674
+ case 2: return [4 /*yield*/, what(remult.dataProvider)];
675
+ case 3:
676
+ _a.sent();
677
+ _a.label = 4;
678
+ case 4: return [2 /*return*/];
679
+ }
680
+ });
681
+ });
682
+ }
@@ -47,4 +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 extends object>(enumObj: theEnum): any[];
50
+ export declare function getEnumValues<theEnum>(enumObj: theEnum): any[];