@s-hirano-ist/s-core 1.2.0 → 1.3.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/dist/articles/entities/article-entity.d.ts +342 -11
- package/dist/articles/entities/article-entity.d.ts.map +1 -1
- package/dist/articles/entities/article-entity.js +295 -9
- package/dist/articles/entities/article-entity.js.map +1 -1
- package/dist/articles/events/article-created-event.d.ts +36 -0
- package/dist/articles/events/article-created-event.d.ts.map +1 -1
- package/dist/articles/events/article-created-event.js +36 -0
- package/dist/articles/events/article-created-event.js.map +1 -1
- package/dist/articles/events/article-deleted-event.d.ts +30 -0
- package/dist/articles/events/article-deleted-event.d.ts.map +1 -1
- package/dist/articles/events/article-deleted-event.js +30 -0
- package/dist/articles/events/article-deleted-event.js.map +1 -1
- package/dist/articles/index.d.ts +33 -0
- package/dist/articles/index.d.ts.map +1 -1
- package/dist/articles/index.js +33 -0
- package/dist/articles/index.js.map +1 -1
- package/dist/articles/repositories/articles-command-repository.interface.d.ts +37 -0
- package/dist/articles/repositories/articles-command-repository.interface.d.ts.map +1 -1
- package/dist/articles/repositories/articles-query-repository.interface.d.ts +53 -0
- package/dist/articles/repositories/articles-query-repository.interface.d.ts.map +1 -1
- package/dist/articles/repositories/category-query-repository.interface.d.ts +29 -0
- package/dist/articles/repositories/category-query-repository.interface.d.ts.map +1 -1
- package/dist/articles/services/articles-domain-service.d.ts +40 -0
- package/dist/articles/services/articles-domain-service.d.ts.map +1 -1
- package/dist/articles/services/articles-domain-service.js +50 -0
- package/dist/articles/services/articles-domain-service.js.map +1 -1
- package/dist/articles/types/cache-strategy.d.ts +19 -0
- package/dist/articles/types/cache-strategy.d.ts.map +1 -1
- package/dist/articles/types/query-params.d.ts +83 -5
- package/dist/articles/types/query-params.d.ts.map +1 -1
- package/dist/articles/types/sort-order.d.ts +12 -0
- package/dist/articles/types/sort-order.d.ts.map +1 -1
- package/dist/books/entities/books-entity.d.ts +348 -13
- package/dist/books/entities/books-entity.d.ts.map +1 -1
- package/dist/books/entities/books-entity.js +296 -11
- package/dist/books/entities/books-entity.js.map +1 -1
- package/dist/books/events/book-created-event.d.ts +32 -0
- package/dist/books/events/book-created-event.d.ts.map +1 -1
- package/dist/books/events/book-created-event.js +32 -0
- package/dist/books/events/book-created-event.js.map +1 -1
- package/dist/books/events/book-deleted-event.d.ts +30 -0
- package/dist/books/events/book-deleted-event.d.ts.map +1 -1
- package/dist/books/events/book-deleted-event.js +30 -0
- package/dist/books/events/book-deleted-event.js.map +1 -1
- package/dist/books/index.d.ts +33 -0
- package/dist/books/index.d.ts.map +1 -1
- package/dist/books/index.js +33 -0
- package/dist/books/index.js.map +1 -1
- package/dist/books/repositories/books-command-repository.interface.d.ts +45 -0
- package/dist/books/repositories/books-command-repository.interface.d.ts.map +1 -1
- package/dist/books/repositories/books-query-repository.interface.d.ts +53 -0
- package/dist/books/repositories/books-query-repository.interface.d.ts.map +1 -1
- package/dist/books/services/books-domain-service.d.ts +40 -0
- package/dist/books/services/books-domain-service.d.ts.map +1 -1
- package/dist/books/services/books-domain-service.js +50 -0
- package/dist/books/services/books-domain-service.js.map +1 -1
- package/dist/books/types/cache-strategy.d.ts +19 -0
- package/dist/books/types/cache-strategy.d.ts.map +1 -1
- package/dist/books/types/query-params.d.ts +44 -3
- package/dist/books/types/query-params.d.ts.map +1 -1
- package/dist/books/types/sort-order.d.ts +12 -0
- package/dist/books/types/sort-order.d.ts.map +1 -1
- package/dist/common/entities/common-entity.d.ts +282 -2
- package/dist/common/entities/common-entity.d.ts.map +1 -1
- package/dist/common/entities/common-entity.js +235 -2
- package/dist/common/entities/common-entity.js.map +1 -1
- package/dist/common/events/base-domain-event.d.ts +32 -0
- package/dist/common/events/base-domain-event.d.ts.map +1 -1
- package/dist/common/events/base-domain-event.js +32 -0
- package/dist/common/events/base-domain-event.js.map +1 -1
- package/dist/common/events/domain-event.interface.d.ts +54 -0
- package/dist/common/events/domain-event.interface.d.ts.map +1 -1
- package/dist/common/events/system-error-event.d.ts +36 -0
- package/dist/common/events/system-error-event.d.ts.map +1 -1
- package/dist/common/events/system-error-event.js +36 -0
- package/dist/common/events/system-error-event.js.map +1 -1
- package/dist/common/events/system-warning-event.d.ts +35 -0
- package/dist/common/events/system-warning-event.d.ts.map +1 -1
- package/dist/common/events/system-warning-event.js +35 -0
- package/dist/common/events/system-warning-event.js.map +1 -1
- package/dist/common/index.d.ts +23 -0
- package/dist/common/index.d.ts.map +1 -1
- package/dist/common/index.js +23 -0
- package/dist/common/index.js.map +1 -1
- package/dist/common/services/entity-factory.d.ts +27 -0
- package/dist/common/services/entity-factory.d.ts.map +1 -1
- package/dist/common/services/entity-factory.js +27 -0
- package/dist/common/services/entity-factory.js.map +1 -1
- package/dist/common/services/id-generator.d.ts +32 -0
- package/dist/common/services/id-generator.d.ts.map +1 -1
- package/dist/common/services/id-generator.js +32 -0
- package/dist/common/services/id-generator.js.map +1 -1
- package/dist/errors/error-classes.d.ts +83 -0
- package/dist/errors/error-classes.d.ts.map +1 -1
- package/dist/errors/error-classes.js +83 -0
- package/dist/errors/error-classes.js.map +1 -1
- package/dist/errors/index.d.ts +29 -0
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/index.js +29 -0
- package/dist/errors/index.js.map +1 -1
- package/dist/images/entities/image-entity.d.ts +373 -4
- package/dist/images/entities/image-entity.d.ts.map +1 -1
- package/dist/images/entities/image-entity.js +320 -3
- package/dist/images/entities/image-entity.js.map +1 -1
- package/dist/images/events/image-created-event.d.ts +30 -0
- package/dist/images/events/image-created-event.d.ts.map +1 -1
- package/dist/images/events/image-created-event.js +30 -0
- package/dist/images/events/image-created-event.js.map +1 -1
- package/dist/images/events/image-deleted-event.d.ts +30 -0
- package/dist/images/events/image-deleted-event.d.ts.map +1 -1
- package/dist/images/events/image-deleted-event.js +30 -0
- package/dist/images/events/image-deleted-event.js.map +1 -1
- package/dist/images/index.d.ts +35 -0
- package/dist/images/index.d.ts.map +1 -1
- package/dist/images/index.js +35 -0
- package/dist/images/index.js.map +1 -1
- package/dist/images/repositories/images-command-repository.interface.d.ts +43 -0
- package/dist/images/repositories/images-command-repository.interface.d.ts.map +1 -1
- package/dist/images/repositories/images-query-repository.interface.d.ts +59 -0
- package/dist/images/repositories/images-query-repository.interface.d.ts.map +1 -1
- package/dist/images/services/images-domain-service.d.ts +40 -0
- package/dist/images/services/images-domain-service.d.ts.map +1 -1
- package/dist/images/services/images-domain-service.js +50 -0
- package/dist/images/services/images-domain-service.js.map +1 -1
- package/dist/images/types/cache-strategy.d.ts +19 -0
- package/dist/images/types/cache-strategy.d.ts.map +1 -1
- package/dist/images/types/query-params.d.ts +44 -3
- package/dist/images/types/query-params.d.ts.map +1 -1
- package/dist/images/types/sort-order.d.ts +12 -0
- package/dist/images/types/sort-order.d.ts.map +1 -1
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -1
- package/dist/notes/entities/note-entity.d.ts +212 -6
- package/dist/notes/entities/note-entity.d.ts.map +1 -1
- package/dist/notes/entities/note-entity.js +182 -4
- package/dist/notes/entities/note-entity.js.map +1 -1
- package/dist/notes/events/note-created-event.d.ts +32 -0
- package/dist/notes/events/note-created-event.d.ts.map +1 -1
- package/dist/notes/events/note-created-event.js +32 -0
- package/dist/notes/events/note-created-event.js.map +1 -1
- package/dist/notes/events/note-deleted-event.d.ts +30 -0
- package/dist/notes/events/note-deleted-event.d.ts.map +1 -1
- package/dist/notes/events/note-deleted-event.js +30 -0
- package/dist/notes/events/note-deleted-event.js.map +1 -1
- package/dist/notes/index.d.ts +32 -0
- package/dist/notes/index.d.ts.map +1 -1
- package/dist/notes/index.js +32 -0
- package/dist/notes/index.js.map +1 -1
- package/dist/notes/repositories/notes-command-repository.interface.d.ts +37 -0
- package/dist/notes/repositories/notes-command-repository.interface.d.ts.map +1 -1
- package/dist/notes/repositories/notes-query-repository.interface.d.ts +53 -0
- package/dist/notes/repositories/notes-query-repository.interface.d.ts.map +1 -1
- package/dist/notes/services/notes-domain-service.d.ts +40 -0
- package/dist/notes/services/notes-domain-service.d.ts.map +1 -1
- package/dist/notes/services/notes-domain-service.js +50 -0
- package/dist/notes/services/notes-domain-service.js.map +1 -1
- package/dist/notes/types/cache-strategy.d.ts +19 -0
- package/dist/notes/types/cache-strategy.d.ts.map +1 -1
- package/dist/notes/types/query-params.d.ts +44 -3
- package/dist/notes/types/query-params.d.ts.map +1 -1
- package/dist/notes/types/sort-order.d.ts +12 -0
- package/dist/notes/types/sort-order.d.ts.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -1,8 +1,51 @@
|
|
|
1
1
|
import type { Status } from "../../common/entities/common-entity";
|
|
2
2
|
import type { Path, UnexportedImage } from "../entities/image-entity";
|
|
3
|
+
/**
|
|
4
|
+
* Command repository interface for the Image domain.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* Follows the CQRS pattern - this interface handles write operations only.
|
|
8
|
+
* Implementations should be provided by the infrastructure layer (e.g., Prisma + MinIO).
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* // Infrastructure implementation
|
|
13
|
+
* class PrismaImagesCommandRepository implements IImagesCommandRepository {
|
|
14
|
+
* async create(data: UnexportedImage) {
|
|
15
|
+
* await prisma.image.create({ data });
|
|
16
|
+
* }
|
|
17
|
+
*
|
|
18
|
+
* async uploadToStorage(path: Path, buffer: Buffer, isThumbnail: boolean) {
|
|
19
|
+
* const bucketPath = isThumbnail ? `thumbnails/${path}` : path;
|
|
20
|
+
* await minio.putObject(bucket, bucketPath, buffer);
|
|
21
|
+
* }
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* @see {@link IImagesQueryRepository} for read operations
|
|
26
|
+
*/
|
|
3
27
|
export type IImagesCommandRepository = {
|
|
28
|
+
/**
|
|
29
|
+
* Creates a new image in the repository.
|
|
30
|
+
*
|
|
31
|
+
* @param data - The unexported image entity to persist
|
|
32
|
+
*/
|
|
4
33
|
create(data: UnexportedImage): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Uploads an image buffer to object storage.
|
|
36
|
+
*
|
|
37
|
+
* @param path - The storage path for the image
|
|
38
|
+
* @param buffer - The image data buffer
|
|
39
|
+
* @param isThumbnail - Whether this is a thumbnail upload
|
|
40
|
+
*/
|
|
5
41
|
uploadToStorage(path: Path, buffer: Buffer, isThumbnail: boolean): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* Deletes an image by its ID.
|
|
44
|
+
*
|
|
45
|
+
* @param id - The image ID to delete
|
|
46
|
+
* @param userId - The user ID for tenant isolation
|
|
47
|
+
* @param status - The expected status of the image
|
|
48
|
+
*/
|
|
6
49
|
deleteById(id: string, userId: string, status: Status): Promise<void>;
|
|
7
50
|
};
|
|
8
51
|
//# sourceMappingURL=images-command-repository.interface.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"images-command-repository.interface.d.ts","sourceRoot":"","sources":["../../../images/repositories/images-command-repository.interface.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qCAAqC,CAAC;AAClE,OAAO,KAAK,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAEtE,MAAM,MAAM,wBAAwB,GAAG;IACtC,MAAM,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"images-command-repository.interface.d.ts","sourceRoot":"","sources":["../../../images/repositories/images-command-repository.interface.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qCAAqC,CAAC;AAClE,OAAO,KAAK,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAEtE;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,MAAM,wBAAwB,GAAG;IACtC;;;;OAIG;IACH,MAAM,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE7C;;;;;;OAMG;IACH,eAAe,CACd,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,OAAO,GAClB,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACtE,CAAC"}
|
|
@@ -1,7 +1,35 @@
|
|
|
1
1
|
import type { Status, UserId } from "../../common/entities/common-entity";
|
|
2
2
|
import type { Path } from "../entities/image-entity";
|
|
3
3
|
import type { ImagesFindManyParams } from "../types/query-params";
|
|
4
|
+
/**
|
|
5
|
+
* Query repository interface for the Image domain.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* Follows the CQRS pattern - this interface handles read operations only.
|
|
9
|
+
* Implementations should be provided by the infrastructure layer (e.g., Prisma + MinIO).
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* // Infrastructure implementation
|
|
14
|
+
* class PrismaImagesQueryRepository implements IImagesQueryRepository {
|
|
15
|
+
* async findByPath(path: Path, userId: UserId) {
|
|
16
|
+
* return await prisma.image.findUnique({
|
|
17
|
+
* where: { path, userId }
|
|
18
|
+
* });
|
|
19
|
+
* }
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @see {@link IImagesCommandRepository} for write operations
|
|
24
|
+
*/
|
|
4
25
|
export type IImagesQueryRepository = {
|
|
26
|
+
/**
|
|
27
|
+
* Finds an image by its path for a specific user.
|
|
28
|
+
*
|
|
29
|
+
* @param path - The validated path to search for
|
|
30
|
+
* @param userId - The user ID for tenant isolation
|
|
31
|
+
* @returns The image data if found, null otherwise
|
|
32
|
+
*/
|
|
5
33
|
findByPath(path: Path, userId: UserId): Promise<{
|
|
6
34
|
id: string;
|
|
7
35
|
path: string;
|
|
@@ -13,14 +41,45 @@ export type IImagesQueryRepository = {
|
|
|
13
41
|
status: string;
|
|
14
42
|
description: string | null;
|
|
15
43
|
} | null>;
|
|
44
|
+
/**
|
|
45
|
+
* Retrieves multiple images with pagination and filtering.
|
|
46
|
+
*
|
|
47
|
+
* @param userId - The user ID for tenant isolation
|
|
48
|
+
* @param status - Filter by UNEXPORTED or EXPORTED status
|
|
49
|
+
* @param params - Optional pagination, sorting, and caching parameters
|
|
50
|
+
* @returns Array of image data objects
|
|
51
|
+
*
|
|
52
|
+
* @see {@link ImagesFindManyParams} for parameter options
|
|
53
|
+
*/
|
|
16
54
|
findMany(userId: UserId, status: Status, params?: ImagesFindManyParams): Promise<{
|
|
17
55
|
id: string;
|
|
18
56
|
path: string;
|
|
19
57
|
height: number | null;
|
|
20
58
|
width: number | null;
|
|
21
59
|
}[]>;
|
|
60
|
+
/**
|
|
61
|
+
* Counts images matching the given criteria.
|
|
62
|
+
*
|
|
63
|
+
* @param userId - The user ID for tenant isolation
|
|
64
|
+
* @param status - Filter by status
|
|
65
|
+
* @returns The count of matching images
|
|
66
|
+
*/
|
|
22
67
|
count(userId: UserId, status: Status): Promise<number>;
|
|
68
|
+
/**
|
|
69
|
+
* Retrieves an image from object storage.
|
|
70
|
+
*
|
|
71
|
+
* @param path - The storage path of the image
|
|
72
|
+
* @param isThumbnail - Whether to retrieve the thumbnail version
|
|
73
|
+
* @returns A readable stream of the image data
|
|
74
|
+
*/
|
|
23
75
|
getFromStorage(path: string, isThumbnail: boolean): Promise<NodeJS.ReadableStream>;
|
|
76
|
+
/**
|
|
77
|
+
* Verifies an image exists in storage or throws an error.
|
|
78
|
+
*
|
|
79
|
+
* @param path - The storage path of the image
|
|
80
|
+
* @param isThumbnail - Whether to check the thumbnail version
|
|
81
|
+
* @throws When the image is not found in storage
|
|
82
|
+
*/
|
|
24
83
|
getFromStorageOrThrow(path: string, isThumbnail: boolean): Promise<void>;
|
|
25
84
|
};
|
|
26
85
|
//# sourceMappingURL=images-query-repository.interface.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"images-query-repository.interface.d.ts","sourceRoot":"","sources":["../../../images/repositories/images-query-repository.interface.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAElE,MAAM,MAAM,sBAAsB,GAAG;IACpC,UAAU,CACT,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,MAAM,GACZ,OAAO,CAAC;QACV,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QACxB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;QACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,IAAI,EAAE,MAAM,EAAE,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;KAC3B,GAAG,IAAI,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"images-query-repository.interface.d.ts","sourceRoot":"","sources":["../../../images/repositories/images-query-repository.interface.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAElE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACpC;;;;;;OAMG;IACH,UAAU,CACT,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,MAAM,GACZ,OAAO,CAAC;QACV,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QACxB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;QACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,IAAI,EAAE,MAAM,EAAE,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;KAC3B,GAAG,IAAI,CAAC,CAAC;IAEV;;;;;;;;;OASG;IACH,QAAQ,CACP,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,oBAAoB,GAC3B,OAAO,CACT;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,EAAE,CAC3E,CAAC;IAEF;;;;;;OAMG;IACH,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAEvD;;;;;;OAMG;IACH,cAAc,CACb,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,OAAO,GAClB,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;IAElC;;;;;;OAMG;IACH,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACzE,CAAC"}
|
|
@@ -1,9 +1,49 @@
|
|
|
1
1
|
import type { UserId } from "../../common/entities/common-entity";
|
|
2
2
|
import type { Path } from "../entities/image-entity";
|
|
3
3
|
import type { IImagesQueryRepository } from "../repositories/images-query-repository.interface";
|
|
4
|
+
/**
|
|
5
|
+
* Domain service for Image business logic.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* Encapsulates complex business rules that don't belong to a single entity.
|
|
9
|
+
* Uses dependency injection for repository access.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* const queryRepo: IImagesQueryRepository = new PrismaImagesQueryRepository();
|
|
14
|
+
* const domainService = new ImagesDomainService(queryRepo);
|
|
15
|
+
*
|
|
16
|
+
* try {
|
|
17
|
+
* await domainService.ensureNoDuplicate(path, userId);
|
|
18
|
+
* // Safe to create the image
|
|
19
|
+
* } catch (error) {
|
|
20
|
+
* if (error instanceof DuplicateError) {
|
|
21
|
+
* // Handle duplicate path
|
|
22
|
+
* }
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* @see {@link IImagesQueryRepository} for repository interface
|
|
27
|
+
* @see {@link DuplicateError} for duplicate handling
|
|
28
|
+
*/
|
|
4
29
|
export declare class ImagesDomainService {
|
|
5
30
|
private readonly imagesQueryRepository;
|
|
31
|
+
/**
|
|
32
|
+
* Creates a new ImagesDomainService instance.
|
|
33
|
+
*
|
|
34
|
+
* @param imagesQueryRepository - The query repository for checking duplicates
|
|
35
|
+
*/
|
|
6
36
|
constructor(imagesQueryRepository: IImagesQueryRepository);
|
|
37
|
+
/**
|
|
38
|
+
* Validates that no image with the same path exists for the user.
|
|
39
|
+
*
|
|
40
|
+
* @param path - The path to check for duplicates
|
|
41
|
+
* @param userId - The user ID for tenant isolation
|
|
42
|
+
* @throws {DuplicateError} When an image with this path already exists
|
|
43
|
+
*
|
|
44
|
+
* @remarks
|
|
45
|
+
* This is a domain invariant check that should be called before creating images.
|
|
46
|
+
*/
|
|
7
47
|
ensureNoDuplicate(path: Path, userId: UserId): Promise<void>;
|
|
8
48
|
}
|
|
9
49
|
//# sourceMappingURL=images-domain-service.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"images-domain-service.d.ts","sourceRoot":"","sources":["../../../images/services/images-domain-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qCAAqC,CAAC;AAElE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mDAAmD,CAAC;
|
|
1
|
+
{"version":3,"file":"images-domain-service.d.ts","sourceRoot":"","sources":["../../../images/services/images-domain-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qCAAqC,CAAC;AAElE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mDAAmD,CAAC;AAqBhG;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBAAa,mBAAmB;IAMnB,OAAO,CAAC,QAAQ,CAAC,qBAAqB;IALlD;;;;OAIG;gBAC0B,qBAAqB,EAAE,sBAAsB;IAE1E;;;;;;;;;OASG;IACU,iBAAiB,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM;CAGzD"}
|
|
@@ -1,14 +1,64 @@
|
|
|
1
1
|
import { DuplicateError } from "../../errors/error-classes";
|
|
2
|
+
/**
|
|
3
|
+
* Checks if an image with the given path already exists.
|
|
4
|
+
*
|
|
5
|
+
* @param imagesQueryRepository - The query repository to check against
|
|
6
|
+
* @param path - The path to check for duplicates
|
|
7
|
+
* @param userId - The user ID for tenant isolation
|
|
8
|
+
* @throws {DuplicateError} When an image with this path already exists
|
|
9
|
+
*
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
2
12
|
async function ensureNoDuplicateImage(imagesQueryRepository, path, userId) {
|
|
3
13
|
const exists = await imagesQueryRepository.findByPath(path, userId);
|
|
4
14
|
if (exists !== null)
|
|
5
15
|
throw new DuplicateError();
|
|
6
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* Domain service for Image business logic.
|
|
19
|
+
*
|
|
20
|
+
* @remarks
|
|
21
|
+
* Encapsulates complex business rules that don't belong to a single entity.
|
|
22
|
+
* Uses dependency injection for repository access.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* const queryRepo: IImagesQueryRepository = new PrismaImagesQueryRepository();
|
|
27
|
+
* const domainService = new ImagesDomainService(queryRepo);
|
|
28
|
+
*
|
|
29
|
+
* try {
|
|
30
|
+
* await domainService.ensureNoDuplicate(path, userId);
|
|
31
|
+
* // Safe to create the image
|
|
32
|
+
* } catch (error) {
|
|
33
|
+
* if (error instanceof DuplicateError) {
|
|
34
|
+
* // Handle duplicate path
|
|
35
|
+
* }
|
|
36
|
+
* }
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* @see {@link IImagesQueryRepository} for repository interface
|
|
40
|
+
* @see {@link DuplicateError} for duplicate handling
|
|
41
|
+
*/
|
|
7
42
|
export class ImagesDomainService {
|
|
8
43
|
imagesQueryRepository;
|
|
44
|
+
/**
|
|
45
|
+
* Creates a new ImagesDomainService instance.
|
|
46
|
+
*
|
|
47
|
+
* @param imagesQueryRepository - The query repository for checking duplicates
|
|
48
|
+
*/
|
|
9
49
|
constructor(imagesQueryRepository) {
|
|
10
50
|
this.imagesQueryRepository = imagesQueryRepository;
|
|
11
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Validates that no image with the same path exists for the user.
|
|
54
|
+
*
|
|
55
|
+
* @param path - The path to check for duplicates
|
|
56
|
+
* @param userId - The user ID for tenant isolation
|
|
57
|
+
* @throws {DuplicateError} When an image with this path already exists
|
|
58
|
+
*
|
|
59
|
+
* @remarks
|
|
60
|
+
* This is a domain invariant check that should be called before creating images.
|
|
61
|
+
*/
|
|
12
62
|
async ensureNoDuplicate(path, userId) {
|
|
13
63
|
return ensureNoDuplicateImage(this.imagesQueryRepository, path, userId);
|
|
14
64
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"images-domain-service.js","sourceRoot":"","sources":["../../../images/services/images-domain-service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAI5D,KAAK,UAAU,sBAAsB,CACpC,qBAA6C,EAC7C,IAAU,EACV,MAAc;IAEd,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACpE,IAAI,MAAM,KAAK,IAAI;QAAE,MAAM,IAAI,cAAc,EAAE,CAAC;AACjD,CAAC;AAED,MAAM,OAAO,mBAAmB;
|
|
1
|
+
{"version":3,"file":"images-domain-service.js","sourceRoot":"","sources":["../../../images/services/images-domain-service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAI5D;;;;;;;;;GASG;AACH,KAAK,UAAU,sBAAsB,CACpC,qBAA6C,EAC7C,IAAU,EACV,MAAc;IAEd,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACpE,IAAI,MAAM,KAAK,IAAI;QAAE,MAAM,IAAI,cAAc,EAAE,CAAC;AACjD,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,OAAO,mBAAmB;IAMF;IAL7B;;;;OAIG;IACH,YAA6B,qBAA6C;QAA7C,0BAAqB,GAArB,qBAAqB,CAAwB;IAAG,CAAC;IAE9E;;;;;;;;;OASG;IACI,KAAK,CAAC,iBAAiB,CAAC,IAAU,EAAE,MAAc;QACxD,OAAO,sBAAsB,CAAC,IAAI,CAAC,qBAAqB,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IACzE,CAAC;CACD"}
|
|
@@ -1,6 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache configuration for repository queries.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* Provides fine-grained control over caching behavior.
|
|
6
|
+
* Uses stale-while-revalidate (SWR) pattern for optimal performance.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const strategy: CacheStrategy = {
|
|
11
|
+
* ttl: 60, // Cache for 60 seconds
|
|
12
|
+
* swr: 300, // Serve stale for 5 minutes while revalidating
|
|
13
|
+
* tags: ["images"] // Cache tags for invalidation
|
|
14
|
+
* };
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
1
17
|
export type CacheStrategy = {
|
|
18
|
+
/** Time-to-live in seconds */
|
|
2
19
|
ttl?: number;
|
|
20
|
+
/** Stale-while-revalidate duration in seconds */
|
|
3
21
|
swr?: number;
|
|
22
|
+
/** Cache tags for targeted invalidation */
|
|
4
23
|
tags?: string[];
|
|
5
24
|
};
|
|
6
25
|
//# sourceMappingURL=cache-strategy.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache-strategy.d.ts","sourceRoot":"","sources":["../../../images/types/cache-strategy.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CAChB,CAAC"}
|
|
1
|
+
{"version":3,"file":"cache-strategy.d.ts","sourceRoot":"","sources":["../../../images/types/cache-strategy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,aAAa,GAAG;IAC3B,8BAA8B;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,2CAA2C;IAC3C,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CAChB,CAAC"}
|
|
@@ -1,14 +1,55 @@
|
|
|
1
1
|
import type { CacheStrategy } from "./cache-strategy";
|
|
2
2
|
import type { SortOrder } from "./sort-order";
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Available fields for sorting image queries.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* All fields correspond to Image entity properties.
|
|
8
|
+
* Used with {@link ImagesOrderBy} to specify sort criteria.
|
|
9
|
+
*/
|
|
10
|
+
export type ImagesOrderByField = "id" | "path" | "contentType" | "fileSize" | "width" | "height" | "tags" | "description" | "status" | "createdAt" | "updatedAt" | "exportedAt";
|
|
11
|
+
/**
|
|
12
|
+
* Sort configuration for image queries.
|
|
13
|
+
*
|
|
14
|
+
* @remarks
|
|
15
|
+
* Maps field names to sort directions.
|
|
16
|
+
* Typically only one field should be specified at a time.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* const orderBy: ImagesOrderBy = { createdAt: "desc" };
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @see {@link ImagesOrderByField} for available fields
|
|
24
|
+
* @see {@link SortOrder} for sort directions
|
|
25
|
+
*/
|
|
26
|
+
export type ImagesOrderBy = {
|
|
5
27
|
[K in ImagesOrderByField]?: SortOrder;
|
|
6
28
|
};
|
|
29
|
+
/**
|
|
30
|
+
* Parameters for paginated image queries.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* const params: ImagesFindManyParams = {
|
|
35
|
+
* orderBy: { createdAt: "desc" },
|
|
36
|
+
* take: 20,
|
|
37
|
+
* skip: 0,
|
|
38
|
+
* cacheStrategy: { ttl: 60, tags: ["images"] },
|
|
39
|
+
* };
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* @see {@link ImagesOrderBy} for sorting options
|
|
43
|
+
* @see {@link CacheStrategy} for caching configuration
|
|
44
|
+
*/
|
|
7
45
|
export type ImagesFindManyParams = {
|
|
46
|
+
/** Sort configuration */
|
|
8
47
|
orderBy?: ImagesOrderBy;
|
|
48
|
+
/** Maximum number of results to return */
|
|
9
49
|
take?: number;
|
|
50
|
+
/** Number of results to skip (for pagination) */
|
|
10
51
|
skip?: number;
|
|
52
|
+
/** Caching configuration for the query */
|
|
11
53
|
cacheStrategy?: CacheStrategy;
|
|
12
54
|
};
|
|
13
|
-
export {};
|
|
14
55
|
//# sourceMappingURL=query-params.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query-params.d.ts","sourceRoot":"","sources":["../../../images/types/query-params.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,
|
|
1
|
+
{"version":3,"file":"query-params.d.ts","sourceRoot":"","sources":["../../../images/types/query-params.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C;;;;;;GAMG;AACH,MAAM,MAAM,kBAAkB,GAC3B,IAAI,GACJ,MAAM,GACN,aAAa,GACb,UAAU,GACV,OAAO,GACP,QAAQ,GACR,MAAM,GACN,aAAa,GACb,QAAQ,GACR,WAAW,GACX,WAAW,GACX,YAAY,CAAC;AAEhB;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,aAAa,GAAG;KAC1B,CAAC,IAAI,kBAAkB,CAAC,CAAC,EAAE,SAAS;CACrC,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,oBAAoB,GAAG;IAClC,yBAAyB;IACzB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,0CAA0C;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0CAA0C;IAC1C,aAAa,CAAC,EAAE,aAAa,CAAC;CAC9B,CAAC"}
|
|
@@ -1,2 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sort direction for query ordering.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* Standard SQL-style sort order used across all domain queries.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* const ascending: SortOrder = "asc";
|
|
10
|
+
* const descending: SortOrder = "desc";
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
1
13
|
export type SortOrder = "asc" | "desc";
|
|
2
14
|
//# sourceMappingURL=sort-order.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sort-order.d.ts","sourceRoot":"","sources":["../../../images/types/sort-order.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,MAAM,CAAC"}
|
|
1
|
+
{"version":3,"file":"sort-order.d.ts","sourceRoot":"","sources":["../../../images/types/sort-order.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,MAAM,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @packageDocumentation
|
|
3
|
+
*
|
|
4
|
+
* Core domain library for the content management system.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* This package provides domain entities, repositories, services, and events
|
|
8
|
+
* following Domain-Driven Design (DDD) and Clean Architecture principles.
|
|
9
|
+
*
|
|
10
|
+
* ## Domains
|
|
11
|
+
*
|
|
12
|
+
* - **Articles** - News and link management with OG metadata
|
|
13
|
+
* - **Books** - ISBN-based book tracking with Google Books integration
|
|
14
|
+
* - **Notes** - Markdown-based content creation
|
|
15
|
+
* - **Images** - File metadata and image processing
|
|
16
|
+
* - **Common** - Shared entities, events, and services
|
|
17
|
+
* - **Errors** - Domain-specific error classes
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* import { Articles, Books, Notes, Images, Common, Errors } from "@repo/core";
|
|
22
|
+
*
|
|
23
|
+
* // Create an article
|
|
24
|
+
* const article = Articles.articleEntity.create({
|
|
25
|
+
* userId: Common.makeUserId("user-123"),
|
|
26
|
+
* title: Articles.makeArticleTitle("My Article"),
|
|
27
|
+
* url: Articles.makeUrl("https://example.com"),
|
|
28
|
+
* });
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
1
31
|
export * as Articles from "./articles";
|
|
2
32
|
export * as Books from "./books";
|
|
3
33
|
export * as Common from "./common";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAGH,OAAO,KAAK,QAAQ,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC;AACjC,OAAO,KAAK,MAAM,MAAM,UAAU,CAAC;AACnC,OAAO,KAAK,MAAM,MAAM,UAAU,CAAC;AACnC,OAAO,KAAK,MAAM,MAAM,UAAU,CAAC;AACnC,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @packageDocumentation
|
|
3
|
+
*
|
|
4
|
+
* Core domain library for the content management system.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* This package provides domain entities, repositories, services, and events
|
|
8
|
+
* following Domain-Driven Design (DDD) and Clean Architecture principles.
|
|
9
|
+
*
|
|
10
|
+
* ## Domains
|
|
11
|
+
*
|
|
12
|
+
* - **Articles** - News and link management with OG metadata
|
|
13
|
+
* - **Books** - ISBN-based book tracking with Google Books integration
|
|
14
|
+
* - **Notes** - Markdown-based content creation
|
|
15
|
+
* - **Images** - File metadata and image processing
|
|
16
|
+
* - **Common** - Shared entities, events, and services
|
|
17
|
+
* - **Errors** - Domain-specific error classes
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* import { Articles, Books, Notes, Images, Common, Errors } from "@repo/core";
|
|
22
|
+
*
|
|
23
|
+
* // Create an article
|
|
24
|
+
* const article = Articles.articleEntity.create({
|
|
25
|
+
* userId: Common.makeUserId("user-123"),
|
|
26
|
+
* title: Articles.makeArticleTitle("My Article"),
|
|
27
|
+
* url: Articles.makeUrl("https://example.com"),
|
|
28
|
+
* });
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
1
31
|
// Domain exports with namespaces
|
|
2
32
|
export * as Articles from "./articles";
|
|
3
33
|
export * as Books from "./books";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,iCAAiC;AACjC,OAAO,KAAK,QAAQ,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC;AACjC,OAAO,KAAK,MAAM,MAAM,UAAU,CAAC;AACnC,OAAO,KAAK,MAAM,MAAM,UAAU,CAAC;AACnC,OAAO,KAAK,MAAM,MAAM,UAAU,CAAC;AACnC,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,iCAAiC;AACjC,OAAO,KAAK,QAAQ,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC;AACjC,OAAO,KAAK,MAAM,MAAM,UAAU,CAAC;AACnC,OAAO,KAAK,MAAM,MAAM,UAAU,CAAC;AACnC,OAAO,KAAK,MAAM,MAAM,UAAU,CAAC;AACnC,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC"}
|