remult 0.26.6 → 0.26.8-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/esm/index.js +1 -1
  2. package/esm/remult-knex/index.js +6 -12
  3. package/esm/remult-mongo.js +2 -6
  4. package/esm/remult-sqlite-core.js +3 -1
  5. package/esm/remult-sqlite3.js +58 -0
  6. package/esm/server/expressBridge.js +4 -1
  7. package/esm/src/data-providers/sql-database.js +41 -5
  8. package/esm/src/filter/filter-interfaces.js +1 -1
  9. package/esm/src/remult3/RepositoryImplementation.js +1 -0
  10. package/esm/src/remult3/{DecoratorReplacer.js → classDescribers.js} +18 -3
  11. package/esm/src/remult3/getEntityRef.js +9 -0
  12. package/esm/src/remult3/remult3.js +14 -2
  13. package/index.d.ts +1 -1
  14. package/index.js +5 -3
  15. package/package.json +6 -1
  16. package/remult-knex/index.js +10 -17
  17. package/remult-mongo.js +3 -7
  18. package/remult-sqlite-core.d.ts +2 -1
  19. package/remult-sqlite-core.js +3 -1
  20. package/remult-sqlite3.d.ts +12 -0
  21. package/remult-sqlite3.js +74 -0
  22. package/server/expressBridge.js +15 -13
  23. package/src/column-interfaces.d.ts +63 -6
  24. package/src/data-providers/sql-database.d.ts +1 -0
  25. package/src/data-providers/sql-database.js +57 -6
  26. package/src/entity.d.ts +10 -4
  27. package/src/filter/filter-interfaces.js +1 -1
  28. package/src/remult3/RepositoryImplementation.js +1 -0
  29. package/src/remult3/classDescribers.d.ts +16 -0
  30. package/src/remult3/classDescribers.js +41 -0
  31. package/src/remult3/getEntityRef.d.ts +9 -0
  32. package/src/remult3/getEntityRef.js +9 -0
  33. package/src/remult3/remult3.d.ts +13 -2
  34. package/src/remult3/remult3.js +14 -2
  35. package/src/sql-command.d.ts +2 -0
  36. package/src/remult3/DecoratorReplacer.d.ts +0 -9
  37. package/src/remult3/DecoratorReplacer.js +0 -24
package/esm/index.js CHANGED
@@ -4,7 +4,7 @@ export { Entity } from './src/remult3/entity.js';
4
4
  export { getEntityRef } from './src/remult3/getEntityRef.js';
5
5
  export { Field, Fields, Relations, } from './src/remult3/Fields.js';
6
6
  export { IdEntity } from './src/remult3/IdEntity.js';
7
- export { describeClass } from './src/remult3/DecoratorReplacer.js';
7
+ export { describeClass, describeBackendMethods, describeEntity, } from './src/remult3/classDescribers.js';
8
8
  export { RestDataProvider } from './src/data-providers/rest-data-provider.js'; //V
9
9
  export { InMemoryDataProvider } from './src/data-providers/in-memory-database.js'; //V
10
10
  export { WebSqlDataProvider } from './src/data-providers/web-sql-data-provider.js'; //V
@@ -7,6 +7,7 @@ import { Sort } from '../src/sort.js';
7
7
  import { ValueConverters } from '../src/valueConverters.js';
8
8
  import { getRepositoryInternals } from '../src/remult3/repository-internals.js';
9
9
  import { remultStatic } from '../src/remult-static.js';
10
+ import { getRowAfterUpdate } from '../src/data-providers/sql-database.js';
10
11
  export class KnexDataProvider {
11
12
  knex;
12
13
  constructor(knex) {
@@ -196,9 +197,6 @@ class KnexEntityDataProvider {
196
197
  const e = await this.init();
197
198
  let f = new FilterConsumerBridgeToKnexRequest(e, this.rawSqlWrapIdentifier);
198
199
  Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(id)).__applyToConsumer(f);
199
- let resultFilter = this.entity.idMetadata.getIdFilter(id);
200
- if (data.id != undefined)
201
- resultFilter = this.entity.idMetadata.getIdFilter(data.id);
202
200
  let updateObject = {};
203
201
  for (const x of this.entity.fields) {
204
202
  if (isDbReadonly(x, e)) {
@@ -215,9 +213,7 @@ class KnexEntityDataProvider {
215
213
  await this.knex(e.$entityName)
216
214
  .update(updateObject)
217
215
  .where((b) => where.forEach((w) => w(b)));
218
- return this.find({
219
- where: Filter.fromEntityFilter(this.entity, resultFilter),
220
- }).then((y) => y[0]);
216
+ return getRowAfterUpdate(this.entity, this, data, id, 'update');
221
217
  }
222
218
  async delete(id) {
223
219
  const e = await this.init();
@@ -230,8 +226,6 @@ class KnexEntityDataProvider {
230
226
  }
231
227
  async insert(data) {
232
228
  const e = await this.init();
233
- let resultFilter;
234
- resultFilter = Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(data[this.entity.idMetadata.field.key]));
235
229
  let insertObject = {};
236
230
  for (const x of this.entity.fields) {
237
231
  if (isDbReadonly(x, e)) {
@@ -256,13 +250,13 @@ class KnexEntityDataProvider {
256
250
  let result = await insert.returning(this.entity.idMetadata.field.key);
257
251
  newId = result[0].id;
258
252
  }
259
- resultFilter = new Filter((x) => x.isEqualTo(this.entity.idMetadata.field, newId));
253
+ return this.find({
254
+ where: new Filter((x) => x.isEqualTo(this.entity.idMetadata.field, newId)),
255
+ }).then((y) => y[0]);
260
256
  }
261
257
  else
262
258
  await insert;
263
- return this.find({ where: resultFilter }).then((y) => {
264
- return y[0];
265
- });
259
+ return getRowAfterUpdate(this.entity, this, data, undefined, 'insert');
266
260
  }
267
261
  }
268
262
  class FilterConsumerBridgeToKnexRequest {
@@ -4,6 +4,7 @@ import { dbNamesOf } from './src/filter/filter-consumer-bridge-to-sql-request.js
4
4
  import { remult as remultContext } from './src/remult-proxy.js';
5
5
  import { getRepository } from './src/remult3/RepositoryImplementation.js';
6
6
  import { getRepositoryInternals } from './src/remult3/repository-internals.js';
7
+ import { getRowAfterUpdate } from './src/data-providers/sql-database.js';
7
8
  export class MongoDataProvider {
8
9
  db;
9
10
  client;
@@ -126,9 +127,6 @@ class MongoEntityDataProvider {
126
127
  let { collection, e } = await this.collection();
127
128
  let f = new FilterConsumerBridgeToMongo(e);
128
129
  Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(id)).__applyToConsumer(f);
129
- let resultFilter = this.entity.idMetadata.getIdFilter(id);
130
- if (data.id != undefined)
131
- resultFilter = this.entity.idMetadata.getIdFilter(data.id);
132
130
  let newR = {};
133
131
  let keys = Object.keys(data);
134
132
  for (const f of this.entity.fields) {
@@ -141,9 +139,7 @@ class MongoEntityDataProvider {
141
139
  let r = await collection.updateOne(await f.resolveWhere(), {
142
140
  $set: newR,
143
141
  }, { session: this.session });
144
- return this.find({
145
- where: Filter.fromEntityFilter(this.entity, resultFilter),
146
- }).then((y) => y[0]);
142
+ return getRowAfterUpdate(this.entity, this, data, id, 'update');
147
143
  }
148
144
  async delete(id) {
149
145
  const { e, collection } = await this.collection();
@@ -4,9 +4,11 @@ import { isAutoIncrement } from './src/remult3/RepositoryImplementation.js';
4
4
  export class SqliteCoreDataProvider {
5
5
  createCommand;
6
6
  end;
7
- constructor(createCommand, end) {
7
+ doesNotSupportReturningSyntax;
8
+ constructor(createCommand, end, doesNotSupportReturningSyntax = false) {
8
9
  this.createCommand = createCommand;
9
10
  this.end = end;
11
+ this.doesNotSupportReturningSyntax = doesNotSupportReturningSyntax;
10
12
  }
11
13
  orderByNullsFirst;
12
14
  getLimitSqlSyntax(limit, offset) {
@@ -0,0 +1,58 @@
1
+ import { SqliteCoreDataProvider } from '../core/remult-sqlite-core.js';
2
+ export class Sqlite3DataProvider extends SqliteCoreDataProvider {
3
+ constructor(db) {
4
+ super(() => new Sqlite3Command(db), async () => {
5
+ db.close();
6
+ }, true);
7
+ }
8
+ }
9
+ class Sqlite3Command {
10
+ db;
11
+ values = {};
12
+ i = 1;
13
+ constructor(db) {
14
+ this.db = db;
15
+ }
16
+ async execute(sql) {
17
+ return new Promise((resolve, error) => {
18
+ if (sql.startsWith('insert into')) {
19
+ this.db.run(sql, this.values, function (err, rows) {
20
+ if (err)
21
+ error(err);
22
+ else
23
+ resolve(new Sqlite3SqlResult([this.lastID]));
24
+ });
25
+ }
26
+ else
27
+ this.db.all(sql, this.values, (err, rows) => {
28
+ if (err)
29
+ error(err);
30
+ else
31
+ resolve(new Sqlite3SqlResult(rows));
32
+ });
33
+ });
34
+ }
35
+ addParameterAndReturnSqlToken(val) {
36
+ return this.param(val);
37
+ }
38
+ param(val) {
39
+ if (val instanceof Date)
40
+ val = val.valueOf();
41
+ if (typeof val === 'boolean')
42
+ val = val ? 1 : 0;
43
+ const key = ':' + this.i++;
44
+ this.values[key.substring(1)] = val;
45
+ return key;
46
+ }
47
+ }
48
+ export class Sqlite3SqlResult {
49
+ result;
50
+ constructor(result) {
51
+ this.result = result;
52
+ this.rows = result;
53
+ }
54
+ rows;
55
+ getColumnKeyInResultForIndexInSelect(index) {
56
+ return Object.keys(this.result[0])[index];
57
+ }
58
+ }
@@ -266,11 +266,14 @@ export class RemultServerImplementation {
266
266
  }
267
267
  //runs with remult but without init request
268
268
  async runWithRemult(what, options) {
269
+ let dataProvider;
270
+ if (!options?.skipDataProvider)
271
+ dataProvider = await this.dataProvider;
269
272
  return await withRemult(async (remult) => {
270
273
  var x = remult;
271
274
  x.liveQueryPublisher = new LiveQueryPublisher(() => remult.subscriptionServer, () => remult.liveQueryStorage, this.runWithSerializedJsonContextData);
272
275
  if (!options?.skipDataProvider)
273
- x.dataProvider = await this.dataProvider;
276
+ x.dataProvider = dataProvider;
274
277
  x.subscriptionServer = this.subscriptionServer;
275
278
  x.liveQueryStorage = this.liveQueryStorage;
276
279
  return await what(x);
@@ -1,7 +1,7 @@
1
1
  import { FilterConsumerBridgeToSqlRequest, dbNamesOf, isDbReadonly, } from '../filter/filter-consumer-bridge-to-sql-request.js';
2
2
  import { Filter, customDatabaseFilterToken, } from '../filter/filter-interfaces.js';
3
3
  import { remult as defaultRemult } from '../remult-proxy.js';
4
- import { getRepository } from '../remult3/RepositoryImplementation.js';
4
+ import { getRepository, isAutoIncrement, } from '../remult3/RepositoryImplementation.js';
5
5
  import { Sort } from '../sort.js';
6
6
  import { ValueConverters } from '../valueConverters.js';
7
7
  import { getRepositoryInternals } from '../remult3/repository-internals.js';
@@ -323,8 +323,6 @@ class ActualSQLServerDataProvider {
323
323
  async update(id, data) {
324
324
  let e = await this.init();
325
325
  let r = this.sql.createCommand();
326
- let f = new FilterConsumerBridgeToSqlRequest(r, e);
327
- Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(id)).__applyToConsumer(f);
328
326
  let statement = 'update ' + e.$entityName + ' set ';
329
327
  let added = false;
330
328
  for (const x of this.entity.fields) {
@@ -341,11 +339,18 @@ class ActualSQLServerDataProvider {
341
339
  }
342
340
  }
343
341
  }
342
+ const idFilter = this.entity.idMetadata.getIdFilter(id);
343
+ let f = new FilterConsumerBridgeToSqlRequest(r, e);
344
+ Filter.fromEntityFilter(this.entity, idFilter).__applyToConsumer(f);
344
345
  statement += await f.resolveWhere();
345
346
  let { colKeys, select } = this.buildSelect(e);
346
- statement += ' returning ' + select;
347
+ if (!this.sql._getSourceSql().doesNotSupportReturningSyntax)
348
+ statement += ' returning ' + select;
347
349
  return r.execute(statement).then((sqlResult) => {
348
350
  this.sql._getSourceSql().afterMutation?.();
351
+ if (this.sql._getSourceSql().doesNotSupportReturningSyntax) {
352
+ return getRowAfterUpdate(this.entity, this, data, id, 'update');
353
+ }
349
354
  if (sqlResult.rows.length != 1)
350
355
  throw new Error('Failed to update row with id ' +
351
356
  id +
@@ -390,9 +395,24 @@ class ActualSQLServerDataProvider {
390
395
  }
391
396
  let statement = `insert into ${e.$entityName} (${cols}) values (${vals})`;
392
397
  let { colKeys, select } = this.buildSelect(e);
393
- statement += ' returning ' + select;
398
+ if (!this.sql._getSourceSql().doesNotSupportReturningSyntax)
399
+ statement += ' returning ' + select;
394
400
  return await r.execute(statement).then((sql) => {
395
401
  this.sql._getSourceSql().afterMutation?.();
402
+ if (this.sql._getSourceSql().doesNotSupportReturningSyntax) {
403
+ if (isAutoIncrement(this.entity.idMetadata.field)) {
404
+ const id = sql.rows[0];
405
+ if (typeof id !== 'number')
406
+ throw new Error('Auto increment, for a database that is does not support returning syntax, should return an array with the single last added id. Instead it returned: ' +
407
+ JSON.stringify(id));
408
+ return this.find({
409
+ where: new Filter((x) => x.isEqualTo(this.entity.idMetadata.field, id)),
410
+ }).then((r) => r[0]);
411
+ }
412
+ else {
413
+ return getRowAfterUpdate(this.entity, this, data, undefined, 'insert');
414
+ }
415
+ }
396
416
  return this.buildResultRow(colKeys, sql.rows[0], sql);
397
417
  });
398
418
  }
@@ -440,3 +460,19 @@ async function bulkInsert(array, db) {
440
460
  await c.execute(sql);
441
461
  }
442
462
  }
463
+ export function getRowAfterUpdate(meta, dataProvider, data, id, operation) {
464
+ const idFilter = id !== undefined ? meta.idMetadata.getIdFilter(id) : {};
465
+ return dataProvider
466
+ .find({
467
+ where: new Filter((x) => {
468
+ for (const field of meta.idMetadata.fields) {
469
+ x.isEqualTo(field, data[field.key] ?? idFilter[field.key]);
470
+ }
471
+ }),
472
+ })
473
+ .then((r) => {
474
+ if (r.length != 1)
475
+ throw new Error(`Failed to ${operation} row - result contained ${r.length} rows`);
476
+ return r[0];
477
+ });
478
+ }
@@ -304,7 +304,7 @@ class filterHelper {
304
304
  processVal(val) {
305
305
  let ei = getEntitySettings(this.metadata.valueType, false);
306
306
  if (ei) {
307
- if (!val) {
307
+ if (val === undefined || val === null) {
308
308
  if (val === null && !this.metadata.allowNull) {
309
309
  const rel = getRelationFieldInfo(this.metadata);
310
310
  if (rel?.type === 'reference')
@@ -1312,6 +1312,7 @@ export class rowHelperImplementation extends rowHelperBase {
1312
1312
  }
1313
1313
  }
1314
1314
  await this.loadDataFrom(updatedRow);
1315
+ e.id = this.getId();
1315
1316
  if (!this.repository._dataProvider.isProxy) {
1316
1317
  if (this.info.entityInfo.saved)
1317
1318
  await this.info.entityInfo.saved(this.instance, e);
@@ -1,6 +1,8 @@
1
- export function describeClass(classType, classDecorator, members, staticMembers) {
2
- if (classDecorator)
3
- classDecorator(classType);
1
+ import { BackendMethod } from '../server-action.js';
2
+ import { Entity } from './entity.js';
3
+ export function describeClass(classType, classDescriber, members, staticMembers) {
4
+ if (classDescriber)
5
+ classDescriber(classType);
4
6
  for (const fieldKey in members) {
5
7
  if (Object.prototype.hasOwnProperty.call(members, fieldKey)) {
6
8
  const element = members[fieldKey];
@@ -18,3 +20,16 @@ export function describeClass(classType, classDecorator, members, staticMembers)
18
20
  Object.defineProperty(classType, staticFieldKey, prop);
19
21
  }
20
22
  }
23
+ export function describeBackendMethods(classType, backendMethods) {
24
+ let result = {};
25
+ for (const key in backendMethods) {
26
+ if (Object.prototype.hasOwnProperty.call(backendMethods, key)) {
27
+ const options = backendMethods[key];
28
+ result[key] = BackendMethod(options);
29
+ }
30
+ }
31
+ describeClass(classType, undefined, undefined, result);
32
+ }
33
+ export function describeEntity(classType, key, fields, options) {
34
+ describeClass(classType, Entity(key, options), fields);
35
+ }
@@ -1,3 +1,12 @@
1
+ /**
2
+ * Retrieves the EntityRef object associated with the specified entity instance.
3
+ * The EntityRef provides methods for performing operations on the entity instance.
4
+ * @param {entityType} entity - The entity instance.
5
+ * @param {boolean} [throwException=true] - Indicates whether to throw an exception if the EntityRef object cannot be retrieved.
6
+ * @returns {EntityRef<entityType>} The EntityRef object associated with the specified entity instance.
7
+ * @throws {Error} If throwException is true and the EntityRef object cannot be retrieved.
8
+ * @see [Active Record & EntityBase](https://remult.dev/docs/active-record)
9
+ */
1
10
  export function getEntityRef(entity, throwException = true) {
2
11
  let x = entity[entityMember];
3
12
  if (!x && throwException)
@@ -1,7 +1,20 @@
1
1
  export {};
2
+ //p1 - remove controller and instance backend methods from the regular backend method article
3
+ //p1 - describe entity
4
+ //p1 - fix includeInApi documentation - figure out why we can't see the see?
2
5
  //y2 - soft-delete-discussion https://discord.com/channels/975754286384418847/1230386433093533698/1230386433093533698
3
- //y1 - In the esm version of our tutorial - the imports are automatically .ts and not .js
6
+ //y1 - In the esm version of our tutorial - the imports are automatically .ts and not .js in react and not in vue
7
+ //y1 - talk about insert with relations
4
8
  //y1 - consider id to also support keyof (id:['company','index']) - had problem with | (keyof Partial<entityType>)[] & `entity`
9
+ //y1 - change exp version to preview?
10
+ //y1 - talk about insert / update / delete with relations
11
+ /*
12
+ repo(Order).insert({},{
13
+ relations:{
14
+ orderItems:[{},{},{}]
15
+ }
16
+ })
17
+ */
5
18
  //y1 - repo batch - for multiple operations:
6
19
  /*
7
20
  const result = await repo.batch(x=>({
@@ -12,7 +25,6 @@ export {};
12
25
  //p1 - add documentation for bun sqlite
13
26
  //p1 - consider upsert (where,set)
14
27
  //p1 - wait a second to close stream
15
- //p1 - describe entity
16
28
  //p1 - prepare the createEntity discussion
17
29
  //p1 - write active record article
18
30
  //p1 - return error on connection error
package/index.d.ts CHANGED
@@ -9,7 +9,7 @@ export { Entity } from './src/remult3/entity.js';
9
9
  export { getEntityRef } from './src/remult3/getEntityRef.js';
10
10
  export { Field, Fields, StringFieldOptions, Relations, } from './src/remult3/Fields.js';
11
11
  export { IdEntity } from './src/remult3/IdEntity.js';
12
- export { describeClass } from './src/remult3/DecoratorReplacer.js';
12
+ export { describeClass, describeBackendMethods, describeEntity, } from './src/remult3/classDescribers.js';
13
13
  export { EntityOptions, PreprocessFilterEvent } from './src/entity.js';
14
14
  export { DataProvider, EntityDataProvider, EntityDataProviderFindOptions, ErrorInfo, RestDataProviderHttpProvider, } from './src/data-interfaces.js';
15
15
  export { SqlCommand, SqlCommandWithParameters, SqlImplementation, SqlResult, } from './src/sql-command.js';
package/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.repo = exports.SubscriptionChannel = exports.InMemoryLiveQueryStorage = exports.remult = exports.ValueConverters = exports.createValueValidator = exports.createValueValidatorWithArgs = exports.createValidatorWithArgs = exports.createValidator = exports.valueValidator = exports.Validators = exports.UrlBuilder = exports.Filter = exports.CompoundIdField = exports.Sort = exports.EventSource = exports.isBackend = exports.withRemult = exports.Remult = exports.Allow = exports.ForbiddenError = exports.ProgressListener = exports.BackendMethod = exports.Controller = exports.JsonEntityOpfsStorage = exports.JsonDataProvider = exports.dbNamesOf = exports.CustomSqlFilterBuilder = exports.SqlDatabase = exports.WebSqlDataProvider = exports.InMemoryDataProvider = exports.RestDataProvider = exports.describeClass = exports.IdEntity = exports.Relations = exports.Fields = exports.Field = exports.getEntityRef = exports.Entity = exports.CaptionTransformer = exports.ValueListInfo = exports.getValueList = exports.ValueListFieldType = exports.getFields = exports.FieldType = exports.ControllerBase = exports.EntityBase = exports.ArrayEntityDataProvider = void 0;
3
+ exports.repo = exports.SubscriptionChannel = exports.InMemoryLiveQueryStorage = exports.remult = exports.ValueConverters = exports.createValueValidator = exports.createValueValidatorWithArgs = exports.createValidatorWithArgs = exports.createValidator = exports.valueValidator = exports.Validators = exports.UrlBuilder = exports.Filter = exports.CompoundIdField = exports.Sort = exports.EventSource = exports.isBackend = exports.withRemult = exports.Remult = exports.Allow = exports.ForbiddenError = exports.ProgressListener = exports.BackendMethod = exports.Controller = exports.JsonEntityOpfsStorage = exports.JsonDataProvider = exports.dbNamesOf = exports.CustomSqlFilterBuilder = exports.SqlDatabase = exports.WebSqlDataProvider = exports.InMemoryDataProvider = exports.RestDataProvider = exports.describeEntity = exports.describeBackendMethods = exports.describeClass = exports.IdEntity = exports.Relations = exports.Fields = exports.Field = exports.getEntityRef = exports.Entity = exports.CaptionTransformer = exports.ValueListInfo = exports.getValueList = exports.ValueListFieldType = exports.getFields = exports.FieldType = exports.ControllerBase = exports.EntityBase = exports.ArrayEntityDataProvider = void 0;
4
4
  var array_entity_data_provider_js_1 = require("./src/data-providers/array-entity-data-provider.js");
5
5
  Object.defineProperty(exports, "ArrayEntityDataProvider", { enumerable: true, get: function () { return array_entity_data_provider_js_1.ArrayEntityDataProvider; } });
6
6
  var RepositoryImplementation_js_1 = require("./src/remult3/RepositoryImplementation.js");
@@ -22,8 +22,10 @@ Object.defineProperty(exports, "Fields", { enumerable: true, get: function () {
22
22
  Object.defineProperty(exports, "Relations", { enumerable: true, get: function () { return Fields_js_1.Relations; } });
23
23
  var IdEntity_js_1 = require("./src/remult3/IdEntity.js");
24
24
  Object.defineProperty(exports, "IdEntity", { enumerable: true, get: function () { return IdEntity_js_1.IdEntity; } });
25
- var DecoratorReplacer_js_1 = require("./src/remult3/DecoratorReplacer.js");
26
- Object.defineProperty(exports, "describeClass", { enumerable: true, get: function () { return DecoratorReplacer_js_1.describeClass; } });
25
+ var classDescribers_js_1 = require("./src/remult3/classDescribers.js");
26
+ Object.defineProperty(exports, "describeClass", { enumerable: true, get: function () { return classDescribers_js_1.describeClass; } });
27
+ Object.defineProperty(exports, "describeBackendMethods", { enumerable: true, get: function () { return classDescribers_js_1.describeBackendMethods; } });
28
+ Object.defineProperty(exports, "describeEntity", { enumerable: true, get: function () { return classDescribers_js_1.describeEntity; } });
27
29
  var rest_data_provider_js_1 = require("./src/data-providers/rest-data-provider.js"); //V
28
30
  Object.defineProperty(exports, "RestDataProvider", { enumerable: true, get: function () { return rest_data_provider_js_1.RestDataProvider; } });
29
31
  var in_memory_database_js_1 = require("./src/data-providers/in-memory-database.js"); //V
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remult",
3
- "version": "0.26.6",
3
+ "version": "0.26.8-next.0",
4
4
  "description": "A CRUD framework for full-stack TypeScript",
5
5
  "homepage": "https://remult.dev",
6
6
  "repository": {
@@ -111,6 +111,11 @@
111
111
  "import": "./esm/remult-better-sqlite3.js",
112
112
  "types": "./remult-better-sqlite3.d.ts"
113
113
  },
114
+ "./remult-sqlite3": {
115
+ "require": "./remult-sqlite3.js",
116
+ "import": "./esm/remult-sqlite3.js",
117
+ "types": "./remult-sqlite3.d.ts"
118
+ },
114
119
  "./remult-bun-sqlite": {
115
120
  "require": "./remult-bun-sqlite.js",
116
121
  "import": "./esm/remult-bun-sqlite.js",
@@ -11,6 +11,7 @@ var sort_js_1 = require("../src/sort.js");
11
11
  var valueConverters_js_1 = require("../src/valueConverters.js");
12
12
  var repository_internals_js_1 = require("../src/remult3/repository-internals.js");
13
13
  var remult_static_js_1 = require("../src/remult-static.js");
14
+ var sql_database_js_1 = require("../src/data-providers/sql-database.js");
14
15
  var KnexDataProvider = /** @class */ (function () {
15
16
  function KnexDataProvider(knex) {
16
17
  var _this = this;
@@ -326,7 +327,7 @@ var KnexEntityDataProvider = /** @class */ (function () {
326
327
  };
327
328
  KnexEntityDataProvider.prototype.update = function (id, data) {
328
329
  return tslib_1.__awaiter(this, void 0, void 0, function () {
329
- var e, f, resultFilter, updateObject, _a, _b, x, v, key, e_4_1, where;
330
+ var e, f, updateObject, _a, _b, x, v, key, e_4_1, where;
330
331
  var e_4, _c;
331
332
  return tslib_1.__generator(this, function (_d) {
332
333
  switch (_d.label) {
@@ -335,9 +336,6 @@ var KnexEntityDataProvider = /** @class */ (function () {
335
336
  e = _d.sent();
336
337
  f = new FilterConsumerBridgeToKnexRequest(e, this.rawSqlWrapIdentifier);
337
338
  filter_interfaces_js_1.Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(id)).__applyToConsumer(f);
338
- resultFilter = this.entity.idMetadata.getIdFilter(id);
339
- if (data.id != undefined)
340
- resultFilter = this.entity.idMetadata.getIdFilter(data.id);
341
339
  updateObject = {};
342
340
  _d.label = 2;
343
341
  case 2:
@@ -380,9 +378,7 @@ var KnexEntityDataProvider = /** @class */ (function () {
380
378
  .where(function (b) { return where.forEach(function (w) { return w(b); }); })];
381
379
  case 12:
382
380
  _d.sent();
383
- return [2 /*return*/, this.find({
384
- where: filter_interfaces_js_1.Filter.fromEntityFilter(this.entity, resultFilter),
385
- }).then(function (y) { return y[0]; })];
381
+ return [2 /*return*/, (0, sql_database_js_1.getRowAfterUpdate)(this.entity, this, data, id, 'update')];
386
382
  }
387
383
  });
388
384
  });
@@ -412,7 +408,7 @@ var KnexEntityDataProvider = /** @class */ (function () {
412
408
  };
413
409
  KnexEntityDataProvider.prototype.insert = function (data) {
414
410
  return tslib_1.__awaiter(this, void 0, void 0, function () {
415
- var e, resultFilter, insertObject, _a, _b, x, v, key, e_5_1, insert, newId_1, result, result;
411
+ var e, insertObject, _a, _b, x, v, key, e_5_1, insert, newId_1, result, result;
416
412
  var e_5, _c;
417
413
  var _this = this;
418
414
  return tslib_1.__generator(this, function (_d) {
@@ -420,7 +416,6 @@ var KnexEntityDataProvider = /** @class */ (function () {
420
416
  case 0: return [4 /*yield*/, this.init()];
421
417
  case 1:
422
418
  e = _d.sent();
423
- resultFilter = filter_interfaces_js_1.Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(data[this.entity.idMetadata.field.key]));
424
419
  insertObject = {};
425
420
  _d.label = 2;
426
421
  case 2:
@@ -469,18 +464,16 @@ var KnexEntityDataProvider = /** @class */ (function () {
469
464
  result = _d.sent();
470
465
  newId_1 = result[0].id;
471
466
  _d.label = 14;
472
- case 14:
473
- resultFilter = new filter_interfaces_js_1.Filter(function (x) {
474
- return x.isEqualTo(_this.entity.idMetadata.field, newId_1);
475
- });
476
- return [3 /*break*/, 17];
467
+ case 14: return [2 /*return*/, this.find({
468
+ where: new filter_interfaces_js_1.Filter(function (x) {
469
+ return x.isEqualTo(_this.entity.idMetadata.field, newId_1);
470
+ }),
471
+ }).then(function (y) { return y[0]; })];
477
472
  case 15: return [4 /*yield*/, insert];
478
473
  case 16:
479
474
  _d.sent();
480
475
  _d.label = 17;
481
- case 17: return [2 /*return*/, this.find({ where: resultFilter }).then(function (y) {
482
- return y[0];
483
- })];
476
+ case 17: return [2 /*return*/, (0, sql_database_js_1.getRowAfterUpdate)(this.entity, this, data, undefined, 'insert')];
484
477
  }
485
478
  });
486
479
  });
package/remult-mongo.js CHANGED
@@ -8,6 +8,7 @@ var filter_consumer_bridge_to_sql_request_js_1 = require("./src/filter/filter-co
8
8
  var remult_proxy_js_1 = require("./src/remult-proxy.js");
9
9
  var RepositoryImplementation_js_1 = require("./src/remult3/RepositoryImplementation.js");
10
10
  var repository_internals_js_1 = require("./src/remult3/repository-internals.js");
11
+ var sql_database_js_1 = require("./src/data-providers/sql-database.js");
11
12
  var MongoDataProvider = /** @class */ (function () {
12
13
  function MongoDataProvider(db, client, options) {
13
14
  this.db = db;
@@ -221,7 +222,7 @@ var MongoEntityDataProvider = /** @class */ (function () {
221
222
  };
222
223
  MongoEntityDataProvider.prototype.update = function (id, data) {
223
224
  return tslib_1.__awaiter(this, void 0, void 0, function () {
224
- var _a, collection, e, f, resultFilter, newR, keys, _b, _c, f_1, r, _d, _e;
225
+ var _a, collection, e, f, newR, keys, _b, _c, f_1, r, _d, _e;
225
226
  var e_4, _f;
226
227
  return tslib_1.__generator(this, function (_g) {
227
228
  switch (_g.label) {
@@ -230,9 +231,6 @@ var MongoEntityDataProvider = /** @class */ (function () {
230
231
  _a = _g.sent(), collection = _a.collection, e = _a.e;
231
232
  f = new FilterConsumerBridgeToMongo(e);
232
233
  index_js_1.Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(id)).__applyToConsumer(f);
233
- resultFilter = this.entity.idMetadata.getIdFilter(id);
234
- if (data.id != undefined)
235
- resultFilter = this.entity.idMetadata.getIdFilter(data.id);
236
234
  newR = {};
237
235
  keys = Object.keys(data);
238
236
  try {
@@ -260,9 +258,7 @@ var MongoEntityDataProvider = /** @class */ (function () {
260
258
  { session: this.session }])];
261
259
  case 3:
262
260
  r = _g.sent();
263
- return [2 /*return*/, this.find({
264
- where: index_js_1.Filter.fromEntityFilter(this.entity, resultFilter),
265
- }).then(function (y) { return y[0]; })];
261
+ return [2 /*return*/, (0, sql_database_js_1.getRowAfterUpdate)(this.entity, this, data, id, 'update')];
266
262
  }
267
263
  });
268
264
  });
@@ -4,7 +4,8 @@ import type { CanBuildMigrations, MigrationBuilder, MigrationCode } from './migr
4
4
  export declare class SqliteCoreDataProvider implements SqlImplementation, CanBuildMigrations {
5
5
  createCommand: () => SqlCommand;
6
6
  end: () => Promise<void>;
7
- constructor(createCommand: () => SqlCommand, end: () => Promise<void>);
7
+ doesNotSupportReturningSyntax: boolean;
8
+ constructor(createCommand: () => SqlCommand, end: () => Promise<void>, doesNotSupportReturningSyntax?: boolean);
8
9
  orderByNullsFirst?: boolean;
9
10
  getLimitSqlSyntax(limit: number, offset: number): string;
10
11
  afterMutation?: VoidFunction;
@@ -6,9 +6,11 @@ var sql_database_js_1 = require("./src/data-providers/sql-database.js");
6
6
  var filter_consumer_bridge_to_sql_request_js_1 = require("./src/filter/filter-consumer-bridge-to-sql-request.js");
7
7
  var RepositoryImplementation_js_1 = require("./src/remult3/RepositoryImplementation.js");
8
8
  var SqliteCoreDataProvider = /** @class */ (function () {
9
- function SqliteCoreDataProvider(createCommand, end) {
9
+ function SqliteCoreDataProvider(createCommand, end, doesNotSupportReturningSyntax) {
10
+ if (doesNotSupportReturningSyntax === void 0) { doesNotSupportReturningSyntax = false; }
10
11
  this.createCommand = createCommand;
11
12
  this.end = end;
13
+ this.doesNotSupportReturningSyntax = doesNotSupportReturningSyntax;
12
14
  }
13
15
  SqliteCoreDataProvider.prototype.getLimitSqlSyntax = function (limit, offset) {
14
16
  return ' limit ' + limit + ' offset ' + offset;
@@ -0,0 +1,12 @@
1
+ import { SqlResult } from '../core/index.js';
2
+ import { SqliteCoreDataProvider } from '../core/remult-sqlite-core.js';
3
+ import { Database } from 'sqlite3';
4
+ export declare class Sqlite3DataProvider extends SqliteCoreDataProvider {
5
+ constructor(db: Database);
6
+ }
7
+ export declare class Sqlite3SqlResult implements SqlResult {
8
+ private result;
9
+ constructor(result: any[]);
10
+ rows: any[];
11
+ getColumnKeyInResultForIndexInSelect(index: number): string;
12
+ }
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Sqlite3SqlResult = exports.Sqlite3DataProvider = void 0;
4
+ var tslib_1 = require("tslib");
5
+ var remult_sqlite_core_js_1 = require("../core/remult-sqlite-core.js");
6
+ var Sqlite3DataProvider = /** @class */ (function (_super) {
7
+ tslib_1.__extends(Sqlite3DataProvider, _super);
8
+ function Sqlite3DataProvider(db) {
9
+ var _this = _super.call(this, function () { return new Sqlite3Command(db); }, function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
10
+ return tslib_1.__generator(this, function (_a) {
11
+ db.close();
12
+ return [2 /*return*/];
13
+ });
14
+ }); }, true) || this;
15
+ return _this;
16
+ }
17
+ return Sqlite3DataProvider;
18
+ }(remult_sqlite_core_js_1.SqliteCoreDataProvider));
19
+ exports.Sqlite3DataProvider = Sqlite3DataProvider;
20
+ var Sqlite3Command = /** @class */ (function () {
21
+ function Sqlite3Command(db) {
22
+ this.db = db;
23
+ this.values = {};
24
+ this.i = 1;
25
+ }
26
+ Sqlite3Command.prototype.execute = function (sql) {
27
+ return tslib_1.__awaiter(this, void 0, void 0, function () {
28
+ var _this = this;
29
+ return tslib_1.__generator(this, function (_a) {
30
+ return [2 /*return*/, new Promise(function (resolve, error) {
31
+ if (sql.startsWith('insert into')) {
32
+ _this.db.run(sql, _this.values, function (err, rows) {
33
+ if (err)
34
+ error(err);
35
+ else
36
+ resolve(new Sqlite3SqlResult([this.lastID]));
37
+ });
38
+ }
39
+ else
40
+ _this.db.all(sql, _this.values, function (err, rows) {
41
+ if (err)
42
+ error(err);
43
+ else
44
+ resolve(new Sqlite3SqlResult(rows));
45
+ });
46
+ })];
47
+ });
48
+ });
49
+ };
50
+ Sqlite3Command.prototype.addParameterAndReturnSqlToken = function (val) {
51
+ return this.param(val);
52
+ };
53
+ Sqlite3Command.prototype.param = function (val) {
54
+ if (val instanceof Date)
55
+ val = val.valueOf();
56
+ if (typeof val === 'boolean')
57
+ val = val ? 1 : 0;
58
+ var key = ':' + this.i++;
59
+ this.values[key.substring(1)] = val;
60
+ return key;
61
+ };
62
+ return Sqlite3Command;
63
+ }());
64
+ var Sqlite3SqlResult = /** @class */ (function () {
65
+ function Sqlite3SqlResult(result) {
66
+ this.result = result;
67
+ this.rows = result;
68
+ }
69
+ Sqlite3SqlResult.prototype.getColumnKeyInResultForIndexInSelect = function (index) {
70
+ return Object.keys(this.result[0])[index];
71
+ };
72
+ return Sqlite3SqlResult;
73
+ }());
74
+ exports.Sqlite3SqlResult = Sqlite3SqlResult;
@@ -499,31 +499,33 @@ var RemultServerImplementation = /** @class */ (function () {
499
499
  //runs with remult but without init request
500
500
  RemultServerImplementation.prototype.runWithRemult = function (what, options) {
501
501
  return tslib_1.__awaiter(this, void 0, void 0, function () {
502
+ var dataProvider;
502
503
  var _this = this;
503
504
  return tslib_1.__generator(this, function (_a) {
504
505
  switch (_a.label) {
505
- case 0: return [4 /*yield*/, (0, context_js_1.withRemult)(function (remult) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
506
- var x, _a;
507
- return tslib_1.__generator(this, function (_b) {
508
- switch (_b.label) {
506
+ case 0:
507
+ if (!!(options === null || options === void 0 ? void 0 : options.skipDataProvider)) return [3 /*break*/, 2];
508
+ return [4 /*yield*/, this.dataProvider];
509
+ case 1:
510
+ dataProvider = _a.sent();
511
+ _a.label = 2;
512
+ case 2: return [4 /*yield*/, (0, context_js_1.withRemult)(function (remult) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
513
+ var x;
514
+ return tslib_1.__generator(this, function (_a) {
515
+ switch (_a.label) {
509
516
  case 0:
510
517
  x = remult;
511
518
  x.liveQueryPublisher = new SubscriptionServer_js_1.LiveQueryPublisher(function () { return remult.subscriptionServer; }, function () { return remult.liveQueryStorage; }, this.runWithSerializedJsonContextData);
512
- if (!!(options === null || options === void 0 ? void 0 : options.skipDataProvider)) return [3 /*break*/, 2];
513
- _a = x;
514
- return [4 /*yield*/, this.dataProvider];
515
- case 1:
516
- _a.dataProvider = _b.sent();
517
- _b.label = 2;
518
- case 2:
519
+ if (!(options === null || options === void 0 ? void 0 : options.skipDataProvider))
520
+ x.dataProvider = dataProvider;
519
521
  x.subscriptionServer = this.subscriptionServer;
520
522
  x.liveQueryStorage = this.liveQueryStorage;
521
523
  return [4 /*yield*/, what(x)];
522
- case 3: return [2 /*return*/, _b.sent()];
524
+ case 1: return [2 /*return*/, _a.sent()];
523
525
  }
524
526
  });
525
527
  }); })];
526
- case 1: return [2 /*return*/, _a.sent()];
528
+ case 3: return [2 /*return*/, _a.sent()];
527
529
  }
528
530
  });
529
531
  });
@@ -11,11 +11,32 @@ export interface FieldOptions<entityType = any, valueType = any> {
11
11
  allowNull?: boolean;
12
12
  /** If a value is required */
13
13
  required?: boolean;
14
- /** If this field data is included in the api.
15
- * @see [allowed](http://remult.dev/docs/allowed.html)*/
14
+ /**
15
+ * Specifies whether this field should be included in the API. This can be configured
16
+ * based on access control levels.
17
+ * @example
18
+ * // Do not include in the API
19
+ * @Fields.string({ includeInApi: false })
20
+ * password = '';
21
+ * // Include in the API for 'admin' only
22
+ * @Fields.number({ includeInApi: 'admin' })
23
+ * salary = 0;
24
+ * @see [allowed](https://remult.dev/docs/allowed.html)
25
+ * @see [Access Control](https://remult.dev/docs/access-control)
26
+ * @type {AllowedForInstance<entityType>}
27
+ */
16
28
  includeInApi?: AllowedForInstance<entityType>;
17
- /** If this field data can be updated in the api.
18
- * @see [allowed](http://remult.dev/docs/allowed.html)*/
29
+ /**
30
+ * Determines whether this field can be updated via the API. This setting can also
31
+ * be controlled based on user roles or other access control checks.
32
+ * @example
33
+ * // Prevent API from updating this field
34
+ * @Fields.string({ allowApiUpdate: false })
35
+ * createdBy = remult.user?.id;
36
+ * @see [allowed](https://remult.dev/docs/allowed.html)
37
+ * @see [Access Control](https://remult.dev/docs/access-control)
38
+ * @type {AllowedForInstance<entityType>}
39
+ */
19
40
  allowApiUpdate?: AllowedForInstance<entityType>;
20
41
  /** An arrow function that'll be used to perform validations on it
21
42
  * @example
@@ -109,6 +130,7 @@ export interface FieldMetadata<valueType = any, entityType = any> {
109
130
  /** A human readable caption for the field. Can be used to achieve a consistent caption for a field throughout the app
110
131
  * @example
111
132
  * <input placeholder={taskRepo.metadata.fields.title.caption}/>
133
+ * @see {@link FieldOptions#caption} for configuration details
112
134
  */
113
135
  readonly caption: string;
114
136
  /** The name of the column in the database that holds the data for this field. If no name is set, the key will be used instead.
@@ -116,6 +138,7 @@ export interface FieldMetadata<valueType = any, entityType = any> {
116
138
  *
117
139
  * @Fields.string({ dbName: 'userName'})
118
140
  * userName=''
141
+ * @see {@link FieldOptions#dbName} for configuration details
119
142
  */
120
143
  dbName: string;
121
144
  /** The field's value type (number,string etc...) */
@@ -124,7 +147,10 @@ export interface FieldMetadata<valueType = any, entityType = any> {
124
147
  readonly options: FieldOptions;
125
148
  /** The `inputType` relevant for this field, determined by the options sent to it's decorator and the valueConverter in these options */
126
149
  readonly inputType: string;
127
- /** if null is allowed for this field */
150
+ /** if null is allowed for this field
151
+ * @see {@link FieldOptions#allowNull} for configuration details
152
+ *
153
+ */
128
154
  readonly allowNull: boolean;
129
155
  /** The class that contains this field
130
156
  * @example
@@ -137,16 +163,45 @@ export interface FieldMetadata<valueType = any, entityType = any> {
137
163
  getDbName(): Promise<string>;
138
164
  /** Indicates if this field is based on a server express */
139
165
  readonly isServerExpression: boolean;
140
- /** indicates that this field should only be included in select statement, and excluded from update or insert. useful for db generated ids etc... */
166
+ /** indicates that this field should only be included in select statement, and excluded from update or insert. useful for db generated ids etc...
167
+ * @see {@link FieldOptions#dbReadOnly} for configuration details
168
+ */
141
169
  readonly dbReadOnly: boolean;
142
170
  /** the Value converter for this field */
143
171
  readonly valueConverter: Required<ValueConverter<valueType>>;
144
172
  /** Get the display value for a specific item
173
+ * @see {@link FieldOptions#displayValue} for configuration details
145
174
  * @example
146
175
  * repo.fields.createDate.displayValue(task) //will display the date as defined in the `displayValue` option defined for it.
147
176
  */
148
177
  displayValue(item: Partial<entityType>): string;
178
+ /**
179
+ * Determines if the current user is allowed to update a specific entity instance.
180
+
181
+ * @example
182
+ * const taskRepo = remult.repo(Task);
183
+ * // Check if the current user is allowed to update a specific task
184
+ * if (taskRepo.metadata.apiUpdateAllowed(task)){
185
+ * // Allow user to edit the entity
186
+ * }
187
+ * @see {@link FieldOptions#allowApiUpdate} for configuration details
188
+ * @param {Partial<entityType>} item - Partial entity instance to check permissions against.
189
+ * @returns {boolean} True if the update is allowed.
190
+ */
149
191
  apiUpdateAllowed(item?: Partial<entityType>): boolean;
192
+ /**
193
+ * Determines if a specific entity field should be included in the API based on the current user's permissions.
194
+ * This method checks visibility permissions for a field within a partial entity instance.
195
+ * @example
196
+ * const employeeRepo = remult.repo(Employee);
197
+ * // Determine if the 'salary' field of an employee should be visible in the API for the current user
198
+ * if (employeeRepo.fields.salary.includedInApi({ id: 123, name: 'John Doe' })) {
199
+ * // The salary field is included in the API
200
+ * }
201
+ * @see {@link FieldOptions#includeInApi} for configuration details
202
+ * @param {Partial<entityType>} item - The partial entity instance used to evaluate field visibility.
203
+ * @returns {boolean} True if the field is included in the API.
204
+ */
150
205
  includedInApi(item?: Partial<entityType>): boolean;
151
206
  /** Adapts the value for usage with html input
152
207
  * @example
@@ -154,6 +209,7 @@ export interface FieldMetadata<valueType = any, entityType = any> {
154
209
  * birthDate = new Date(1976,5,16)
155
210
  * //...
156
211
  * input.value = repo.fields.birthDate.toInput(person) // will return '1976-06-16'
212
+ * @see {@link ValueConverter#toInput} for configuration details
157
213
  */
158
214
  toInput(value: valueType, inputType?: string): string;
159
215
  /** Adapts the value for usage with html input
@@ -162,6 +218,7 @@ export interface FieldMetadata<valueType = any, entityType = any> {
162
218
  * birthDate = new Date(1976,5,16)
163
219
  * //...
164
220
  * person.birthDate = repo.fields.birthDate.fromInput(personFormState) // will return Date
221
+ * @see {@link ValueConverter#fromInput} for configuration details
165
222
  */
166
223
  fromInput(inputValue: string, inputType?: string): valueType;
167
224
  }
@@ -44,3 +44,4 @@ export declare class SqlDatabase implements DataProvider, HasWrapIdentifier, Can
44
44
  private createdEntities;
45
45
  end: () => Promise<void>;
46
46
  }
47
+ export declare function getRowAfterUpdate<entityType>(meta: EntityMetadata<entityType>, dataProvider: EntityDataProvider, data: any, id: any, operation: string): any;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SqlDatabase = void 0;
3
+ exports.getRowAfterUpdate = exports.SqlDatabase = void 0;
4
4
  var tslib_1 = require("tslib");
5
5
  var filter_consumer_bridge_to_sql_request_js_1 = require("../filter/filter-consumer-bridge-to-sql-request.js");
6
6
  var filter_interfaces_js_1 = require("../filter/filter-interfaces.js");
@@ -475,7 +475,7 @@ var ActualSQLServerDataProvider = /** @class */ (function () {
475
475
  };
476
476
  ActualSQLServerDataProvider.prototype.update = function (id, data) {
477
477
  return tslib_1.__awaiter(this, void 0, void 0, function () {
478
- var e, r, f, statement, added, _a, _b, x, v, _c, _d, colKeys, select;
478
+ var e, r, statement, added, _a, _b, x, v, idFilter, f, _c, _d, colKeys, select;
479
479
  var e_5, _e;
480
480
  var _this = this;
481
481
  return tslib_1.__generator(this, function (_f) {
@@ -484,8 +484,6 @@ var ActualSQLServerDataProvider = /** @class */ (function () {
484
484
  case 1:
485
485
  e = _f.sent();
486
486
  r = this.sql.createCommand();
487
- f = new filter_consumer_bridge_to_sql_request_js_1.FilterConsumerBridgeToSqlRequest(r, e);
488
- filter_interfaces_js_1.Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(id)).__applyToConsumer(f);
489
487
  statement = 'update ' + e.$entityName + ' set ';
490
488
  added = false;
491
489
  try {
@@ -512,15 +510,22 @@ var ActualSQLServerDataProvider = /** @class */ (function () {
512
510
  }
513
511
  finally { if (e_5) throw e_5.error; }
514
512
  }
513
+ idFilter = this.entity.idMetadata.getIdFilter(id);
514
+ f = new filter_consumer_bridge_to_sql_request_js_1.FilterConsumerBridgeToSqlRequest(r, e);
515
+ filter_interfaces_js_1.Filter.fromEntityFilter(this.entity, idFilter).__applyToConsumer(f);
515
516
  _c = statement;
516
517
  return [4 /*yield*/, f.resolveWhere()];
517
518
  case 2:
518
519
  statement = _c + _f.sent();
519
520
  _d = this.buildSelect(e), colKeys = _d.colKeys, select = _d.select;
520
- statement += ' returning ' + select;
521
+ if (!this.sql._getSourceSql().doesNotSupportReturningSyntax)
522
+ statement += ' returning ' + select;
521
523
  return [2 /*return*/, r.execute(statement).then(function (sqlResult) {
522
524
  var _a, _b;
523
525
  (_b = (_a = _this.sql._getSourceSql()).afterMutation) === null || _b === void 0 ? void 0 : _b.call(_a);
526
+ if (_this.sql._getSourceSql().doesNotSupportReturningSyntax) {
527
+ return getRowAfterUpdate(_this.entity, _this, data, id, 'update');
528
+ }
524
529
  if (sqlResult.rows.length != 1)
525
530
  throw new Error('Failed to update row with id ' +
526
531
  id +
@@ -600,10 +605,27 @@ var ActualSQLServerDataProvider = /** @class */ (function () {
600
605
  }
601
606
  statement = "insert into ".concat(e.$entityName, " (").concat(cols, ") values (").concat(vals, ")");
602
607
  _c = this.buildSelect(e), colKeys = _c.colKeys, select = _c.select;
603
- statement += ' returning ' + select;
608
+ if (!this.sql._getSourceSql().doesNotSupportReturningSyntax)
609
+ statement += ' returning ' + select;
604
610
  return [4 /*yield*/, r.execute(statement).then(function (sql) {
605
611
  var _a, _b;
606
612
  (_b = (_a = _this.sql._getSourceSql()).afterMutation) === null || _b === void 0 ? void 0 : _b.call(_a);
613
+ if (_this.sql._getSourceSql().doesNotSupportReturningSyntax) {
614
+ if ((0, RepositoryImplementation_js_1.isAutoIncrement)(_this.entity.idMetadata.field)) {
615
+ var id_1 = sql.rows[0];
616
+ if (typeof id_1 !== 'number')
617
+ throw new Error('Auto increment, for a database that is does not support returning syntax, should return an array with the single last added id. Instead it returned: ' +
618
+ JSON.stringify(id_1));
619
+ return _this.find({
620
+ where: new filter_interfaces_js_1.Filter(function (x) {
621
+ return x.isEqualTo(_this.entity.idMetadata.field, id_1);
622
+ }),
623
+ }).then(function (r) { return r[0]; });
624
+ }
625
+ else {
626
+ return getRowAfterUpdate(_this.entity, _this, data, undefined, 'insert');
627
+ }
628
+ }
607
629
  return _this.buildResultRow(colKeys, sql.rows[0], sql);
608
630
  })];
609
631
  case 2: return [2 /*return*/, _e.sent()];
@@ -696,3 +718,32 @@ function bulkInsert(array, db) {
696
718
  });
697
719
  });
698
720
  }
721
+ function getRowAfterUpdate(meta, dataProvider, data, id, operation) {
722
+ var idFilter = id !== undefined ? meta.idMetadata.getIdFilter(id) : {};
723
+ return dataProvider
724
+ .find({
725
+ where: new filter_interfaces_js_1.Filter(function (x) {
726
+ var e_7, _a;
727
+ var _b;
728
+ try {
729
+ for (var _c = tslib_1.__values(meta.idMetadata.fields), _d = _c.next(); !_d.done; _d = _c.next()) {
730
+ var field = _d.value;
731
+ x.isEqualTo(field, (_b = data[field.key]) !== null && _b !== void 0 ? _b : idFilter[field.key]);
732
+ }
733
+ }
734
+ catch (e_7_1) { e_7 = { error: e_7_1 }; }
735
+ finally {
736
+ try {
737
+ if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
738
+ }
739
+ finally { if (e_7) throw e_7.error; }
740
+ }
741
+ }),
742
+ })
743
+ .then(function (r) {
744
+ if (r.length != 1)
745
+ throw new Error("Failed to ".concat(operation, " row - result contained ").concat(r.length, " rows"));
746
+ return r[0];
747
+ });
748
+ }
749
+ exports.getRowAfterUpdate = getRowAfterUpdate;
package/src/entity.d.ts CHANGED
@@ -15,13 +15,19 @@ export interface EntityOptions<entityType = any> {
15
15
  allowApiRead?: Allowed;
16
16
  /**
17
17
  * Determines if this entity can be updated through the api.
18
- * @see [allowed](http://remult.dev/docs/allowed.html)*/
18
+ * @see [allowed](http://remult.dev/docs/allowed.html)
19
+ * @see [Access Control](https://remult.dev/docs/access-control)
20
+ * */
19
21
  allowApiUpdate?: AllowedForInstance<entityType>;
20
22
  /** Determines if entries for this entity can be deleted through the api.
21
- * @see [allowed](http://remult.dev/docs/allowed.html)*/
23
+ * @see [allowed](http://remult.dev/docs/allowed.html)
24
+ * @see [Access Control](https://remult.dev/docs/access-control)
25
+ * */
22
26
  allowApiDelete?: AllowedForInstance<entityType>;
23
27
  /** Determines if new entries for this entity can be posted through the api.
24
- * @see [allowed](http://remult.dev/docs/allowed.html)*/
28
+ * @see [allowed](http://remult.dev/docs/allowed.html)
29
+ * @see [Access Control](https://remult.dev/docs/access-control)
30
+ * */
25
31
  allowApiInsert?: AllowedForInstance<entityType>;
26
32
  /** sets the `allowApiUpdate`, `allowApiDelete` and `allowApiInsert` properties in a single set */
27
33
  allowApiCrud?: Allowed;
@@ -190,7 +196,7 @@ export interface PreprocessFilterEvent<entityType> {
190
196
  * @param filter Optional filter to analyze. If not provided, the current filter being preprocessed is used.
191
197
  * @returns A promise that resolves to a FilterPreciseValues object containing the precise values for each property.
192
198
 
193
- * {@Link FilterPreciseValues }
199
+ * @see {@Link FilterPreciseValues }
194
200
  */
195
201
  getFilterPreciseValues(filter?: EntityFilter<entityType>): Promise<FilterPreciseValues<entityType>>;
196
202
  }
@@ -396,7 +396,7 @@ var filterHelper = /** @class */ (function () {
396
396
  filterHelper.prototype.processVal = function (val) {
397
397
  var ei = (0, getEntityRef_js_1.getEntitySettings)(this.metadata.valueType, false);
398
398
  if (ei) {
399
- if (!val) {
399
+ if (val === undefined || val === null) {
400
400
  if (val === null && !this.metadata.allowNull) {
401
401
  var rel = (0, relationInfoMember_js_1.getRelationFieldInfo)(this.metadata);
402
402
  if ((rel === null || rel === void 0 ? void 0 : rel.type) === 'reference')
@@ -2185,6 +2185,7 @@ var rowHelperImplementation = /** @class */ (function (_super) {
2185
2185
  case 20: return [4 /*yield*/, this.loadDataFrom(updatedRow)];
2186
2186
  case 21:
2187
2187
  _p.sent();
2188
+ e.id = this.getId();
2188
2189
  if (!!this.repository._dataProvider.isProxy) return [3 /*break*/, 31];
2189
2190
  if (!this.info.entityInfo.saved) return [3 /*break*/, 23];
2190
2191
  return [4 /*yield*/, this.info.entityInfo.saved(this.instance, e)];
@@ -0,0 +1,16 @@
1
+ import { ClassType } from '../../classType.js';
2
+ import { EntityOptions } from '../entity.js';
3
+ import { BackendMethodOptions } from '../server-action.js';
4
+ type Descriptor<T = any> = (a: T, b: string, c?: any) => void;
5
+ type FieldsDescriptor<T> = T extends new (...args: any[]) => infer R ? {
6
+ [K in keyof R]?: Descriptor;
7
+ } : never;
8
+ type StaticMemberDescriptors<T> = {
9
+ [K in keyof T]?: Descriptor;
10
+ };
11
+ export declare function describeClass<classType>(classType: classType, classDescriber: ((x: any, context?: any) => any) | undefined, members?: FieldsDescriptor<classType> | undefined, staticMembers?: StaticMemberDescriptors<classType>): void;
12
+ export declare function describeBackendMethods<T>(classType: T, backendMethods: {
13
+ [K in keyof T]?: BackendMethodOptions<unknown>;
14
+ }): void;
15
+ export declare function describeEntity<entityType extends ClassType<any>>(classType: entityType, key: string, fields: FieldsDescriptor<entityType>, options?: EntityOptions<InstanceType<entityType>>): void;
16
+ export {};
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.describeEntity = exports.describeBackendMethods = exports.describeClass = void 0;
4
+ var server_action_js_1 = require("../server-action.js");
5
+ var entity_js_1 = require("./entity.js");
6
+ function describeClass(classType, classDescriber, members, staticMembers) {
7
+ if (classDescriber)
8
+ classDescriber(classType);
9
+ for (var fieldKey in members) {
10
+ if (Object.prototype.hasOwnProperty.call(members, fieldKey)) {
11
+ var element = members[fieldKey];
12
+ var prop = Object.getOwnPropertyDescriptor(classType.prototype, fieldKey);
13
+ element(classType.prototype, fieldKey, prop);
14
+ if (prop)
15
+ Object.defineProperty(classType.prototype, fieldKey, prop);
16
+ }
17
+ }
18
+ for (var staticFieldKey in staticMembers) {
19
+ var staticElement = staticMembers[staticFieldKey];
20
+ var prop = Object.getOwnPropertyDescriptor(classType, staticFieldKey);
21
+ staticElement(classType, staticFieldKey, prop);
22
+ if (prop)
23
+ Object.defineProperty(classType, staticFieldKey, prop);
24
+ }
25
+ }
26
+ exports.describeClass = describeClass;
27
+ function describeBackendMethods(classType, backendMethods) {
28
+ var result = {};
29
+ for (var key in backendMethods) {
30
+ if (Object.prototype.hasOwnProperty.call(backendMethods, key)) {
31
+ var options = backendMethods[key];
32
+ result[key] = (0, server_action_js_1.BackendMethod)(options);
33
+ }
34
+ }
35
+ describeClass(classType, undefined, undefined, result);
36
+ }
37
+ exports.describeBackendMethods = describeBackendMethods;
38
+ function describeEntity(classType, key, fields, options) {
39
+ describeClass(classType, (0, entity_js_1.Entity)(key, options), fields);
40
+ }
41
+ exports.describeEntity = describeEntity;
@@ -1,6 +1,15 @@
1
1
  import type { ClassType } from '../../classType.js';
2
2
  import type { EntityOptionsFactory } from './RepositoryImplementation.js';
3
3
  import type { EntityRef } from './remult3.js';
4
+ /**
5
+ * Retrieves the EntityRef object associated with the specified entity instance.
6
+ * The EntityRef provides methods for performing operations on the entity instance.
7
+ * @param {entityType} entity - The entity instance.
8
+ * @param {boolean} [throwException=true] - Indicates whether to throw an exception if the EntityRef object cannot be retrieved.
9
+ * @returns {EntityRef<entityType>} The EntityRef object associated with the specified entity instance.
10
+ * @throws {Error} If throwException is true and the EntityRef object cannot be retrieved.
11
+ * @see [Active Record & EntityBase](https://remult.dev/docs/active-record)
12
+ */
4
13
  export declare function getEntityRef<entityType>(entity: entityType, throwException?: boolean): EntityRef<entityType>;
5
14
  export declare const entityMember: unique symbol;
6
15
  export declare const entityInfo: unique symbol;
@@ -1,6 +1,15 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getEntityKey = exports.getEntitySettings = exports.entityInfo_key = exports.entityInfo = exports.entityMember = exports.getEntityRef = void 0;
4
+ /**
5
+ * Retrieves the EntityRef object associated with the specified entity instance.
6
+ * The EntityRef provides methods for performing operations on the entity instance.
7
+ * @param {entityType} entity - The entity instance.
8
+ * @param {boolean} [throwException=true] - Indicates whether to throw an exception if the EntityRef object cannot be retrieved.
9
+ * @returns {EntityRef<entityType>} The EntityRef object associated with the specified entity instance.
10
+ * @throws {Error} If throwException is true and the EntityRef object cannot be retrieved.
11
+ * @see [Active Record & EntityBase](https://remult.dev/docs/active-record)
12
+ */
4
13
  function getEntityRef(entity, throwException) {
5
14
  var _a;
6
15
  if (throwException === void 0) { throwException = true; }
@@ -188,17 +188,22 @@ export interface EntityMetadata<entityType = any> {
188
188
  /** A human readable caption for the entity. Can be used to achieve a consistent caption for a field throughout the app
189
189
  * @example
190
190
  * <h1>Create a new item in {taskRepo.metadata.caption}</h1>
191
+ * @see {@link EntityOptions.caption}
191
192
  */
192
193
  readonly caption: string;
193
194
  /** The name of the table in the database that holds the data for this entity.
194
195
  * If no name is set in the entity options, the `key` will be used instead.
196
+ * @see {@link EntityOptions.dbName}
195
197
  */
196
198
  readonly dbName: string;
197
- /** The options send to the `Entity`'s decorator */
199
+ /** The options send to the `Entity`'s decorator
200
+ * @see {@link EntityOptions}
201
+ */
198
202
  readonly options: EntityOptions;
199
203
  /** The class type of the entity */
200
204
  readonly entityType: ClassType<entityType>;
201
205
  /** true if the current user is allowed to update an entity instance
206
+ * @see {@link EntityOptions.allowApiUpdate
202
207
  * @example
203
208
  * const taskRepo = remult.repo(Task);
204
209
  * if (taskRepo.metadata.apiUpdateAllowed(task)){
@@ -207,6 +212,7 @@ export interface EntityMetadata<entityType = any> {
207
212
  */
208
213
  apiUpdateAllowed(item?: entityType): boolean;
209
214
  /** true if the current user is allowed to read from entity
215
+ * @see {@link EntityOptions.allowApiRead}
210
216
  * @example
211
217
  * const taskRepo = remult.repo(Task);
212
218
  * if (taskRepo.metadata.apiReadAllowed){
@@ -215,6 +221,7 @@ export interface EntityMetadata<entityType = any> {
215
221
  */
216
222
  readonly apiReadAllowed: boolean;
217
223
  /** true if the current user is allowed to delete an entity instance
224
+ * * @see {@link EntityOptions.allowApiDelete}
218
225
  * @example
219
226
  * const taskRepo = remult.repo(Task);
220
227
  * if (taskRepo.metadata.apiDeleteAllowed(task)){
@@ -223,6 +230,7 @@ export interface EntityMetadata<entityType = any> {
223
230
  */
224
231
  apiDeleteAllowed(item?: entityType): boolean;
225
232
  /** true if the current user is allowed to create an entity instance
233
+ * @see {@link EntityOptions.allowApiInsert}
226
234
  * @example
227
235
  * const taskRepo = remult.repo(Task);
228
236
  * if (taskRepo.metadata.apiInsertAllowed(task)){
@@ -233,7 +241,10 @@ export interface EntityMetadata<entityType = any> {
233
241
  /**
234
242
  * @deprecated Returns the dbName - based on it's `dbName` option and it's `sqlExpression` option */
235
243
  getDbName(): Promise<string>;
236
- /** Metadata for the Entity's id */
244
+ /** Metadata for the Entity's id
245
+ * @see {@link EntityOptions.id} for configuration
246
+ *
247
+ */
237
248
  readonly idMetadata: IdMetadata<entityType>;
238
249
  }
239
250
  export declare type MembersOnly<T> = {
@@ -1,8 +1,21 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ //p1 - remove controller and instance backend methods from the regular backend method article
4
+ //p1 - describe entity
5
+ //p1 - fix includeInApi documentation - figure out why we can't see the see?
3
6
  //y2 - soft-delete-discussion https://discord.com/channels/975754286384418847/1230386433093533698/1230386433093533698
4
- //y1 - In the esm version of our tutorial - the imports are automatically .ts and not .js
7
+ //y1 - In the esm version of our tutorial - the imports are automatically .ts and not .js in react and not in vue
8
+ //y1 - talk about insert with relations
5
9
  //y1 - consider id to also support keyof (id:['company','index']) - had problem with | (keyof Partial<entityType>)[] & `entity`
10
+ //y1 - change exp version to preview?
11
+ //y1 - talk about insert / update / delete with relations
12
+ /*
13
+ repo(Order).insert({},{
14
+ relations:{
15
+ orderItems:[{},{},{}]
16
+ }
17
+ })
18
+ */
6
19
  //y1 - repo batch - for multiple operations:
7
20
  /*
8
21
  const result = await repo.batch(x=>({
@@ -13,7 +26,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
13
26
  //p1 - add documentation for bun sqlite
14
27
  //p1 - consider upsert (where,set)
15
28
  //p1 - wait a second to close stream
16
- //p1 - describe entity
17
29
  //p1 - prepare the createEntity discussion
18
30
  //p1 - write active record article
19
31
  //p1 - return error on connection error
@@ -6,6 +6,8 @@ export interface SqlImplementation extends HasWrapIdentifier {
6
6
  entityIsUsedForTheFirstTime(entity: EntityMetadata): Promise<void>;
7
7
  ensureSchema?(entities: EntityMetadata[]): Promise<void>;
8
8
  supportsJsonColumnType?: boolean;
9
+ /** true by default */
10
+ doesNotSupportReturningSyntax?: boolean;
9
11
  orderByNullsFirst?: boolean;
10
12
  end(): Promise<void>;
11
13
  afterMutation?: VoidFunction;
@@ -1,9 +0,0 @@
1
- type Decorator<T = any> = (a: T, b: string, c?: any) => void;
2
- type Decorators<T> = T extends new (...args: any[]) => infer R ? {
3
- [K in keyof R]?: Decorator;
4
- } : never;
5
- type StaticDecorators<T> = {
6
- [K in keyof T]?: Decorator;
7
- };
8
- export declare function describeClass<classType>(classType: classType, classDecorator: ((x: any, context?: any) => any) | undefined, members?: Decorators<classType> | undefined, staticMembers?: StaticDecorators<classType>): void;
9
- export {};
@@ -1,24 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.describeClass = void 0;
4
- function describeClass(classType, classDecorator, members, staticMembers) {
5
- if (classDecorator)
6
- classDecorator(classType);
7
- for (var fieldKey in members) {
8
- if (Object.prototype.hasOwnProperty.call(members, fieldKey)) {
9
- var element = members[fieldKey];
10
- var prop = Object.getOwnPropertyDescriptor(classType.prototype, fieldKey);
11
- element(classType.prototype, fieldKey, prop);
12
- if (prop)
13
- Object.defineProperty(classType.prototype, fieldKey, prop);
14
- }
15
- }
16
- for (var staticFieldKey in staticMembers) {
17
- var staticElement = staticMembers[staticFieldKey];
18
- var prop = Object.getOwnPropertyDescriptor(classType, staticFieldKey);
19
- staticElement(classType, staticFieldKey, prop);
20
- if (prop)
21
- Object.defineProperty(classType, staticFieldKey, prop);
22
- }
23
- }
24
- exports.describeClass = describeClass;