@nymphjs/nymph 1.0.0-beta.13 → 1.0.0-beta.130

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 (123) hide show
  1. package/CHANGELOG.md +644 -0
  2. package/README.md +34 -24
  3. package/dist/Entity.d.ts +304 -15
  4. package/dist/Entity.js +609 -154
  5. package/dist/Entity.js.map +1 -1
  6. package/dist/Entity.test.js +75 -29
  7. package/dist/Entity.test.js.map +1 -1
  8. package/dist/Entity.types.d.ts +275 -28
  9. package/dist/Entity.types.js +1 -2
  10. package/dist/Nymph.d.ts +575 -11
  11. package/dist/Nymph.js +825 -288
  12. package/dist/Nymph.js.map +1 -1
  13. package/dist/Nymph.types.d.ts +117 -7
  14. package/dist/Nymph.types.js +47 -5
  15. package/dist/Nymph.types.js.map +1 -1
  16. package/dist/conf/defaults.d.ts +1 -1
  17. package/dist/conf/defaults.js +26 -3
  18. package/dist/conf/defaults.js.map +1 -1
  19. package/dist/conf/index.d.ts +50 -2
  20. package/dist/conf/index.js +2 -8
  21. package/dist/conf/index.js.map +1 -1
  22. package/dist/decorators/index.d.ts +1 -0
  23. package/dist/decorators/index.js +2 -0
  24. package/dist/decorators/index.js.map +1 -0
  25. package/dist/decorators/transactional.d.ts +12 -0
  26. package/dist/decorators/transactional.js +45 -0
  27. package/dist/decorators/transactional.js.map +1 -0
  28. package/dist/driver/NymphDriver.d.ts +209 -41
  29. package/dist/driver/NymphDriver.js +737 -242
  30. package/dist/driver/NymphDriver.js.map +1 -1
  31. package/dist/driver/index.d.ts +1 -2
  32. package/dist/driver/index.js +2 -23
  33. package/dist/driver/index.js.map +1 -1
  34. package/dist/driver/testArtifacts.d.ts +4 -4
  35. package/dist/driver/testArtifacts.js +2016 -1086
  36. package/dist/driver/testArtifacts.js.map +1 -1
  37. package/dist/errors/ClassNotAvailableError.js +1 -5
  38. package/dist/errors/ClassNotAvailableError.js.map +1 -1
  39. package/dist/errors/EntityConflictError.js +1 -5
  40. package/dist/errors/EntityConflictError.js.map +1 -1
  41. package/dist/errors/EntityInvalidDataError.d.ts +6 -0
  42. package/dist/errors/EntityInvalidDataError.js +8 -6
  43. package/dist/errors/EntityInvalidDataError.js.map +1 -1
  44. package/dist/errors/EntityIsSleepingReferenceError.js +1 -5
  45. package/dist/errors/EntityIsSleepingReferenceError.js.map +1 -1
  46. package/dist/errors/EntityUniqueConstraintError.d.ts +3 -0
  47. package/dist/errors/EntityUniqueConstraintError.js +7 -0
  48. package/dist/errors/EntityUniqueConstraintError.js.map +1 -0
  49. package/dist/errors/HttpError.d.ts +5 -0
  50. package/dist/errors/HttpError.js +11 -0
  51. package/dist/errors/HttpError.js.map +1 -0
  52. package/dist/errors/InvalidParametersError.js +1 -5
  53. package/dist/errors/InvalidParametersError.js.map +1 -1
  54. package/dist/errors/InvalidStateError.d.ts +3 -0
  55. package/dist/errors/InvalidStateError.js +7 -0
  56. package/dist/errors/InvalidStateError.js.map +1 -0
  57. package/dist/errors/MethodFailedError.d.ts +3 -0
  58. package/dist/errors/MethodFailedError.js +7 -0
  59. package/dist/errors/MethodFailedError.js.map +1 -0
  60. package/dist/errors/NotConfiguredError.js +1 -5
  61. package/dist/errors/NotConfiguredError.js.map +1 -1
  62. package/dist/errors/QueryFailedError.d.ts +2 -1
  63. package/dist/errors/QueryFailedError.js +5 -6
  64. package/dist/errors/QueryFailedError.js.map +1 -1
  65. package/dist/errors/UnableToConnectError.js +1 -5
  66. package/dist/errors/UnableToConnectError.js.map +1 -1
  67. package/dist/errors/index.d.ts +12 -8
  68. package/dist/errors/index.js +12 -24
  69. package/dist/errors/index.js.map +1 -1
  70. package/dist/helpers/index.d.ts +1 -0
  71. package/dist/helpers/index.js +2 -0
  72. package/dist/helpers/index.js.map +1 -0
  73. package/dist/helpers/transaction.d.ts +15 -0
  74. package/dist/helpers/transaction.js +47 -0
  75. package/dist/helpers/transaction.js.map +1 -0
  76. package/dist/index.d.ts +13 -11
  77. package/dist/index.js +16 -34
  78. package/dist/index.js.map +1 -1
  79. package/dist/nymphJoiProps.d.ts +1 -1
  80. package/dist/nymphJoiProps.js +10 -15
  81. package/dist/nymphJoiProps.js.map +1 -1
  82. package/dist/testArtifacts.d.ts +21 -6
  83. package/dist/testArtifacts.js +124 -50
  84. package/dist/testArtifacts.js.map +1 -1
  85. package/dist/testMocks.d.ts +27 -12
  86. package/dist/testMocks.js +44 -33
  87. package/dist/testMocks.js.map +1 -1
  88. package/dist/utils.d.ts +5 -5
  89. package/dist/utils.js +106 -68
  90. package/dist/utils.js.map +1 -1
  91. package/jest.config.js +31 -5
  92. package/package.json +43 -24
  93. package/src/Entity.test.ts +73 -23
  94. package/src/Entity.ts +555 -169
  95. package/src/Entity.types.ts +102 -90
  96. package/src/Nymph.ts +930 -517
  97. package/src/Nymph.types.ts +145 -39
  98. package/src/conf/defaults.ts +34 -1
  99. package/src/conf/index.ts +50 -2
  100. package/src/decorators/index.ts +1 -0
  101. package/src/decorators/transactional.ts +63 -0
  102. package/src/driver/NymphDriver.ts +987 -417
  103. package/src/driver/index.ts +1 -3
  104. package/src/driver/testArtifacts.ts +2699 -1628
  105. package/src/errors/EntityUniqueConstraintError.ts +6 -0
  106. package/src/errors/HttpError.ts +12 -0
  107. package/src/errors/InvalidStateError.ts +6 -0
  108. package/src/errors/MethodFailedError.ts +6 -0
  109. package/src/errors/QueryFailedError.ts +3 -1
  110. package/src/errors/index.ts +16 -8
  111. package/src/helpers/index.ts +1 -0
  112. package/src/helpers/transaction.ts +65 -0
  113. package/src/index.ts +13 -11
  114. package/src/nymphJoiProps.ts +2 -2
  115. package/src/testArtifacts.ts +69 -20
  116. package/src/testMocks.ts +107 -41
  117. package/src/utils.ts +102 -67
  118. package/tsconfig.json +6 -3
  119. package/typedoc.json +4 -0
  120. package/dist/conf/d.d.ts +0 -6
  121. package/dist/conf/d.js +0 -3
  122. package/dist/conf/d.js.map +0 -1
  123. package/src/conf/d.ts +0 -24
@@ -1,5 +1,5 @@
1
- import { EntityConstructor, EntityInterface } from './Entity.types';
2
- import Nymph from './Nymph';
1
+ import type { EntityConstructor, EntityInterface } from './Entity.types.js';
2
+ import type Nymph from './Nymph.js';
3
3
 
4
4
  export type NymphEventType =
5
5
  | 'connect'
@@ -36,11 +36,11 @@ export type NymphEventType =
36
36
  | 'afterRollbackTransaction';
37
37
  export type NymphConnectCallback = (
38
38
  nymph: Nymph,
39
- result: Promise<boolean>
39
+ result: Promise<boolean>,
40
40
  ) => Promise<void>;
41
41
  export type NymphDisconnectCallback = (
42
42
  nymph: Nymph,
43
- result: Promise<boolean>
43
+ result: Promise<boolean>,
44
44
  ) => Promise<void>;
45
45
  /**
46
46
  * The NymphQueryCallback will be called on both top level and qref queries.
@@ -56,142 +56,192 @@ export type NymphDisconnectCallback = (
56
56
  export type NymphQueryCallback = (
57
57
  nymph: Nymph,
58
58
  options: Options,
59
- selectors: FormattedSelector[]
59
+ selectors: FormattedSelector[],
60
60
  ) => void;
61
61
  export type NymphBeforeGetEntityCallback = (
62
62
  nymph: Nymph,
63
63
  options: Options,
64
- selectors: Selector[]
64
+ selectors: Selector[],
65
65
  ) => Promise<void>;
66
66
  export type NymphBeforeGetEntitiesCallback = (
67
67
  nymph: Nymph,
68
68
  options: Options,
69
- selectors: Selector[]
69
+ selectors: Selector[],
70
70
  ) => Promise<void>;
71
71
  export type NymphBeforeSaveEntityCallback = (
72
72
  nymph: Nymph,
73
- entity: EntityInterface
73
+ entity: EntityInterface,
74
74
  ) => Promise<void>;
75
75
  export type NymphAfterSaveEntityCallback = (
76
76
  nymph: Nymph,
77
- result: Promise<boolean>
77
+ result: Promise<boolean>,
78
78
  ) => Promise<void>;
79
79
  export type NymphFailedSaveEntityCallback = (
80
80
  nymph: Nymph,
81
- error: any
81
+ error: any,
82
82
  ) => Promise<void>;
83
83
  export type NymphBeforeDeleteEntityCallback = (
84
84
  nymph: Nymph,
85
- entity: EntityInterface
85
+ entity: EntityInterface,
86
86
  ) => Promise<void>;
87
87
  export type NymphAfterDeleteEntityCallback = (
88
88
  nymph: Nymph,
89
- result: Promise<boolean>
89
+ result: Promise<boolean>,
90
90
  ) => Promise<void>;
91
91
  export type NymphFailedDeleteEntityCallback = (
92
92
  nymph: Nymph,
93
- error: any
93
+ error: any,
94
94
  ) => Promise<void>;
95
95
  export type NymphBeforeDeleteEntityByIDCallback = (
96
96
  nymph: Nymph,
97
97
  guid: string,
98
- className?: string
98
+ className?: string,
99
99
  ) => Promise<void>;
100
100
  export type NymphAfterDeleteEntityByIDCallback = (
101
101
  nymph: Nymph,
102
- result: Promise<boolean>
102
+ result: Promise<boolean>,
103
103
  ) => Promise<void>;
104
104
  export type NymphFailedDeleteEntityByIDCallback = (
105
105
  nymph: Nymph,
106
- error: any
106
+ error: any,
107
107
  ) => Promise<void>;
108
108
  export type NymphBeforeNewUIDCallback = (
109
109
  nymph: Nymph,
110
- name: string
110
+ name: string,
111
111
  ) => Promise<void>;
112
112
  export type NymphAfterNewUIDCallback = (
113
113
  nymph: Nymph,
114
- result: Promise<number | null>
114
+ result: Promise<number | null>,
115
115
  ) => Promise<void>;
116
116
  export type NymphFailedNewUIDCallback = (
117
117
  nymph: Nymph,
118
- error: any
118
+ error: any,
119
119
  ) => Promise<void>;
120
120
  export type NymphBeforeSetUIDCallback = (
121
121
  nymph: Nymph,
122
122
  name: string,
123
- value: number
123
+ value: number,
124
124
  ) => Promise<void>;
125
125
  export type NymphAfterSetUIDCallback = (
126
126
  nymph: Nymph,
127
- result: Promise<boolean>
127
+ result: Promise<boolean>,
128
128
  ) => Promise<void>;
129
129
  export type NymphFailedSetUIDCallback = (
130
130
  nymph: Nymph,
131
- error: any
131
+ error: any,
132
132
  ) => Promise<void>;
133
133
  export type NymphBeforeRenameUIDCallback = (
134
134
  nymph: Nymph,
135
135
  oldName: string,
136
- newName: string
136
+ newName: string,
137
137
  ) => Promise<void>;
138
138
  export type NymphAfterRenameUIDCallback = (
139
139
  nymph: Nymph,
140
- result: Promise<boolean>
140
+ result: Promise<boolean>,
141
141
  ) => Promise<void>;
142
142
  export type NymphFailedRenameUIDCallback = (
143
143
  nymph: Nymph,
144
- error: any
144
+ error: any,
145
145
  ) => Promise<void>;
146
146
  export type NymphBeforeDeleteUIDCallback = (
147
147
  nymph: Nymph,
148
- name: string
148
+ name: string,
149
149
  ) => Promise<void>;
150
150
  export type NymphAfterDeleteUIDCallback = (
151
151
  nymph: Nymph,
152
- result: Promise<boolean>
152
+ result: Promise<boolean>,
153
153
  ) => Promise<void>;
154
154
  export type NymphFailedDeleteUIDCallback = (
155
155
  nymph: Nymph,
156
- error: any
156
+ error: any,
157
157
  ) => Promise<void>;
158
158
  export type NymphBeforeStartTransactionCallback = (
159
159
  nymph: Nymph,
160
- name: string
160
+ name: string,
161
161
  ) => Promise<void>;
162
162
  export type NymphAfterStartTransactionCallback = (
163
163
  nymph: Nymph,
164
164
  name: string,
165
- result: Nymph
165
+ result: Nymph,
166
166
  ) => Promise<void>;
167
167
  export type NymphBeforeCommitTransactionCallback = (
168
168
  nymph: Nymph,
169
- name: string
169
+ name: string,
170
170
  ) => Promise<void>;
171
171
  export type NymphAfterCommitTransactionCallback = (
172
172
  nymph: Nymph,
173
173
  name: string,
174
- result: boolean
174
+ result: boolean,
175
175
  ) => Promise<void>;
176
176
  export type NymphBeforeRollbackTransactionCallback = (
177
177
  nymph: Nymph,
178
- name: string
178
+ name: string,
179
179
  ) => Promise<void>;
180
180
  export type NymphAfterRollbackTransactionCallback = (
181
181
  nymph: Nymph,
182
182
  name: string,
183
- result: boolean
183
+ result: boolean,
184
184
  ) => Promise<void>;
185
185
 
186
186
  export type Options<T extends EntityConstructor = EntityConstructor> = {
187
+ /**
188
+ * The Entity class to query.
189
+ */
187
190
  class?: T;
191
+ /**
192
+ * The limit of entities to be returned. Not needed when using `getEntity`, as
193
+ * it always returns only one.
194
+ */
188
195
  limit?: number;
196
+ /**
197
+ * The offset from the first matching entity, in order, to start retrieving.
198
+ */
189
199
  offset?: number;
200
+ /**
201
+ * If true, entities will be retrieved from newest to oldest/largest to
202
+ * smallest (with regard to `sort`).
203
+ */
190
204
  reverse?: boolean;
191
- sort?: 'cdate' | 'mdate';
192
- return?: 'entity' | 'guid' | 'count';
205
+ /**
206
+ * How to sort the entities. Should be "cdate", "mdate", or the name of a
207
+ * property.
208
+ *
209
+ * When null, the result set will not be sorted in any particular way. It's
210
+ * really up to the DB how the results are returned. In this case, limit and
211
+ * offset may work for pagination, but shouldn't be relied on.
212
+ */
213
+ sort?: 'cdate' | 'mdate' | string | null;
214
+ /**
215
+ * What to return, the entities with their data, just the entity data, just
216
+ * the GUIDs, or just a count.
217
+ *
218
+ * 'object' is only available on the server, as there are substantial security
219
+ * implications with using it. It returns the full entity data as a simple
220
+ * object. You should only use it for operations where instantiating the
221
+ * entities has an unacceptable performance cost.
222
+ */
223
+ return?: 'entity' | 'object' | 'guid' | 'count';
224
+ /**
225
+ * Will be 'client' if the query came from a REST request or the PubSub
226
+ * server. (Mainly used in Tilmeld for access control.)
227
+ */
193
228
  source?: string;
229
+ /**
230
+ * If true, Nymph will skip the cache and retrieve the entity from the DB.
231
+ */
194
232
  skipCache?: boolean;
233
+ /**
234
+ * The level(s) of access control requested.
235
+ *
236
+ * Limiting the access control can significantly improve the performance of
237
+ * the query.
238
+ */
239
+ acRequest?: TilmeldAccessRequest;
240
+ /**
241
+ * If true, Tilmeld will not filter returned entities according to access
242
+ * controls. (If Tilmeld is installed.) (This is always set to false by the
243
+ * REST endpoint and PubSub server.)
244
+ */
195
245
  skipAc?: boolean;
196
246
  };
197
247
 
@@ -218,6 +268,9 @@ type PrimitiveSelector = {
218
268
  contain?: [string, any];
219
269
  '!contain'?: PrimitiveSelector['contain'];
220
270
 
271
+ search?: [string, string];
272
+ '!search'?: PrimitiveSelector['search'];
273
+
221
274
  match?: [string, string];
222
275
  '!match'?: PrimitiveSelector['match'];
223
276
 
@@ -273,6 +326,9 @@ export type Selector = {
273
326
  contain?: Clause<OrWithTime<PrimitiveSelector['contain']>>;
274
327
  '!contain'?: Clause<OrWithTime<PrimitiveSelector['contain']>>;
275
328
 
329
+ search?: Clause<PrimitiveSelector['search']>;
330
+ '!search'?: Clause<PrimitiveSelector['search']>;
331
+
276
332
  match?: Clause<PrimitiveSelector['match']>;
277
333
  '!match'?: Clause<PrimitiveSelector['match']>;
278
334
 
@@ -328,6 +384,9 @@ export type FormattedSelector = {
328
384
  contain?: PrimitiveSelector['contain'][];
329
385
  '!contain'?: PrimitiveSelector['contain'][];
330
386
 
387
+ search?: PrimitiveSelector['search'][];
388
+ '!search'?: PrimitiveSelector['search'][];
389
+
331
390
  match?: PrimitiveSelector['match'][];
332
391
  '!match'?: PrimitiveSelector['match'][];
333
392
 
@@ -370,6 +429,50 @@ export enum TilmeldAccessLevels {
370
429
  FULL_ACCESS = 4,
371
430
  }
372
431
 
432
+ export enum TilmeldAccessRequest {
433
+ /**
434
+ * The default level, any possible entity readable by the user.
435
+ *
436
+ * This is the same as or-ing together all of the levels listed below.
437
+ */
438
+ ALL_LEVELS = 0,
439
+ /**
440
+ * Owned by and accessible to the user.
441
+ *
442
+ * This is always enabled, so not including it has no effect. It's here to
443
+ * allow the selection of _only_ user owned entities.
444
+ */
445
+ USER_OWNED = 1,
446
+ /**
447
+ * Owned by and accessible to the user's primary group.
448
+ */
449
+ PRIMARY_GROUP_OWNED = 2,
450
+ /**
451
+ * Owned by and accessible to one of the user's secondary groups.
452
+ */
453
+ SECONDARY_GROUP_OWNED = 4,
454
+ /**
455
+ * The user is listed in an access control list.
456
+ */
457
+ USER_ACCESSIBLE = 8,
458
+ /**
459
+ * The user's primary group is listed in an access control list.
460
+ */
461
+ PRIMARY_GROUP_ACCESSIBLE = 16,
462
+ /**
463
+ * One of the user's secondary groups is listed in an access control list.
464
+ */
465
+ SECONDARY_GROUP_ACCESSIBLE = 32,
466
+ /**
467
+ * The entity is accessible to any user.
468
+ */
469
+ OTHER_ACCESSIBLE = 64,
470
+ /**
471
+ * The entity is not owned by anyone.
472
+ */
473
+ UNOWNED = 128,
474
+ }
475
+
373
476
  export interface TilmeldInterface {
374
477
  nymph: Nymph;
375
478
  request: any;
@@ -377,9 +480,12 @@ export interface TilmeldInterface {
377
480
  init(nymph: Nymph): void;
378
481
  clone(): TilmeldInterface;
379
482
  gatekeeper(ability?: string): boolean;
380
- authenticate(): void;
483
+ authenticate(skipXsrfToken?: boolean, skipRenew?: boolean): Promise<boolean>;
381
484
  clearSession(): void;
382
485
  extractToken(token: string): Promise<any>;
383
- fillSession(user: any): void;
384
- checkClientUIDPermissions(name: string, type?: TilmeldAccessLevels): boolean;
486
+ fillSession(user: any): Promise<void>;
487
+ checkClientUIDPermissions(
488
+ name: string,
489
+ type?: TilmeldAccessLevels,
490
+ ): Promise<boolean>;
385
491
  }
@@ -1,8 +1,41 @@
1
- import { Config } from './d';
1
+ import createDebug, { type Debugger } from 'debug';
2
+
3
+ import type { Config } from './index.js';
4
+
5
+ const debuggers: {
6
+ [k: string]: { info: Debugger; log: Debugger; error: Debugger };
7
+ } = {};
8
+
9
+ function getDebuggers(source: string) {
10
+ if (source in debuggers) {
11
+ return debuggers[source];
12
+ }
13
+
14
+ debuggers[source] = {
15
+ info: createDebug(`nymphjs:info:${source}`),
16
+ log: createDebug(`nymphjs:log:${source}`),
17
+ error: createDebug(`nymphjs:error:${source}`),
18
+ };
19
+
20
+ debuggers[source].info.log = console.info.bind(console);
21
+ debuggers[source].log.log = console.log.bind(console);
22
+
23
+ return debuggers[source];
24
+ }
2
25
 
3
26
  export default {
4
27
  cache: false,
5
28
  cacheThreshold: 4,
6
29
  cacheLimit: 50,
7
30
  emptyListError: false,
31
+ updateMDate: true,
32
+ debugInfo: (source, message) => {
33
+ getDebuggers(source).info(message);
34
+ },
35
+ debugLog: (source, message) => {
36
+ getDebuggers(source).log(message);
37
+ },
38
+ debugError: (source, message) => {
39
+ getDebuggers(source).error(message);
40
+ },
8
41
  } as Config;
package/src/conf/index.ts CHANGED
@@ -1,4 +1,52 @@
1
- export { Config } from './d';
1
+ /**
2
+ * Nymph Config
3
+ */
4
+ export interface Config {
5
+ /**
6
+ * Cache recently retrieved entities to speed up database queries. Uses more
7
+ * memory.
8
+ */
9
+ cache: boolean;
10
+ /**
11
+ * Cache entities after they're accessed this many times.
12
+ */
13
+ cacheThreshold: number;
14
+ /**
15
+ * The number of recently retrieved entities to cache. If you're running out
16
+ * of memory, try lowering this value. 0 means unlimited.
17
+ */
18
+ cacheLimit: number;
19
+ /**
20
+ * When querying for multiple entities with NymphREST, if the list is empty,
21
+ * return a 404 error.
22
+ */
23
+ emptyListError: boolean;
24
+ /**
25
+ * Whether to update the mdate property when saving an entity.
26
+ *
27
+ * You should probably only set this to false if you're doing migrations. Some
28
+ * things depend on having updated mdates (like conflict detection to avoid
29
+ * lost updates).
30
+ *
31
+ * If you set this to a number, that amount of milliseconds will be added to
32
+ * the mdate on any entities that are saved. This should solve the lost update
33
+ * problem when set to a small number (~1000), and keep mdates relatively
34
+ * unchanged.
35
+ */
36
+ updateMDate: boolean | number;
37
+ /**
38
+ * A function to log info messages. By default, uses the `debug` package.
39
+ */
40
+ debugInfo: (source: string, message: string) => void;
41
+ /**
42
+ * A function to log debug messages. By default, uses the `debug` package.
43
+ */
44
+ debugLog: (source: string, message: string) => void;
45
+ /**
46
+ * A function to log error messages. By default, uses the `debug` package.
47
+ */
48
+ debugError: (source: string, message: string) => void;
49
+ }
2
50
 
3
- import defaults from './defaults';
51
+ import defaults from './defaults.js';
4
52
  export { defaults as ConfigDefaults };
@@ -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
+ }