@vritti/api-sdk 0.3.3 → 0.3.6

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 (133) hide show
  1. package/dist/auth.cjs +942 -0
  2. package/dist/auth.cjs.map +1 -0
  3. package/dist/auth.d.cts +95 -0
  4. package/dist/auth.d.ts +95 -0
  5. package/dist/auth.js +884 -0
  6. package/dist/auth.js.map +1 -0
  7. package/dist/cache.cjs +182 -14
  8. package/dist/cache.cjs.map +1 -1
  9. package/dist/cache.d.cts +48 -4
  10. package/dist/cache.d.ts +48 -4
  11. package/dist/cache.js +177 -12
  12. package/dist/cache.js.map +1 -1
  13. package/dist/catalog-resolver.cjs +337 -140
  14. package/dist/catalog-resolver.cjs.map +1 -1
  15. package/dist/catalog-resolver.d.cts +70 -45
  16. package/dist/catalog-resolver.d.ts +70 -45
  17. package/dist/catalog-resolver.js +322 -135
  18. package/dist/catalog-resolver.js.map +1 -1
  19. package/dist/code-pattern-BP4zkNtT.d.cts +12 -0
  20. package/dist/code-pattern-BP4zkNtT.d.ts +12 -0
  21. package/dist/context.cjs +93 -0
  22. package/dist/context.cjs.map +1 -0
  23. package/dist/context.d.cts +19 -0
  24. package/dist/context.d.ts +19 -0
  25. package/dist/context.js +64 -0
  26. package/dist/context.js.map +1 -0
  27. package/dist/countries.cjs +1559 -0
  28. package/dist/countries.cjs.map +1 -0
  29. package/dist/countries.d.cts +13 -0
  30. package/dist/countries.d.ts +13 -0
  31. package/dist/countries.js +1532 -0
  32. package/dist/countries.js.map +1 -0
  33. package/dist/data-table.cjs +1280 -0
  34. package/dist/data-table.cjs.map +1 -0
  35. package/dist/data-table.d.cts +123 -0
  36. package/dist/data-table.d.ts +123 -0
  37. package/dist/data-table.js +1260 -0
  38. package/dist/data-table.js.map +1 -0
  39. package/dist/database.cjs +1510 -0
  40. package/dist/database.cjs.map +1 -0
  41. package/dist/database.d.cts +287 -0
  42. package/dist/database.d.ts +287 -0
  43. package/dist/database.js +1469 -0
  44. package/dist/database.js.map +1 -0
  45. package/dist/decimal.d.cts +0 -1
  46. package/dist/decimal.d.ts +0 -1
  47. package/dist/decorators.cjs +1817 -0
  48. package/dist/decorators.cjs.map +1 -0
  49. package/dist/decorators.d.cts +24 -0
  50. package/dist/decorators.d.ts +24 -0
  51. package/dist/decorators.js +1780 -0
  52. package/dist/decorators.js.map +1 -0
  53. package/dist/drizzle-pg-core.cjs +25 -0
  54. package/dist/drizzle-pg-core.cjs.map +1 -1
  55. package/dist/drizzle-pg-core.d.cts +7 -0
  56. package/dist/drizzle-pg-core.d.ts +7 -0
  57. package/dist/drizzle-pg-core.js +22 -0
  58. package/dist/drizzle-pg-core.js.map +1 -1
  59. package/dist/exceptions.cjs +301 -0
  60. package/dist/exceptions.cjs.map +1 -0
  61. package/dist/exceptions.d.cts +90 -0
  62. package/dist/exceptions.d.ts +90 -0
  63. package/dist/exceptions.js +257 -0
  64. package/dist/exceptions.js.map +1 -0
  65. package/dist/filter.types-PuARU9XD.d.cts +50 -0
  66. package/dist/filter.types-PuARU9XD.d.ts +50 -0
  67. package/dist/filters.cjs +407 -0
  68. package/dist/filters.cjs.map +1 -0
  69. package/dist/filters.d.cts +55 -0
  70. package/dist/filters.d.ts +55 -0
  71. package/dist/filters.js +377 -0
  72. package/dist/filters.js.map +1 -0
  73. package/dist/index.cjs +0 -5423
  74. package/dist/index.cjs.map +1 -1
  75. package/dist/index.d.cts +93 -854
  76. package/dist/index.d.ts +93 -854
  77. package/dist/index.js +0 -5301
  78. package/dist/index.js.map +1 -1
  79. package/dist/is-country.decorator-CcNwSSr5.d.cts +5 -0
  80. package/dist/is-country.decorator-CcNwSSr5.d.ts +5 -0
  81. package/dist/license.d.cts +1 -1
  82. package/dist/license.d.ts +1 -1
  83. package/dist/lodash.cjs.map +1 -1
  84. package/dist/lodash.js.map +1 -1
  85. package/dist/logger.cjs +862 -0
  86. package/dist/logger.cjs.map +1 -0
  87. package/dist/logger.d.cts +118 -0
  88. package/dist/logger.d.ts +118 -0
  89. package/dist/logger.js +816 -0
  90. package/dist/logger.js.map +1 -0
  91. package/dist/migrate.cjs +2 -2
  92. package/dist/migrate.cjs.map +1 -1
  93. package/dist/migrate.d.cts +2 -0
  94. package/dist/migrate.d.ts +2 -0
  95. package/dist/migrate.js +2 -2
  96. package/dist/migrate.js.map +1 -1
  97. package/dist/money.cjs +18 -3
  98. package/dist/money.cjs.map +1 -1
  99. package/dist/money.d.cts +13 -13
  100. package/dist/money.d.ts +13 -13
  101. package/dist/money.js +18 -3
  102. package/dist/money.js.map +1 -1
  103. package/dist/nats.cjs +34 -35
  104. package/dist/nats.cjs.map +1 -1
  105. package/dist/nats.d.cts +14 -14
  106. package/dist/nats.d.ts +14 -14
  107. package/dist/nats.js +32 -33
  108. package/dist/nats.js.map +1 -1
  109. package/dist/root.cjs +225 -0
  110. package/dist/root.cjs.map +1 -0
  111. package/dist/root.d.cts +4 -0
  112. package/dist/root.d.ts +4 -0
  113. package/dist/root.js +200 -0
  114. package/dist/root.js.map +1 -0
  115. package/dist/{types-DQRwj5rr.d.cts → types-BBK6MG-U.d.cts} +37 -6
  116. package/dist/{types-DQRwj5rr.d.ts → types-BBK6MG-U.d.ts} +37 -6
  117. package/dist/types.cjs +19 -0
  118. package/dist/types.cjs.map +1 -0
  119. package/dist/types.d.cts +17 -0
  120. package/dist/types.d.ts +17 -0
  121. package/dist/types.js +1 -0
  122. package/dist/types.js.map +1 -0
  123. package/dist/utils.cjs +273 -0
  124. package/dist/utils.cjs.map +1 -0
  125. package/dist/utils.d.cts +8 -0
  126. package/dist/utils.d.ts +8 -0
  127. package/dist/utils.js +245 -0
  128. package/dist/utils.js.map +1 -0
  129. package/package.json +122 -1
  130. package/dist/cache.service-_SuJIcNA.d.cts +0 -20
  131. package/dist/cache.service-_SuJIcNA.d.ts +0 -20
  132. package/dist/fastify-augmentation-CQPBNS5a.d.cts +0 -105
  133. package/dist/fastify-augmentation-CQPBNS5a.d.ts +0 -105
@@ -1,26 +1,130 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
3
 
4
+ // src/catalog-resolver/permission-deps.ts
5
+ function buildDependsMap(permissions) {
6
+ const present = new Set(permissions.map((p) => p.code));
7
+ const map = /* @__PURE__ */ new Map();
8
+ for (const p of permissions) {
9
+ map.set(p.code, (p.dependsOn ?? []).filter((dep) => dep !== p.code && present.has(dep)));
10
+ }
11
+ return map;
12
+ }
13
+ __name(buildDependsMap, "buildDependsMap");
14
+ function prereqClosure(code, deps) {
15
+ const out = /* @__PURE__ */ new Set();
16
+ const seen = /* @__PURE__ */ new Set([
17
+ code
18
+ ]);
19
+ const stack = [
20
+ code
21
+ ];
22
+ while (stack.length > 0) {
23
+ const current = stack.pop();
24
+ for (const dep of deps.get(current) ?? []) {
25
+ if (seen.has(dep)) continue;
26
+ seen.add(dep);
27
+ out.add(dep);
28
+ stack.push(dep);
29
+ }
30
+ }
31
+ return [
32
+ ...out
33
+ ];
34
+ }
35
+ __name(prereqClosure, "prereqClosure");
36
+ function cascadeLocked(codes, directlyLocked, deps) {
37
+ const locked = /* @__PURE__ */ new Set();
38
+ const visiting = /* @__PURE__ */ new Set();
39
+ const check = /* @__PURE__ */ __name((code) => {
40
+ if (locked.has(code)) return true;
41
+ if (directlyLocked.has(code)) {
42
+ locked.add(code);
43
+ return true;
44
+ }
45
+ if (visiting.has(code)) return false;
46
+ visiting.add(code);
47
+ const viaDep = (deps.get(code) ?? []).some(check);
48
+ visiting.delete(code);
49
+ if (viaDep) locked.add(code);
50
+ return viaDep;
51
+ }, "check");
52
+ for (const code of codes) check(code);
53
+ return locked;
54
+ }
55
+ __name(cascadeLocked, "cascadeLocked");
56
+ function filterGrantedByDeps(granted, deps) {
57
+ const ok = /* @__PURE__ */ new Set();
58
+ const visiting = /* @__PURE__ */ new Set();
59
+ const check = /* @__PURE__ */ __name((code) => {
60
+ if (ok.has(code)) return true;
61
+ if (!granted.has(code)) return false;
62
+ if (visiting.has(code)) return true;
63
+ visiting.add(code);
64
+ const satisfied = (deps.get(code) ?? []).every(check);
65
+ visiting.delete(code);
66
+ if (satisfied) ok.add(code);
67
+ return satisfied;
68
+ }, "check");
69
+ for (const code of granted) check(code);
70
+ return ok;
71
+ }
72
+ __name(filterGrantedByDeps, "filterGrantedByDeps");
73
+
74
+ // src/catalog-resolver/types.ts
75
+ var PLATFORMS = [
76
+ "web",
77
+ "mobile"
78
+ ];
79
+ var SITE_TYPES = [
80
+ "OUTLET",
81
+ "WAREHOUSE",
82
+ "PRODUCTION"
83
+ ];
84
+ var SERVICE_CODES = [
85
+ "GITEA"
86
+ ];
87
+ function snapshotFeatureKey(code, scope) {
88
+ return `${scope}.${code}`;
89
+ }
90
+ __name(snapshotFeatureKey, "snapshotFeatureKey");
91
+ var SNAPSHOT_SCHEMA_VERSION = 2;
92
+
4
93
  // src/catalog-resolver/catalog.builder.ts
5
- function buildBuCatalog(snapshot, businessCode, planCode, buLocks, bucket) {
94
+ function featureAppliesAtNode(applicableSiteTypes, siteType) {
95
+ return applicableSiteTypes.includes(siteType);
96
+ }
97
+ __name(featureAppliesAtNode, "featureAppliesAtNode");
98
+ function findFeatureByCode(snapshot, code) {
99
+ for (const feature of Object.values(snapshot.features ?? {})) {
100
+ if (feature?.code === code) return feature;
101
+ }
102
+ return void 0;
103
+ }
104
+ __name(findFeatureByCode, "findFeatureByCode");
105
+ function buildSiteCatalog(snapshot, businessCode, planCode, siteLocks, bucket, siteType, scope, availableServices = []) {
6
106
  if (!businessCode) return [];
7
107
  const business = snapshot.businesses?.[businessCode];
8
108
  if (!business) return [];
9
109
  const plan = planCode ? business.plans?.[planCode] : void 0;
10
110
  const plans = business.plans ?? {};
11
111
  const catalog = [];
12
- for (const app of business.apps) {
13
- const businessAppFeatures = app.features.map((code) => snapshot.features?.[code]).filter((f) => !!f && !!(f.microfrontends?.web || f.microfrontends?.mobile));
112
+ const sortedApps = [
113
+ ...business.apps
114
+ ].sort((a, b) => a.name.localeCompare(b.name));
115
+ for (const app of sortedApps) {
116
+ const businessAppFeatures = app.features.filter((ref) => scope === void 0 || ref.scope === scope).map((ref) => snapshot.features?.[snapshotFeatureKey(ref.code, ref.scope)]).filter((f) => !!f && !!(f.microfrontends?.web || f.microfrontends?.mobile) && (siteType === void 0 || featureAppliesAtNode(f.applicableSiteTypes, siteType)));
14
117
  if (businessAppFeatures.length === 0) continue;
15
118
  for (const feature of businessAppFeatures) {
16
119
  const membership = plan?.unlockedPermissions?.[feature.code];
17
120
  const web = feature.microfrontends?.web;
18
121
  const mobile = feature.microfrontends?.mobile;
19
- const permissions = buildPermissions(feature, businessCode, membership, buLocks, plans, bucket);
20
122
  const memberOnBucket = membership?.[bucket] !== void 0;
21
- const buPlatformLocked = buLocks?.[feature.code]?.[bucket] === null;
22
- const locked = !memberOnBucket || buPlatformLocked;
23
- const lockReason = !memberOnBucket ? "PLAN" : buPlatformLocked ? "BU" : null;
123
+ const sitePlatformLocked = siteLocks?.[feature.code]?.[bucket] === null;
124
+ const missingServices = unmetServices(feature, availableServices);
125
+ const permissions = buildPermissions(feature, businessCode, membership, siteLocks, plans, bucket, missingServices);
126
+ const lockReason = resolveLockReason(!memberOnBucket, sitePlatformLocked, missingServices);
127
+ const locked = lockReason !== null;
24
128
  const unlockPlans = lockReason === "PLAN" ? plansIncludingFeature(plans, feature.code, bucket) : [];
25
129
  catalog.push({
26
130
  code: feature.code,
@@ -46,48 +150,81 @@ function buildBuCatalog(snapshot, businessCode, planCode, buLocks, bucket) {
46
150
  locked,
47
151
  lockReason,
48
152
  unlockPlans,
153
+ missingServices,
49
154
  permissions
50
155
  });
51
156
  }
52
157
  }
53
158
  return catalog;
54
159
  }
55
- __name(buildBuCatalog, "buildBuCatalog");
160
+ __name(buildSiteCatalog, "buildSiteCatalog");
56
161
  function isPlanMember(entry) {
57
162
  return !!entry && (entry.web !== void 0 || entry.mobile !== void 0);
58
163
  }
59
164
  __name(isPlanMember, "isPlanMember");
60
- function isBuLockedOnPlatform(entry, platform, code) {
165
+ function resolveLockReason(planLocked, siteLocked, missingServices) {
166
+ if (planLocked) return "PLAN";
167
+ if (siteLocked) return "SITE";
168
+ if (missingServices.length > 0) return "SERVICE";
169
+ return null;
170
+ }
171
+ __name(resolveLockReason, "resolveLockReason");
172
+ function unmetServices(feature, availableServices) {
173
+ return (feature.requiredServices ?? []).filter((service) => !availableServices.includes(service));
174
+ }
175
+ __name(unmetServices, "unmetServices");
176
+ function isSiteLockedOnPlatform(entry, platform, code) {
61
177
  const locks = entry?.[platform];
62
178
  return locks === null || (locks?.includes(code) ?? false);
63
179
  }
64
- __name(isBuLockedOnPlatform, "isBuLockedOnPlatform");
65
- function buildPermissions(feature, businessCode, planMembership, buLocks, plans, bucket) {
180
+ __name(isSiteLockedOnPlatform, "isSiteLockedOnPlatform");
181
+ function buildPermissions(feature, businessCode, planMembership, siteLocks, plans, bucket, missingServices = []) {
66
182
  const planUnlocked = new Set(planMembership?.[bucket] ?? []);
67
- const lockEntry = buLocks?.[feature.code];
68
- return (feature.permissions ?? []).filter((p) => p.isGlobal || p.businesses.includes(businessCode)).map((p) => {
69
- const planAllows = planUnlocked.has(p.code);
70
- const buAllows = !isBuLockedOnPlatform(lockEntry, bucket, p.code);
71
- const locked = !planAllows || !buAllows;
72
- const lockReason = !planAllows ? "PLAN" : !buAllows ? "BU" : null;
73
- const unlockPlans = lockReason === "PLAN" ? plansUnlockingPermission(plans, feature.code, p.code, bucket) : [];
183
+ const lockEntry = siteLocks?.[feature.code];
184
+ const perms = (feature.permissions ?? []).filter((p) => p.isGlobal || p.businesses.includes(businessCode));
185
+ const deps = buildDependsMap(perms);
186
+ const codes = perms.map((p) => p.code);
187
+ const directlyPlanLocked = /* @__PURE__ */ new Set();
188
+ const directlySiteLocked = /* @__PURE__ */ new Set();
189
+ for (const p of perms) {
190
+ if (!planUnlocked.has(p.code)) directlyPlanLocked.add(p.code);
191
+ if (isSiteLockedOnPlatform(lockEntry, bucket, p.code)) directlySiteLocked.add(p.code);
192
+ }
193
+ const directlyLocked = /* @__PURE__ */ new Set([
194
+ ...directlyPlanLocked,
195
+ ...directlySiteLocked
196
+ ]);
197
+ const lockedSet = cascadeLocked(codes, directlyLocked, deps);
198
+ return perms.map((p) => {
199
+ const closure = [
200
+ p.code,
201
+ ...prereqClosure(p.code, deps)
202
+ ];
203
+ const cascaded = lockedSet.has(p.code);
204
+ const planReason = cascaded && closure.some((c) => directlyPlanLocked.has(c));
205
+ const siteReason = cascaded && closure.some((c) => directlySiteLocked.has(c));
206
+ const lockReason = resolveLockReason(planReason, siteReason, missingServices);
207
+ const locked = lockReason !== null;
208
+ const unlockPlans = lockReason === "PLAN" ? plansUnlockingClosure(plans, feature.code, closure, bucket) : [];
74
209
  return {
75
210
  code: p.code,
76
211
  locked,
77
212
  lockReason,
78
- unlockPlans
213
+ unlockPlans,
214
+ missingServices
79
215
  };
80
216
  });
81
217
  }
82
218
  __name(buildPermissions, "buildPermissions");
83
- function plansUnlockingPermission(plans, featureCode, permCode, bucket) {
219
+ function plansUnlockingClosure(plans, featureCode, closure, bucket) {
84
220
  const result = [];
85
221
  for (const [code, plan] of Object.entries(plans)) {
86
- if (plan.unlockedPermissions?.[featureCode]?.[bucket]?.includes(permCode)) result.push(code);
222
+ const unlocked = plan.unlockedPermissions?.[featureCode]?.[bucket];
223
+ if (unlocked && closure.every((c) => unlocked.includes(c))) result.push(code);
87
224
  }
88
225
  return result;
89
226
  }
90
- __name(plansUnlockingPermission, "plansUnlockingPermission");
227
+ __name(plansUnlockingClosure, "plansUnlockingClosure");
91
228
  function plansIncludingFeature(plans, featureCode, bucket) {
92
229
  const result = [];
93
230
  for (const [code, plan] of Object.entries(plans)) {
@@ -96,115 +233,13 @@ function plansIncludingFeature(plans, featureCode, bucket) {
96
233
  return result;
97
234
  }
98
235
  __name(plansIncludingFeature, "plansIncludingFeature");
99
- function buildBuRoles(snapshot, businessCode) {
236
+ function buildSiteRoles(snapshot, businessCode) {
100
237
  if (!businessCode) return [];
101
238
  const business = snapshot.businesses?.[businessCode];
102
239
  if (!business) return [];
103
240
  return Object.values(business.roleTemplates ?? {});
104
241
  }
105
- __name(buildBuRoles, "buildBuRoles");
106
-
107
- // src/catalog-resolver/types.ts
108
- var PLATFORMS = [
109
- "web",
110
- "mobile"
111
- ];
112
- var SNAPSHOT_SCHEMA_VERSION = 1;
113
-
114
- // src/catalog-resolver/bu-matrix.builder.ts
115
- function buildBuMatrix(snapshot, businessCode, planCode, buLocks) {
116
- const business = businessCode ? snapshot.businesses?.[businessCode] : void 0;
117
- const plans = business?.plans ?? {};
118
- const plan = planCode ? plans[planCode] : void 0;
119
- const planMeta = {
120
- code: planCode ?? "",
121
- name: plan?.name ?? planCode ?? ""
122
- };
123
- const locks = buLocks ?? {};
124
- if (!business || !plan) return {
125
- plan: planMeta,
126
- apps: [],
127
- locks
128
- };
129
- const apps = [];
130
- for (const app of business.apps) {
131
- let unlockedCount = 0;
132
- let totalCount = 0;
133
- const features = [];
134
- for (const code of app.features) {
135
- const feature = snapshot.features?.[code];
136
- if (!feature) continue;
137
- const platforms = PLATFORMS.filter((p) => !!feature.microfrontends?.[p]);
138
- if (platforms.length === 0) continue;
139
- const membership = plan.unlockedPermissions?.[code];
140
- const featureInPlan = isPlanMember(membership);
141
- const buEntry = buLocks?.[code];
142
- const permissions = (feature.permissions ?? []).filter((p) => p.isGlobal || p.businesses.includes(businessCode ?? "")).map((p) => {
143
- const cell = /* @__PURE__ */ __name((plat) => {
144
- if (!platforms.includes(plat)) return null;
145
- const planCodes = membership?.[plat];
146
- const inPlan = featureInPlan && planCodes !== void 0 && planCodes.includes(p.code);
147
- const selected = inPlan && !isBuLockedOnPlatform(buEntry, plat, p.code);
148
- const availableIn = inPlan ? [] : plansUnlockingPerm(plans, code, p.code, plat, planCode);
149
- totalCount += 1;
150
- if (inPlan) unlockedCount += 1;
151
- return {
152
- inPlan,
153
- selected,
154
- availableIn
155
- };
156
- }, "cell");
157
- return {
158
- code: p.code,
159
- label: p.label,
160
- web: cell("web"),
161
- mobile: cell("mobile")
162
- };
163
- });
164
- features.push({
165
- code: feature.code,
166
- name: feature.name,
167
- icon: feature.lucideIcon ?? null,
168
- platforms,
169
- inPlan: featureInPlan,
170
- availableIn: featureInPlan ? [] : plansIncludingFeature2(plans, code, planCode),
171
- permissions
172
- });
173
- }
174
- apps.push({
175
- code: app.code,
176
- name: app.name,
177
- icon: app.icon ?? null,
178
- unlockedCount,
179
- totalCount,
180
- features
181
- });
182
- }
183
- return {
184
- plan: planMeta,
185
- apps,
186
- locks
187
- };
188
- }
189
- __name(buildBuMatrix, "buildBuMatrix");
190
- function plansUnlockingPerm(plans, featureCode, permCode, platform, excludeCode) {
191
- const names = [];
192
- for (const [code, p] of Object.entries(plans)) {
193
- if (code === excludeCode) continue;
194
- if ((p.unlockedPermissions?.[featureCode]?.[platform] ?? []).includes(permCode)) names.push(p.name);
195
- }
196
- return names;
197
- }
198
- __name(plansUnlockingPerm, "plansUnlockingPerm");
199
- function plansIncludingFeature2(plans, featureCode, excludeCode) {
200
- const names = [];
201
- for (const [code, p] of Object.entries(plans)) {
202
- if (code === excludeCode) continue;
203
- if (isPlanMember(p.unlockedPermissions?.[featureCode])) names.push(p.name);
204
- }
205
- return names;
206
- }
207
- __name(plansIncludingFeature2, "plansIncludingFeature");
242
+ __name(buildSiteRoles, "buildSiteRoles");
208
243
 
209
244
  // src/catalog-resolver/compose-role-grants.ts
210
245
  function unionBucket(base, add) {
@@ -245,13 +280,35 @@ __name(composeRoleGrants, "composeRoleGrants");
245
280
 
246
281
  // src/catalog-resolver/resolve-user-features.ts
247
282
  function resolveUserFeatures(params) {
248
- const { snapshot, businessCode, planCode, buLocks, roleFeatures, platform } = params;
283
+ const { snapshot, businessCode, planCode, siteLocks, roleFeatures, platform, siteType, scope, availableServices } = params;
249
284
  const bucket = platform === "web" ? "web" : "mobile";
250
- const catalog = buildBuCatalog(snapshot, businessCode, planCode, buLocks, bucket);
285
+ const featureByCode = /* @__PURE__ */ __name((code) => scope ? snapshot.features?.[snapshotFeatureKey(code, scope)] : findFeatureByCode(snapshot, code), "featureByCode");
286
+ const catalog = buildSiteCatalog(snapshot, businessCode, planCode, siteLocks, bucket, siteType, scope, availableServices);
251
287
  const catalogMap = new Map(catalog.map((f) => [
252
288
  f.code,
253
289
  f
254
290
  ]));
291
+ const businessPlans = snapshot.businesses[businessCode]?.plans ?? {};
292
+ const currentUnlockedCodes = /* @__PURE__ */ new Set();
293
+ if (planCode && businessPlans[planCode]) {
294
+ for (const [featureCode, platforms] of Object.entries(businessPlans[planCode].unlockedPermissions ?? {})) {
295
+ if (platforms?.[bucket] !== void 0) currentUnlockedCodes.add(featureCode);
296
+ }
297
+ }
298
+ const planAdds = /* @__PURE__ */ new Map();
299
+ for (const [planKey, plan] of Object.entries(businessPlans)) {
300
+ if (planKey === planCode) continue;
301
+ const adds = [];
302
+ for (const [featureCode, platforms] of Object.entries(plan.unlockedPermissions ?? {})) {
303
+ if (platforms?.[bucket] === void 0 || currentUnlockedCodes.has(featureCode)) continue;
304
+ const name = featureByCode(featureCode)?.name;
305
+ if (name) adds.push({
306
+ code: featureCode,
307
+ name
308
+ });
309
+ }
310
+ planAdds.set(planKey, adds);
311
+ }
255
312
  const grantedFeatures = /* @__PURE__ */ new Map();
256
313
  for (const [code, grant] of Object.entries(roleFeatures ?? {})) {
257
314
  const granted = grant?.[bucket];
@@ -265,18 +322,24 @@ function resolveUserFeatures(params) {
265
322
  if (!catalogEntry) continue;
266
323
  const route = pickRouteForPlatform(catalogEntry, platform);
267
324
  if (!route) continue;
325
+ const featureDeps = buildDependsMap(featureByCode(code)?.permissions ?? []);
268
326
  const permByCode = new Map((catalogEntry.permissions ?? []).map((p) => [
269
327
  p.code,
270
328
  p
271
329
  ]));
272
330
  const grantedPerms = [
273
- ...permsSet
331
+ ...filterGrantedByDeps(permsSet, featureDeps)
274
332
  ];
275
333
  const lockedPermissions = grantedPerms.map((c) => permByCode.get(c)).filter((p) => !!p?.locked).map((p) => ({
276
334
  code: p.code,
277
335
  reason: p.lockReason ?? null,
278
- unlockPlans: p.unlockPlans ?? []
336
+ unlockPlans: p.unlockPlans ?? [],
337
+ missingServices: p.missingServices ?? []
279
338
  }));
339
+ const upsell = catalogEntry.locked && catalogEntry.lockReason === "PLAN" ? (catalogEntry.unlockPlans ?? []).map((plan) => ({
340
+ plan,
341
+ features: (planAdds.get(plan) ?? []).filter((f) => f.code !== code).map((f) => f.name)
342
+ })).filter((group) => group.features.length > 0) : [];
280
343
  features.push({
281
344
  code,
282
345
  name: catalogEntry.name,
@@ -287,7 +350,9 @@ function resolveUserFeatures(params) {
287
350
  locked: catalogEntry.locked ?? false,
288
351
  lockReason: catalogEntry.lockReason ?? null,
289
352
  unlockPlans: catalogEntry.unlockPlans ?? [],
353
+ missingServices: catalogEntry.missingServices ?? [],
290
354
  lockedPermissions,
355
+ upsell,
291
356
  route,
292
357
  appCode: catalogEntry.appCode,
293
358
  appName: catalogEntry.appName,
@@ -295,6 +360,7 @@ function resolveUserFeatures(params) {
295
360
  appSortOrder: catalogEntry.appSortOrder
296
361
  });
297
362
  }
363
+ features.sort((a, b) => a.appName.localeCompare(b.appName));
298
364
  return features;
299
365
  }
300
366
  __name(resolveUserFeatures, "resolveUserFeatures");
@@ -315,16 +381,137 @@ function pickRouteForPlatform(entry, platform) {
315
381
  };
316
382
  }
317
383
  __name(pickRouteForPlatform, "pickRouteForPlatform");
384
+
385
+ // src/catalog-resolver/site-matrix.builder.ts
386
+ function buildSiteMatrix(snapshot, businessCode, planCode, siteLocks, siteType) {
387
+ return buildMatrix(snapshot, businessCode, planCode, siteLocks, false, siteType);
388
+ }
389
+ __name(buildSiteMatrix, "buildSiteMatrix");
390
+ function buildPlanMatrix(snapshot, businessCode, planCode, siteLocks) {
391
+ return buildMatrix(snapshot, businessCode, planCode, siteLocks, true);
392
+ }
393
+ __name(buildPlanMatrix, "buildPlanMatrix");
394
+ function buildMatrix(snapshot, businessCode, planCode, siteLocks, allScopes, siteType) {
395
+ const business = businessCode ? snapshot.businesses?.[businessCode] : void 0;
396
+ const plans = business?.plans ?? {};
397
+ const plan = planCode ? plans[planCode] : void 0;
398
+ const planMeta = {
399
+ code: planCode ?? "",
400
+ name: plan?.name ?? planCode ?? ""
401
+ };
402
+ const locks = siteLocks ?? {};
403
+ if (!business || !plan) return {
404
+ plan: planMeta,
405
+ apps: [],
406
+ locks
407
+ };
408
+ const apps = [];
409
+ for (const app of business.apps) {
410
+ let unlockedCount = 0;
411
+ let totalCount = 0;
412
+ const features = [];
413
+ for (const ref of app.features) {
414
+ if (!allScopes && ref.scope !== "SITE") continue;
415
+ const code = ref.code;
416
+ const feature = snapshot.features?.[snapshotFeatureKey(code, ref.scope)];
417
+ if (!feature) continue;
418
+ if (siteType !== void 0 && !featureAppliesAtNode(feature.applicableSiteTypes, siteType)) continue;
419
+ const platforms = PLATFORMS.filter((p) => !!feature.microfrontends?.[p]);
420
+ if (platforms.length === 0) continue;
421
+ const membership = plan.unlockedPermissions?.[code];
422
+ const featureInPlan = isPlanMember(membership);
423
+ const siteEntry = siteLocks?.[code];
424
+ const permissions = (feature.permissions ?? []).filter((p) => p.isGlobal || p.businesses.includes(businessCode ?? "")).map((p) => {
425
+ const cell = /* @__PURE__ */ __name((plat) => {
426
+ if (!platforms.includes(plat)) return null;
427
+ const planCodes = membership?.[plat];
428
+ const inPlan = featureInPlan && planCodes !== void 0 && planCodes.includes(p.code);
429
+ const selected = inPlan && !isSiteLockedOnPlatform(siteEntry, plat, p.code);
430
+ const availableIn = inPlan ? [] : plansUnlockingPerm(plans, code, p.code, plat, planCode);
431
+ totalCount += 1;
432
+ if (inPlan) unlockedCount += 1;
433
+ return {
434
+ inPlan,
435
+ selected,
436
+ availableIn
437
+ };
438
+ }, "cell");
439
+ return {
440
+ code: p.code,
441
+ label: p.label,
442
+ dependsOn: p.dependsOn ?? [],
443
+ web: cell("web"),
444
+ mobile: cell("mobile")
445
+ };
446
+ });
447
+ features.push({
448
+ code: feature.code,
449
+ name: feature.name,
450
+ icon: feature.lucideIcon ?? null,
451
+ scope: feature.scope,
452
+ applicableSiteTypes: feature.applicableSiteTypes,
453
+ platforms,
454
+ inPlan: featureInPlan,
455
+ availableIn: featureInPlan ? [] : plansIncludingFeature2(plans, code, planCode),
456
+ permissions
457
+ });
458
+ }
459
+ if (features.length === 0) continue;
460
+ apps.push({
461
+ code: app.code,
462
+ name: app.name,
463
+ icon: app.icon ?? null,
464
+ unlockedCount,
465
+ totalCount,
466
+ features
467
+ });
468
+ }
469
+ apps.sort((a, b) => a.name.localeCompare(b.name));
470
+ return {
471
+ plan: planMeta,
472
+ apps,
473
+ locks
474
+ };
475
+ }
476
+ __name(buildMatrix, "buildMatrix");
477
+ function plansUnlockingPerm(plans, featureCode, permCode, platform, excludeCode) {
478
+ const names = [];
479
+ for (const [code, p] of Object.entries(plans)) {
480
+ if (code === excludeCode) continue;
481
+ if ((p.unlockedPermissions?.[featureCode]?.[platform] ?? []).includes(permCode)) names.push(p.name);
482
+ }
483
+ return names;
484
+ }
485
+ __name(plansUnlockingPerm, "plansUnlockingPerm");
486
+ function plansIncludingFeature2(plans, featureCode, excludeCode) {
487
+ const names = [];
488
+ for (const [code, p] of Object.entries(plans)) {
489
+ if (code === excludeCode) continue;
490
+ if (isPlanMember(p.unlockedPermissions?.[featureCode])) names.push(p.name);
491
+ }
492
+ return names;
493
+ }
494
+ __name(plansIncludingFeature2, "plansIncludingFeature");
318
495
  export {
319
496
  PLATFORMS,
497
+ SERVICE_CODES,
498
+ SITE_TYPES,
320
499
  SNAPSHOT_SCHEMA_VERSION,
321
- buildBuCatalog,
322
- buildBuMatrix,
323
- buildBuRoles,
500
+ buildDependsMap,
501
+ buildPlanMatrix,
502
+ buildSiteCatalog,
503
+ buildSiteMatrix,
504
+ buildSiteRoles,
505
+ cascadeLocked,
324
506
  composeRoleGrants,
325
- isBuLockedOnPlatform,
507
+ featureAppliesAtNode,
508
+ filterGrantedByDeps,
509
+ findFeatureByCode,
326
510
  isPlanMember,
511
+ isSiteLockedOnPlatform,
327
512
  pickRouteForPlatform,
328
- resolveUserFeatures
513
+ prereqClosure,
514
+ resolveUserFeatures,
515
+ snapshotFeatureKey
329
516
  };
330
517
  //# sourceMappingURL=catalog-resolver.js.map