@endora-commerce/mod-meta-ads 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 (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +57 -2
  3. package/dist/admin/api/meta-ads-client.d.ts +32 -0
  4. package/dist/admin/api/meta-ads-client.d.ts.map +1 -0
  5. package/dist/admin/api/meta-ads-client.js +34 -0
  6. package/dist/admin/api/meta-ads-client.js.map +1 -0
  7. package/dist/admin/index.d.ts +30 -0
  8. package/dist/admin/index.d.ts.map +1 -0
  9. package/dist/admin/index.js +59 -0
  10. package/dist/admin/index.js.map +1 -0
  11. package/dist/admin/pages/CustomEventMappingEditPage.d.ts +4 -0
  12. package/dist/admin/pages/CustomEventMappingEditPage.d.ts.map +1 -0
  13. package/dist/admin/pages/CustomEventMappingEditPage.js +85 -0
  14. package/dist/admin/pages/CustomEventMappingEditPage.js.map +1 -0
  15. package/dist/admin/pages/CustomEventMappingsListPage.d.ts +7 -0
  16. package/dist/admin/pages/CustomEventMappingsListPage.d.ts.map +1 -0
  17. package/dist/admin/pages/CustomEventMappingsListPage.js +90 -0
  18. package/dist/admin/pages/CustomEventMappingsListPage.js.map +1 -0
  19. package/dist/backend/entities/meta-custom-event-mapping.entity.d.ts +24 -0
  20. package/dist/backend/entities/meta-custom-event-mapping.entity.d.ts.map +1 -0
  21. package/dist/backend/entities/meta-custom-event-mapping.entity.js +75 -0
  22. package/dist/backend/entities/meta-custom-event-mapping.entity.js.map +1 -0
  23. package/dist/backend/index.d.ts +67 -0
  24. package/dist/backend/index.d.ts.map +1 -0
  25. package/dist/backend/index.js +56 -0
  26. package/dist/backend/index.js.map +1 -0
  27. package/dist/backend/routes.admin.d.ts +16 -0
  28. package/dist/backend/routes.admin.d.ts.map +1 -0
  29. package/dist/backend/routes.admin.js +31 -0
  30. package/dist/backend/routes.admin.js.map +1 -0
  31. package/dist/backend/routes.storefront.d.ts +11 -0
  32. package/dist/backend/routes.storefront.d.ts.map +1 -0
  33. package/dist/backend/routes.storefront.js +13 -0
  34. package/dist/backend/routes.storefront.js.map +1 -0
  35. package/dist/backend/services/custom-event-mappings.service.d.ts +40 -0
  36. package/dist/backend/services/custom-event-mappings.service.d.ts.map +1 -0
  37. package/dist/backend/services/custom-event-mappings.service.js +134 -0
  38. package/dist/backend/services/custom-event-mappings.service.js.map +1 -0
  39. package/dist/backend/services/meta-config.service.d.ts +22 -0
  40. package/dist/backend/services/meta-config.service.d.ts.map +1 -0
  41. package/dist/backend/services/meta-config.service.js +55 -0
  42. package/dist/backend/services/meta-config.service.js.map +1 -0
  43. package/dist/manifest.d.ts +194 -0
  44. package/dist/manifest.d.ts.map +1 -0
  45. package/dist/manifest.js +102 -0
  46. package/dist/manifest.js.map +1 -0
  47. package/dist/migrations/20260728T002715_meta_ads_init.d.ts +12 -0
  48. package/dist/migrations/20260728T002715_meta_ads_init.d.ts.map +1 -0
  49. package/dist/migrations/20260728T002715_meta_ads_init.js +35 -0
  50. package/dist/migrations/20260728T002715_meta_ads_init.js.map +1 -0
  51. package/dist/migrations/index.d.ts +27 -0
  52. package/dist/migrations/index.d.ts.map +1 -0
  53. package/dist/migrations/index.js +29 -0
  54. package/dist/migrations/index.js.map +1 -0
  55. package/docs/meta-ads.md +82 -0
  56. package/i18n/en.json +35 -0
  57. package/i18n/pl.json +35 -0
  58. package/package.json +89 -3
  59. package/tailwind.css +14 -0
@@ -0,0 +1,67 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql';
2
+ import type { FastifyRequest } from 'fastify';
3
+ import type { AuditPort } from '@endora-commerce/platform/kernel';
4
+ import type { ModuleContext } from '@endora-commerce/platform/kernel';
5
+ import type { RequireAdminFactory } from '@endora-commerce/platform/kernel';
6
+ import type { SettingsReadPort } from '@endora-commerce/platform/kernel';
7
+ import { StorefrontRevalidator } from '@endora-commerce/platform/http';
8
+ import { MetaConfigService } from './services/meta-config.service.js';
9
+ import { MetaCustomEventMappingsService, type MetaAuditContext } from './services/custom-event-mappings.service.js';
10
+ import { MetaCustomEventMapping } from './entities/meta-custom-event-mapping.entity.js';
11
+ /**
12
+ * `meta_ads` — the module that kept calling the storefront after it was
13
+ * switched off (feature 072, wave 2, T108).
14
+ *
15
+ * The routes were already gated: `plugin.ts` wrapped them in
16
+ * `defineModuleRoutes('meta_ads', …)`. The **subscription** was not. A root
17
+ * passed `onSettingChanged` as a bare `eventBus.on('settings.value_changed', …)`,
18
+ * so the handler stayed live regardless of the module's effective state, and
19
+ * every `meta_ads.*` settings change fired
20
+ *
21
+ * revalidator.revalidate(['meta:config'])
22
+ *
23
+ * — an outbound HTTP POST to the storefront, asking it to rebuild a cache tag
24
+ * for a module that was serving nothing. Constitution XVII item 2 asks for
25
+ * EventBus subscriptions to go through `subscribeForModule` for exactly this
26
+ * reason, and `ctx.subscribe` is that wrapper, so the conversion closes it by
27
+ * construction rather than by remembering.
28
+ *
29
+ * The same shape is in `linkedin_ads`, `google_analytics` and
30
+ * `google_tag_manager`; the first is converted alongside this one.
31
+ *
32
+ * `auditLog` stops being optional. It is the append-only sink for mapping
33
+ * changes — the record of who altered the pixel's event mapping — and an
34
+ * optional audit sink defaults to *not recording*. Both roots passed one, so
35
+ * nothing was lost; this is the ninth time in this transition that shape has
36
+ * been removed rather than the ninth incident.
37
+ */
38
+ interface MetaAdsServices {
39
+ readonly revalidator: StorefrontRevalidator;
40
+ readonly mappings: MetaCustomEventMappingsService;
41
+ readonly configService: MetaConfigService;
42
+ readonly invalidateConfig: () => void;
43
+ }
44
+ export interface MetaAdsCradle {
45
+ readonly emFactory: () => EntityManager;
46
+ readonly auditLogService: AuditPort;
47
+ readonly settingsReadPort: SettingsReadPort;
48
+ readonly requireAdmin: RequireAdminFactory;
49
+ /** How this composition names the acting admin; `null` for a non-admin caller. */
50
+ readonly adminAuditActorResolver: (request: FastifyRequest) => MetaAuditContext;
51
+ readonly metaAdsServices: MetaAdsServices;
52
+ }
53
+ export declare function registerModule(ctx: ModuleContext): void;
54
+ /**
55
+ * The module's persisted entity classes, on the `./backend` subpath, as one
56
+ * array and **no named class export** (D-168).
57
+ *
58
+ * This is the shape the platform reads when the package is *installed*: the
59
+ * boot-time loader (`src/packages/package-runtime.ts`, `exported['entities']`)
60
+ * and the static declaration reader (`scripts/lib/package-declarations.ts`),
61
+ * which is the third source of `check:module-boundary`'s `table→owner` map and
62
+ * the package pass of `check-entity-tenant-classification`. A missing array is
63
+ * answered with `[]` — zero entities registered, no error anywhere.
64
+ */
65
+ export declare const entities: (typeof MetaCustomEventMapping)[];
66
+ export {};
67
+ //# 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;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAEtE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAC5E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EACL,8BAA8B,EAC9B,KAAK,gBAAgB,EACtB,MAAM,6CAA6C,CAAC;AAGrD,OAAO,EAAE,sBAAsB,EAAE,MAAM,gDAAgD,CAAC;AAExF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,UAAU,eAAe;IACvB,QAAQ,CAAC,WAAW,EAAE,qBAAqB,CAAC;IAC5C,QAAQ,CAAC,QAAQ,EAAE,8BAA8B,CAAC;IAClD,QAAQ,CAAC,aAAa,EAAE,iBAAiB,CAAC;IAC1C,QAAQ,CAAC,gBAAgB,EAAE,MAAM,IAAI,CAAC;CACvC;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,aAAa,CAAC;IACxC,QAAQ,CAAC,eAAe,EAAE,SAAS,CAAC;IACpC,QAAQ,CAAC,gBAAgB,EAAE,gBAAgB,CAAC;IAC5C,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;IAC3C,kFAAkF;IAClF,QAAQ,CAAC,uBAAuB,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,gBAAgB,CAAC;IAChF,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;CAC3C;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CA6CvD;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,QAAQ,mCAEpB,CAAC"}
@@ -0,0 +1,56 @@
1
+ import { lazyPort } from '@endora-commerce/platform/kernel';
2
+ import { StorefrontRevalidator } from '@endora-commerce/platform/http';
3
+ import { MetaConfigService } from './services/meta-config.service.js';
4
+ import { MetaCustomEventMappingsService, } from './services/custom-event-mappings.service.js';
5
+ import { registerMetaAdsAdminRoutes } from './routes.admin.js';
6
+ import { registerMetaAdsStorefrontRoutes } from './routes.storefront.js';
7
+ import { MetaCustomEventMapping } from './entities/meta-custom-event-mapping.entity.js';
8
+ export function registerModule(ctx) {
9
+ ctx.di.register({
10
+ metaAdsServices: ctx
11
+ .asFunction(({ emFactory, auditLogService }) => {
12
+ const revalidator = new StorefrontRevalidator({
13
+ baseUrl: process.env['STOREFRONT_BASE_URL'],
14
+ secret: process.env['REVALIDATE_SECRET'],
15
+ });
16
+ const invalidateConfig = () => {
17
+ void revalidator.revalidate(['meta:config']);
18
+ };
19
+ const mappings = new MetaCustomEventMappingsService(emFactory, auditLogService, invalidateConfig);
20
+ const configService = new MetaConfigService(lazyPort(ctx, 'settingsReadPort'), (channelId) => mappings.loadForChannel(channelId));
21
+ return { revalidator, mappings, configService, invalidateConfig };
22
+ })
23
+ .singleton(),
24
+ });
25
+ ctx.subscribe('settings.value_changed', (payload) => {
26
+ const settingCode = payload.settingCode;
27
+ if (typeof settingCode !== 'string' || !settingCode.startsWith('meta_ads.'))
28
+ return;
29
+ ctx.cradle().metaAdsServices.invalidateConfig();
30
+ });
31
+ ctx.routes(async (app) => {
32
+ const { metaAdsServices, requireAdmin, adminAuditActorResolver } = ctx.cradle();
33
+ metaAdsServices.revalidator.setLogger(app.log);
34
+ await registerMetaAdsStorefrontRoutes(app, { configService: metaAdsServices.configService });
35
+ await registerMetaAdsAdminRoutes(app, {
36
+ mappings: metaAdsServices.mappings,
37
+ requireAdmin,
38
+ resolveAuditContext: adminAuditActorResolver,
39
+ });
40
+ });
41
+ }
42
+ /**
43
+ * The module's persisted entity classes, on the `./backend` subpath, as one
44
+ * array and **no named class export** (D-168).
45
+ *
46
+ * This is the shape the platform reads when the package is *installed*: the
47
+ * boot-time loader (`src/packages/package-runtime.ts`, `exported['entities']`)
48
+ * and the static declaration reader (`scripts/lib/package-declarations.ts`),
49
+ * which is the third source of `check:module-boundary`'s `table→owner` map and
50
+ * the package pass of `check-entity-tenant-classification`. A missing array is
51
+ * answered with `[]` — zero entities registered, no error anywhere.
52
+ */
53
+ export const entities = [
54
+ MetaCustomEventMapping,
55
+ ];
56
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/backend/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAG5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EACL,8BAA8B,GAE/B,MAAM,6CAA6C,CAAC;AACrD,OAAO,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,+BAA+B,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,EAAE,sBAAsB,EAAE,MAAM,gDAAgD,CAAC;AA+CxF,MAAM,UAAU,cAAc,CAAC,GAAkB;IAC/C,GAAG,CAAC,EAAE,CAAC,QAAQ,CAAC;QACd,eAAe,EAAE,GAAG;aACjB,UAAU,CACT,CAAC,EAAE,SAAS,EAAE,eAAe,EAAiB,EAAmB,EAAE;YACjE,MAAM,WAAW,GAAG,IAAI,qBAAqB,CAAC;gBAC5C,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC;gBAC3C,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC;aACzC,CAAC,CAAC;YACH,MAAM,gBAAgB,GAAG,GAAS,EAAE;gBAClC,KAAK,WAAW,CAAC,UAAU,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC;YAC/C,CAAC,CAAC;YACF,MAAM,QAAQ,GAAG,IAAI,8BAA8B,CACjD,SAAS,EACT,eAAe,EACf,gBAAgB,CACjB,CAAC;YACF,MAAM,aAAa,GAAG,IAAI,iBAAiB,CACzC,QAAQ,CAAmB,GAAG,EAAE,kBAAkB,CAAC,EACnD,CAAC,SAAS,EAAE,EAAE,CACd,QAAQ,CAAC,cAAc,CAAC,SAAS,CAAC,CACnC,CAAC;YACF,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,aAAa,EAAE,gBAAgB,EAAE,CAAC;QACpE,CAAC,CACF;aACA,SAAS,EAAE;KACf,CAAC,CAAC;IAEH,GAAG,CAAC,SAAS,CAAC,wBAAwB,EAAE,CAAC,OAAO,EAAE,EAAE;QAClD,MAAM,WAAW,GAAI,OAAqC,CAAC,WAAW,CAAC;QACvE,IAAI,OAAO,WAAW,KAAK,QAAQ,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,WAAW,CAAC;YAAE,OAAO;QACpF,GAAG,CAAC,MAAM,EAAiB,CAAC,eAAe,CAAC,gBAAgB,EAAE,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACvB,MAAM,EAAE,eAAe,EAAE,YAAY,EAAE,uBAAuB,EAAE,GAC9D,GAAG,CAAC,MAAM,EAAiB,CAAC;QAC9B,eAAe,CAAC,WAAW,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC/C,MAAM,+BAA+B,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,eAAe,CAAC,aAAa,EAAE,CAAC,CAAC;QAC7F,MAAM,0BAA0B,CAAC,GAAG,EAAE;YACpC,QAAQ,EAAE,eAAe,CAAC,QAAQ;YAClC,YAAY;YACZ,mBAAmB,EAAE,uBAAuB;SAC7C,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,sBAAsB;CACvB,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify';
2
+ import type { MetaAuditContext, MetaCustomEventMappingsService } from './services/custom-event-mappings.service.js';
3
+ export interface MetaAdsAdminDeps {
4
+ mappings: MetaCustomEventMappingsService;
5
+ requireAdmin: (permission?: string) => (req: FastifyRequest, reply: FastifyReply) => Promise<void>;
6
+ resolveAuditContext: (req: FastifyRequest) => MetaAuditContext;
7
+ }
8
+ /**
9
+ * Admin routes for the Meta Ads module (feature 064, US4). Custom-event mapping
10
+ * CRUD plus the closed action catalogue that drives the admin picker.
11
+ *
12
+ * Per-channel Settings (Pixel ID, consent) are managed through the generic
13
+ * Settings admin surface, not here.
14
+ */
15
+ export declare function registerMetaAdsAdminRoutes(app: FastifyInstance, deps: MetaAdsAdminDeps): 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,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAO7E,OAAO,KAAK,EACV,gBAAgB,EAChB,8BAA8B,EAC/B,MAAM,6CAA6C,CAAC;AAErD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,8BAA8B,CAAC;IACzC,YAAY,EAAE,CAAC,UAAU,CAAC,EAAE,MAAM,KAAK,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACnG,mBAAmB,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,gBAAgB,CAAC;CAChE;AAED;;;;;;GAMG;AACH,wBAAsB,0BAA0B,CAC9C,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,IAAI,CAAC,CAkDf"}
@@ -0,0 +1,31 @@
1
+ import { META_TRIGGER_ACTIONS, createMetaCustomEventMappingSchema, updateMetaCustomEventMappingSchema, } from '@endora-commerce/contracts';
2
+ import { META_ADS_READ_PERMISSION, META_ADS_WRITE_PERMISSION } from '../manifest.js';
3
+ /**
4
+ * Admin routes for the Meta Ads module (feature 064, US4). Custom-event mapping
5
+ * CRUD plus the closed action catalogue that drives the admin picker.
6
+ *
7
+ * Per-channel Settings (Pixel ID, consent) are managed through the generic
8
+ * Settings admin surface, not here.
9
+ */
10
+ export async function registerMetaAdsAdminRoutes(app, deps) {
11
+ const { mappings, requireAdmin, resolveAuditContext } = deps;
12
+ const BASE = '/api/v1/admin/meta-ads';
13
+ app.get(`${BASE}/action-catalogue`, { preHandler: requireAdmin(META_ADS_READ_PERMISSION) }, async () => ({ data: META_TRIGGER_ACTIONS }));
14
+ app.get(`${BASE}/custom-events`, { preHandler: requireAdmin(META_ADS_READ_PERMISSION) }, async () => ({ data: await mappings.list() }));
15
+ app.get(`${BASE}/custom-events/:id`, { preHandler: requireAdmin(META_ADS_READ_PERMISSION) }, async (request) => ({ data: await mappings.get(request.params.id) }));
16
+ app.post(`${BASE}/custom-events`, { preHandler: requireAdmin(META_ADS_WRITE_PERMISSION) }, async (request, reply) => {
17
+ const body = createMetaCustomEventMappingSchema.parse(request.body);
18
+ const created = await mappings.create(body, resolveAuditContext(request));
19
+ return reply.status(201).send({ data: created });
20
+ });
21
+ app.patch(`${BASE}/custom-events/:id`, { preHandler: requireAdmin(META_ADS_WRITE_PERMISSION) }, async (request) => {
22
+ const body = updateMetaCustomEventMappingSchema.parse(request.body);
23
+ const updated = await mappings.update(request.params.id, body, resolveAuditContext(request));
24
+ return { data: updated };
25
+ });
26
+ app.delete(`${BASE}/custom-events/:id`, { preHandler: requireAdmin(META_ADS_WRITE_PERMISSION) }, async (request, reply) => {
27
+ await mappings.remove(request.params.id, resolveAuditContext(request));
28
+ return reply.status(204).send();
29
+ });
30
+ }
31
+ //# sourceMappingURL=routes.admin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.admin.js","sourceRoot":"","sources":["../../src/backend/routes.admin.ts"],"names":[],"mappings":"AACA,OAAO,EACL,oBAAoB,EACpB,kCAAkC,EAClC,kCAAkC,GACnC,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,wBAAwB,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAYrF;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,GAAoB,EACpB,IAAsB;IAEtB,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,mBAAmB,EAAE,GAAG,IAAI,CAAC;IAC7D,MAAM,IAAI,GAAG,wBAAwB,CAAC;IAEtC,GAAG,CAAC,GAAG,CACL,GAAG,IAAI,mBAAmB,EAC1B,EAAE,UAAU,EAAE,YAAY,CAAC,wBAAwB,CAAC,EAAE,EACtD,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,oBAAoB,EAAE,CAAC,CAC7C,CAAC;IAEF,GAAG,CAAC,GAAG,CACL,GAAG,IAAI,gBAAgB,EACvB,EAAE,UAAU,EAAE,YAAY,CAAC,wBAAwB,CAAC,EAAE,EACtD,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAC9C,CAAC;IAEF,GAAG,CAAC,GAAG,CACL,GAAG,IAAI,oBAAoB,EAC3B,EAAE,UAAU,EAAE,YAAY,CAAC,wBAAwB,CAAC,EAAE,EACtD,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CACrE,CAAC;IAEF,GAAG,CAAC,IAAI,CACN,GAAG,IAAI,gBAAgB,EACvB,EAAE,UAAU,EAAE,YAAY,CAAC,yBAAyB,CAAC,EAAE,EACvD,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QACvB,MAAM,IAAI,GAAG,kCAAkC,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACpE,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,mBAAmB,CAAC,OAAO,CAAC,CAAC,CAAC;QAC1E,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;IACnD,CAAC,CACF,CAAC;IAEF,GAAG,CAAC,KAAK,CACP,GAAG,IAAI,oBAAoB,EAC3B,EAAE,UAAU,EAAE,YAAY,CAAC,yBAAyB,CAAC,EAAE,EACvD,KAAK,EAAE,OAAO,EAAE,EAAE;QAChB,MAAM,IAAI,GAAG,kCAAkC,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACpE,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,IAAI,EAAE,mBAAmB,CAAC,OAAO,CAAC,CAAC,CAAC;QAC7F,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IAC3B,CAAC,CACF,CAAC;IAEF,GAAG,CAAC,MAAM,CACR,GAAG,IAAI,oBAAoB,EAC3B,EAAE,UAAU,EAAE,YAAY,CAAC,yBAAyB,CAAC,EAAE,EACvD,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QACvB,MAAM,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,mBAAmB,CAAC,OAAO,CAAC,CAAC,CAAC;QACvE,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAClC,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+ import type { MetaConfigService } from './services/meta-config.service.js';
3
+ export interface MetaAdsStorefrontDeps {
4
+ configService: MetaConfigService;
5
+ }
6
+ /**
7
+ * Public storefront routes for the Meta Ads module (feature 064). Wrapped by
8
+ * `defineModuleRoutes` in plugin.ts so they 503 when the module is disabled.
9
+ */
10
+ export declare function registerMetaAdsStorefrontRoutes(app: FastifyInstance, deps: MetaAdsStorefrontDeps): Promise<void>;
11
+ //# sourceMappingURL=routes.storefront.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.storefront.d.ts","sourceRoot":"","sources":["../../src/backend/routes.storefront.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAE3E,MAAM,WAAW,qBAAqB;IACpC,aAAa,EAAE,iBAAiB,CAAC;CAClC;AAED;;;GAGG;AACH,wBAAsB,+BAA+B,CACnD,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,qBAAqB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAMf"}
@@ -0,0 +1,13 @@
1
+ import { getResolvedChannel } from '@endora-commerce/platform/kernel';
2
+ /**
3
+ * Public storefront routes for the Meta Ads module (feature 064). Wrapped by
4
+ * `defineModuleRoutes` in plugin.ts so they 503 when the module is disabled.
5
+ */
6
+ export async function registerMetaAdsStorefrontRoutes(app, deps) {
7
+ app.get('/api/v1/storefront/meta-ads/config', async (request, reply) => {
8
+ const channel = getResolvedChannel(request);
9
+ const config = await deps.configService.getConfig(channel.id);
10
+ return reply.send({ data: config });
11
+ });
12
+ }
13
+ //# sourceMappingURL=routes.storefront.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.storefront.js","sourceRoot":"","sources":["../../src/backend/routes.storefront.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAOtE;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,+BAA+B,CACnD,GAAoB,EACpB,IAA2B;IAE3B,GAAG,CAAC,GAAG,CAAC,oCAAoC,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QACrE,MAAM,OAAO,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;QAC5C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC9D,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,40 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql';
2
+ import { type CreateMetaCustomEventMapping, type MetaCustomEventMapping as MetaCustomEventMappingDto, type MetaStorefrontMapping, type UpdateMetaCustomEventMapping } from '@endora-commerce/contracts';
3
+ export interface MetaAuditContext {
4
+ actorAdminUserId: string | null;
5
+ }
6
+ export interface MetaAuditSink {
7
+ record(input: {
8
+ action: string;
9
+ objectType: string;
10
+ objectId: string;
11
+ actorAdminUserId?: string | null;
12
+ stateBefore?: Record<string, unknown>;
13
+ stateAfter?: Record<string, unknown>;
14
+ }): Promise<unknown>;
15
+ }
16
+ /**
17
+ * CRUD + storefront resolution for Meta custom-event mappings (feature 064,
18
+ * US4). Boundary validation lives in the Zod schemas at the route; this service
19
+ * owns the optimistic-version guard, persistence, and audit.
20
+ */
21
+ export declare class MetaCustomEventMappingsService {
22
+ private readonly emFactory;
23
+ private readonly audit?;
24
+ private readonly onChange?;
25
+ constructor(emFactory: () => EntityManager, audit?: MetaAuditSink | undefined, onChange?: (() => void) | undefined);
26
+ list(): Promise<MetaCustomEventMappingDto[]>;
27
+ get(id: string): Promise<MetaCustomEventMappingDto>;
28
+ create(input: CreateMetaCustomEventMapping, ctx: MetaAuditContext): Promise<MetaCustomEventMappingDto>;
29
+ update(id: string, input: UpdateMetaCustomEventMapping, ctx: MetaAuditContext): Promise<MetaCustomEventMappingDto>;
30
+ remove(id: string, ctx: MetaAuditContext): Promise<void>;
31
+ /**
32
+ * Enabled mappings applicable to a channel (channel-scoped OR all-channels).
33
+ * A NULL `salesChannelId` means "every channel" by explicit design, not by
34
+ * fail-open (Principle XII).
35
+ */
36
+ loadForChannel(salesChannelId: string): Promise<MetaStorefrontMapping[]>;
37
+ private notFound;
38
+ private toDto;
39
+ }
40
+ //# sourceMappingURL=custom-event-mappings.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-event-mappings.service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/custom-event-mappings.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAEL,KAAK,4BAA4B,EACjC,KAAK,sBAAsB,IAAI,yBAAyB,EACxD,KAAK,qBAAqB,EAE1B,KAAK,4BAA4B,EAClC,MAAM,4BAA4B,CAAC;AAIpC,MAAM,WAAW,gBAAgB;IAC/B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,KAAK,EAAE;QACZ,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACjC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACtC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACtC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACtB;AAED;;;;GAIG;AACH,qBAAa,8BAA8B;IAEvC,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;gBAFT,SAAS,EAAE,MAAM,aAAa,EAC9B,KAAK,CAAC,EAAE,aAAa,YAAA,EACrB,QAAQ,CAAC,GAAE,MAAM,IAAI,aAAA;IAGlC,IAAI,IAAI,OAAO,CAAC,yBAAyB,EAAE,CAAC;IAM5C,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,yBAAyB,CAAC;IAOnD,MAAM,CACV,KAAK,EAAE,4BAA4B,EACnC,GAAG,EAAE,gBAAgB,GACpB,OAAO,CAAC,yBAAyB,CAAC;IAoB/B,MAAM,CACV,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,4BAA4B,EACnC,GAAG,EAAE,gBAAgB,GACpB,OAAO,CAAC,yBAAyB,CAAC;IAwC/B,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB9D;;;;OAIG;IACG,cAAc,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,EAAE,CAAC;IAa9E,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,KAAK;CAcd"}
@@ -0,0 +1,134 @@
1
+ import { ERROR_CODES, } from '@endora-commerce/contracts';
2
+ import { HttpError } from '@endora-commerce/platform/http';
3
+ import { MetaCustomEventMapping } from '../entities/meta-custom-event-mapping.entity.js';
4
+ /**
5
+ * CRUD + storefront resolution for Meta custom-event mappings (feature 064,
6
+ * US4). Boundary validation lives in the Zod schemas at the route; this service
7
+ * owns the optimistic-version guard, persistence, and audit.
8
+ */
9
+ export class MetaCustomEventMappingsService {
10
+ emFactory;
11
+ audit;
12
+ onChange;
13
+ constructor(emFactory, audit, onChange) {
14
+ this.emFactory = emFactory;
15
+ this.audit = audit;
16
+ this.onChange = onChange;
17
+ }
18
+ async list() {
19
+ const em = this.emFactory();
20
+ const rows = await em.find(MetaCustomEventMapping, {}, { orderBy: { createdAt: 'asc' } });
21
+ return rows.map((r) => this.toDto(r));
22
+ }
23
+ async get(id) {
24
+ const em = this.emFactory();
25
+ const row = await em.findOne(MetaCustomEventMapping, { id });
26
+ if (!row)
27
+ throw this.notFound(id);
28
+ return this.toDto(row);
29
+ }
30
+ async create(input, ctx) {
31
+ const em = this.emFactory();
32
+ const row = em.create(MetaCustomEventMapping, {
33
+ salesChannelId: input.salesChannelId ?? null,
34
+ triggerAction: input.triggerAction,
35
+ eventName: input.eventName,
36
+ enabled: input.enabled ?? true,
37
+ });
38
+ await em.persistAndFlush(row);
39
+ await this.audit?.record({
40
+ action: 'meta_ads.custom_event.created',
41
+ objectType: 'meta_custom_event_mapping',
42
+ objectId: row.id,
43
+ actorAdminUserId: ctx.actorAdminUserId,
44
+ stateAfter: { triggerAction: row.triggerAction, eventName: row.eventName },
45
+ });
46
+ this.onChange?.();
47
+ return this.toDto(row);
48
+ }
49
+ async update(id, input, ctx) {
50
+ const em = this.emFactory();
51
+ const row = await em.findOne(MetaCustomEventMapping, { id });
52
+ if (!row)
53
+ throw this.notFound(id);
54
+ if (row.version !== input.version) {
55
+ throw new HttpError(409, ERROR_CODES.VERSION_CONFLICT, 'Custom event mapping was modified concurrently.');
56
+ }
57
+ const before = {
58
+ triggerAction: row.triggerAction,
59
+ eventName: row.eventName,
60
+ enabled: row.enabled,
61
+ };
62
+ if (input.salesChannelId !== undefined)
63
+ row.salesChannelId = input.salesChannelId;
64
+ if (input.triggerAction !== undefined)
65
+ row.triggerAction = input.triggerAction;
66
+ if (input.eventName !== undefined)
67
+ row.eventName = input.eventName;
68
+ if (input.enabled !== undefined)
69
+ row.enabled = input.enabled;
70
+ row.version += 1;
71
+ await em.persistAndFlush(row);
72
+ await this.audit?.record({
73
+ action: 'meta_ads.custom_event.updated',
74
+ objectType: 'meta_custom_event_mapping',
75
+ objectId: row.id,
76
+ actorAdminUserId: ctx.actorAdminUserId,
77
+ stateBefore: before,
78
+ stateAfter: {
79
+ triggerAction: row.triggerAction,
80
+ eventName: row.eventName,
81
+ enabled: row.enabled,
82
+ },
83
+ });
84
+ this.onChange?.();
85
+ return this.toDto(row);
86
+ }
87
+ async remove(id, ctx) {
88
+ const em = this.emFactory();
89
+ const row = await em.findOne(MetaCustomEventMapping, { id });
90
+ if (!row)
91
+ throw this.notFound(id);
92
+ const before = { triggerAction: row.triggerAction, eventName: row.eventName };
93
+ await em.removeAndFlush(row);
94
+ await this.audit?.record({
95
+ action: 'meta_ads.custom_event.deleted',
96
+ objectType: 'meta_custom_event_mapping',
97
+ objectId: id,
98
+ actorAdminUserId: ctx.actorAdminUserId,
99
+ stateBefore: before,
100
+ });
101
+ this.onChange?.();
102
+ }
103
+ /**
104
+ * Enabled mappings applicable to a channel (channel-scoped OR all-channels).
105
+ * A NULL `salesChannelId` means "every channel" by explicit design, not by
106
+ * fail-open (Principle XII).
107
+ */
108
+ async loadForChannel(salesChannelId) {
109
+ const em = this.emFactory();
110
+ const rows = await em.find(MetaCustomEventMapping, { enabled: true, $or: [{ salesChannelId: null }, { salesChannelId }] }, { orderBy: { createdAt: 'asc' } });
111
+ return rows.map((r) => ({
112
+ triggerAction: r.triggerAction,
113
+ eventName: r.eventName,
114
+ }));
115
+ }
116
+ notFound(id) {
117
+ return new HttpError(404, ERROR_CODES.NOT_FOUND, `Custom event mapping not found: ${id}`);
118
+ }
119
+ toDto(row) {
120
+ return {
121
+ id: row.id,
122
+ // `?? null` because a freshly-created entity leaves an omitted optional
123
+ // property undefined, and JSON.stringify would drop the key entirely.
124
+ salesChannelId: row.salesChannelId ?? null,
125
+ triggerAction: row.triggerAction,
126
+ eventName: row.eventName,
127
+ enabled: row.enabled,
128
+ version: row.version,
129
+ createdAt: row.createdAt.toISOString(),
130
+ updatedAt: row.updatedAt.toISOString(),
131
+ };
132
+ }
133
+ }
134
+ //# sourceMappingURL=custom-event-mappings.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-event-mappings.service.js","sourceRoot":"","sources":["../../../src/backend/services/custom-event-mappings.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { type MetaStorefrontConfig, type MetaStorefrontMapping } from '@endora-commerce/contracts';
2
+ import type { SettingsReadPort } from '@endora-commerce/platform/kernel';
3
+ /** Loader for a channel's enabled custom-event mappings in the storefront shape. */
4
+ export type MetaCustomEventsLoader = (salesChannelId: string) => Promise<MetaStorefrontMapping[]>;
5
+ /**
6
+ * Resolves the per-sales-channel Meta Ads configuration exposed to the
7
+ * storefront.
8
+ *
9
+ * A blank or absent Pixel ID means the channel is untracked (FR-004): the
10
+ * returned config is fully disabled regardless of the master switch. Every
11
+ * setting read degrades to its default rather than throwing — an incomplete
12
+ * settings catalogue must not be able to fail a storefront render (FR-013).
13
+ */
14
+ export declare class MetaConfigService {
15
+ private readonly settings;
16
+ private readonly loadCustomEvents;
17
+ constructor(settings: SettingsReadPort, loadCustomEvents?: MetaCustomEventsLoader);
18
+ getConfig(salesChannelId: string): Promise<MetaStorefrontConfig>;
19
+ private readBool;
20
+ private readString;
21
+ }
22
+ //# sourceMappingURL=meta-config.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"meta-config.service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/meta-config.service.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC3B,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAEzE,oFAAoF;AACpF,MAAM,MAAM,sBAAsB,GAAG,CACnC,cAAc,EAAE,MAAM,KACnB,OAAO,CAAC,qBAAqB,EAAE,CAAC,CAAC;AAEtC;;;;;;;;GAQG;AACH,qBAAa,iBAAiB;IAE1B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,gBAAgB;gBADhB,QAAQ,EAAE,gBAAgB,EAC1B,gBAAgB,GAAE,sBAAuC;IAGtE,SAAS,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC;YAuBxD,QAAQ;YAYR,UAAU;CAWzB"}
@@ -0,0 +1,55 @@
1
+ import { z } from 'zod';
2
+ import { META_ADS_SETTING_CODES, META_DISABLED_CONFIG, } from '@endora-commerce/contracts';
3
+ /**
4
+ * Resolves the per-sales-channel Meta Ads configuration exposed to the
5
+ * storefront.
6
+ *
7
+ * A blank or absent Pixel ID means the channel is untracked (FR-004): the
8
+ * returned config is fully disabled regardless of the master switch. Every
9
+ * setting read degrades to its default rather than throwing — an incomplete
10
+ * settings catalogue must not be able to fail a storefront render (FR-013).
11
+ */
12
+ export class MetaConfigService {
13
+ settings;
14
+ loadCustomEvents;
15
+ constructor(settings, loadCustomEvents = async () => []) {
16
+ this.settings = settings;
17
+ this.loadCustomEvents = loadCustomEvents;
18
+ }
19
+ async getConfig(salesChannelId) {
20
+ const [enabled, pixelIdRaw, requireConsent] = await Promise.all([
21
+ this.readBool(META_ADS_SETTING_CODES.ENABLED, salesChannelId, false),
22
+ this.readString(META_ADS_SETTING_CODES.PIXEL_ID, salesChannelId, ''),
23
+ this.readBool(META_ADS_SETTING_CODES.REQUIRE_CONSENT, salesChannelId, true),
24
+ ]);
25
+ const pixelId = pixelIdRaw.trim() || null;
26
+ // Untracked channel. The consent choice is still reported so the storefront
27
+ // banner behaves consistently across channels.
28
+ if (!enabled || pixelId === null) {
29
+ return { ...META_DISABLED_CONFIG, requireConsent };
30
+ }
31
+ return {
32
+ enabled: true,
33
+ pixelId,
34
+ requireConsent,
35
+ customEvents: await this.loadCustomEvents(salesChannelId),
36
+ };
37
+ }
38
+ async readBool(code, salesChannelId, fallback) {
39
+ try {
40
+ return await this.settings.get(code, salesChannelId, z.boolean());
41
+ }
42
+ catch {
43
+ return fallback;
44
+ }
45
+ }
46
+ async readString(code, salesChannelId, fallback) {
47
+ try {
48
+ return await this.settings.get(code, salesChannelId, z.string());
49
+ }
50
+ catch {
51
+ return fallback;
52
+ }
53
+ }
54
+ }
55
+ //# sourceMappingURL=meta-config.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"meta-config.service.js","sourceRoot":"","sources":["../../../src/backend/services/meta-config.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,sBAAsB,EACtB,oBAAoB,GAGrB,MAAM,4BAA4B,CAAC;AAQpC;;;;;;;;GAQG;AACH,MAAM,OAAO,iBAAiB;IAET;IACA;IAFnB,YACmB,QAA0B,EAC1B,mBAA2C,KAAK,IAAI,EAAE,CAAC,EAAE;QADzD,aAAQ,GAAR,QAAQ,CAAkB;QAC1B,qBAAgB,GAAhB,gBAAgB,CAAyC;IACzE,CAAC;IAEJ,KAAK,CAAC,SAAS,CAAC,cAAsB;QACpC,MAAM,CAAC,OAAO,EAAE,UAAU,EAAE,cAAc,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YAC9D,IAAI,CAAC,QAAQ,CAAC,sBAAsB,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,CAAC;YACpE,IAAI,CAAC,UAAU,CAAC,sBAAsB,CAAC,QAAQ,EAAE,cAAc,EAAE,EAAE,CAAC;YACpE,IAAI,CAAC,QAAQ,CAAC,sBAAsB,CAAC,eAAe,EAAE,cAAc,EAAE,IAAI,CAAC;SAC5E,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,EAAE,IAAI,IAAI,CAAC;QAE1C,4EAA4E;QAC5E,+CAA+C;QAC/C,IAAI,CAAC,OAAO,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACjC,OAAO,EAAE,GAAG,oBAAoB,EAAE,cAAc,EAAE,CAAC;QACrD,CAAC;QAED,OAAO;YACL,OAAO,EAAE,IAAI;YACb,OAAO;YACP,cAAc;YACd,YAAY,EAAE,MAAM,IAAI,CAAC,gBAAgB,CAAC,cAAc,CAAC;SAC1D,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,QAAQ,CACpB,IAAY,EACZ,cAAsB,EACtB,QAAiB;QAEjB,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;QACpE,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,QAAQ,CAAC;QAClB,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,UAAU,CACtB,IAAY,EACZ,cAAsB,EACtB,QAAgB;QAEhB,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;QACnE,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,QAAQ,CAAC;QAClB,CAAC;IACH,CAAC;CACF"}