@endora-commerce/mod-settings 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 (127) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +58 -0
  3. package/dist/admin/api/settings-client.d.ts +47 -0
  4. package/dist/admin/api/settings-client.d.ts.map +1 -0
  5. package/dist/admin/api/settings-client.js +41 -0
  6. package/dist/admin/api/settings-client.js.map +1 -0
  7. package/dist/admin/components/ActivationPointerRow.d.ts +21 -0
  8. package/dist/admin/components/ActivationPointerRow.d.ts.map +1 -0
  9. package/dist/admin/components/ActivationPointerRow.js +28 -0
  10. package/dist/admin/components/ActivationPointerRow.js.map +1 -0
  11. package/dist/admin/components/AssetIdSettingInput.d.ts +12 -0
  12. package/dist/admin/components/AssetIdSettingInput.d.ts.map +1 -0
  13. package/dist/admin/components/AssetIdSettingInput.js +10 -0
  14. package/dist/admin/components/AssetIdSettingInput.js.map +1 -0
  15. package/dist/admin/components/ConfigurationReferenceInput.d.ts +39 -0
  16. package/dist/admin/components/ConfigurationReferenceInput.d.ts.map +1 -0
  17. package/dist/admin/components/ConfigurationReferenceInput.js +74 -0
  18. package/dist/admin/components/ConfigurationReferenceInput.js.map +1 -0
  19. package/dist/admin/components/ConflictBanner.d.ts +13 -0
  20. package/dist/admin/components/ConflictBanner.d.ts.map +1 -0
  21. package/dist/admin/components/ConflictBanner.js +12 -0
  22. package/dist/admin/components/ConflictBanner.js.map +1 -0
  23. package/dist/admin/components/ImageSettingInput.d.ts +34 -0
  24. package/dist/admin/components/ImageSettingInput.d.ts.map +1 -0
  25. package/dist/admin/components/ImageSettingInput.js +36 -0
  26. package/dist/admin/components/ImageSettingInput.js.map +1 -0
  27. package/dist/admin/components/QuoteRequestsSettingsTab.d.ts +3 -0
  28. package/dist/admin/components/QuoteRequestsSettingsTab.d.ts.map +1 -0
  29. package/dist/admin/components/QuoteRequestsSettingsTab.js +69 -0
  30. package/dist/admin/components/QuoteRequestsSettingsTab.js.map +1 -0
  31. package/dist/admin/components/SellerCompanyDataInput.d.ts +22 -0
  32. package/dist/admin/components/SellerCompanyDataInput.d.ts.map +1 -0
  33. package/dist/admin/components/SellerCompanyDataInput.js +53 -0
  34. package/dist/admin/components/SellerCompanyDataInput.js.map +1 -0
  35. package/dist/admin/components/SettingRowEditor.d.ts +55 -0
  36. package/dist/admin/components/SettingRowEditor.d.ts.map +1 -0
  37. package/dist/admin/components/SettingRowEditor.js +160 -0
  38. package/dist/admin/components/SettingRowEditor.js.map +1 -0
  39. package/dist/admin/index.d.ts +39 -0
  40. package/dist/admin/index.d.ts.map +1 -0
  41. package/dist/admin/index.js +87 -0
  42. package/dist/admin/index.js.map +1 -0
  43. package/dist/admin/pages/CachePage.d.ts +14 -0
  44. package/dist/admin/pages/CachePage.d.ts.map +1 -0
  45. package/dist/admin/pages/CachePage.js +124 -0
  46. package/dist/admin/pages/CachePage.js.map +1 -0
  47. package/dist/admin/pages/GroupsPage.d.ts +17 -0
  48. package/dist/admin/pages/GroupsPage.d.ts.map +1 -0
  49. package/dist/admin/pages/GroupsPage.js +84 -0
  50. package/dist/admin/pages/GroupsPage.js.map +1 -0
  51. package/dist/admin/pages/SettingsPage.d.ts +20 -0
  52. package/dist/admin/pages/SettingsPage.d.ts.map +1 -0
  53. package/dist/admin/pages/SettingsPage.js +453 -0
  54. package/dist/admin/pages/SettingsPage.js.map +1 -0
  55. package/dist/backend/cli/cache-clear.d.ts +23 -0
  56. package/dist/backend/cli/cache-clear.d.ts.map +1 -0
  57. package/dist/backend/cli/cache-clear.js +37 -0
  58. package/dist/backend/cli/cache-clear.js.map +1 -0
  59. package/dist/backend/index.d.ts +100 -0
  60. package/dist/backend/index.d.ts.map +1 -0
  61. package/dist/backend/index.js +125 -0
  62. package/dist/backend/index.js.map +1 -0
  63. package/dist/backend/routes.admin.d.ts +308 -0
  64. package/dist/backend/routes.admin.d.ts.map +1 -0
  65. package/dist/backend/routes.admin.js +188 -0
  66. package/dist/backend/routes.admin.js.map +1 -0
  67. package/dist/backend/routes.cache.d.ts +20 -0
  68. package/dist/backend/routes.cache.d.ts.map +1 -0
  69. package/dist/backend/routes.cache.js +22 -0
  70. package/dist/backend/routes.cache.js.map +1 -0
  71. package/dist/backend/routes.homepage.d.ts +15 -0
  72. package/dist/backend/routes.homepage.d.ts.map +1 -0
  73. package/dist/backend/routes.homepage.js +20 -0
  74. package/dist/backend/routes.homepage.js.map +1 -0
  75. package/dist/backend/routes.product-card-buttons.d.ts +15 -0
  76. package/dist/backend/routes.product-card-buttons.d.ts.map +1 -0
  77. package/dist/backend/routes.product-card-buttons.js +21 -0
  78. package/dist/backend/routes.product-card-buttons.js.map +1 -0
  79. package/dist/backend/routes.speculation-rules.d.ts +16 -0
  80. package/dist/backend/routes.speculation-rules.d.ts.map +1 -0
  81. package/dist/backend/routes.speculation-rules.js +21 -0
  82. package/dist/backend/routes.speculation-rules.js.map +1 -0
  83. package/dist/backend/routes.storefront.d.ts +15 -0
  84. package/dist/backend/routes.storefront.d.ts.map +1 -0
  85. package/dist/backend/routes.storefront.js +20 -0
  86. package/dist/backend/routes.storefront.js.map +1 -0
  87. package/dist/backend/services/cache-admin.service.d.ts +83 -0
  88. package/dist/backend/services/cache-admin.service.d.ts.map +1 -0
  89. package/dist/backend/services/cache-admin.service.js +148 -0
  90. package/dist/backend/services/cache-admin.service.js.map +1 -0
  91. package/dist/backend/services/homepage-resolver.d.ts +25 -0
  92. package/dist/backend/services/homepage-resolver.d.ts.map +1 -0
  93. package/dist/backend/services/homepage-resolver.js +31 -0
  94. package/dist/backend/services/homepage-resolver.js.map +1 -0
  95. package/dist/backend/services/product-card-buttons-resolver.d.ts +25 -0
  96. package/dist/backend/services/product-card-buttons-resolver.d.ts.map +1 -0
  97. package/dist/backend/services/product-card-buttons-resolver.js +37 -0
  98. package/dist/backend/services/product-card-buttons-resolver.js.map +1 -0
  99. package/dist/backend/services/registered-settings-manifests.d.ts +45 -0
  100. package/dist/backend/services/registered-settings-manifests.d.ts.map +1 -0
  101. package/dist/backend/services/registered-settings-manifests.js +55 -0
  102. package/dist/backend/services/registered-settings-manifests.js.map +1 -0
  103. package/dist/backend/services/setting-write-validators.d.ts +43 -0
  104. package/dist/backend/services/setting-write-validators.d.ts.map +1 -0
  105. package/dist/backend/services/setting-write-validators.js +43 -0
  106. package/dist/backend/services/setting-write-validators.js.map +1 -0
  107. package/dist/backend/services/settings-admin.service.d.ts +206 -0
  108. package/dist/backend/services/settings-admin.service.d.ts.map +1 -0
  109. package/dist/backend/services/settings-admin.service.js +642 -0
  110. package/dist/backend/services/settings-admin.service.js.map +1 -0
  111. package/dist/backend/services/shop-info-resolver.d.ts +15 -0
  112. package/dist/backend/services/shop-info-resolver.d.ts.map +1 -0
  113. package/dist/backend/services/shop-info-resolver.js +44 -0
  114. package/dist/backend/services/shop-info-resolver.js.map +1 -0
  115. package/dist/backend/services/speculation-rules-resolver.d.ts +28 -0
  116. package/dist/backend/services/speculation-rules-resolver.d.ts.map +1 -0
  117. package/dist/backend/services/speculation-rules-resolver.js +39 -0
  118. package/dist/backend/services/speculation-rules-resolver.js.map +1 -0
  119. package/dist/manifest.d.ts +194 -0
  120. package/dist/manifest.d.ts.map +1 -0
  121. package/dist/manifest.js +379 -0
  122. package/dist/manifest.js.map +1 -0
  123. package/docs/settings/index.md +171 -0
  124. package/i18n/en.json +167 -0
  125. package/i18n/pl.json +167 -0
  126. package/package.json +96 -0
  127. package/tailwind.css +14 -0
@@ -0,0 +1,148 @@
1
+ import { inProcessCaches, } from '@endora-commerce/platform/kernel';
2
+ import { SETTINGS_CACHE_KEY_PREFIX, SETTINGS_CACHE_NAMESPACE, } from '@endora-commerce/platform/kernel';
3
+ import { SALES_CHANNELS_CACHE_KEY_PREFIX, SALES_CHANNELS_CACHE_NAMESPACE, } from '@endora-commerce/platform/kernel';
4
+ export const CACHE_NAMESPACES = [
5
+ {
6
+ key: SETTINGS_CACHE_NAMESPACE,
7
+ label: 'Settings',
8
+ description: 'Resolved platform / per-channel setting values.',
9
+ patterns: [`${SETTINGS_CACHE_KEY_PREFIX}*`],
10
+ },
11
+ {
12
+ key: SALES_CHANNELS_CACHE_NAMESPACE,
13
+ label: 'Sales channels',
14
+ description: 'Resolved sales-channel configuration.',
15
+ patterns: [`${SALES_CHANNELS_CACHE_KEY_PREFIX}*`],
16
+ },
17
+ {
18
+ key: 'blog',
19
+ label: 'Blog',
20
+ description: 'Storefront blog index, posts, categories and tag pages.',
21
+ patterns: ['blog:v1:*'],
22
+ },
23
+ {
24
+ key: 'cms',
25
+ label: 'CMS',
26
+ description: 'Storefront CMS pages, blocks and hook content.',
27
+ patterns: ['cms:v1:*'],
28
+ },
29
+ {
30
+ key: 'megamenu',
31
+ label: 'Mega menu',
32
+ description: 'Resolved storefront mega-menu trees.',
33
+ patterns: ['megamenu:v1:*'],
34
+ },
35
+ {
36
+ key: 'dictionaries',
37
+ label: 'Dictionaries',
38
+ description: 'Resolved dictionary registries and entries (countries, etc.).',
39
+ patterns: ['dictionary:*'],
40
+ },
41
+ {
42
+ key: 'carts',
43
+ label: 'Cart pricing',
44
+ description: 'Cached cart re-pricing snapshots.',
45
+ patterns: ['b2b:cart:recompute:*'],
46
+ },
47
+ // No `modules` entry. It cleared `b2b:module:enabled-set`, which no code path
48
+ // ever read: module presence is held in memory and refreshed from PostgreSQL
49
+ // on a `b2b:module:state-changed` notification. The mirror is gone (feature
50
+ // 073, T140), and offering an operator a button that clears a key nobody
51
+ // reads is worse than offering nothing — it is where they look first when
52
+ // presence is wrong.
53
+ ];
54
+ /**
55
+ * Admin-facing cache maintenance. Lets an operator flush selected cache
56
+ * namespaces so freshly-published content / changed settings show up without
57
+ * waiting for TTL expiry.
58
+ *
59
+ * Six of the eight namespaces live only in Redis, so SCAN+DEL over their key
60
+ * patterns is a complete clear. Two — `settings` and `sales_channels` — keep a
61
+ * per-process layer in front of Redis, and for those the clear goes through
62
+ * the cache that owns both layers (see {@link InProcessCacheRegistry}), which
63
+ * is also the only way to get the ordering right: shared layer first, prefix
64
+ * marked for the whole operation.
65
+ *
66
+ * The cost of that ordering is worth naming. Immediately after a clear both
67
+ * layers are cold and the marked prefix sends reads through to PostgreSQL, so
68
+ * a platform-wide burst of setting reads lands on the database. The queries are
69
+ * trivial (small table, unique index on `code`), but the connection pool is
70
+ * not: it is the scarce resource. That is acceptable for a rare,
71
+ * operator-initiated action and unacceptable in a loop, which is why the route
72
+ * carries its own rate limit (`routes.cache.ts`).
73
+ */
74
+ export class CacheAdminService {
75
+ redis;
76
+ caches;
77
+ constructor(redis,
78
+ /** Injectable so a test does not clear this process's real caches. */
79
+ caches = inProcessCaches) {
80
+ this.redis = redis;
81
+ this.caches = caches;
82
+ }
83
+ get enabled() {
84
+ return this.redis !== undefined;
85
+ }
86
+ listNamespaces() {
87
+ return CACHE_NAMESPACES.map((n) => ({
88
+ key: n.key,
89
+ label: n.label,
90
+ description: n.description,
91
+ }));
92
+ }
93
+ /**
94
+ * Clear the given namespaces (or every namespace when passed `'all'`).
95
+ * Unknown keys are ignored. Returns the per-namespace deleted-key counts.
96
+ */
97
+ async clear(namespaces) {
98
+ const wanted = namespaces === 'all'
99
+ ? CACHE_NAMESPACES
100
+ : CACHE_NAMESPACES.filter((n) => namespaces.includes(n.key));
101
+ const cleared = [];
102
+ let totalDeletedKeys = 0;
103
+ for (const ns of wanted) {
104
+ const deleted = await this.clearNamespace(ns);
105
+ cleared.push({ key: ns.key, deletedKeysCount: deleted });
106
+ totalDeletedKeys += deleted;
107
+ }
108
+ return { cleared, totalDeletedKeys };
109
+ }
110
+ /**
111
+ * Prefer the owning cache when this process has one: it drops the shared
112
+ * layer and its own in-memory layer as one marked operation. A process that
113
+ * registered none — the `cache:clear` CLI, a worker without the module —
114
+ * has no in-memory layer to be stale, so clearing the Redis keys is the
115
+ * whole job.
116
+ */
117
+ async clearNamespace(ns) {
118
+ if (this.caches.has(ns.key)) {
119
+ return (await this.caches.clear(ns.key)) ?? 0;
120
+ }
121
+ if (!this.redis)
122
+ return 0;
123
+ let deleted = 0;
124
+ for (const pattern of ns.patterns) {
125
+ deleted += await this.scanDelete(pattern);
126
+ }
127
+ return deleted;
128
+ }
129
+ async scanDelete(pattern) {
130
+ const redis = this.redis;
131
+ if (!redis)
132
+ return 0;
133
+ // A glob-free pattern is a single literal key — delete it directly.
134
+ if (!/[*?[\]]/.test(pattern)) {
135
+ return redis.del(pattern);
136
+ }
137
+ let cursor = '0';
138
+ let deleted = 0;
139
+ do {
140
+ const [next, keys] = await redis.scan(cursor, 'MATCH', pattern, 'COUNT', 200);
141
+ cursor = next;
142
+ if (keys.length > 0)
143
+ deleted += await redis.del(...keys);
144
+ } while (cursor !== '0');
145
+ return deleted;
146
+ }
147
+ }
148
+ //# sourceMappingURL=cache-admin.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-admin.service.js","sourceRoot":"","sources":["../../../src/backend/services/cache-admin.service.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,eAAe,GAEhB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACL,yBAAyB,EACzB,wBAAwB,GACzB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACL,+BAA+B,EAC/B,8BAA8B,GAC/B,MAAM,kCAAkC,CAAC;AA6B1C,MAAM,CAAC,MAAM,gBAAgB,GAA8B;IACzD;QACE,GAAG,EAAE,wBAAwB;QAC7B,KAAK,EAAE,UAAU;QACjB,WAAW,EAAE,iDAAiD;QAC9D,QAAQ,EAAE,CAAC,GAAG,yBAAyB,GAAG,CAAC;KAC5C;IACD;QACE,GAAG,EAAE,8BAA8B;QACnC,KAAK,EAAE,gBAAgB;QACvB,WAAW,EAAE,uCAAuC;QACpD,QAAQ,EAAE,CAAC,GAAG,+BAA+B,GAAG,CAAC;KAClD;IACD;QACE,GAAG,EAAE,MAAM;QACX,KAAK,EAAE,MAAM;QACb,WAAW,EAAE,yDAAyD;QACtE,QAAQ,EAAE,CAAC,WAAW,CAAC;KACxB;IACD;QACE,GAAG,EAAE,KAAK;QACV,KAAK,EAAE,KAAK;QACZ,WAAW,EAAE,gDAAgD;QAC7D,QAAQ,EAAE,CAAC,UAAU,CAAC;KACvB;IACD;QACE,GAAG,EAAE,UAAU;QACf,KAAK,EAAE,WAAW;QAClB,WAAW,EAAE,sCAAsC;QACnD,QAAQ,EAAE,CAAC,eAAe,CAAC;KAC5B;IACD;QACE,GAAG,EAAE,cAAc;QACnB,KAAK,EAAE,cAAc;QACrB,WAAW,EAAE,+DAA+D;QAC5E,QAAQ,EAAE,CAAC,cAAc,CAAC;KAC3B;IACD;QACE,GAAG,EAAE,OAAO;QACZ,KAAK,EAAE,cAAc;QACrB,WAAW,EAAE,mCAAmC;QAChD,QAAQ,EAAE,CAAC,sBAAsB,CAAC;KACnC;IACD,8EAA8E;IAC9E,6EAA6E;IAC7E,4EAA4E;IAC5E,yEAAyE;IACzE,0EAA0E;IAC1E,qBAAqB;CACb,CAAC;AAOX;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,OAAO,iBAAiB;IAET;IAEA;IAHnB,YACmB,KAAa;IAC9B,sEAAsE;IACrD,SAAiC,eAAe;QAFhD,UAAK,GAAL,KAAK,CAAQ;QAEb,WAAM,GAAN,MAAM,CAA0C;IAChE,CAAC;IAEJ,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC;IAClC,CAAC;IAED,cAAc;QACZ,OAAO,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAClC,GAAG,EAAE,CAAC,CAAC,GAAG;YACV,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,WAAW,EAAE,CAAC,CAAC,WAAW;SAC3B,CAAC,CAAC,CAAC;IACN,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,KAAK,CACT,UAA4B;QAE5B,MAAM,MAAM,GACV,UAAU,KAAK,KAAK;YAClB,CAAC,CAAC,gBAAgB;YAClB,CAAC,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAEjE,MAAM,OAAO,GAAuB,EAAE,CAAC;QACvC,IAAI,gBAAgB,GAAG,CAAC,CAAC;QACzB,KAAK,MAAM,EAAE,IAAI,MAAM,EAAE,CAAC;YACxB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;YAC9C,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,EAAE,gBAAgB,EAAE,OAAO,EAAE,CAAC,CAAC;YACzD,gBAAgB,IAAI,OAAO,CAAC;QAC9B,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE,CAAC;IACvC,CAAC;IAED;;;;;;OAMG;IACK,KAAK,CAAC,cAAc,CAAC,EAAkB;QAC7C,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;YAC5B,OAAO,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;QAChD,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,KAAK;YAAE,OAAO,CAAC,CAAC;QAC1B,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,KAAK,MAAM,OAAO,IAAI,EAAE,CAAC,QAAQ,EAAE,CAAC;YAClC,OAAO,IAAI,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QAC5C,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAEO,KAAK,CAAC,UAAU,CAAC,OAAe;QACtC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACzB,IAAI,CAAC,KAAK;YAAE,OAAO,CAAC,CAAC;QACrB,oEAAoE;QACpE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YAC7B,OAAO,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC5B,CAAC;QACD,IAAI,MAAM,GAAG,GAAG,CAAC;QACjB,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,GAAG,CAAC;YACF,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;YAC9E,MAAM,GAAG,IAAI,CAAC;YACd,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;gBAAE,OAAO,IAAI,MAAM,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;QAC3D,CAAC,QAAQ,MAAM,KAAK,GAAG,EAAE;QACzB,OAAO,OAAO,CAAC;IACjB,CAAC;CACF"}
@@ -0,0 +1,25 @@
1
+ import type { HomepageConfig } from '@endora-commerce/contracts';
2
+ import type { SettingsReadPort } from '@endora-commerce/platform/kernel';
3
+ /**
4
+ * Resolves the storefront home-page configuration for a sales channel from the
5
+ * `homepage_cms_page_slug` setting. An unset / not-registered / out-of-scope
6
+ * setting resolves to `{ cmsPageSlug: null }` so the storefront falls back to
7
+ * its built-in landing page and a missing setting never 500s a public page.
8
+ *
9
+ * There is no "missing channel" arm any more (feature 075 / D-87): the channel
10
+ * arrives resolved, and the resolver middleware has already refused an unknown
11
+ * or inactive one before a handler runs.
12
+ */
13
+ export declare class HomepageResolver {
14
+ private readonly settingsService;
15
+ constructor(settingsService: SettingsReadPort);
16
+ /**
17
+ * Feature 075 / D-87 — the resolved request channel's id, passed in by the
18
+ * route. It used to be the channel *code*, looked back up here with a raw
19
+ * `select id from sales_channels`: a re-resolution of a channel the resolver
20
+ * middleware had already resolved (feature 053, FR-011), across a boundary
21
+ * no import specifier named.
22
+ */
23
+ resolve(salesChannelId: string): Promise<HomepageConfig>;
24
+ }
25
+ //# sourceMappingURL=homepage-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"homepage-resolver.d.ts","sourceRoot":"","sources":["../../../src/backend/services/homepage-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAIzE;;;;;;;;;GASG;AACH,qBAAa,gBAAgB;IACf,OAAO,CAAC,QAAQ,CAAC,eAAe;gBAAf,eAAe,EAAE,gBAAgB;IAE9D;;;;;;OAMG;IACG,OAAO,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;CAM/D"}
@@ -0,0 +1,31 @@
1
+ const HOMEPAGE_SETTING_CODE = 'homepage_cms_page_slug';
2
+ /**
3
+ * Resolves the storefront home-page configuration for a sales channel from the
4
+ * `homepage_cms_page_slug` setting. An unset / not-registered / out-of-scope
5
+ * setting resolves to `{ cmsPageSlug: null }` so the storefront falls back to
6
+ * its built-in landing page and a missing setting never 500s a public page.
7
+ *
8
+ * There is no "missing channel" arm any more (feature 075 / D-87): the channel
9
+ * arrives resolved, and the resolver middleware has already refused an unknown
10
+ * or inactive one before a handler runs.
11
+ */
12
+ export class HomepageResolver {
13
+ settingsService;
14
+ constructor(settingsService) {
15
+ this.settingsService = settingsService;
16
+ }
17
+ /**
18
+ * Feature 075 / D-87 — the resolved request channel's id, passed in by the
19
+ * route. It used to be the channel *code*, looked back up here with a raw
20
+ * `select id from sales_channels`: a re-resolution of a channel the resolver
21
+ * middleware had already resolved (feature 053, FR-011), across a boundary
22
+ * no import specifier named.
23
+ */
24
+ async resolve(salesChannelId) {
25
+ const resolved = await this.settingsService.getMany([HOMEPAGE_SETTING_CODE], salesChannelId);
26
+ const r = resolved.get(HOMEPAGE_SETTING_CODE);
27
+ const slug = r && r.ok && typeof r.value === 'string' ? r.value.trim() : '';
28
+ return { cmsPageSlug: slug === '' ? null : slug };
29
+ }
30
+ }
31
+ //# sourceMappingURL=homepage-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"homepage-resolver.js","sourceRoot":"","sources":["../../../src/backend/services/homepage-resolver.ts"],"names":[],"mappings":"AAGA,MAAM,qBAAqB,GAAG,wBAAwB,CAAC;AAEvD;;;;;;;;;GASG;AACH,MAAM,OAAO,gBAAgB;IACE;IAA7B,YAA6B,eAAiC;QAAjC,oBAAe,GAAf,eAAe,CAAkB;IAAG,CAAC;IAElE;;;;;;OAMG;IACH,KAAK,CAAC,OAAO,CAAC,cAAsB;QAClC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,qBAAqB,CAAC,EAAE,cAAc,CAAC,CAAC;QAC7F,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;QAC9C,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5E,OAAO,EAAE,WAAW,EAAE,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACpD,CAAC;CACF"}
@@ -0,0 +1,25 @@
1
+ import type { SettingsReadPort } from '@endora-commerce/platform/kernel';
2
+ export interface ProductCardButtonsConfig {
3
+ showAddToCart: boolean;
4
+ showAddToShoppingList: boolean;
5
+ showAddToQuote: boolean;
6
+ }
7
+ /**
8
+ * Resolves the storefront product-card button visibility toggles for a sales
9
+ * channel. Each flag defaults to `true` (button shown) when its setting is
10
+ * unset / not-registered / out-of-scope, so a settings hiccup never hides a
11
+ * storefront affordance and never 500s a public page.
12
+ */
13
+ export declare class ProductCardButtonsResolver {
14
+ private readonly settingsService;
15
+ constructor(settingsService: SettingsReadPort);
16
+ /**
17
+ * Feature 075 / D-87 — the resolved request channel's id, passed in by the
18
+ * route. It used to be the channel *code*, looked back up here with a raw
19
+ * `select id from sales_channels`: a re-resolution of a channel the resolver
20
+ * middleware had already resolved (feature 053, FR-011), across a boundary
21
+ * no import specifier named.
22
+ */
23
+ resolve(salesChannelId: string): Promise<ProductCardButtonsConfig>;
24
+ }
25
+ //# sourceMappingURL=product-card-buttons-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"product-card-buttons-resolver.d.ts","sourceRoot":"","sources":["../../../src/backend/services/product-card-buttons-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAQzE,MAAM,WAAW,wBAAwB;IACvC,aAAa,EAAE,OAAO,CAAC;IACvB,qBAAqB,EAAE,OAAO,CAAC;IAC/B,cAAc,EAAE,OAAO,CAAC;CACzB;AAED;;;;;GAKG;AACH,qBAAa,0BAA0B;IACzB,OAAO,CAAC,QAAQ,CAAC,eAAe;gBAAf,eAAe,EAAE,gBAAgB;IAE9D;;;;;;OAMG;IACG,OAAO,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,wBAAwB,CAAC;CAezE"}
@@ -0,0 +1,37 @@
1
+ const ADD_TO_CART_CODE = 'storefront.product_card.show_add_to_cart';
2
+ const ADD_TO_SHOPPING_LIST_CODE = 'storefront.product_card.show_add_to_shopping_list';
3
+ // Owned by the quote_requests module's manifest; read here by code so the
4
+ // three product-card button toggles resolve through one endpoint.
5
+ const ADD_TO_QUOTE_CODE = 'quote_requests.show_add_to_quote_on_card';
6
+ /**
7
+ * Resolves the storefront product-card button visibility toggles for a sales
8
+ * channel. Each flag defaults to `true` (button shown) when its setting is
9
+ * unset / not-registered / out-of-scope, so a settings hiccup never hides a
10
+ * storefront affordance and never 500s a public page.
11
+ */
12
+ export class ProductCardButtonsResolver {
13
+ settingsService;
14
+ constructor(settingsService) {
15
+ this.settingsService = settingsService;
16
+ }
17
+ /**
18
+ * Feature 075 / D-87 — the resolved request channel's id, passed in by the
19
+ * route. It used to be the channel *code*, looked back up here with a raw
20
+ * `select id from sales_channels`: a re-resolution of a channel the resolver
21
+ * middleware had already resolved (feature 053, FR-011), across a boundary
22
+ * no import specifier named.
23
+ */
24
+ async resolve(salesChannelId) {
25
+ const resolved = await this.settingsService.getMany([ADD_TO_CART_CODE, ADD_TO_SHOPPING_LIST_CODE, ADD_TO_QUOTE_CODE], salesChannelId);
26
+ const flag = (code) => {
27
+ const r = resolved.get(code);
28
+ return r && r.ok && typeof r.value === 'boolean' ? r.value : true;
29
+ };
30
+ return {
31
+ showAddToCart: flag(ADD_TO_CART_CODE),
32
+ showAddToShoppingList: flag(ADD_TO_SHOPPING_LIST_CODE),
33
+ showAddToQuote: flag(ADD_TO_QUOTE_CODE),
34
+ };
35
+ }
36
+ }
37
+ //# sourceMappingURL=product-card-buttons-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"product-card-buttons-resolver.js","sourceRoot":"","sources":["../../../src/backend/services/product-card-buttons-resolver.ts"],"names":[],"mappings":"AAEA,MAAM,gBAAgB,GAAG,0CAA0C,CAAC;AACpE,MAAM,yBAAyB,GAAG,mDAAmD,CAAC;AACtF,0EAA0E;AAC1E,kEAAkE;AAClE,MAAM,iBAAiB,GAAG,0CAA0C,CAAC;AAQrE;;;;;GAKG;AACH,MAAM,OAAO,0BAA0B;IACR;IAA7B,YAA6B,eAAiC;QAAjC,oBAAe,GAAf,eAAe,CAAkB;IAAG,CAAC;IAElE;;;;;;OAMG;IACH,KAAK,CAAC,OAAO,CAAC,cAAsB;QAClC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,OAAO,CACjD,CAAC,gBAAgB,EAAE,yBAAyB,EAAE,iBAAiB,CAAC,EAChE,cAAc,CACf,CAAC;QACF,MAAM,IAAI,GAAG,CAAC,IAAY,EAAW,EAAE;YACrC,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC7B,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;QACpE,CAAC,CAAC;QACF,OAAO;YACL,aAAa,EAAE,IAAI,CAAC,gBAAgB,CAAC;YACrC,qBAAqB,EAAE,IAAI,CAAC,yBAAyB,CAAC;YACtD,cAAc,EAAE,IAAI,CAAC,iBAAiB,CAAC;SACxC,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,45 @@
1
+ import type { ModuleSettingsManifest, SettingsManifestCollectionPort, SettingsManifestSource } from '@endora-commerce/contracts';
2
+ /**
3
+ * Every settings manifest the boot-time {@link ManifestReconciler} must walk,
4
+ * derived from the module registry rather than hand-listed.
5
+ *
6
+ * This used to be a literal array in `composition.ts`, and a module could
7
+ * declare `settings: defineModuleSettingsManifest(...)` in its manifest yet be
8
+ * forgotten there. When that happened the module's `setting_groups` /
9
+ * `settings` rows were never created, so `/settings` silently omitted the whole
10
+ * group and `GET /api/v1/admin/settings/<code>` answered 404
11
+ * `SETTING_NOT_REGISTERED`. Eight modules had drifted that way — `ksef`, `mfa`,
12
+ * `carts`, `customers`, `orders`, `organizations`, `quick_order`, `returns` —
13
+ * and nothing caught it, not least because the test harness kept its own
14
+ * (differently stale) copy of the list.
15
+ *
16
+ * Deriving from the registry makes registration the single act that wires a
17
+ * module in, matching how permissions, i18n bundles, and command-palette
18
+ * actions already work.
19
+ *
20
+ * **The registry arrives as an argument (feature 075, Phase C).** It used to be
21
+ * imported from `_lifecycle`, which made `settings` name another module's file
22
+ * for a fact it does not own: which modules a deployment ships — core plus that
23
+ * deployment's overlay modules — is a composition-root input by nature, and it
24
+ * is why `resolvedModuleRegistry` is a platform-owned container name rather
25
+ * than a port. Both roots call this function and both already hold the
26
+ * registry, so the caller passes what it already has. Nothing is gated here on
27
+ * purpose: a module that is switched off keeps its settings rows and keeps its
28
+ * group on `/settings`, because a deactivation is not an uninstall
29
+ * (Constitution XVII) and the operator has to be able to switch it back on.
30
+ */
31
+ export declare function collectRegisteredSettingsManifests(registry: ReadonlyArray<SettingsManifestSource>): ModuleSettingsManifest[];
32
+ /**
33
+ * The published face of the derivation above — feature 080 (T040b).
34
+ *
35
+ * Both composition roots called the function by importing this file, which is
36
+ * a root value import of a module's source and stops having a spelling the day
37
+ * `settings` becomes a package (D-160.6.1). The rule is unchanged and stays
38
+ * here, where the settings module's own manifest is: the roots keep supplying
39
+ * the registry, because which modules a deployment ships is theirs to say, and
40
+ * resolve the assembly from the container they already composed.
41
+ */
42
+ export declare class SettingsManifestCollectionService implements SettingsManifestCollectionPort {
43
+ collect(registry: ReadonlyArray<SettingsManifestSource>): ModuleSettingsManifest[];
44
+ }
45
+ //# sourceMappingURL=registered-settings-manifests.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registered-settings-manifests.d.ts","sourceRoot":"","sources":["../../../src/backend/services/registered-settings-manifests.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,sBAAsB,EACtB,8BAA8B,EAC9B,sBAAsB,EACvB,MAAM,4BAA4B,CAAC;AAGpC;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,kCAAkC,CAChD,QAAQ,EAAE,aAAa,CAAC,sBAAsB,CAAC,GAC9C,sBAAsB,EAAE,CAU1B;AAED;;;;;;;;;GASG;AACH,qBAAa,iCAAkC,YAAW,8BAA8B;IACtF,OAAO,CAAC,QAAQ,EAAE,aAAa,CAAC,sBAAsB,CAAC,GAAG,sBAAsB,EAAE;CAGnF"}
@@ -0,0 +1,55 @@
1
+ import { settingsManifestWithRecentActivity } from '@endora-commerce/contracts';
2
+ import { settingsManifest as settingsModuleManifest } from '../../manifest.js';
3
+ /**
4
+ * Every settings manifest the boot-time {@link ManifestReconciler} must walk,
5
+ * derived from the module registry rather than hand-listed.
6
+ *
7
+ * This used to be a literal array in `composition.ts`, and a module could
8
+ * declare `settings: defineModuleSettingsManifest(...)` in its manifest yet be
9
+ * forgotten there. When that happened the module's `setting_groups` /
10
+ * `settings` rows were never created, so `/settings` silently omitted the whole
11
+ * group and `GET /api/v1/admin/settings/<code>` answered 404
12
+ * `SETTING_NOT_REGISTERED`. Eight modules had drifted that way — `ksef`, `mfa`,
13
+ * `carts`, `customers`, `orders`, `organizations`, `quick_order`, `returns` —
14
+ * and nothing caught it, not least because the test harness kept its own
15
+ * (differently stale) copy of the list.
16
+ *
17
+ * Deriving from the registry makes registration the single act that wires a
18
+ * module in, matching how permissions, i18n bundles, and command-palette
19
+ * actions already work.
20
+ *
21
+ * **The registry arrives as an argument (feature 075, Phase C).** It used to be
22
+ * imported from `_lifecycle`, which made `settings` name another module's file
23
+ * for a fact it does not own: which modules a deployment ships — core plus that
24
+ * deployment's overlay modules — is a composition-root input by nature, and it
25
+ * is why `resolvedModuleRegistry` is a platform-owned container name rather
26
+ * than a port. Both roots call this function and both already hold the
27
+ * registry, so the caller passes what it already has. Nothing is gated here on
28
+ * purpose: a module that is switched off keeps its settings rows and keeps its
29
+ * group on `/settings`, because a deactivation is not an uninstall
30
+ * (Constitution XVII) and the operator has to be able to switch it back on.
31
+ */
32
+ export function collectRegisteredSettingsManifests(registry) {
33
+ // The settings module goes first: every other manifest's settings fall back
34
+ // to its `general` group, which must exist before they are inserted.
35
+ const rest = registry
36
+ .map((entry) => settingsManifestWithRecentActivity(entry.manifest, entry.recentActivity))
37
+ .filter((manifest) => manifest !== undefined && manifest.moduleCode !== settingsModuleManifest.moduleCode);
38
+ return [settingsModuleManifest, ...rest];
39
+ }
40
+ /**
41
+ * The published face of the derivation above — feature 080 (T040b).
42
+ *
43
+ * Both composition roots called the function by importing this file, which is
44
+ * a root value import of a module's source and stops having a spelling the day
45
+ * `settings` becomes a package (D-160.6.1). The rule is unchanged and stays
46
+ * here, where the settings module's own manifest is: the roots keep supplying
47
+ * the registry, because which modules a deployment ships is theirs to say, and
48
+ * resolve the assembly from the container they already composed.
49
+ */
50
+ export class SettingsManifestCollectionService {
51
+ collect(registry) {
52
+ return collectRegisteredSettingsManifests(registry);
53
+ }
54
+ }
55
+ //# sourceMappingURL=registered-settings-manifests.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registered-settings-manifests.js","sourceRoot":"","sources":["../../../src/backend/services/registered-settings-manifests.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kCAAkC,EAAE,MAAM,4BAA4B,CAAC;AAMhF,OAAO,EAAE,gBAAgB,IAAI,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAE/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,UAAU,kCAAkC,CAChD,QAA+C;IAE/C,4EAA4E;IAC5E,qEAAqE;IACrE,MAAM,IAAI,GAAG,QAAQ;SAClB,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,kCAAkC,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC;SACxF,MAAM,CACL,CAAC,QAAQ,EAAsC,EAAE,CAC/C,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,UAAU,KAAK,sBAAsB,CAAC,UAAU,CACtF,CAAC;IACJ,OAAO,CAAC,sBAAsB,EAAE,GAAG,IAAI,CAAC,CAAC;AAC3C,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,OAAO,iCAAiC;IAC5C,OAAO,CAAC,QAA+C;QACrD,OAAO,kCAAkC,CAAC,QAAQ,CAAC,CAAC;IACtD,CAAC;CACF"}
@@ -0,0 +1,43 @@
1
+ import type { SettingWriteValidator, SettingWriteValidatorRegistryPort } from '@endora-commerce/contracts';
2
+ import type { ModulePresencePort } from './settings-admin.service.js';
3
+ /**
4
+ * Where a module says whether the value another module is about to store is
5
+ * legal — feature 078, D-95.2.
6
+ *
7
+ * The division of labour is the whole point of the seam:
8
+ *
9
+ * > `settings` answers "what would every channel's value be after this write".
10
+ * > The module that declared the setting answers "is that legal".
11
+ *
12
+ * `settings` owns three-tier resolution and keeps owning it; it never learns
13
+ * what an invoice number is. `invoices` never re-derives the resolution.
14
+ *
15
+ * **Absent-contributor policy: `skip`.** A validator whose contributing module
16
+ * is not effectively present is not enumerated, so its refusal does not answer
17
+ * for a module an operator switched off.
18
+ *
19
+ * That filter is redundant today, and it is written anyway. `assertWritable`
20
+ * already refuses every write to a switched-off module's settings before
21
+ * validation runs — but that refusal has one exception, a module's own
22
+ * activation control, and a second exception added later must not silently let
23
+ * an absent module's validator answer for a code.
24
+ */
25
+ export declare class SettingWriteValidatorRegistry implements SettingWriteValidatorRegistryPort {
26
+ /**
27
+ * Absent in a composition with no lifecycle (unit tests): every contributor
28
+ * then counts as present, which is the pre-073 behaviour rather than a
29
+ * fall-open — there is no activation axis to resolve at all.
30
+ */
31
+ private readonly presence?;
32
+ private readonly validators;
33
+ constructor(
34
+ /**
35
+ * Absent in a composition with no lifecycle (unit tests): every contributor
36
+ * then counts as present, which is the pre-073 behaviour rather than a
37
+ * fall-open — there is no activation axis to resolve at all.
38
+ */
39
+ presence?: ModulePresencePort | undefined);
40
+ register(validator: SettingWriteValidator): void;
41
+ forCode(code: string): readonly SettingWriteValidator[];
42
+ }
43
+ //# sourceMappingURL=setting-write-validators.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setting-write-validators.d.ts","sourceRoot":"","sources":["../../../src/backend/services/setting-write-validators.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,iCAAiC,EAClC,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAEtE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,6BAA8B,YAAW,iCAAiC;IAInF;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;IAR5B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA+B;;IAGxD;;;;OAIG;IACc,QAAQ,CAAC,EAAE,kBAAkB,YAAA;IAGhD,QAAQ,CAAC,SAAS,EAAE,qBAAqB,GAAG,IAAI;IAIhD,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,qBAAqB,EAAE;CAOxD"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Where a module says whether the value another module is about to store is
3
+ * legal — feature 078, D-95.2.
4
+ *
5
+ * The division of labour is the whole point of the seam:
6
+ *
7
+ * > `settings` answers "what would every channel's value be after this write".
8
+ * > The module that declared the setting answers "is that legal".
9
+ *
10
+ * `settings` owns three-tier resolution and keeps owning it; it never learns
11
+ * what an invoice number is. `invoices` never re-derives the resolution.
12
+ *
13
+ * **Absent-contributor policy: `skip`.** A validator whose contributing module
14
+ * is not effectively present is not enumerated, so its refusal does not answer
15
+ * for a module an operator switched off.
16
+ *
17
+ * That filter is redundant today, and it is written anyway. `assertWritable`
18
+ * already refuses every write to a switched-off module's settings before
19
+ * validation runs — but that refusal has one exception, a module's own
20
+ * activation control, and a second exception added later must not silently let
21
+ * an absent module's validator answer for a code.
22
+ */
23
+ export class SettingWriteValidatorRegistry {
24
+ presence;
25
+ validators = [];
26
+ constructor(
27
+ /**
28
+ * Absent in a composition with no lifecycle (unit tests): every contributor
29
+ * then counts as present, which is the pre-073 behaviour rather than a
30
+ * fall-open — there is no activation axis to resolve at all.
31
+ */
32
+ presence) {
33
+ this.presence = presence;
34
+ }
35
+ register(validator) {
36
+ this.validators.push(validator);
37
+ }
38
+ forCode(code) {
39
+ return this.validators.filter((validator) => validator.codes.includes(code) &&
40
+ (this.presence?.presenceOf(validator.contributorModuleId) ?? true));
41
+ }
42
+ }
43
+ //# sourceMappingURL=setting-write-validators.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setting-write-validators.js","sourceRoot":"","sources":["../../../src/backend/services/setting-write-validators.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,OAAO,6BAA6B;IASrB;IARF,UAAU,GAA4B,EAAE,CAAC;IAE1D;IACE;;;;OAIG;IACc,QAA6B;QAA7B,aAAQ,GAAR,QAAQ,CAAqB;IAC7C,CAAC;IAEJ,QAAQ,CAAC,SAAgC;QACvC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAClC,CAAC;IAED,OAAO,CAAC,IAAY;QAClB,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,CAC3B,CAAC,SAAS,EAAE,EAAE,CACZ,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;YAC9B,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,SAAS,CAAC,mBAAmB,CAAC,IAAI,IAAI,CAAC,CACrE,CAAC;IACJ,CAAC;CACF"}