@smartsoft001/domain-core 2.75.0 → 2.80.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.
package/index.cjs ADDED
@@ -0,0 +1,152 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // packages/shared/domain-core/src/index.ts
31
+ var src_exports = {};
32
+ __export(src_exports, {
33
+ AndSpecification: () => AndSpecification,
34
+ BasicSpecification: () => BasicSpecification,
35
+ DomainForbiddenError: () => DomainForbiddenError,
36
+ DomainValidationError: () => DomainValidationError,
37
+ IAttachmentRepository: () => IAttachmentRepository,
38
+ IItemRepository: () => IItemRepository,
39
+ IUnitOfWork: () => IUnitOfWork,
40
+ MergeSpecification: () => MergeSpecification,
41
+ OrSpecification: () => OrSpecification
42
+ });
43
+ module.exports = __toCommonJS(src_exports);
44
+
45
+ // packages/shared/models/src/lib/symbols.ts
46
+ var SYMBOL_MODEL = Symbol.for("smartsoft:model");
47
+ var SYMBOL_FIELD = Symbol.for("smartsoft:field");
48
+
49
+ // packages/shared/models/src/lib/decorators/model/model.decorator.ts
50
+ var import_reflect_metadata = require("reflect-metadata");
51
+
52
+ // packages/shared/models/src/lib/decorators/field/field.decorator.ts
53
+ var import_reflect_metadata2 = require("reflect-metadata");
54
+
55
+ // packages/shared/utils/src/lib/services/password/password.service.ts
56
+ var md5_ = __toESM(require("md5"));
57
+
58
+ // packages/shared/utils/src/lib/services/object/object.service.ts
59
+ var import_flatted = require("flatted");
60
+
61
+ // packages/shared/utils/src/lib/services/guid/guid.service.ts
62
+ var import_guid_typescript = require("guid-typescript");
63
+
64
+ // packages/shared/utils/src/lib/services/array/array.service.ts
65
+ var _ = __toESM(require("lodash"));
66
+
67
+ // packages/shared/domain-core/src/lib/errors.ts
68
+ var DomainValidationError = class _DomainValidationError extends Error {
69
+ constructor(msg) {
70
+ super(msg);
71
+ this.type = _DomainValidationError;
72
+ }
73
+ };
74
+ var DomainForbiddenError = class _DomainForbiddenError extends Error {
75
+ constructor(msg) {
76
+ super(msg);
77
+ this.type = _DomainForbiddenError;
78
+ }
79
+ };
80
+
81
+ // packages/shared/domain-core/src/lib/repositories.ts
82
+ var IUnitOfWork = class {
83
+ };
84
+ var IItemRepository = class {
85
+ };
86
+ var IAttachmentRepository = class {
87
+ };
88
+
89
+ // packages/shared/domain-core/src/lib/specifications.ts
90
+ var BasicSpecification = class {
91
+ /**
92
+ * Creates an instance of BasicSpecification.
93
+ *
94
+ * @param {any} criteria - The criteria used to filter entities.
95
+ */
96
+ constructor(criteria) {
97
+ this.criteria = criteria;
98
+ }
99
+ };
100
+ var MergeSpecification = class extends BasicSpecification {
101
+ /**
102
+ * Creates an instance of MergeSpecification.
103
+ *
104
+ * @param {...ISpecification[]} specs - An array of specifications to be merged.
105
+ */
106
+ constructor(...specs) {
107
+ let criteria = {};
108
+ specs.forEach((spec) => {
109
+ criteria = {
110
+ ...criteria,
111
+ ...spec.criteria
112
+ };
113
+ });
114
+ super(criteria);
115
+ }
116
+ };
117
+ var OrSpecification = class extends BasicSpecification {
118
+ /**
119
+ * Creates an instance of OrSpecification.
120
+ *
121
+ * @param {...ISpecification[]} spec - An array of specifications to be combined using a logical OR.
122
+ */
123
+ constructor(...spec) {
124
+ super({
125
+ $or: spec.map((c) => c.criteria)
126
+ });
127
+ }
128
+ };
129
+ var AndSpecification = class extends BasicSpecification {
130
+ /**
131
+ * Creates an instance of AndSpecification.
132
+ *
133
+ * @param {...ISpecification[]} spec - An array of specifications to be combined using a logical AND.
134
+ */
135
+ constructor(...spec) {
136
+ super({
137
+ $and: spec.map((c) => c.criteria)
138
+ });
139
+ }
140
+ };
141
+ // Annotate the CommonJS export names for ESM import in node:
142
+ 0 && (module.exports = {
143
+ AndSpecification,
144
+ BasicSpecification,
145
+ DomainForbiddenError,
146
+ DomainValidationError,
147
+ IAttachmentRepository,
148
+ IItemRepository,
149
+ IUnitOfWork,
150
+ MergeSpecification,
151
+ OrSpecification
152
+ });
package/package.json CHANGED
@@ -2,11 +2,23 @@
2
2
  "name": "@smartsoft001/domain-core",
3
3
  "type": "commonjs",
4
4
  "dependencies": {
5
+ "@angular/core": "20.1.0",
6
+ "@smartsoft001/models": "^2.76.0",
7
+ "@smartsoft001/users": "^2.76.0",
8
+ "flatted": "3.3.3",
9
+ "guid-typescript": "^1.0.9",
10
+ "lodash": "4.17.21",
11
+ "md5": "^2.3.0",
12
+ "reflect-metadata": "^0.2.1",
5
13
  "rxjs": "^7.8.1",
6
- "@smartsoft001/models": "^2.75.0",
7
- "@smartsoft001/users": "^2.75.0"
14
+ "tslib": "^2.3.0"
8
15
  },
9
- "version": "2.75.0",
16
+ "version": "2.80.0",
10
17
  "main": "./src/index.js",
11
- "typings": "./src/index.d.ts"
12
- }
18
+ "typings": "./src/index.d.ts",
19
+ "resolutions": {
20
+ "paypal-rest-sdk": "1.8.1",
21
+ "tslib": "^2.3.0",
22
+ "zone.js": "~0.15.0"
23
+ }
24
+ }
@@ -0,0 +1,8 @@
1
+ export declare class DomainValidationError extends Error {
2
+ type: typeof DomainValidationError;
3
+ constructor(msg: string);
4
+ }
5
+ export declare class DomainForbiddenError extends Error {
6
+ type: typeof DomainForbiddenError;
7
+ constructor(msg: string);
8
+ }
@@ -0,0 +1,18 @@
1
+ export interface IEntity<T> {
2
+ id: T;
3
+ }
4
+ export interface IAddress {
5
+ city: string;
6
+ street: string;
7
+ buildingNumber: string;
8
+ flatNumber?: string;
9
+ zipCode: string;
10
+ }
11
+ export interface IDateRange {
12
+ start: `${string}-${string}-${string}`;
13
+ end: `${string}-${string}-${string}`;
14
+ }
15
+ export interface IFactory<T, TConfig> {
16
+ create(config: NonNullable<TConfig>): Promise<T>;
17
+ }
18
+ export { ISpecification } from '@smartsoft001/models';
@@ -0,0 +1,320 @@
1
+ import { Observable } from 'rxjs';
2
+ import { IUser } from '@smartsoft001/users';
3
+ import { IEntity, ISpecification } from './interfaces';
4
+ /**
5
+ * ITransaction defines the structure of a transaction context that can be used to
6
+ * perform a series of database operations as a single atomic unit of work.
7
+ *
8
+ * @interface ITransaction
9
+ */
10
+ export interface ITransaction {
11
+ /**
12
+ * The connection object that provides access to the database.
13
+ *
14
+ * This property typically represents the active database connection that is used
15
+ * to execute operations within the transaction. The exact type of the connection
16
+ * may vary depending on the database being used (e.g., MongoDB, SQL, etc.).
17
+ *
18
+ * @type {any}
19
+ */
20
+ connection: any;
21
+ }
22
+ /**
23
+ * IUnitOfWork is an abstract class that defines the contract for managing transactional operations
24
+ * across multiple repositories or services. This pattern ensures that a set of operations either
25
+ * all succeed or all fail, maintaining data consistency.
26
+ *
27
+ * @interface IUnitOfWork
28
+ */
29
+ export declare abstract class IUnitOfWork {
30
+ /**
31
+ * Executes a set of operations within a transactional scope.
32
+ *
33
+ * @param {function(ITransaction): Promise<void>} definition - A function that contains the operations to be executed within the transaction.
34
+ * The function receives an `ITransaction` object that provides the necessary context for the transaction.
35
+ *
36
+ * @returns {Promise<void>} - A promise that resolves when the transactional operations are completed successfully.
37
+ *
38
+ * @throws {Error} - Throws an error if the transaction fails, in which case all operations are rolled back.
39
+ *
40
+ * @example
41
+ * await this.unitOfWork.scope(async (tx) => {
42
+ * await this.itemRepository.updatePartial(
43
+ * {
44
+ * id: id1,
45
+ * status: "finished",
46
+ * },
47
+ * user,
48
+ * { transaction: tx }
49
+ * );
50
+ *
51
+ * await this.itemRepository.updatePartial(
52
+ * {
53
+ * id: id2,
54
+ * status: "finished",
55
+ * },
56
+ * user,
57
+ * { transaction: tx }
58
+ * );
59
+ * });
60
+ */
61
+ abstract scope(definition: (transaction: ITransaction) => Promise<void>): Promise<void>;
62
+ }
63
+ /**
64
+ *
65
+ * @interface IItemRepositoryOptions
66
+ */
67
+ export interface IItemRepositoryOptions {
68
+ transaction: ITransaction;
69
+ }
70
+ /**
71
+ * IItemRepository is an abstract class that defines the contract for a repository
72
+ * responsible for managing entities of type `T` in a storage system. This repository
73
+ * interface provides a set of methods for creating, updating, deleting, and querying
74
+ * entities, with support for transactional operations and various query criteria.
75
+ *
76
+ * @template T - The type of entity that this repository will manage. The entity should extend `IEntity<string>`.
77
+ *
78
+ * @interface IItemRepository
79
+ */
80
+ export declare abstract class IItemRepository<T extends IEntity<string>> {
81
+ /**
82
+ * Creates a new entity in the storage system.
83
+ *
84
+ * @param {T} item - The entity to be created.
85
+ * @param {IUser} user - The user performing the operation.
86
+ * @param {IItemRepositoryOptions} [options] - Optional parameters for the operation, including transaction context.
87
+ *
88
+ * @returns {Promise<void>} - A promise that resolves when the entity is successfully created.
89
+ */
90
+ abstract create(item: T, user: IUser | null, options?: IItemRepositoryOptions): Promise<void>;
91
+ /**
92
+ * Creates multiple entities in the storage system.
93
+ *
94
+ * @param {T[]} list - The list of entities to be created.
95
+ * @param {IUser} user - The user performing the operation.
96
+ * @param {IItemRepositoryOptions} [options] - Optional parameters for the operation, including transaction context.
97
+ *
98
+ * @returns {Promise<void>} - A promise that resolves when all entities are successfully created.
99
+ */
100
+ abstract createMany(list: T[], user: IUser, options?: IItemRepositoryOptions): Promise<void>;
101
+ /**
102
+ * Updates an existing entity in the storage system.
103
+ *
104
+ * @param {T} item - The entity to be updated.
105
+ * @param {IUser} user - The user performing the operation.
106
+ * @param {IItemRepositoryOptions} [options] - Optional parameters for the operation, including transaction context.
107
+ *
108
+ * @returns {Promise<void>} - A promise that resolves when the entity is successfully updated.
109
+ */
110
+ abstract update(item: T, user: IUser | null, options?: IItemRepositoryOptions): Promise<void>;
111
+ /**
112
+ * Partially updates an existing entity in the storage system.
113
+ *
114
+ * @param {Partial<T> & { id: string }} item - The partial entity data to be updated along with the entity's ID.
115
+ * @param {IUser} user - The user performing the operation.
116
+ * @param {IItemRepositoryOptions} [options] - Optional parameters for the operation, including transaction context.
117
+ *
118
+ * @returns {Promise<void>} - A promise that resolves when the entity is successfully updated.
119
+ */
120
+ abstract updatePartial(item: Partial<T> & {
121
+ id: string;
122
+ }, user: IUser | null, options?: IItemRepositoryOptions): Promise<void>;
123
+ /**
124
+ * Partially updates multiple entities that match the specified criteria.
125
+ *
126
+ * @param {any} criteria - The criteria used to select the entities to be updated.
127
+ * @param {Partial<T>} set - The partial data to be set on the matching entities.
128
+ * @param {IUser} user - The user performing the operation.
129
+ * @param {IItemRepositoryOptions} [options] - Optional parameters for the operation, including transaction context.
130
+ *
131
+ * @returns {Promise<void>} - A promise that resolves when the entities are successfully updated.
132
+ */
133
+ abstract updatePartialManyByCriteria(criteria: any, set: Partial<T>, user: IUser, options?: IItemRepositoryOptions): Promise<void>;
134
+ /**
135
+ * Partially updates multiple entities that match the specified specification.
136
+ *
137
+ * @param {ISpecification} spec - The specification used to select the entities to be updated.
138
+ * @param {Partial<T>} set - The partial data to be set on the matching entities.
139
+ * @param {IUser} user - The user performing the operation.
140
+ * @param {IItemRepositoryOptions} [options] - Optional parameters for the operation, including transaction context.
141
+ *
142
+ * @returns {Promise<void>} - A promise that resolves when the entities are successfully updated.
143
+ */
144
+ abstract updatePartialManyBySpecification(spec: ISpecification, set: Partial<T>, user: IUser, options?: IItemRepositoryOptions): Promise<void>;
145
+ /**
146
+ * Deletes an entity from the storage system by its ID.
147
+ *
148
+ * @param {string} id - The ID of the entity to be deleted.
149
+ * @param {IUser} user - The user performing the operation.
150
+ * @param {IItemRepositoryOptions} [options] - Optional parameters for the operation, including transaction context.
151
+ *
152
+ * @returns {Promise<void>} - A promise that resolves when the entity is successfully deleted.
153
+ */
154
+ abstract delete(id: string, user: IUser, options?: IItemRepositoryOptions): Promise<void>;
155
+ /**
156
+ * Retrieves an entity from the storage system by its ID.
157
+ *
158
+ * @param {string} id - The ID of the entity to be retrieved.
159
+ * @param {IItemRepositoryOptions} [repoOptions] - Optional parameters for the operation, including transaction context.
160
+ *
161
+ * @returns {Promise<T>} - A promise that resolves to the retrieved entity.
162
+ */
163
+ abstract getById(id: string, repoOptions?: IItemRepositoryOptions): Promise<T>;
164
+ /**
165
+ * Retrieves entities from the storage system that match the specified criteria.
166
+ *
167
+ * @param {any} criteria - The criteria used to select the entities.
168
+ * @param {any} [options] - Optional parameters for the operation, such as pagination or sorting.
169
+ *
170
+ * @returns {Promise<{ data: T[]; totalCount: number }>} - A promise that resolves to an object containing the matching entities and the total count.
171
+ */
172
+ abstract getByCriteria(criteria: any, options?: any): Promise<{
173
+ data: T[];
174
+ totalCount: number;
175
+ }>;
176
+ /**
177
+ * Retrieves entities from the storage system that match the specified specification.
178
+ *
179
+ * @param {ISpecification} spec - The specification used to select the entities.
180
+ * @param {any} [options] - Optional parameters for the operation, such as pagination or sorting.
181
+ *
182
+ * @returns {Promise<{ data: T[]; totalCount: number }>} - A promise that resolves to an object containing the matching entities and the total count.
183
+ */
184
+ abstract getBySpecification(spec: ISpecification, options?: any): Promise<{
185
+ data: T[];
186
+ totalCount: number;
187
+ }>;
188
+ /**
189
+ * Counts the number of entities in the storage system that match the specified criteria.
190
+ *
191
+ * @param {any} criteria - The criteria used to count the entities.
192
+ *
193
+ * @returns {Promise<number>} - A promise that resolves to the count of matching entities.
194
+ */
195
+ abstract countByCriteria(criteria: any): Promise<number>;
196
+ /**
197
+ * Counts the number of entities in the storage system that match the specified specification.
198
+ *
199
+ * @param {ISpecification} spec - The specification used to count the entities.
200
+ *
201
+ * @returns {Promise<number>} - A promise that resolves to the count of matching entities.
202
+ */
203
+ abstract countBySpecification(spec: ISpecification): Promise<number>;
204
+ /**
205
+ * Clears all entities from the storage system.
206
+ *
207
+ * @param user
208
+ *
209
+ * @returns {Promise<void>} - A promise that resolves when the storage system is cleared.
210
+ */
211
+ abstract clear(user: IUser | IItemRepositoryOptions): Promise<void>;
212
+ /**
213
+ * Returns an observable that emits changes to entities that match the specified criteria.
214
+ *
215
+ * @param {any} criteria - The criteria used to select the entities to observe.
216
+ *
217
+ * @returns {Observable<any>} - An observable that emits changes to the matching entities.
218
+ */
219
+ abstract changesByCriteria(criteria: {
220
+ id?: string;
221
+ }): Observable<any>;
222
+ }
223
+ /**
224
+ * IAttachmentRepository is an abstract class that defines the contract for managing file attachments
225
+ * in a storage system. This interface can be implemented to work with various storage backends, such as
226
+ * MongoDB GridFS, Amazon S3, Google Cloud Storage, or any other file storage solution.
227
+ *
228
+ * @template T - The type of the entity that this repository will manage. The entity should extend `IEntity<string>`.
229
+ *
230
+ * @interface IAttachmentRepository
231
+ */
232
+ export declare abstract class IAttachmentRepository<T extends IEntity<string>> {
233
+ /**
234
+ * Uploads a file to the storage system.
235
+ *
236
+ * @param {Object} data - The data required to upload the file.
237
+ * @param {string} data.id - A unique identifier for the file.
238
+ * @param {string} data.fileName - The name of the file to be uploaded.
239
+ * @param {Stream} data.stream - The stream of the file to be uploaded.
240
+ * @param {string} data.mimeType - The MIME type of the file.
241
+ * @param {string} data.encoding - The encoding of the file.
242
+ *
243
+ * @param {Object} [options] - Optional parameters for the upload.
244
+ * @param {Function} [options.streamCallback] - A callback function that gets invoked with the upload stream.
245
+ *
246
+ * @returns {Promise<void>} - A promise that resolves when the upload is complete.
247
+ *
248
+ * @throws {Error} - Throws an error if the upload fails.
249
+ *
250
+ * @example
251
+ * // Example usage with MongoDB implementation:
252
+ * const repository = new MongoAttachmentRepository(config);
253
+ *
254
+ * const fileStream = fs.createReadStream('/path/to/file');
255
+ *
256
+ * await repository.upload({
257
+ * id: 'unique-file-id',
258
+ * fileName: 'example.txt',
259
+ * stream: fileStream,
260
+ * mimeType: 'text/plain',
261
+ * encoding: 'utf-8'
262
+ * }, {
263
+ * streamCallback: (writeStream) => {
264
+ * console.log('Upload started');
265
+ * }
266
+ * });
267
+ *
268
+ * console.log('File uploaded successfully');
269
+ */
270
+ abstract upload(data: {
271
+ id: string;
272
+ fileName: string;
273
+ stream: any;
274
+ mimeType: string;
275
+ encoding: string;
276
+ }, options?: {
277
+ streamCallback?: (r: any) => void;
278
+ }): Promise<void>;
279
+ /**
280
+ * Retrieves metadata information about a file stored in the storage system.
281
+ *
282
+ * @param {string} id - The unique identifier of the file.
283
+ *
284
+ * @returns {Promise<{ fileName: string, contentType: string, length: number } | null>}
285
+ * - A promise that resolves to an object containing file metadata, or `null` if the file is not found.
286
+ *
287
+ * @throws {Error} - Throws an error if retrieving the file information fails.
288
+ */
289
+ abstract getInfo(id: string): Promise<{
290
+ fileName: string;
291
+ contentType: string;
292
+ length: number;
293
+ }>;
294
+ /**
295
+ * Retrieves a stream for downloading a file from the storage system.
296
+ *
297
+ * @param {string} id - The unique identifier of the file.
298
+ * @param {Object} [options] - Optional parameters for retrieving a specific range of the file.
299
+ * @param {number} [options.start] - The starting byte position for the stream.
300
+ * @param {number} [options.end] - The ending byte position for the stream.
301
+ *
302
+ * @returns {Promise<any>} - A promise that resolves to a readable stream of the file.
303
+ *
304
+ * @throws {Error} - Throws an error if retrieving the file stream fails.
305
+ */
306
+ abstract getStream(id: string, options?: {
307
+ start: number;
308
+ end: number;
309
+ }): Promise<any>;
310
+ /**
311
+ * Deletes a file from the storage system.
312
+ *
313
+ * @param {string} id - The unique identifier of the file to be deleted.
314
+ *
315
+ * @returns {Promise<void>} - A promise that resolves when the file has been successfully deleted.
316
+ *
317
+ * @throws {Error} - Throws an error if the deletion fails.
318
+ */
319
+ abstract delete(id: string): Promise<void>;
320
+ }
@@ -1,5 +1,4 @@
1
1
  import { ISpecification } from './interfaces';
2
-
3
2
  /**
4
3
  * BasicSpecification is a class that implements the `ISpecification` interface and serves as a base
5
4
  * class for defining basic query criteria. It holds a single set of criteria that can be used to
@@ -11,15 +10,15 @@ import { ISpecification } from './interfaces';
11
10
  * @class BasicSpecification
12
11
  * @implements {ISpecification}
13
12
  */
14
- export class BasicSpecification implements ISpecification {
15
- /**
16
- * Creates an instance of BasicSpecification.
17
- *
18
- * @param {any} criteria - The criteria used to filter entities.
19
- */
20
- constructor(public readonly criteria: any) {}
13
+ export declare class BasicSpecification implements ISpecification {
14
+ readonly criteria: any;
15
+ /**
16
+ * Creates an instance of BasicSpecification.
17
+ *
18
+ * @param {any} criteria - The criteria used to filter entities.
19
+ */
20
+ constructor(criteria: any);
21
21
  }
22
-
23
22
  /**
24
23
  * MergeSpecification is a class that extends `BasicSpecification` and allows for the merging of multiple
25
24
  * specifications into a single specification. The resulting specification combines the criteria of all provided
@@ -30,26 +29,14 @@ export class BasicSpecification implements ISpecification {
30
29
  * @class MergeSpecification
31
30
  * @extends {BasicSpecification}
32
31
  */
33
- export class MergeSpecification extends BasicSpecification {
34
- /**
35
- * Creates an instance of MergeSpecification.
36
- *
37
- * @param {...ISpecification[]} specs - An array of specifications to be merged.
38
- */
39
- constructor(...specs: Array<ISpecification>) {
40
- let criteria = {};
41
-
42
- specs.forEach((spec) => {
43
- criteria = {
44
- ...criteria,
45
- ...spec.criteria,
46
- };
47
- });
48
-
49
- super(criteria);
50
- }
32
+ export declare class MergeSpecification extends BasicSpecification {
33
+ /**
34
+ * Creates an instance of MergeSpecification.
35
+ *
36
+ * @param {...ISpecification[]} specs - An array of specifications to be merged.
37
+ */
38
+ constructor(...specs: Array<ISpecification>);
51
39
  }
52
-
53
40
  /**
54
41
  * OrSpecification is a class that extends `BasicSpecification` and allows for the combination of
55
42
  * multiple specifications using a logical OR operation. The resulting specification matches entities that satisfy
@@ -61,19 +48,14 @@ export class MergeSpecification extends BasicSpecification {
61
48
  * @class OrSpecification
62
49
  * @extends {BasicSpecification}
63
50
  */
64
- export class OrSpecification extends BasicSpecification {
65
- /**
66
- * Creates an instance of OrSpecification.
67
- *
68
- * @param {...ISpecification[]} spec - An array of specifications to be combined using a logical OR.
69
- */
70
- constructor(...spec: Array<ISpecification>) {
71
- super({
72
- $or: spec.map((c) => c.criteria),
73
- });
74
- }
51
+ export declare class OrSpecification extends BasicSpecification {
52
+ /**
53
+ * Creates an instance of OrSpecification.
54
+ *
55
+ * @param {...ISpecification[]} spec - An array of specifications to be combined using a logical OR.
56
+ */
57
+ constructor(...spec: Array<ISpecification>);
75
58
  }
76
-
77
59
  /**
78
60
  * AndSpecification is a class that extends `BasicSpecification` and allows for the combination of
79
61
  * multiple specifications using a logical AND operation. The resulting specification matches entities that satisfy
@@ -85,15 +67,11 @@ export class OrSpecification extends BasicSpecification {
85
67
  * @class AndSpecification
86
68
  * @extends {BasicSpecification}
87
69
  */
88
- export class AndSpecification extends BasicSpecification {
89
- /**
90
- * Creates an instance of AndSpecification.
91
- *
92
- * @param {...ISpecification[]} spec - An array of specifications to be combined using a logical AND.
93
- */
94
- constructor(...spec: Array<ISpecification>) {
95
- super({
96
- $and: spec.map((c) => c.criteria),
97
- });
98
- }
70
+ export declare class AndSpecification extends BasicSpecification {
71
+ /**
72
+ * Creates an instance of AndSpecification.
73
+ *
74
+ * @param {...ISpecification[]} spec - An array of specifications to be combined using a logical AND.
75
+ */
76
+ constructor(...spec: Array<ISpecification>);
99
77
  }
package/.eslintrc.json DELETED
@@ -1,24 +0,0 @@
1
- {
2
- "extends": ["../../../.eslintrc.json"],
3
- "ignorePatterns": ["!**/*"],
4
- "rules": {},
5
- "overrides": [
6
- {
7
- "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
8
- "rules": {}
9
- },
10
- {
11
- "files": ["*.ts", "*.tsx"],
12
- "rules": {}
13
- },
14
- {
15
- "files": ["*.js", "*.jsx"],
16
- "rules": {}
17
- },
18
- {
19
- "files": "package.json",
20
- "parser": "jsonc-eslint-parser",
21
- "rules": {}
22
- }
23
- ]
24
- }
package/jest.config.ts DELETED
@@ -1,11 +0,0 @@
1
- /* eslint-disable */
2
- export default {
3
- displayName: 'domain-core',
4
- preset: '../../../jest.preset.js',
5
- testEnvironment: 'node',
6
- transform: {
7
- '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }],
8
- },
9
- moduleFileExtensions: ['ts', 'js', 'html'],
10
- coverageDirectory: '../../../coverage/packages/shared/domain-core',
11
- };
package/project.json DELETED
@@ -1,46 +0,0 @@
1
- {
2
- "name": "domain-core",
3
- "$schema": "../../../node_modules/nx/schemas/project-schema.json",
4
- "sourceRoot": "packages/shared/domain-core/src",
5
- "projectType": "library",
6
- "tags": [],
7
- "targets": {
8
- "build": {
9
- "executor": "@nx/esbuild:esbuild",
10
- "outputs": ["{options.outputPath}"],
11
- "options": {
12
- "outputPath": "dist/packages/shared/domain-core",
13
- "main": "packages/shared/domain-core/src/index.ts",
14
- "tsConfig": "packages/shared/domain-core/tsconfig.lib.json",
15
- "assets": ["packages/shared/domain-core/*.md"],
16
- "generatePackageJson": true,
17
- "format": ["cjs"]
18
- }
19
- },
20
- "deploy": {
21
- "executor": "ngx-deploy-npm:deploy",
22
- "options": {
23
- "access": "public",
24
- "distFolderPath": "dist/packages/shared/domain-core"
25
- },
26
- "dependsOn": ["build"]
27
- },
28
- "lint": {
29
- "executor": "@nx/eslint:lint",
30
- "outputs": ["{options.outputFile}"],
31
- "options": {
32
- "lintFilePatterns": [
33
- "packages/shared/domain-core/**/*.{ts,tsx,js,jsx}",
34
- "packages/shared/domain-core/package.json"
35
- ]
36
- }
37
- },
38
- "test": {
39
- "executor": "@nx/jest:jest",
40
- "outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
41
- "options": {
42
- "jestConfig": "packages/shared/domain-core/jest.config.ts"
43
- }
44
- }
45
- }
46
- }
package/src/lib/errors.ts DELETED
@@ -1,15 +0,0 @@
1
- export class DomainValidationError extends Error {
2
- type = DomainValidationError;
3
-
4
- constructor(msg: string) {
5
- super(msg);
6
- }
7
- }
8
-
9
- export class DomainForbiddenError extends Error {
10
- type = DomainForbiddenError;
11
-
12
- constructor(msg: string) {
13
- super(msg);
14
- }
15
- }
@@ -1,40 +0,0 @@
1
- /*
2
- * Interface for entities
3
- */
4
- export interface IEntity<T> {
5
- id: T;
6
- }
7
-
8
- /*
9
- * Address interface
10
- */
11
- export interface IAddress {
12
- city: string;
13
- street: string;
14
- buildingNumber: string;
15
- flatNumber?: string;
16
- zipCode: string;
17
- }
18
-
19
- /*
20
- * Date range interface
21
- */
22
- export interface IDateRange {
23
- /*
24
- * YYYY-MM-DD
25
- */
26
- start: `${string}-${string}-${string}`;
27
- /*
28
- * YYYY-MM-DD
29
- */
30
- end: `${string}-${string}-${string}`;
31
- }
32
-
33
- /*
34
- * Factory interface
35
- */
36
- export interface IFactory<T, TConfig> {
37
- create(config: NonNullable<TConfig>): Promise<T>;
38
- }
39
-
40
- export { ISpecification } from '@smartsoft001/models';
@@ -1,373 +0,0 @@
1
- import { Observable } from 'rxjs';
2
-
3
- import { IUser } from '@smartsoft001/users';
4
-
5
- import { IEntity, ISpecification } from './interfaces';
6
-
7
- /**
8
- * ITransaction defines the structure of a transaction context that can be used to
9
- * perform a series of database operations as a single atomic unit of work.
10
- *
11
- * @interface ITransaction
12
- */
13
- export interface ITransaction {
14
- /**
15
- * The connection object that provides access to the database.
16
- *
17
- * This property typically represents the active database connection that is used
18
- * to execute operations within the transaction. The exact type of the connection
19
- * may vary depending on the database being used (e.g., MongoDB, SQL, etc.).
20
- *
21
- * @type {any}
22
- */
23
- connection: any;
24
- }
25
-
26
- /**
27
- * IUnitOfWork is an abstract class that defines the contract for managing transactional operations
28
- * across multiple repositories or services. This pattern ensures that a set of operations either
29
- * all succeed or all fail, maintaining data consistency.
30
- *
31
- * @interface IUnitOfWork
32
- */
33
- export abstract class IUnitOfWork {
34
- /**
35
- * Executes a set of operations within a transactional scope.
36
- *
37
- * @param {function(ITransaction): Promise<void>} definition - A function that contains the operations to be executed within the transaction.
38
- * The function receives an `ITransaction` object that provides the necessary context for the transaction.
39
- *
40
- * @returns {Promise<void>} - A promise that resolves when the transactional operations are completed successfully.
41
- *
42
- * @throws {Error} - Throws an error if the transaction fails, in which case all operations are rolled back.
43
- *
44
- * @example
45
- * await this.unitOfWork.scope(async (tx) => {
46
- * await this.itemRepository.updatePartial(
47
- * {
48
- * id: id1,
49
- * status: "finished",
50
- * },
51
- * user,
52
- * { transaction: tx }
53
- * );
54
- *
55
- * await this.itemRepository.updatePartial(
56
- * {
57
- * id: id2,
58
- * status: "finished",
59
- * },
60
- * user,
61
- * { transaction: tx }
62
- * );
63
- * });
64
- */
65
- abstract scope(
66
- definition: (transaction: ITransaction) => Promise<void>,
67
- ): Promise<void>;
68
- }
69
-
70
- /**
71
- *
72
- * @interface IItemRepositoryOptions
73
- */
74
- export interface IItemRepositoryOptions {
75
- transaction: ITransaction;
76
- }
77
-
78
- /**
79
- * IItemRepository is an abstract class that defines the contract for a repository
80
- * responsible for managing entities of type `T` in a storage system. This repository
81
- * interface provides a set of methods for creating, updating, deleting, and querying
82
- * entities, with support for transactional operations and various query criteria.
83
- *
84
- * @template T - The type of entity that this repository will manage. The entity should extend `IEntity<string>`.
85
- *
86
- * @interface IItemRepository
87
- */
88
- export abstract class IItemRepository<T extends IEntity<string>> {
89
- /**
90
- * Creates a new entity in the storage system.
91
- *
92
- * @param {T} item - The entity to be created.
93
- * @param {IUser} user - The user performing the operation.
94
- * @param {IItemRepositoryOptions} [options] - Optional parameters for the operation, including transaction context.
95
- *
96
- * @returns {Promise<void>} - A promise that resolves when the entity is successfully created.
97
- */
98
- abstract create(
99
- item: T,
100
- user: IUser | null,
101
- options?: IItemRepositoryOptions,
102
- ): Promise<void>;
103
-
104
- /**
105
- * Creates multiple entities in the storage system.
106
- *
107
- * @param {T[]} list - The list of entities to be created.
108
- * @param {IUser} user - The user performing the operation.
109
- * @param {IItemRepositoryOptions} [options] - Optional parameters for the operation, including transaction context.
110
- *
111
- * @returns {Promise<void>} - A promise that resolves when all entities are successfully created.
112
- */
113
- abstract createMany(
114
- list: T[],
115
- user: IUser,
116
- options?: IItemRepositoryOptions,
117
- ): Promise<void>;
118
-
119
- /**
120
- * Updates an existing entity in the storage system.
121
- *
122
- * @param {T} item - The entity to be updated.
123
- * @param {IUser} user - The user performing the operation.
124
- * @param {IItemRepositoryOptions} [options] - Optional parameters for the operation, including transaction context.
125
- *
126
- * @returns {Promise<void>} - A promise that resolves when the entity is successfully updated.
127
- */
128
- abstract update(
129
- item: T,
130
- user: IUser | null,
131
- options?: IItemRepositoryOptions,
132
- ): Promise<void>;
133
-
134
- /**
135
- * Partially updates an existing entity in the storage system.
136
- *
137
- * @param {Partial<T> & { id: string }} item - The partial entity data to be updated along with the entity's ID.
138
- * @param {IUser} user - The user performing the operation.
139
- * @param {IItemRepositoryOptions} [options] - Optional parameters for the operation, including transaction context.
140
- *
141
- * @returns {Promise<void>} - A promise that resolves when the entity is successfully updated.
142
- */
143
- abstract updatePartial(
144
- item: Partial<T> & { id: string },
145
- user: IUser | null,
146
- options?: IItemRepositoryOptions,
147
- ): Promise<void>;
148
-
149
- /**
150
- * Partially updates multiple entities that match the specified criteria.
151
- *
152
- * @param {any} criteria - The criteria used to select the entities to be updated.
153
- * @param {Partial<T>} set - The partial data to be set on the matching entities.
154
- * @param {IUser} user - The user performing the operation.
155
- * @param {IItemRepositoryOptions} [options] - Optional parameters for the operation, including transaction context.
156
- *
157
- * @returns {Promise<void>} - A promise that resolves when the entities are successfully updated.
158
- */
159
- abstract updatePartialManyByCriteria(
160
- criteria: any,
161
- set: Partial<T>,
162
- user: IUser,
163
- options?: IItemRepositoryOptions,
164
- ): Promise<void>;
165
-
166
- /**
167
- * Partially updates multiple entities that match the specified specification.
168
- *
169
- * @param {ISpecification} spec - The specification used to select the entities to be updated.
170
- * @param {Partial<T>} set - The partial data to be set on the matching entities.
171
- * @param {IUser} user - The user performing the operation.
172
- * @param {IItemRepositoryOptions} [options] - Optional parameters for the operation, including transaction context.
173
- *
174
- * @returns {Promise<void>} - A promise that resolves when the entities are successfully updated.
175
- */
176
- abstract updatePartialManyBySpecification(
177
- spec: ISpecification,
178
- set: Partial<T>,
179
- user: IUser,
180
- options?: IItemRepositoryOptions,
181
- ): Promise<void>;
182
-
183
- /**
184
- * Deletes an entity from the storage system by its ID.
185
- *
186
- * @param {string} id - The ID of the entity to be deleted.
187
- * @param {IUser} user - The user performing the operation.
188
- * @param {IItemRepositoryOptions} [options] - Optional parameters for the operation, including transaction context.
189
- *
190
- * @returns {Promise<void>} - A promise that resolves when the entity is successfully deleted.
191
- */
192
- abstract delete(
193
- id: string,
194
- user: IUser,
195
- options?: IItemRepositoryOptions,
196
- ): Promise<void>;
197
-
198
- /**
199
- * Retrieves an entity from the storage system by its ID.
200
- *
201
- * @param {string} id - The ID of the entity to be retrieved.
202
- * @param {IItemRepositoryOptions} [repoOptions] - Optional parameters for the operation, including transaction context.
203
- *
204
- * @returns {Promise<T>} - A promise that resolves to the retrieved entity.
205
- */
206
- abstract getById(
207
- id: string,
208
- repoOptions?: IItemRepositoryOptions,
209
- ): Promise<T>;
210
-
211
- /**
212
- * Retrieves entities from the storage system that match the specified criteria.
213
- *
214
- * @param {any} criteria - The criteria used to select the entities.
215
- * @param {any} [options] - Optional parameters for the operation, such as pagination or sorting.
216
- *
217
- * @returns {Promise<{ data: T[]; totalCount: number }>} - A promise that resolves to an object containing the matching entities and the total count.
218
- */
219
- abstract getByCriteria(
220
- criteria: any,
221
- options?: any,
222
- ): Promise<{ data: T[]; totalCount: number }>;
223
-
224
- /**
225
- * Retrieves entities from the storage system that match the specified specification.
226
- *
227
- * @param {ISpecification} spec - The specification used to select the entities.
228
- * @param {any} [options] - Optional parameters for the operation, such as pagination or sorting.
229
- *
230
- * @returns {Promise<{ data: T[]; totalCount: number }>} - A promise that resolves to an object containing the matching entities and the total count.
231
- */
232
- abstract getBySpecification(
233
- spec: ISpecification,
234
- options?: any,
235
- ): Promise<{ data: T[]; totalCount: number }>;
236
-
237
- /**
238
- * Counts the number of entities in the storage system that match the specified criteria.
239
- *
240
- * @param {any} criteria - The criteria used to count the entities.
241
- *
242
- * @returns {Promise<number>} - A promise that resolves to the count of matching entities.
243
- */
244
- abstract countByCriteria(criteria: any): Promise<number>;
245
-
246
- /**
247
- * Counts the number of entities in the storage system that match the specified specification.
248
- *
249
- * @param {ISpecification} spec - The specification used to count the entities.
250
- *
251
- * @returns {Promise<number>} - A promise that resolves to the count of matching entities.
252
- */
253
- abstract countBySpecification(spec: ISpecification): Promise<number>;
254
-
255
- /**
256
- * Clears all entities from the storage system.
257
- *
258
- * @param user
259
- *
260
- * @returns {Promise<void>} - A promise that resolves when the storage system is cleared.
261
- */
262
- abstract clear(user: IUser | IItemRepositoryOptions): Promise<void>;
263
-
264
- /**
265
- * Returns an observable that emits changes to entities that match the specified criteria.
266
- *
267
- * @param {any} criteria - The criteria used to select the entities to observe.
268
- *
269
- * @returns {Observable<any>} - An observable that emits changes to the matching entities.
270
- */
271
- abstract changesByCriteria(criteria: { id?: string }): Observable<any>;
272
- }
273
-
274
- /**
275
- * IAttachmentRepository is an abstract class that defines the contract for managing file attachments
276
- * in a storage system. This interface can be implemented to work with various storage backends, such as
277
- * MongoDB GridFS, Amazon S3, Google Cloud Storage, or any other file storage solution.
278
- *
279
- * @template T - The type of the entity that this repository will manage. The entity should extend `IEntity<string>`.
280
- *
281
- * @interface IAttachmentRepository
282
- */
283
- export abstract class IAttachmentRepository<T extends IEntity<string>> {
284
- /**
285
- * Uploads a file to the storage system.
286
- *
287
- * @param {Object} data - The data required to upload the file.
288
- * @param {string} data.id - A unique identifier for the file.
289
- * @param {string} data.fileName - The name of the file to be uploaded.
290
- * @param {Stream} data.stream - The stream of the file to be uploaded.
291
- * @param {string} data.mimeType - The MIME type of the file.
292
- * @param {string} data.encoding - The encoding of the file.
293
- *
294
- * @param {Object} [options] - Optional parameters for the upload.
295
- * @param {Function} [options.streamCallback] - A callback function that gets invoked with the upload stream.
296
- *
297
- * @returns {Promise<void>} - A promise that resolves when the upload is complete.
298
- *
299
- * @throws {Error} - Throws an error if the upload fails.
300
- *
301
- * @example
302
- * // Example usage with MongoDB implementation:
303
- * const repository = new MongoAttachmentRepository(config);
304
- *
305
- * const fileStream = fs.createReadStream('/path/to/file');
306
- *
307
- * await repository.upload({
308
- * id: 'unique-file-id',
309
- * fileName: 'example.txt',
310
- * stream: fileStream,
311
- * mimeType: 'text/plain',
312
- * encoding: 'utf-8'
313
- * }, {
314
- * streamCallback: (writeStream) => {
315
- * console.log('Upload started');
316
- * }
317
- * });
318
- *
319
- * console.log('File uploaded successfully');
320
- */
321
- abstract upload(
322
- data: {
323
- id: string;
324
- fileName: string;
325
- stream: any;
326
- mimeType: string;
327
- encoding: string;
328
- },
329
- options?: { streamCallback?: (r: any) => void },
330
- ): Promise<void>;
331
-
332
- /**
333
- * Retrieves metadata information about a file stored in the storage system.
334
- *
335
- * @param {string} id - The unique identifier of the file.
336
- *
337
- * @returns {Promise<{ fileName: string, contentType: string, length: number } | null>}
338
- * - A promise that resolves to an object containing file metadata, or `null` if the file is not found.
339
- *
340
- * @throws {Error} - Throws an error if retrieving the file information fails.
341
- */
342
- abstract getInfo(
343
- id: string,
344
- ): Promise<{ fileName: string; contentType: string; length: number }>;
345
-
346
- /**
347
- * Retrieves a stream for downloading a file from the storage system.
348
- *
349
- * @param {string} id - The unique identifier of the file.
350
- * @param {Object} [options] - Optional parameters for retrieving a specific range of the file.
351
- * @param {number} [options.start] - The starting byte position for the stream.
352
- * @param {number} [options.end] - The ending byte position for the stream.
353
- *
354
- * @returns {Promise<any>} - A promise that resolves to a readable stream of the file.
355
- *
356
- * @throws {Error} - Throws an error if retrieving the file stream fails.
357
- */
358
- abstract getStream(
359
- id: string,
360
- options?: { start: number; end: number },
361
- ): Promise<any>;
362
-
363
- /**
364
- * Deletes a file from the storage system.
365
- *
366
- * @param {string} id - The unique identifier of the file to be deleted.
367
- *
368
- * @returns {Promise<void>} - A promise that resolves when the file has been successfully deleted.
369
- *
370
- * @throws {Error} - Throws an error if the deletion fails.
371
- */
372
- abstract delete(id: string): Promise<void>;
373
- }
@@ -1,68 +0,0 @@
1
- import { ISpecification } from './interfaces';
2
- import {
3
- BasicSpecification,
4
- MergeSpecification,
5
- OrSpecification,
6
- AndSpecification,
7
- } from './specifications';
8
-
9
- describe('shared-domain-core: BasicSpecification', () => {
10
- it('should create a BasicSpecification with the given criteria', () => {
11
- const criteria = { key: 'value' };
12
-
13
- const spec = new BasicSpecification(criteria);
14
-
15
- expect(spec.criteria).toEqual(criteria);
16
- });
17
- });
18
-
19
- describe('shared-domain-core: MergeSpecification', () => {
20
- it('should merge multiple specifications into one', () => {
21
- const spec1: ISpecification = { criteria: { key1: 'value1' } };
22
- const spec2: ISpecification = { criteria: { key2: 'value2' } };
23
-
24
- const spec = new MergeSpecification(spec1, spec2);
25
-
26
- expect(spec.criteria).toEqual({
27
- key1: 'value1',
28
- key2: 'value2',
29
- });
30
- });
31
-
32
- it('should override criteria with the same key', () => {
33
- const spec1: ISpecification = { criteria: { key: 'value1' } };
34
- const spec2: ISpecification = { criteria: { key: 'value2' } };
35
-
36
- const spec = new MergeSpecification(spec1, spec2);
37
-
38
- expect(spec.criteria).toEqual({
39
- key: 'value2',
40
- });
41
- });
42
- });
43
-
44
- describe('shared-domain-core: OrSpecification', () => {
45
- it('should combine multiple specifications using logical OR', () => {
46
- const spec1: ISpecification = { criteria: { key1: 'value1' } };
47
- const spec2: ISpecification = { criteria: { key2: 'value2' } };
48
-
49
- const spec = new OrSpecification(spec1, spec2);
50
-
51
- expect(spec.criteria).toEqual({
52
- $or: [{ key1: 'value1' }, { key2: 'value2' }],
53
- });
54
- });
55
- });
56
-
57
- describe('shared-domain-core: AndSpecification', () => {
58
- it('should combine multiple specifications using logical AND', () => {
59
- const spec1: ISpecification = { criteria: { key1: 'value1' } };
60
- const spec2: ISpecification = { criteria: { key2: 'value2' } };
61
-
62
- const spec = new AndSpecification(spec1, spec2);
63
-
64
- expect(spec.criteria).toEqual({
65
- $and: [{ key1: 'value1' }, { key2: 'value2' }],
66
- });
67
- });
68
- });
package/tsconfig.json DELETED
@@ -1,22 +0,0 @@
1
- {
2
- "extends": "../../../tsconfig.base.json",
3
- "compilerOptions": {
4
- "module": "commonjs",
5
- "forceConsistentCasingInFileNames": true,
6
- "strict": true,
7
- "noImplicitOverride": true,
8
- "noPropertyAccessFromIndexSignature": true,
9
- "noImplicitReturns": true,
10
- "noFallthroughCasesInSwitch": true
11
- },
12
- "files": [],
13
- "include": [],
14
- "references": [
15
- {
16
- "path": "./tsconfig.lib.json"
17
- },
18
- {
19
- "path": "./tsconfig.spec.json"
20
- }
21
- ]
22
- }
package/tsconfig.lib.json DELETED
@@ -1,10 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "../../../dist/out-tsc",
5
- "declaration": true,
6
- "types": ["node"]
7
- },
8
- "include": ["src/**/*.ts"],
9
- "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"]
10
- }
@@ -1,14 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "../../../dist/out-tsc",
5
- "module": "commonjs",
6
- "types": ["jest", "node"]
7
- },
8
- "include": [
9
- "jest.config.ts",
10
- "src/**/*.test.ts",
11
- "src/**/*.spec.ts",
12
- "src/**/*.d.ts"
13
- ]
14
- }
File without changes