@nymphjs/nymph 1.0.0-beta.121 → 1.0.0-beta.123

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 (53) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +12 -4
  3. package/dist/Entity.d.ts +4 -3
  4. package/dist/Entity.js +33 -6
  5. package/dist/Entity.js.map +1 -1
  6. package/dist/Entity.test.js +19 -12
  7. package/dist/Entity.test.js.map +1 -1
  8. package/dist/Entity.types.d.ts +19 -15
  9. package/dist/decorators/index.d.ts +1 -0
  10. package/dist/decorators/index.js +2 -0
  11. package/dist/decorators/index.js.map +1 -0
  12. package/dist/decorators/transactional.d.ts +12 -0
  13. package/dist/decorators/transactional.js +45 -0
  14. package/dist/decorators/transactional.js.map +1 -0
  15. package/dist/driver/NymphDriver.js +14 -6
  16. package/dist/driver/NymphDriver.js.map +1 -1
  17. package/dist/driver/testArtifacts.js +115 -36
  18. package/dist/driver/testArtifacts.js.map +1 -1
  19. package/dist/errors/MethodFailedError.d.ts +3 -0
  20. package/dist/errors/MethodFailedError.js +7 -0
  21. package/dist/errors/MethodFailedError.js.map +1 -0
  22. package/dist/errors/index.d.ts +1 -0
  23. package/dist/errors/index.js +1 -0
  24. package/dist/errors/index.js.map +1 -1
  25. package/dist/helpers/index.d.ts +1 -0
  26. package/dist/helpers/index.js +2 -0
  27. package/dist/helpers/index.js.map +1 -0
  28. package/dist/helpers/transaction.d.ts +15 -0
  29. package/dist/helpers/transaction.js +47 -0
  30. package/dist/helpers/transaction.js.map +1 -0
  31. package/dist/index.d.ts +2 -0
  32. package/dist/index.js +2 -0
  33. package/dist/index.js.map +1 -1
  34. package/dist/testArtifacts.d.ts +2 -0
  35. package/dist/testArtifacts.js +113 -34
  36. package/dist/testArtifacts.js.map +1 -1
  37. package/dist/testMocks.js +2 -2
  38. package/dist/testMocks.js.map +1 -1
  39. package/package.json +14 -5
  40. package/src/Entity.test.ts +19 -12
  41. package/src/Entity.ts +34 -9
  42. package/src/Entity.types.ts +19 -15
  43. package/src/decorators/index.ts +1 -0
  44. package/src/decorators/transactional.ts +63 -0
  45. package/src/driver/NymphDriver.ts +18 -6
  46. package/src/driver/testArtifacts.ts +146 -36
  47. package/src/errors/MethodFailedError.ts +6 -0
  48. package/src/errors/index.ts +2 -0
  49. package/src/helpers/index.ts +1 -0
  50. package/src/helpers/transaction.ts +65 -0
  51. package/src/index.ts +2 -0
  52. package/src/testArtifacts.ts +49 -0
  53. package/src/testMocks.ts +4 -2
@@ -1,41 +1,120 @@
1
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
2
+ var useValue = arguments.length > 2;
3
+ for (var i = 0; i < initializers.length; i++) {
4
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
5
+ }
6
+ return useValue ? value : void 0;
7
+ };
8
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
9
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
10
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
11
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
12
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
13
+ var _, done = false;
14
+ for (var i = decorators.length - 1; i >= 0; i--) {
15
+ var context = {};
16
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
17
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
18
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
19
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
20
+ if (kind === "accessor") {
21
+ if (result === void 0) continue;
22
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
23
+ if (_ = accept(result.get)) descriptor.get = _;
24
+ if (_ = accept(result.set)) descriptor.set = _;
25
+ if (_ = accept(result.init)) initializers.unshift(_);
26
+ }
27
+ else if (_ = accept(result)) {
28
+ if (kind === "field") initializers.unshift(_);
29
+ else descriptor[key] = _;
30
+ }
31
+ }
32
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
33
+ done = true;
34
+ };
1
35
  import Entity from './Entity.js';
36
+ import { transactional } from './decorators/index.js';
37
+ import { transaction } from './helpers/index.js';
2
38
  /**
3
39
  * This class is a test class that extends the Entity class.
4
40
  */
5
- export class TestModel extends Entity {
6
- static ETYPE = 'test_model';
7
- static class = 'TestModel';
8
- $privateData = ['boolean'];
9
- static searchRestrictedData = ['fish'];
10
- $allowlistData = ['string', 'array', 'mdate'];
11
- $protectedTags = ['test', 'notag'];
12
- $allowlistTags = ['newtag'];
13
- constructor() {
14
- super();
15
- this.$addTag('test');
16
- this.$data.boolean = true;
17
- this.$data.uniques = [];
18
- }
19
- async $getUniques() {
20
- return this.$data.uniques ?? [];
21
- }
22
- $useProtectedData() {
23
- const $allowlistData = this.$allowlistData;
24
- const $allowlistTags = this.$allowlistTags;
25
- const $protectedData = this.$protectedData;
26
- const $protectedTags = this.$protectedTags;
27
- delete this.$allowlistData;
28
- delete this.$allowlistTags;
29
- this.$protectedData = ['number'];
30
- this.$protectedTags = [];
31
- return () => {
32
- this.$allowlistData = $allowlistData;
33
- this.$allowlistTags = $allowlistTags;
34
- this.$protectedData = $protectedData;
35
- this.$protectedTags = $protectedTags;
36
- };
37
- }
38
- }
41
+ let TestModel = (() => {
42
+ let _classSuper = Entity;
43
+ let _instanceExtraInitializers = [];
44
+ let _$testTransactionalDecorator_decorators;
45
+ return class TestModel extends _classSuper {
46
+ static {
47
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
48
+ _$testTransactionalDecorator_decorators = [transactional];
49
+ __esDecorate(this, null, _$testTransactionalDecorator_decorators, { kind: "method", name: "$testTransactionalDecorator", static: false, private: false, access: { has: obj => "$testTransactionalDecorator" in obj, get: obj => obj.$testTransactionalDecorator }, metadata: _metadata }, null, _instanceExtraInitializers);
50
+ if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
51
+ }
52
+ static ETYPE = 'test_model';
53
+ static class = 'TestModel';
54
+ $privateData = (__runInitializers(this, _instanceExtraInitializers), ['boolean']);
55
+ static searchRestrictedData = ['fish'];
56
+ $allowlistData = ['string', 'array', 'mdate'];
57
+ $protectedTags = ['test', 'notag'];
58
+ $allowlistTags = ['newtag'];
59
+ constructor() {
60
+ super();
61
+ this.$addTag('test');
62
+ this.$data.boolean = true;
63
+ this.$data.uniques = [];
64
+ }
65
+ async $getUniques() {
66
+ return this.$data.uniques ?? [];
67
+ }
68
+ $useProtectedData() {
69
+ const $allowlistData = this.$allowlistData;
70
+ const $allowlistTags = this.$allowlistTags;
71
+ const $protectedData = this.$protectedData;
72
+ const $protectedTags = this.$protectedTags;
73
+ delete this.$allowlistData;
74
+ delete this.$allowlistTags;
75
+ this.$protectedData = ['number'];
76
+ this.$protectedTags = [];
77
+ return () => {
78
+ this.$allowlistData = $allowlistData;
79
+ this.$allowlistTags = $allowlistTags;
80
+ this.$protectedData = $protectedData;
81
+ this.$protectedTags = $protectedTags;
82
+ };
83
+ }
84
+ async $testTransactionalDecorator(throwError) {
85
+ this.$data.string = 'this has been changed';
86
+ if (this.$data.reference) {
87
+ await this.$data.reference.$wake();
88
+ this.$data.reference.string = 'this has been changed too';
89
+ await this.$data.reference.$save();
90
+ }
91
+ await this.$save();
92
+ if (throwError) {
93
+ throw Error('An error.');
94
+ }
95
+ return 'Success.';
96
+ }
97
+ async $testTransactionHelper(throwError) {
98
+ return await transaction(this.$nymph, 'test-transaction', async (nymph) => {
99
+ this.$setNymph(nymph);
100
+ this.$data.string = 'this has been changed';
101
+ if (this.$data.reference) {
102
+ await this.$data.reference.$wake();
103
+ this.$data.reference.string = 'this has been changed too';
104
+ await this.$data.reference.$save();
105
+ }
106
+ await this.$save();
107
+ if (throwError) {
108
+ throw Error('An error.');
109
+ }
110
+ return 'Success.';
111
+ }, async (nymph) => {
112
+ this.$setNymph(nymph);
113
+ });
114
+ }
115
+ };
116
+ })();
117
+ export { TestModel };
39
118
  /**
40
119
  * This class is a test class that extends the Entity class.
41
120
  */
@@ -1 +1 @@
1
- {"version":3,"file":"testArtifacts.js","sourceRoot":"","sources":["../src/testArtifacts.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAC;AA0BjC;;GAEG;AACH,MAAM,OAAO,SAAU,SAAQ,MAAqB;IAClD,MAAM,CAAC,KAAK,GAAG,YAAY,CAAC;IAC5B,MAAM,CAAC,KAAK,GAAG,WAAW,CAAC;IAEjB,YAAY,GAAG,CAAC,SAAS,CAAC,CAAC;IAC9B,MAAM,CAAC,oBAAoB,GAAG,CAAC,MAAM,CAAC,CAAC;IACpC,cAAc,GAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAC/C,cAAc,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,cAAc,GAAI,CAAC,QAAQ,CAAC,CAAC;IAEvC;QACE,KAAK,EAAE,CAAC;QAER,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACrB,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC;QAC1B,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC;IAC1B,CAAC;IAEM,KAAK,CAAC,WAAW;QACtB,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC;IAClC,CAAC;IAEM,iBAAiB;QACtB,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC;QAC3C,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC;QAC3C,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC;QAC3C,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC;QAE3C,OAAO,IAAI,CAAC,cAAc,CAAC;QAC3B,OAAO,IAAI,CAAC,cAAc,CAAC;QAC3B,IAAI,CAAC,cAAc,GAAG,CAAC,QAAQ,CAAC,CAAC;QACjC,IAAI,CAAC,cAAc,GAAG,EAAE,CAAC;QAEzB,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;YACrC,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;YACrC,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;YACrC,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;QACvC,CAAC,CAAC;IACJ,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,UAAW,SAAQ,SAAS;IACvC,MAAM,CAAC,KAAK,GAAG,cAAc,CAAC;IAC9B,MAAM,CAAC,KAAK,GAAG,YAAY,CAAC;CAC7B;AAID;;GAEG;AACH,MAAM,OAAO,cAAe,SAAQ,MAA0B;IAC5D,MAAM,CAAC,KAAK,GAAG,kBAAkB,CAAC;IAClC,MAAM,CAAC,KAAK,GAAG,gBAAgB,CAAC;CACjC"}
1
+ {"version":3,"file":"testArtifacts.js","sourceRoot":"","sources":["../src/testArtifacts.ts"],"names":[],"mappings":"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"}
package/dist/testMocks.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { guid } from '@nymphjs/guid';
2
- import { ClassNotAvailableError } from './errors/index.js';
2
+ import { ClassNotAvailableError, EntityConflictError } from './errors/index.js';
3
3
  import Entity from './Entity.js';
4
4
  const entities = {};
5
5
  export class MockNymphDriver {
@@ -75,7 +75,7 @@ export class MockNymph {
75
75
  }
76
76
  else if (entity.guid in entities &&
77
77
  (entity.mdate ?? 0) < (entities[entity.guid].mdate ?? 0)) {
78
- return false;
78
+ throw new EntityConflictError("Entity either doesn't exist or is newer in the DB.");
79
79
  }
80
80
  entity.mdate = Date.now();
81
81
  const entityJson = {
@@ -1 +1 @@
1
- {"version":3,"file":"testMocks.js","sourceRoot":"","sources":["../src/testMocks.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"testMocks.js","sourceRoot":"","sources":["../src/testMocks.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,14 +1,23 @@
1
1
  {
2
2
  "name": "@nymphjs/nymph",
3
- "version": "1.0.0-beta.121",
4
- "description": "Nymph.js - Nymph ORM",
3
+ "version": "1.0.0-beta.123",
4
+ "description": "Application Data and Access Framework for Node.js and the Browser",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
8
8
  "keywords": [
9
9
  "nymph",
10
+ "nymph.js",
11
+ "application data framework",
12
+ "application data and access framework",
13
+ "object relational mapper",
10
14
  "ORM",
11
- "object relational mapper"
15
+ "user management",
16
+ "group management",
17
+ "application framework",
18
+ "data framework",
19
+ "database abstraction",
20
+ "database abstraction layer"
12
21
  ],
13
22
  "scripts": {
14
23
  "clean": "test -d dist && rm -r dist || true",
@@ -34,7 +43,7 @@
34
43
  },
35
44
  "license": "Apache-2.0",
36
45
  "dependencies": {
37
- "@nymphjs/guid": "^1.0.0-beta.121",
46
+ "@nymphjs/guid": "^1.0.0-beta.123",
38
47
  "@sciactive/tokenizer": "^3.4.0",
39
48
  "debug": "^4.4.3",
40
49
  "joi": "^18.2.5",
@@ -57,5 +66,5 @@
57
66
  "ts-node": "^10.9.2",
58
67
  "typescript": "^7.0.2"
59
68
  },
60
- "gitHead": "74e39aff81353d16831656431e97b66ac6effc18"
69
+ "gitHead": "c37dc940d7bf39e0f63158add6d0c54e7c050782"
61
70
  }
@@ -2,6 +2,7 @@ import { jest } from '@jest/globals';
2
2
 
3
3
  import { cloneDeep } from 'lodash-es';
4
4
 
5
+ import { EntityConflictError } from './errors/index.js';
5
6
  import { MockNymph } from './testMocks.js';
6
7
  import {
7
8
  TestBModel as TestBModelClass,
@@ -44,22 +45,22 @@ describe('Entity', () => {
44
45
  expect(testEntity.array).toEqual(['full', 'of', 'values', 500]);
45
46
  expect(testEntity.number).toEqual(30);
46
47
 
47
- expect(await testEntity.$save()).toEqual(true);
48
+ await testEntity.$save();
48
49
  expect(typeof testEntity.guid).toEqual('string');
49
50
 
50
51
  entityReferenceTest = await TestModel.factory();
51
52
  entityReferenceTest.string = 'wrong';
52
- expect(await entityReferenceTest.$save()).toEqual(true);
53
+ await entityReferenceTest.$save();
53
54
  entityReferenceGuid = entityReferenceTest.guid as string;
54
55
  testEntity.reference = entityReferenceTest;
55
56
  testEntity.refArray = [entityReferenceTest];
56
57
  testEntity.refObject = {
57
58
  entity: entityReferenceTest,
58
59
  };
59
- expect(await testEntity.$save()).toEqual(true);
60
+ await testEntity.$save();
60
61
 
61
62
  entityReferenceTest.test = 'good';
62
- expect(await entityReferenceTest.$save()).toEqual(true);
63
+ await entityReferenceTest.$save();
63
64
  });
64
65
 
65
66
  it('comparisons work', async () => {
@@ -118,14 +119,14 @@ describe('Entity', () => {
118
119
  it('refresh updates', async () => {
119
120
  expect(testEntity.string).toEqual('test');
120
121
  testEntity.string = 'updated';
121
- expect(await testEntity.$save()).toEqual(true);
122
+ await testEntity.$save();
122
123
  await testEntity.$refresh();
123
- expect(await testEntity.$save()).toEqual(true);
124
+ await testEntity.$save();
124
125
 
125
126
  const retrieve = await TestModel.factory(testEntity.guid as string);
126
127
  expect(retrieve.string).toEqual('updated');
127
128
  retrieve.string = 'test';
128
- expect(await retrieve.$save()).toBe(true);
129
+ await retrieve.$save();
129
130
 
130
131
  await testEntity.$refresh();
131
132
  expect(testEntity.string).toEqual('test');
@@ -135,12 +136,18 @@ describe('Entity', () => {
135
136
  await new Promise((resolve) => setTimeout(resolve, 500));
136
137
 
137
138
  const testEntityCopy = await TestModel.factory(testEntity.guid as string);
138
- expect(await testEntityCopy.$save()).toEqual(true);
139
+ await testEntityCopy.$save();
139
140
  expect(testEntityCopy.mdate ?? 0).toBeGreaterThan(testEntity.mdate ?? 0);
140
141
 
141
142
  await new Promise((resolve) => setTimeout(resolve, 500));
142
143
 
143
- expect(await testEntity.$save()).toEqual(false);
144
+ try {
145
+ await testEntity.$save();
146
+
147
+ throw new Error('Should have thrown.');
148
+ } catch (e: any) {
149
+ expect(e).toBeInstanceOf(EntityConflictError);
150
+ }
144
151
 
145
152
  await testEntity.$refresh();
146
153
 
@@ -189,12 +196,12 @@ describe('Entity', () => {
189
196
 
190
197
  // Remove all tags.
191
198
  testEntity.$removeTag('test');
192
- expect(await testEntity.$save()).toEqual(true);
199
+ await testEntity.$save();
193
200
  expect(await testEntity.$refresh()).toEqual(true);
194
201
  expect(testEntity.$hasTag('test')).toEqual(false);
195
202
  expect(testEntity.$getTags()).toEqual([]);
196
203
  testEntity.$addTag('test');
197
- expect(await testEntity.$save()).toEqual(true);
204
+ await testEntity.$save();
198
205
  expect(testEntity.$hasTag('test')).toEqual(true);
199
206
  });
200
207
 
@@ -396,7 +403,7 @@ describe('Entity', () => {
396
403
  // Test that an old JSON payload causes a conflict.
397
404
  const json = JSON.stringify(testEntity);
398
405
 
399
- expect(await testEntity.$save()).toEqual(true);
406
+ await testEntity.$save();
400
407
 
401
408
  let thrown = false;
402
409
  let thrownName: string = '';
package/src/Entity.ts CHANGED
@@ -19,6 +19,7 @@ import {
19
19
  EntityIsSleepingReferenceError,
20
20
  InvalidParametersError,
21
21
  InvalidStateError,
22
+ MethodFailedError,
22
23
  } from './errors/index.js';
23
24
  import {
24
25
  entitiesToReferences,
@@ -604,6 +605,28 @@ export default class Entity<
604
605
  return obj;
605
606
  }
606
607
 
608
+ public $setNymph(nymph: Nymph) {
609
+ this.$nymph = nymph;
610
+ if (!this.$asleep()) {
611
+ const recurseData = (data: any) => {
612
+ if (Array.isArray(data)) {
613
+ for (let name in data) {
614
+ recurseData(data[name]);
615
+ }
616
+ } else if (data instanceof Entity) {
617
+ if (data.$nymph !== nymph) {
618
+ data.$setNymph(nymph);
619
+ }
620
+ } else if (typeof data === 'object') {
621
+ for (let name in data) {
622
+ recurseData(data[name]);
623
+ }
624
+ }
625
+ };
626
+ recurseData(this.$data);
627
+ }
628
+ }
629
+
607
630
  public $getGuaranteedGUID() {
608
631
  if (this.guid !== null) {
609
632
  return this.guid;
@@ -648,10 +671,12 @@ export default class Entity<
648
671
  return this.$clientEnabledMethods;
649
672
  }
650
673
 
651
- public async $delete(): Promise<boolean> {
652
- this.$check();
674
+ public async $delete(): Promise<void> {
675
+ await this.$wake();
653
676
 
654
- return await this.$nymph.deleteEntity(this);
677
+ if (!(await this.$nymph.deleteEntity(this))) {
678
+ throw new MethodFailedError('Failed to delete entity.');
679
+ }
655
680
  }
656
681
 
657
682
  public $equals(object: any) {
@@ -1203,16 +1228,14 @@ export default class Entity<
1203
1228
  }
1204
1229
  const refresh = await this.$nymph.getEntity(
1205
1230
  {
1206
- class: this.$nymph.getEntityClass(
1207
- this.constructor as EntityConstructor,
1208
- ),
1231
+ class: this.constructor as EntityConstructor,
1209
1232
  skipCache: true,
1210
1233
  skipAc: this.$skipAc,
1211
1234
  },
1212
1235
  { type: '&', guid: this.guid },
1213
1236
  );
1214
1237
  if (refresh == null) {
1215
- return 0;
1238
+ throw new MethodFailedError("The entity's data could not be refreshed.");
1216
1239
  }
1217
1240
  this.tags = refresh.tags;
1218
1241
  this.cdate = refresh.cdate;
@@ -1227,10 +1250,12 @@ export default class Entity<
1227
1250
  this.tags = difference(this.tags, tags);
1228
1251
  }
1229
1252
 
1230
- public async $save(): Promise<boolean> {
1253
+ public async $save(): Promise<void> {
1231
1254
  await this.$wake();
1232
1255
 
1233
- return await this.$nymph.saveEntity(this);
1256
+ if (!(await this.$nymph.saveEntity(this))) {
1257
+ throw new MethodFailedError('Failed to save entity.');
1258
+ }
1234
1259
  }
1235
1260
 
1236
1261
  public $toReference(existingOnly?: boolean) {
@@ -69,9 +69,9 @@ export interface DataObjectInterface {
69
69
  /**
70
70
  * Delete the object from storage.
71
71
  *
72
- * @returns True on success, false on failure.
72
+ * An error is thrown on failure.
73
73
  */
74
- $delete(): Promise<boolean>;
74
+ $delete(): Promise<void>;
75
75
  /**
76
76
  * Perform a more strict comparison of this object to another.
77
77
  *
@@ -100,18 +100,16 @@ export interface DataObjectInterface {
100
100
  /**
101
101
  * Refresh the object from storage. (Bypasses Nymph's cache.)
102
102
  *
103
- * If the object has been deleted from storage, the database cannot be
104
- * reached, or a database error occurs, `refresh()` will return 0.
105
- *
106
- * @returns False if the data has not been saved, 0 if it can't be refreshed, true on success.
103
+ * @returns False if the data has not been saved, true on success. An error is
104
+ * thrown on failure.
107
105
  */
108
- $refresh(): Promise<boolean | 0>;
106
+ $refresh(): Promise<boolean>;
109
107
  /**
110
108
  * Save the object to storage.
111
109
  *
112
- * @returns True on success, false on failure.
110
+ * An error is thrown on failure.
113
111
  */
114
- $save(): Promise<boolean>;
112
+ $save(): Promise<void>;
115
113
  /**
116
114
  * The object's data.
117
115
  */
@@ -172,7 +170,8 @@ export interface EntityInterface extends DataObjectInterface {
172
170
  /**
173
171
  * Get the client enabled methods.
174
172
  *
175
- * @returns The names of methods allowed to be called by the frontend with serverCall.
173
+ * @returns The names of methods allowed to be called by the frontend with
174
+ * serverCall.
176
175
  */
177
176
  $getClientEnabledMethods(): string[];
178
177
  /**
@@ -181,7 +180,8 @@ export interface EntityInterface extends DataObjectInterface {
181
180
  * This should only be used by Nymph to save the data into storage.
182
181
  *
183
182
  * @param includeSData Whether to include the serialized data as well.
184
- * @param referenceOnlyExisting Whether to only turn existing entities into references.
183
+ * @param referenceOnlyExisting Whether to only turn existing entities into
184
+ * references.
185
185
  * @returns The entity's data object.
186
186
  */
187
187
  $getData(includeSData?: boolean, referenceOnlyExisting?: boolean): EntityData;
@@ -256,8 +256,10 @@ export interface EntityInterface extends DataObjectInterface {
256
256
  * - $allowlistTags
257
257
  * - $allowlistData
258
258
  *
259
- * @param input The input data. Please note, this will be modified (destroyed).
260
- * @param allowConflict Allow to accept data that is older than the current data.
259
+ * @param input The input data. Please note, this will be modified
260
+ * (destroyed).
261
+ * @param allowConflict Allow to accept data that is older than the current
262
+ * data.
261
263
  */
262
264
  $jsonAcceptData(input: EntityJson, allowConflict?: boolean): void;
263
265
  /**
@@ -270,8 +272,10 @@ export interface EntityInterface extends DataObjectInterface {
270
272
  * - $allowlistTags
271
273
  * - $allowlistData
272
274
  *
273
- * @param patch The patch data. Please note, this will be modified (destroyed).
274
- * @param allowConflict Allow to accept data that is older than the current data.
275
+ * @param patch The patch data. Please note, this will be modified
276
+ * (destroyed).
277
+ * @param allowConflict Allow to accept data that is older than the current
278
+ * data.
275
279
  */
276
280
  $jsonAcceptPatch(patch: EntityPatch, allowConflict?: boolean): void;
277
281
  /**
@@ -0,0 +1 @@
1
+ export * from './transactional.js';
@@ -0,0 +1,63 @@
1
+ import { nanoid } from '@nymphjs/guid';
2
+
3
+ import Entity from '../Entity.js';
4
+
5
+ /**
6
+ * A decorator that runs the decorated method inside a transaction.
7
+ *
8
+ * If the method resolves, the transaction is committed. If the method throws,
9
+ * the transaction is rolled back.
10
+ *
11
+ * Uses $setNymph to set the transactional instance of Nymph on the entity and
12
+ * return to the original instance after the transaction is committed or rolled
13
+ * back.
14
+ */
15
+ export function transactional(
16
+ target: (...args: any[]) => Promise<any>,
17
+ context: ClassMethodDecoratorContext,
18
+ ) {
19
+ const name = String(context.name);
20
+ return async function (this: Entity, ...args: unknown[]) {
21
+ const transactionName = `${name}-${nanoid()}`;
22
+ const nymph = this.$nymph;
23
+ nymph.config.debugInfo(
24
+ 'nymph:transactional',
25
+ `Starting transaction ${transactionName}`,
26
+ );
27
+ const tnymph = await nymph.startTransaction(transactionName);
28
+ this.$setNymph(tnymph);
29
+ let committed = false;
30
+ let result: any;
31
+ try {
32
+ result = await target.apply(this, args);
33
+ nymph.config.debugInfo(
34
+ 'nymph:transactional',
35
+ `Committing transaction ${transactionName}`,
36
+ );
37
+ committed = await tnymph.commit(transactionName);
38
+ } catch (e: any) {
39
+ try {
40
+ nymph.config.debugInfo(
41
+ 'nymph:transactional',
42
+ `Rolling back transaction ${transactionName}, reason: ${e.message}`,
43
+ );
44
+ await tnymph.rollback(transactionName);
45
+ } catch (e: any) {
46
+ nymph.config.debugError(
47
+ 'nymph:transactional',
48
+ `Rollback of transaction ${transactionName} failed, reason: ${e.message}`,
49
+ );
50
+ }
51
+ this.$setNymph(nymph);
52
+ throw e;
53
+ }
54
+
55
+ this.$setNymph(nymph);
56
+
57
+ if (!committed) {
58
+ throw new Error('Transaction could not be committed.');
59
+ }
60
+
61
+ return result;
62
+ };
63
+ }
@@ -1598,18 +1598,30 @@ export default abstract class NymphDriver {
1598
1598
  sdata,
1599
1599
  );
1600
1600
  }
1601
+
1602
+ if (!success) {
1603
+ entity.guid = originalGuid;
1604
+ entity.cdate = originalCdate;
1605
+ entity.mdate = originalMdate;
1606
+ }
1607
+
1608
+ return success;
1601
1609
  } catch (e: any) {
1610
+ if (commitTransactionCallback) {
1611
+ try {
1612
+ await commitTransactionCallback(false);
1613
+ } catch (e: any) {
1614
+ this.nymph.config.debugError(
1615
+ 'nymph',
1616
+ `Rollback of entity save transaction failed, reason: ${e.message}`,
1617
+ );
1618
+ }
1619
+ }
1602
1620
  entity.guid = originalGuid;
1603
1621
  entity.cdate = originalCdate;
1604
1622
  entity.mdate = originalMdate;
1605
1623
  throw e;
1606
1624
  }
1607
- if (!success) {
1608
- entity.guid = originalGuid;
1609
- entity.cdate = originalCdate;
1610
- entity.mdate = originalMdate;
1611
- }
1612
- return success;
1613
1625
  }
1614
1626
 
1615
1627
  /**