@endora-commerce/mod-admin-notifications 0.0.0-stage → 0.100.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +49 -2
  3. package/dist/backend/entities/admin-notification-read.entity.d.ts +15 -0
  4. package/dist/backend/entities/admin-notification-read.entity.d.ts.map +1 -0
  5. package/dist/backend/entities/admin-notification-read.entity.js +42 -0
  6. package/dist/backend/entities/admin-notification-read.entity.js.map +1 -0
  7. package/dist/backend/entities/admin-notification.entity.d.ts +31 -0
  8. package/dist/backend/entities/admin-notification.entity.d.ts.map +1 -0
  9. package/dist/backend/entities/admin-notification.entity.js +95 -0
  10. package/dist/backend/entities/admin-notification.entity.js.map +1 -0
  11. package/dist/backend/index.d.ts +25 -0
  12. package/dist/backend/index.d.ts.map +1 -0
  13. package/dist/backend/index.js +70 -0
  14. package/dist/backend/index.js.map +1 -0
  15. package/dist/backend/plugin.d.ts +26 -0
  16. package/dist/backend/plugin.d.ts.map +1 -0
  17. package/dist/backend/plugin.js +17 -0
  18. package/dist/backend/plugin.js.map +1 -0
  19. package/dist/backend/routes.admin.d.ts +16 -0
  20. package/dist/backend/routes.admin.d.ts.map +1 -0
  21. package/dist/backend/routes.admin.js +78 -0
  22. package/dist/backend/routes.admin.js.map +1 -0
  23. package/dist/backend/services/admin-notification-port.d.ts +17 -0
  24. package/dist/backend/services/admin-notification-port.d.ts.map +1 -0
  25. package/dist/backend/services/admin-notification-port.js +33 -0
  26. package/dist/backend/services/admin-notification-port.js.map +1 -0
  27. package/dist/backend/services/admin-notification-service.d.ts +78 -0
  28. package/dist/backend/services/admin-notification-service.d.ts.map +1 -0
  29. package/dist/backend/services/admin-notification-service.js +284 -0
  30. package/dist/backend/services/admin-notification-service.js.map +1 -0
  31. package/dist/manifest.d.ts +171 -0
  32. package/dist/manifest.d.ts.map +1 -0
  33. package/dist/manifest.js +46 -0
  34. package/dist/manifest.js.map +1 -0
  35. package/dist/migrations/20260611T140350_admin_notifications_init.d.ts +19 -0
  36. package/dist/migrations/20260611T140350_admin_notifications_init.d.ts.map +1 -0
  37. package/dist/migrations/20260611T140350_admin_notifications_init.js +82 -0
  38. package/dist/migrations/20260611T140350_admin_notifications_init.js.map +1 -0
  39. package/dist/migrations/index.d.ts +27 -0
  40. package/dist/migrations/index.d.ts.map +1 -0
  41. package/dist/migrations/index.js +29 -0
  42. package/dist/migrations/index.js.map +1 -0
  43. package/package.json +63 -3
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Endora sp. z o.o. and the Endora Commerce contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,50 @@
1
- # Temporary Holding Version
1
+ <!-- Generated by `pnpm --filter backend run manifests:generate`. Delete this line to take the file over; a README without it is never regenerated. -->
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ # @endora-commerce/mod-admin-notifications
4
+
5
+ In-app notification feed surfaced in the admin top-nav bell. Generic so any module can write entries.
6
+
7
+ ## What this is
8
+
9
+ An **Endora Commerce module package**. Its module id is `admin_notifications` — the identity of record in the platform's registries, in its migrations and in its setting, permission and translation codes.
10
+
11
+ A module is not imported by application code. The platform discovers the extension packages an instance has installed, reads the manifest on the root subpath and composes the module from it, so installing this package and starting the instance is the whole integration.
12
+
13
+ ## Entry points
14
+
15
+ | Import | Contents |
16
+ | --- | --- |
17
+ | `@endora-commerce/mod-admin-notifications` | the module manifest — its id, version, dependencies, settings and activation |
18
+ | `@endora-commerce/mod-admin-notifications/backend` | the composition root the platform calls, with the entities, services, routes and workers it registers |
19
+ | `@endora-commerce/mod-admin-notifications/migrations` | the module’s own schema migrations, in the order the platform runs them |
20
+
21
+ ## Depends on
22
+
23
+ Everything below is a **peer** dependency, so the application resolves exactly one copy of each — two copies of a host or of React are a runtime failure rather than a type error. An entry marked *optional* is needed only by the layers that use it.
24
+
25
+ **Endora packages**
26
+
27
+ - `@endora-commerce/contracts`
28
+ - `@endora-commerce/platform`
29
+
30
+ **Third-party**
31
+
32
+ - `@mikro-orm/core` ^6
33
+ - `@mikro-orm/migrations` ^6
34
+ - `@mikro-orm/postgresql` ^6
35
+ - `fastify` ^5
36
+ - `zod` ^4
37
+
38
+ ## What the tarball carries
39
+
40
+ - `dist/` — the compiled JavaScript and its type declarations
41
+
42
+ ## Install
43
+
44
+ ```bash
45
+ pnpm add @endora-commerce/mod-admin-notifications
46
+ ```
47
+
48
+ ## Licence
49
+
50
+ MIT — the text is in `LICENSE`, beside this file.
@@ -0,0 +1,15 @@
1
+ import { OptionalProps } from '@mikro-orm/core';
2
+ /**
3
+ * AdminNotificationRead — per-admin read cursor for `audience='all_admins'`
4
+ * broadcast notifications. One row per (notification, admin).
5
+ *
6
+ * For `audience='admin_user'` notifications the read state lives on the
7
+ * notification row itself (`AdminNotification.readAt`).
8
+ */
9
+ export declare class AdminNotificationRead {
10
+ [OptionalProps]?: 'readAt';
11
+ notificationId: string;
12
+ adminUserId: string;
13
+ readAt: Date;
14
+ }
15
+ //# sourceMappingURL=admin-notification-read.entity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-notification-read.entity.d.ts","sourceRoot":"","sources":["../../../src/backend/entities/admin-notification-read.entity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,aAAa,EAAwB,MAAM,iBAAiB,CAAC;AAG9E;;;;;;GAMG;AACH,qBAEa,qBAAqB;IAChC,CAAC,aAAa,CAAC,CAAC,EAAE,QAAQ,CAAC;IAG3B,cAAc,EAAG,MAAM,CAAC;IAGxB,WAAW,EAAG,MAAM,CAAC;IAGrB,MAAM,EAAE,IAAI,CAAc;CAC3B"}
@@ -0,0 +1,42 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { Entity, OptionalProps, PrimaryKey, Property } from '@mikro-orm/core';
11
+ import { GlobalEntity } from '@endora-commerce/platform/tenancy';
12
+ /**
13
+ * AdminNotificationRead — per-admin read cursor for `audience='all_admins'`
14
+ * broadcast notifications. One row per (notification, admin).
15
+ *
16
+ * For `audience='admin_user'` notifications the read state lives on the
17
+ * notification row itself (`AdminNotification.readAt`).
18
+ */
19
+ let AdminNotificationRead = class AdminNotificationRead {
20
+ [OptionalProps];
21
+ notificationId;
22
+ adminUserId;
23
+ readAt = new Date();
24
+ };
25
+ __decorate([
26
+ PrimaryKey({ type: 'uuid' }),
27
+ __metadata("design:type", String)
28
+ ], AdminNotificationRead.prototype, "notificationId", void 0);
29
+ __decorate([
30
+ PrimaryKey({ type: 'uuid' }),
31
+ __metadata("design:type", String)
32
+ ], AdminNotificationRead.prototype, "adminUserId", void 0);
33
+ __decorate([
34
+ Property({ type: 'datetime' }),
35
+ __metadata("design:type", Date)
36
+ ], AdminNotificationRead.prototype, "readAt", void 0);
37
+ AdminNotificationRead = __decorate([
38
+ GlobalEntity(),
39
+ Entity({ tableName: 'admin_notification_reads' })
40
+ ], AdminNotificationRead);
41
+ export { AdminNotificationRead };
42
+ //# sourceMappingURL=admin-notification-read.entity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-notification-read.entity.js","sourceRoot":"","sources":["../../../src/backend/entities/admin-notification-read.entity.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC9E,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AAEjE;;;;;;GAMG;AAGI,IAAM,qBAAqB,GAA3B,MAAM,qBAAqB;IAChC,CAAC,aAAa,CAAC,CAAY;IAG3B,cAAc,CAAU;IAGxB,WAAW,CAAU;IAGrB,MAAM,GAAS,IAAI,IAAI,EAAE,CAAC;CAC3B,CAAA;AAPC;IADC,UAAU,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;6DACL;AAGxB;IADC,UAAU,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;0DACR;AAGrB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;8BACvB,IAAI;qDAAc;AAVf,qBAAqB;IAFjC,YAAY,EAAE;IACd,MAAM,CAAC,EAAE,SAAS,EAAE,0BAA0B,EAAE,CAAC;GACrC,qBAAqB,CAWjC"}
@@ -0,0 +1,31 @@
1
+ import { OptionalProps } from '@mikro-orm/core';
2
+ /**
3
+ * AdminNotification — one row per in-app notification entry.
4
+ *
5
+ * Two audience modes:
6
+ * - `all_admins`: every admin sees the entry. Per-admin read state is
7
+ * tracked in the `admin_notification_reads` bridge table.
8
+ * - `admin_user`: targets exactly one admin (the `target_admin_user_id`
9
+ * must be non-null). Read state lives on the row itself in `read_at`.
10
+ *
11
+ * `archived_at` is set by the application's `pruneStale()` helper so older
12
+ * unread entries do not pile up forever. The schema does not cap the row
13
+ * count — the pruner does.
14
+ */
15
+ export declare class AdminNotification {
16
+ [OptionalProps]?: 'id' | 'createdAt' | 'targetAdminUserId' | 'subjectType' | 'subjectId' | 'body' | 'linkPath' | 'readAt' | 'archivedAt';
17
+ id: string;
18
+ audience: AdminNotificationAudience;
19
+ targetAdminUserId?: string | null;
20
+ kind: string;
21
+ subjectType?: string | null;
22
+ subjectId?: string | null;
23
+ title: string;
24
+ body?: string | null;
25
+ linkPath?: string | null;
26
+ createdAt: Date;
27
+ readAt?: Date | null;
28
+ archivedAt?: Date | null;
29
+ }
30
+ export type AdminNotificationAudience = 'all_admins' | 'admin_user';
31
+ //# sourceMappingURL=admin-notification.entity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-notification.entity.d.ts","sourceRoot":"","sources":["../../../src/backend/entities/admin-notification.entity.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,aAAa,EAAwB,MAAM,iBAAiB,CAAC;AAGrF;;;;;;;;;;;;GAYG;AACH,qBAEa,iBAAiB;IAC5B,CAAC,aAAa,CAAC,CAAC,EACZ,IAAI,GACJ,WAAW,GACX,mBAAmB,GACnB,aAAa,GACb,WAAW,GACX,MAAM,GACN,UAAU,GACV,QAAQ,GACR,YAAY,CAAC;IAGjB,EAAE,EAAE,MAAM,CAAgB;IAG1B,QAAQ,EAAG,yBAAyB,CAAC;IAIrC,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAGlC,IAAI,EAAG,MAAM,CAAC;IAGd,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAG5B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAG1B,KAAK,EAAG,MAAM,CAAC;IAGf,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAGrB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAGzB,SAAS,EAAE,IAAI,CAAc;IAG7B,MAAM,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAGrB,UAAU,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,MAAM,yBAAyB,GAAG,YAAY,GAAG,YAAY,CAAC"}
@@ -0,0 +1,95 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { randomUUID } from 'node:crypto';
11
+ import { Entity, Index, OptionalProps, PrimaryKey, Property } from '@mikro-orm/core';
12
+ import { GlobalEntity } from '@endora-commerce/platform/tenancy';
13
+ /**
14
+ * AdminNotification — one row per in-app notification entry.
15
+ *
16
+ * Two audience modes:
17
+ * - `all_admins`: every admin sees the entry. Per-admin read state is
18
+ * tracked in the `admin_notification_reads` bridge table.
19
+ * - `admin_user`: targets exactly one admin (the `target_admin_user_id`
20
+ * must be non-null). Read state lives on the row itself in `read_at`.
21
+ *
22
+ * `archived_at` is set by the application's `pruneStale()` helper so older
23
+ * unread entries do not pile up forever. The schema does not cap the row
24
+ * count — the pruner does.
25
+ */
26
+ let AdminNotification = class AdminNotification {
27
+ [OptionalProps];
28
+ id = randomUUID();
29
+ audience;
30
+ targetAdminUserId;
31
+ kind;
32
+ subjectType;
33
+ subjectId;
34
+ title;
35
+ body;
36
+ linkPath;
37
+ createdAt = new Date();
38
+ readAt;
39
+ archivedAt;
40
+ };
41
+ __decorate([
42
+ PrimaryKey({ type: 'uuid' }),
43
+ __metadata("design:type", String)
44
+ ], AdminNotification.prototype, "id", void 0);
45
+ __decorate([
46
+ Property({ type: 'string', length: 16 }),
47
+ __metadata("design:type", String)
48
+ ], AdminNotification.prototype, "audience", void 0);
49
+ __decorate([
50
+ Property({ type: 'uuid', nullable: true }),
51
+ Index(),
52
+ __metadata("design:type", Object)
53
+ ], AdminNotification.prototype, "targetAdminUserId", void 0);
54
+ __decorate([
55
+ Property({ type: 'string', length: 64 }),
56
+ __metadata("design:type", String)
57
+ ], AdminNotification.prototype, "kind", void 0);
58
+ __decorate([
59
+ Property({ type: 'string', length: 64, nullable: true }),
60
+ __metadata("design:type", Object)
61
+ ], AdminNotification.prototype, "subjectType", void 0);
62
+ __decorate([
63
+ Property({ type: 'uuid', nullable: true }),
64
+ __metadata("design:type", Object)
65
+ ], AdminNotification.prototype, "subjectId", void 0);
66
+ __decorate([
67
+ Property({ type: 'string', length: 255 }),
68
+ __metadata("design:type", String)
69
+ ], AdminNotification.prototype, "title", void 0);
70
+ __decorate([
71
+ Property({ type: 'text', nullable: true }),
72
+ __metadata("design:type", Object)
73
+ ], AdminNotification.prototype, "body", void 0);
74
+ __decorate([
75
+ Property({ type: 'string', length: 255, nullable: true }),
76
+ __metadata("design:type", Object)
77
+ ], AdminNotification.prototype, "linkPath", void 0);
78
+ __decorate([
79
+ Property({ type: 'datetime' }),
80
+ __metadata("design:type", Date)
81
+ ], AdminNotification.prototype, "createdAt", void 0);
82
+ __decorate([
83
+ Property({ type: 'datetime', nullable: true }),
84
+ __metadata("design:type", Object)
85
+ ], AdminNotification.prototype, "readAt", void 0);
86
+ __decorate([
87
+ Property({ type: 'datetime', nullable: true }),
88
+ __metadata("design:type", Object)
89
+ ], AdminNotification.prototype, "archivedAt", void 0);
90
+ AdminNotification = __decorate([
91
+ GlobalEntity(),
92
+ Entity({ tableName: 'admin_notifications' })
93
+ ], AdminNotification);
94
+ export { AdminNotification };
95
+ //# sourceMappingURL=admin-notification.entity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-notification.entity.js","sourceRoot":"","sources":["../../../src/backend/entities/admin-notification.entity.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AACrF,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AAEjE;;;;;;;;;;;;GAYG;AAGI,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IAC5B,CAAC,aAAa,CAAC,CASE;IAGjB,EAAE,GAAW,UAAU,EAAE,CAAC;IAG1B,QAAQ,CAA6B;IAIrC,iBAAiB,CAAiB;IAGlC,IAAI,CAAU;IAGd,WAAW,CAAiB;IAG5B,SAAS,CAAiB;IAG1B,KAAK,CAAU;IAGf,IAAI,CAAiB;IAGrB,QAAQ,CAAiB;IAGzB,SAAS,GAAS,IAAI,IAAI,EAAE,CAAC;IAG7B,MAAM,CAAe;IAGrB,UAAU,CAAe;CAC1B,CAAA;AAnCC;IADC,UAAU,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;6CACH;AAG1B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;;mDACJ;AAIrC;IAFC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1C,KAAK,EAAE;;4DAC0B;AAGlC;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;;+CAC3B;AAGd;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;sDAC7B;AAG5B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;oDACjB;AAG1B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;;gDAC3B;AAGf;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;+CACtB;AAGrB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;mDACjC;AAGzB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;8BACpB,IAAI;oDAAc;AAG7B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;iDAC1B;AAGrB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;qDACtB;AA/Cd,iBAAiB;IAF7B,YAAY,EAAE;IACd,MAAM,CAAC,EAAE,SAAS,EAAE,qBAAqB,EAAE,CAAC;GAChC,iBAAiB,CAgD7B"}
@@ -0,0 +1,25 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql';
2
+ import { type ModuleContext } from '@endora-commerce/platform/kernel';
3
+ import type { RequireAdminFactory } from '@endora-commerce/platform/kernel';
4
+ import { AdminNotificationService } from './services/admin-notification-service.js';
5
+ import { AdminNotificationRead } from './entities/admin-notification-read.entity.js';
6
+ import { AdminNotification } from './entities/admin-notification.entity.js';
7
+ export interface AdminNotificationsCradle {
8
+ readonly emFactory: () => EntityManager;
9
+ readonly requireAdmin: RequireAdminFactory;
10
+ readonly adminNotificationService: AdminNotificationService;
11
+ }
12
+ export declare function registerModule(ctx: ModuleContext): void;
13
+ /**
14
+ * The module's persisted entity classes, on the `./backend` subpath, as one
15
+ * array and **no named class export** (D-168).
16
+ *
17
+ * This is the shape the platform reads when the package is *installed*: the
18
+ * boot-time loader (`src/packages/package-runtime.ts`, `exported['entities']`)
19
+ * and the static declaration reader (`scripts/lib/package-declarations.ts`),
20
+ * which is the third source of `check:module-boundary`'s `table→owner` map and
21
+ * the package pass of `check-entity-tenant-classification`. A missing array is
22
+ * answered with `[]` — zero entities registered, no error anywhere.
23
+ */
24
+ export declare const entities: (typeof AdminNotification | typeof AdminNotificationRead)[];
25
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/backend/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,OAAO,EAAY,KAAK,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAChF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAE5E,OAAO,EAAE,wBAAwB,EAAE,MAAM,0CAA0C,CAAC;AAEpF,OAAO,EAAE,qBAAqB,EAAE,MAAM,8CAA8C,CAAC;AACrF,OAAO,EAAE,iBAAiB,EAAE,MAAM,yCAAyC,CAAC;AAsB5E,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,SAAS,EAAE,MAAM,aAAa,CAAC;IACxC,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;IAC3C,QAAQ,CAAC,wBAAwB,EAAE,wBAAwB,CAAC;CAC7D;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CA+CvD;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,QAAQ,6DAGpB,CAAC"}
@@ -0,0 +1,70 @@
1
+ import { lazyPort } from '@endora-commerce/platform/kernel';
2
+ import { registerAdminNotificationsRoutes } from './routes.admin.js';
3
+ import { AdminNotificationService } from './services/admin-notification-service.js';
4
+ import { createAdminNotificationRecordPort } from './services/admin-notification-port.js';
5
+ import { AdminNotificationRead } from './entities/admin-notification-read.entity.js';
6
+ import { AdminNotification } from './entities/admin-notification.entity.js';
7
+ /**
8
+ * `admin_notifications` — a module four other modules write into (feature 072,
9
+ * wave 1).
10
+ *
11
+ * `organizations`, `inventory`, `quote_requests` and the moderation wiring all
12
+ * hold `adminNotificationService`, which is why it is a **port**: the write is
13
+ * a cross-module call, so it answers on this module's effective state instead
14
+ * of succeeding into a module the operator switched off. What that buys is
15
+ * narrow and worth stating — an operator who turns notifications off stops
16
+ * accumulating rows nobody will read, rather than filling a table whose surface
17
+ * is gone.
18
+ *
19
+ * The unread cap stays a constant rather than a setting. It is a storage
20
+ * guard, not an operator preference, and nothing in the tree ever passed a
21
+ * value other than the default.
22
+ */
23
+ /** Cap of unread entries per audience before older ones auto-archive. */
24
+ const MAX_UNREAD_PER_AUDIENCE = 50;
25
+ export function registerModule(ctx) {
26
+ /**
27
+ * Feature 075, Phase P — the one method the one cross-module consumer calls.
28
+ *
29
+ * `organizations` records a notification when a new organisation registers.
30
+ * The module's other four methods are its own admin surface — listing,
31
+ * marking read, archiving, counting — and stay unpublished. The adapter
32
+ * exists because `record` answers with the `AdminNotification` entity, and a
33
+ * port answers with a record.
34
+ */
35
+ ctx.di.providePort('adminNotificationRecordPort', ctx
36
+ .asFunction(() => createAdminNotificationRecordPort(() => ctx.cradle().adminNotificationService))
37
+ .singleton());
38
+ ctx.di.providePort('adminNotificationService', ctx
39
+ .asFunction(({ emFactory }) => new AdminNotificationService(emFactory, {
40
+ maxUnreadPerAudience: MAX_UNREAD_PER_AUDIENCE,
41
+ }))
42
+ .singleton());
43
+ ctx.routes(async (app) => {
44
+ const { requireAdmin } = ctx.cradle();
45
+ await registerAdminNotificationsRoutes(app, {
46
+ // Lazily, even though the module owns this port: route *registration* runs
47
+ // inside `buildServer` whatever the module's effective state is, so
48
+ // destructuring the gate here would stop the next start instead of
49
+ // stopping the routes (D-40).
50
+ adminNotificationService: lazyPort(ctx, 'adminNotificationService'),
51
+ requireAdmin,
52
+ });
53
+ });
54
+ }
55
+ /**
56
+ * The module's persisted entity classes, on the `./backend` subpath, as one
57
+ * array and **no named class export** (D-168).
58
+ *
59
+ * This is the shape the platform reads when the package is *installed*: the
60
+ * boot-time loader (`src/packages/package-runtime.ts`, `exported['entities']`)
61
+ * and the static declaration reader (`scripts/lib/package-declarations.ts`),
62
+ * which is the third source of `check:module-boundary`'s `table→owner` map and
63
+ * the package pass of `check-entity-tenant-classification`. A missing array is
64
+ * answered with `[]` — zero entities registered, no error anywhere.
65
+ */
66
+ export const entities = [
67
+ AdminNotificationRead,
68
+ AdminNotification,
69
+ ];
70
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/backend/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAsB,MAAM,kCAAkC,CAAC;AAEhF,OAAO,EAAE,gCAAgC,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,EAAE,wBAAwB,EAAE,MAAM,0CAA0C,CAAC;AACpF,OAAO,EAAE,iCAAiC,EAAE,MAAM,uCAAuC,CAAC;AAC1F,OAAO,EAAE,qBAAqB,EAAE,MAAM,8CAA8C,CAAC;AACrF,OAAO,EAAE,iBAAiB,EAAE,MAAM,yCAAyC,CAAC;AAE5E;;;;;;;;;;;;;;;GAeG;AAEH,yEAAyE;AACzE,MAAM,uBAAuB,GAAG,EAAE,CAAC;AAQnC,MAAM,UAAU,cAAc,CAAC,GAAkB;IAC/C;;;;;;;;OAQG;IACH,GAAG,CAAC,EAAE,CAAC,WAAW,CAChB,6BAA6B,EAC7B,GAAG;SACA,UAAU,CAAC,GAAG,EAAE,CACf,iCAAiC,CAC/B,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,EAA4B,CAAC,wBAAwB,CACtE,CACF;SACA,SAAS,EAAE,CACf,CAAC;IAEF,GAAG,CAAC,EAAE,CAAC,WAAW,CAChB,0BAA0B,EAC1B,GAAG;SACA,UAAU,CACT,CAAC,EAAE,SAAS,EAA4B,EAAE,EAAE,CAC1C,IAAI,wBAAwB,CAAC,SAAS,EAAE;QACtC,oBAAoB,EAAE,uBAAuB;KAC9C,CAAC,CACL;SACA,SAAS,EAAE,CACf,CAAC;IAEF,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACvB,MAAM,EAAE,YAAY,EAAE,GAAG,GAAG,CAAC,MAAM,EAA4B,CAAC;QAChE,MAAM,gCAAgC,CAAC,GAAG,EAAE;YAC1C,2EAA2E;YAC3E,oEAAoE;YACpE,mEAAmE;YACnE,8BAA8B;YAC9B,wBAAwB,EAAE,QAAQ,CAChC,GAAG,EACH,0BAA0B,CAC3B;YACD,YAAY;SACb,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,qBAAqB;IACrB,iBAAiB;CAClB,CAAC"}
@@ -0,0 +1,26 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+ import type { EntityManager } from '@mikro-orm/postgresql';
3
+ import { AdminNotificationService } from './services/admin-notification-service.js';
4
+ import type { RequireAdminFactory } from '@endora-commerce/platform/kernel';
5
+ /**
6
+ * Composition root for the admin_notifications module.
7
+ *
8
+ * The service is exposed on the handle so other modules (organizations,
9
+ * inventory, quote_requests, …) can write entries by injection without
10
+ * reaching across module boundaries. The admin routes are mounted lazily
11
+ * via `plugin(app)` and are gated by the existing admin-permission factory.
12
+ */
13
+ export interface AdminNotificationsModuleOptions {
14
+ emFactory: () => EntityManager;
15
+ requireAdmin: RequireAdminFactory;
16
+ /** Cap of unread per audience before older entries auto-archive. Default 50. */
17
+ maxUnreadPerAudience?: number;
18
+ }
19
+ export interface AdminNotificationsModuleHandle {
20
+ adminNotificationService: AdminNotificationService;
21
+ }
22
+ export declare function adminNotificationsModule(options: AdminNotificationsModuleOptions): {
23
+ plugin: (app: FastifyInstance) => Promise<void>;
24
+ handle: AdminNotificationsModuleHandle;
25
+ };
26
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/backend/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,wBAAwB,EAAE,MAAM,0CAA0C,CAAC;AAEpF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAE5E;;;;;;;GAOG;AACH,MAAM,WAAW,+BAA+B;IAC9C,SAAS,EAAE,MAAM,aAAa,CAAC;IAC/B,YAAY,EAAE,mBAAmB,CAAC;IAClC,gFAAgF;IAChF,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,8BAA8B;IAC7C,wBAAwB,EAAE,wBAAwB,CAAC;CACpD;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,+BAA+B,GAAG;IAClF,MAAM,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,MAAM,EAAE,8BAA8B,CAAC;CACxC,CAcA"}
@@ -0,0 +1,17 @@
1
+ import { AdminNotificationService } from './services/admin-notification-service.js';
2
+ import { registerAdminNotificationsRoutes } from './routes.admin.js';
3
+ export function adminNotificationsModule(options) {
4
+ const adminNotificationService = new AdminNotificationService(options.emFactory, {
5
+ maxUnreadPerAudience: options.maxUnreadPerAudience ?? 50,
6
+ });
7
+ return {
8
+ handle: { adminNotificationService },
9
+ plugin: async (app) => {
10
+ await registerAdminNotificationsRoutes(app, {
11
+ adminNotificationService,
12
+ requireAdmin: options.requireAdmin,
13
+ });
14
+ },
15
+ };
16
+ }
17
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/backend/plugin.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,wBAAwB,EAAE,MAAM,0CAA0C,CAAC;AACpF,OAAO,EAAE,gCAAgC,EAAE,MAAM,mBAAmB,CAAC;AAsBrE,MAAM,UAAU,wBAAwB,CAAC,OAAwC;IAI/E,MAAM,wBAAwB,GAAG,IAAI,wBAAwB,CAAC,OAAO,CAAC,SAAS,EAAE;QAC/E,oBAAoB,EAAE,OAAO,CAAC,oBAAoB,IAAI,EAAE;KACzD,CAAC,CAAC;IAEH,OAAO;QACL,MAAM,EAAE,EAAE,wBAAwB,EAAE;QACpC,MAAM,EAAE,KAAK,EAAE,GAAoB,EAAiB,EAAE;YACpD,MAAM,gCAAgC,CAAC,GAAG,EAAE;gBAC1C,wBAAwB;gBACxB,YAAY,EAAE,OAAO,CAAC,YAAY;aACnC,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+ import type { AdminNotificationService } from './services/admin-notification-service.js';
3
+ import type { RequireAdminFactory } from '@endora-commerce/platform/kernel';
4
+ export interface AdminNotificationsRoutesDeps {
5
+ adminNotificationService: AdminNotificationService;
6
+ requireAdmin: RequireAdminFactory;
7
+ }
8
+ /**
9
+ * Routes for the admin notification bell (feature 026, US1).
10
+ *
11
+ * The list endpoint resolves per-admin `isRead` by joining
12
+ * `admin_notification_reads` for `audience='all_admins'` rows. Read state
13
+ * for `audience='admin_user'` lives directly on the notification row.
14
+ */
15
+ export declare function registerAdminNotificationsRoutes(app: FastifyInstance, deps: AdminNotificationsRoutesDeps): Promise<void>;
16
+ //# sourceMappingURL=routes.admin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.admin.d.ts","sourceRoot":"","sources":["../../src/backend/routes.admin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAkB,MAAM,SAAS,CAAC;AAqB/D,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,0CAA0C,CAAC;AAEzF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAQ5E,MAAM,WAAW,4BAA4B;IAC3C,wBAAwB,EAAE,wBAAwB,CAAC;IACnD,YAAY,EAAE,mBAAmB,CAAC;CACnC;AAED;;;;;;GAMG;AACH,wBAAsB,gCAAgC,CACpD,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,4BAA4B,GACjC,OAAO,CAAC,IAAI,CAAC,CAqDf"}
@@ -0,0 +1,78 @@
1
+ import { z } from 'zod';
2
+ import { ERROR_CODES } from '@endora-commerce/contracts';
3
+ import { HttpError } from '@endora-commerce/platform/http';
4
+ /**
5
+ * Resolve the acting admin's id. Production decorates `request.actor` via the
6
+ * auth plugin; the test harness decorates `request.testActor` — both shapes
7
+ * are checked (the `admin:read` gate has already run).
8
+ */
9
+ function resolveAdminUserId(request) {
10
+ const r = request;
11
+ const actor = r.testActor?.kind === 'admin' ? r.testActor : r.actor;
12
+ if (!actor || actor.kind !== 'admin' || !actor.adminUserId) {
13
+ throw new HttpError(401, ERROR_CODES.UNAUTHORIZED, 'Admin session required.');
14
+ }
15
+ return actor.adminUserId;
16
+ }
17
+ import { NotificationNotFoundError } from './services/admin-notification-service.js';
18
+ const listQuerySchema = z.object({
19
+ unread: z.coerce.boolean().default(false),
20
+ limit: z.coerce.number().int().positive().max(100).default(25),
21
+ cursor: z.string().optional(),
22
+ });
23
+ /**
24
+ * Routes for the admin notification bell (feature 026, US1).
25
+ *
26
+ * The list endpoint resolves per-admin `isRead` by joining
27
+ * `admin_notification_reads` for `audience='all_admins'` rows. Read state
28
+ * for `audience='admin_user'` lives directly on the notification row.
29
+ */
30
+ export async function registerAdminNotificationsRoutes(app, deps) {
31
+ const { adminNotificationService, requireAdmin } = deps;
32
+ const gate = requireAdmin('admin:read');
33
+ app.get('/api/v1/admin/notifications', { preHandler: gate }, async (request) => {
34
+ const adminUserId = resolveAdminUserId(request);
35
+ const query = listQuerySchema.parse(request.query);
36
+ const page = await adminNotificationService.listForAdmin({
37
+ adminUserId,
38
+ unread: query.unread,
39
+ limit: query.limit,
40
+ cursor: query.cursor ?? null,
41
+ });
42
+ return {
43
+ items: page.items.map((it) => ({
44
+ id: it.id,
45
+ audience: it.audience,
46
+ kind: it.kind,
47
+ subjectType: it.subjectType,
48
+ subjectId: it.subjectId,
49
+ title: it.title,
50
+ body: it.body,
51
+ linkPath: it.linkPath,
52
+ createdAt: it.createdAt.toISOString(),
53
+ isRead: it.isRead,
54
+ })),
55
+ nextCursor: page.nextCursor,
56
+ unreadCount: page.unreadCount,
57
+ };
58
+ });
59
+ app.post('/api/v1/admin/notifications/:id/read', { preHandler: gate }, async (request, reply) => {
60
+ const adminUserId = resolveAdminUserId(request);
61
+ try {
62
+ await adminNotificationService.markRead(request.params.id, adminUserId);
63
+ }
64
+ catch (err) {
65
+ if (err instanceof NotificationNotFoundError) {
66
+ throw new HttpError(404, ERROR_CODES.NOT_FOUND, 'Notification not found.');
67
+ }
68
+ throw err;
69
+ }
70
+ return reply.status(204).send();
71
+ });
72
+ app.post('/api/v1/admin/notifications/mark-all-read', { preHandler: gate }, async (request) => {
73
+ const adminUserId = resolveAdminUserId(request);
74
+ const markedCount = await adminNotificationService.markAllRead(adminUserId);
75
+ return { markedCount };
76
+ });
77
+ }
78
+ //# sourceMappingURL=routes.admin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.admin.js","sourceRoot":"","sources":["../../src/backend/routes.admin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { AdminNotificationRecord, AdminNotificationRecordPort } from '@endora-commerce/contracts';
2
+ import type { AdminNotification } from '../entities/admin-notification.entity.js';
3
+ import type { AdminNotificationService } from './admin-notification-service.js';
4
+ /**
5
+ * The published face of `admin_notifications` (feature 075, Phase P).
6
+ *
7
+ * One cross-module consumer — `organizations` records a notification when a
8
+ * new organisation registers — so the port is that one method, not the
9
+ * module's five. The other four are its own admin surface: listing, marking
10
+ * read, archiving, counting.
11
+ *
12
+ * The adapter exists for the usual reason: `record` answers with the
13
+ * `AdminNotification` entity, and the port answers with the record.
14
+ */
15
+ export declare function createAdminNotificationRecordPort(getService: () => AdminNotificationService): AdminNotificationRecordPort;
16
+ export declare function toAdminNotificationRecord(notification: AdminNotification): AdminNotificationRecord;
17
+ //# sourceMappingURL=admin-notification-port.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-notification-port.d.ts","sourceRoot":"","sources":["../../../src/backend/services/admin-notification-port.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,uBAAuB,EACvB,2BAA2B,EAC5B,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0CAA0C,CAAC;AAClF,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC;AAEhF;;;;;;;;;;GAUG;AACH,wBAAgB,iCAAiC,CAC/C,UAAU,EAAE,MAAM,wBAAwB,GACzC,2BAA2B,CAM7B;AAED,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,iBAAiB,GAC9B,uBAAuB,CAazB"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * The published face of `admin_notifications` (feature 075, Phase P).
3
+ *
4
+ * One cross-module consumer — `organizations` records a notification when a
5
+ * new organisation registers — so the port is that one method, not the
6
+ * module's five. The other four are its own admin surface: listing, marking
7
+ * read, archiving, counting.
8
+ *
9
+ * The adapter exists for the usual reason: `record` answers with the
10
+ * `AdminNotification` entity, and the port answers with the record.
11
+ */
12
+ export function createAdminNotificationRecordPort(getService) {
13
+ return {
14
+ async record(input) {
15
+ return toAdminNotificationRecord(await getService().record(input));
16
+ },
17
+ };
18
+ }
19
+ export function toAdminNotificationRecord(notification) {
20
+ return {
21
+ id: notification.id,
22
+ audience: notification.audience,
23
+ targetAdminUserId: notification.targetAdminUserId ?? null,
24
+ kind: notification.kind,
25
+ subjectType: notification.subjectType ?? null,
26
+ subjectId: notification.subjectId ?? null,
27
+ title: notification.title,
28
+ body: notification.body ?? null,
29
+ linkPath: notification.linkPath ?? null,
30
+ createdAt: notification.createdAt,
31
+ };
32
+ }
33
+ //# sourceMappingURL=admin-notification-port.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-notification-port.js","sourceRoot":"","sources":["../../../src/backend/services/admin-notification-port.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;GAUG;AACH,MAAM,UAAU,iCAAiC,CAC/C,UAA0C;IAE1C,OAAO;QACL,KAAK,CAAC,MAAM,CAAC,KAAK;YAChB,OAAO,yBAAyB,CAAC,MAAM,UAAU,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACrE,CAAC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,yBAAyB,CACvC,YAA+B;IAE/B,OAAO;QACL,EAAE,EAAE,YAAY,CAAC,EAAE;QACnB,QAAQ,EAAE,YAAY,CAAC,QAAQ;QAC/B,iBAAiB,EAAE,YAAY,CAAC,iBAAiB,IAAI,IAAI;QACzD,IAAI,EAAE,YAAY,CAAC,IAAI;QACvB,WAAW,EAAE,YAAY,CAAC,WAAW,IAAI,IAAI;QAC7C,SAAS,EAAE,YAAY,CAAC,SAAS,IAAI,IAAI;QACzC,KAAK,EAAE,YAAY,CAAC,KAAK;QACzB,IAAI,EAAE,YAAY,CAAC,IAAI,IAAI,IAAI;QAC/B,QAAQ,EAAE,YAAY,CAAC,QAAQ,IAAI,IAAI;QACvC,SAAS,EAAE,YAAY,CAAC,SAAS;KAClC,CAAC;AACJ,CAAC"}