guardrail-core 1.0.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 (189) hide show
  1. package/dist/__tests__/autopilot.test.d.ts +7 -0
  2. package/dist/__tests__/autopilot.test.d.ts.map +1 -0
  3. package/dist/__tests__/autopilot.test.js +156 -0
  4. package/dist/__tests__/tier-config.test.d.ts +9 -0
  5. package/dist/__tests__/tier-config.test.d.ts.map +1 -0
  6. package/dist/__tests__/tier-config.test.js +230 -0
  7. package/dist/__tests__/utils/hash-inline.test.d.ts +2 -0
  8. package/dist/__tests__/utils/hash-inline.test.d.ts.map +1 -0
  9. package/dist/__tests__/utils/hash-inline.test.js +62 -0
  10. package/dist/__tests__/utils/hash.test.d.ts +3 -0
  11. package/dist/__tests__/utils/hash.test.d.ts.map +1 -0
  12. package/dist/__tests__/utils/hash.test.js +95 -0
  13. package/dist/__tests__/utils/simple.test.d.ts +1 -0
  14. package/dist/__tests__/utils/simple.test.d.ts.map +1 -0
  15. package/dist/__tests__/utils/simple.test.js +10 -0
  16. package/dist/__tests__/utils/utils-simple.test.d.ts +1 -0
  17. package/dist/__tests__/utils/utils-simple.test.d.ts.map +1 -0
  18. package/dist/__tests__/utils/utils-simple.test.js +6 -0
  19. package/dist/__tests__/utils/utils.test.d.ts +15 -0
  20. package/dist/__tests__/utils/utils.test.d.ts.map +1 -0
  21. package/dist/__tests__/utils/utils.test.js +172 -0
  22. package/dist/autopilot/autopilot-runner.d.ts +33 -0
  23. package/dist/autopilot/autopilot-runner.d.ts.map +1 -0
  24. package/dist/autopilot/autopilot-runner.js +479 -0
  25. package/dist/autopilot/index.d.ts +6 -0
  26. package/dist/autopilot/index.d.ts.map +1 -0
  27. package/dist/autopilot/index.js +25 -0
  28. package/dist/autopilot/types.d.ts +102 -0
  29. package/dist/autopilot/types.d.ts.map +1 -0
  30. package/dist/autopilot/types.js +18 -0
  31. package/dist/cache/index.d.ts +7 -0
  32. package/dist/cache/index.d.ts.map +1 -0
  33. package/dist/cache/index.js +22 -0
  34. package/dist/cache/redis-cache.d.ts +145 -0
  35. package/dist/cache/redis-cache.d.ts.map +1 -0
  36. package/dist/cache/redis-cache.js +459 -0
  37. package/dist/ci/github-actions.d.ts +77 -0
  38. package/dist/ci/github-actions.d.ts.map +1 -0
  39. package/dist/ci/github-actions.js +277 -0
  40. package/dist/ci/index.d.ts +12 -0
  41. package/dist/ci/index.d.ts.map +1 -0
  42. package/dist/ci/index.js +27 -0
  43. package/dist/ci/pre-commit.d.ts +65 -0
  44. package/dist/ci/pre-commit.d.ts.map +1 -0
  45. package/dist/ci/pre-commit.js +286 -0
  46. package/dist/entitlements.d.ts +149 -0
  47. package/dist/entitlements.d.ts.map +1 -0
  48. package/dist/entitlements.js +464 -0
  49. package/dist/env.d.ts +113 -0
  50. package/dist/env.d.ts.map +1 -0
  51. package/dist/env.js +204 -0
  52. package/dist/fix-packs/__tests__/generate-fix-packs.test.d.ts +7 -0
  53. package/dist/fix-packs/__tests__/generate-fix-packs.test.d.ts.map +1 -0
  54. package/dist/fix-packs/__tests__/generate-fix-packs.test.js +250 -0
  55. package/dist/fix-packs/generate-fix-packs.d.ts +15 -0
  56. package/dist/fix-packs/generate-fix-packs.d.ts.map +1 -0
  57. package/dist/fix-packs/generate-fix-packs.js +505 -0
  58. package/dist/fix-packs/index.d.ts +8 -0
  59. package/dist/fix-packs/index.d.ts.map +1 -0
  60. package/dist/fix-packs/index.js +23 -0
  61. package/dist/fix-packs/types.d.ts +113 -0
  62. package/dist/fix-packs/types.d.ts.map +1 -0
  63. package/dist/fix-packs/types.js +71 -0
  64. package/dist/index.d.ts +13 -0
  65. package/dist/index.d.ts.map +1 -0
  66. package/dist/index.js +28 -0
  67. package/dist/metrics/prometheus.d.ts +99 -0
  68. package/dist/metrics/prometheus.d.ts.map +1 -0
  69. package/dist/metrics/prometheus.js +306 -0
  70. package/dist/quota-ledger.d.ts +119 -0
  71. package/dist/quota-ledger.d.ts.map +1 -0
  72. package/dist/quota-ledger.js +462 -0
  73. package/dist/rbac/__tests__/permissions.test.d.ts +8 -0
  74. package/dist/rbac/__tests__/permissions.test.d.ts.map +1 -0
  75. package/dist/rbac/__tests__/permissions.test.js +350 -0
  76. package/dist/rbac/index.d.ts +9 -0
  77. package/dist/rbac/index.d.ts.map +1 -0
  78. package/dist/rbac/index.js +32 -0
  79. package/dist/rbac/permissions.d.ts +71 -0
  80. package/dist/rbac/permissions.d.ts.map +1 -0
  81. package/dist/rbac/permissions.js +247 -0
  82. package/dist/rbac/types.d.ts +69 -0
  83. package/dist/rbac/types.d.ts.map +1 -0
  84. package/dist/rbac/types.js +213 -0
  85. package/dist/tier-config.d.ts +203 -0
  86. package/dist/tier-config.d.ts.map +1 -0
  87. package/dist/tier-config.js +675 -0
  88. package/dist/types.d.ts +365 -0
  89. package/dist/types.d.ts.map +1 -0
  90. package/dist/types.js +5 -0
  91. package/dist/utils.d.ts +36 -0
  92. package/dist/utils.d.ts.map +1 -0
  93. package/dist/utils.js +127 -0
  94. package/dist/verified-autofix/__tests__/format-validator.test.d.ts +11 -0
  95. package/dist/verified-autofix/__tests__/format-validator.test.d.ts.map +1 -0
  96. package/dist/verified-autofix/__tests__/format-validator.test.js +285 -0
  97. package/dist/verified-autofix/__tests__/pipeline.test.d.ts +11 -0
  98. package/dist/verified-autofix/__tests__/pipeline.test.d.ts.map +1 -0
  99. package/dist/verified-autofix/__tests__/pipeline.test.js +389 -0
  100. package/dist/verified-autofix/__tests__/repo-fingerprint.test.d.ts +11 -0
  101. package/dist/verified-autofix/__tests__/repo-fingerprint.test.d.ts.map +1 -0
  102. package/dist/verified-autofix/__tests__/repo-fingerprint.test.js +236 -0
  103. package/dist/verified-autofix/__tests__/workspace.test.d.ts +11 -0
  104. package/dist/verified-autofix/__tests__/workspace.test.d.ts.map +1 -0
  105. package/dist/verified-autofix/__tests__/workspace.test.js +314 -0
  106. package/dist/verified-autofix/format-validator.d.ts +101 -0
  107. package/dist/verified-autofix/format-validator.d.ts.map +1 -0
  108. package/dist/verified-autofix/format-validator.js +446 -0
  109. package/dist/verified-autofix/index.d.ts +14 -0
  110. package/dist/verified-autofix/index.d.ts.map +1 -0
  111. package/dist/verified-autofix/index.js +39 -0
  112. package/dist/verified-autofix/pipeline.d.ts +68 -0
  113. package/dist/verified-autofix/pipeline.d.ts.map +1 -0
  114. package/dist/verified-autofix/pipeline.js +330 -0
  115. package/dist/verified-autofix/repo-fingerprint.d.ts +56 -0
  116. package/dist/verified-autofix/repo-fingerprint.d.ts.map +1 -0
  117. package/dist/verified-autofix/repo-fingerprint.js +396 -0
  118. package/dist/verified-autofix/workspace.d.ts +83 -0
  119. package/dist/verified-autofix/workspace.d.ts.map +1 -0
  120. package/dist/verified-autofix/workspace.js +454 -0
  121. package/dist/verified-autofix.d.ts +182 -0
  122. package/dist/verified-autofix.d.ts.map +1 -0
  123. package/dist/verified-autofix.js +1021 -0
  124. package/dist/visualization/dependency-graph.d.ts +79 -0
  125. package/dist/visualization/dependency-graph.d.ts.map +1 -0
  126. package/dist/visualization/dependency-graph.js +399 -0
  127. package/dist/visualization/index.d.ts +5 -0
  128. package/dist/visualization/index.d.ts.map +1 -0
  129. package/dist/visualization/index.js +20 -0
  130. package/package.json +29 -0
  131. package/src/__tests__/autopilot.test.ts +196 -0
  132. package/src/__tests__/tier-config.test.ts +289 -0
  133. package/src/__tests__/utils/hash-inline.test.ts +76 -0
  134. package/src/__tests__/utils/hash.test.ts +119 -0
  135. package/src/__tests__/utils/simple.test.ts +10 -0
  136. package/src/__tests__/utils/utils-simple.test.ts +5 -0
  137. package/src/__tests__/utils/utils.test.ts +203 -0
  138. package/src/autopilot/autopilot-runner.ts +503 -0
  139. package/src/autopilot/index.ts +6 -0
  140. package/src/autopilot/types.ts +119 -0
  141. package/src/cache/index.ts +7 -0
  142. package/src/cache/redis-cache.d.ts +155 -0
  143. package/src/cache/redis-cache.d.ts.map +1 -0
  144. package/src/cache/redis-cache.ts +517 -0
  145. package/src/ci/github-actions.ts +335 -0
  146. package/src/ci/index.ts +12 -0
  147. package/src/ci/pre-commit.ts +338 -0
  148. package/src/db/usage-schema.prisma +114 -0
  149. package/src/entitlements.ts +570 -0
  150. package/src/env.d.ts +68 -0
  151. package/src/env.d.ts.map +1 -0
  152. package/src/env.ts +247 -0
  153. package/src/fix-packs/__tests__/generate-fix-packs.test.ts +317 -0
  154. package/src/fix-packs/generate-fix-packs.ts +577 -0
  155. package/src/fix-packs/index.ts +8 -0
  156. package/src/fix-packs/types.ts +206 -0
  157. package/src/index.d.ts +7 -0
  158. package/src/index.d.ts.map +1 -0
  159. package/src/index.ts +12 -0
  160. package/src/metrics/prometheus.d.ts +104 -0
  161. package/src/metrics/prometheus.d.ts.map +1 -0
  162. package/src/metrics/prometheus.ts +446 -0
  163. package/src/quota-ledger.ts +548 -0
  164. package/src/rbac/__tests__/permissions.test.ts +446 -0
  165. package/src/rbac/index.ts +46 -0
  166. package/src/rbac/permissions.ts +301 -0
  167. package/src/rbac/types.ts +298 -0
  168. package/src/tier-config.json +157 -0
  169. package/src/tier-config.ts +815 -0
  170. package/src/types.d.ts +365 -0
  171. package/src/types.d.ts.map +1 -0
  172. package/src/types.ts +441 -0
  173. package/src/utils.d.ts +36 -0
  174. package/src/utils.d.ts.map +1 -0
  175. package/src/utils.ts +140 -0
  176. package/src/verified-autofix/__tests__/format-validator.test.ts +335 -0
  177. package/src/verified-autofix/__tests__/pipeline.test.ts +419 -0
  178. package/src/verified-autofix/__tests__/repo-fingerprint.test.ts +241 -0
  179. package/src/verified-autofix/__tests__/workspace.test.ts +373 -0
  180. package/src/verified-autofix/format-validator.ts +517 -0
  181. package/src/verified-autofix/index.ts +63 -0
  182. package/src/verified-autofix/pipeline.ts +403 -0
  183. package/src/verified-autofix/repo-fingerprint.ts +459 -0
  184. package/src/verified-autofix/workspace.ts +531 -0
  185. package/src/verified-autofix.ts +1187 -0
  186. package/src/visualization/dependency-graph.d.ts +85 -0
  187. package/src/visualization/dependency-graph.d.ts.map +1 -0
  188. package/src/visualization/dependency-graph.ts +495 -0
  189. package/src/visualization/index.ts +5 -0
@@ -0,0 +1,675 @@
1
+ "use strict";
2
+ /**
3
+ * Canonical Tier Configuration
4
+ *
5
+ * SINGLE SOURCE OF TRUTH for all tier definitions across:
6
+ * - Backend entitlements
7
+ * - Rate limiting
8
+ * - Stripe billing mapping
9
+ * - Landing page pricing
10
+ * - CLI entitlements
11
+ *
12
+ * DO NOT define tier configurations anywhere else in the codebase.
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.TIER_CONFIG = exports.SEAT_PRICING = exports.FEATURES = exports.TIER_ORDER = exports.PURCHASABLE_TIERS = exports.TIERS = void 0;
16
+ exports.isValidTier = isValidTier;
17
+ exports.getTierConfig = getTierConfig;
18
+ exports.getAllTierConfigs = getAllTierConfigs;
19
+ exports.getPurchasableTierConfigs = getPurchasableTierConfigs;
20
+ exports.compareTiers = compareTiers;
21
+ exports.isTierHigher = isTierHigher;
22
+ exports.getMinimumTierForFeature = getMinimumTierForFeature;
23
+ exports.tierHasFeature = tierHasFeature;
24
+ exports.getEffectiveLimit = getEffectiveLimit;
25
+ exports.formatLimit = formatLimit;
26
+ exports.getStripePriceId = getStripePriceId;
27
+ exports.getTierFromStripePriceId = getTierFromStripePriceId;
28
+ exports.getPricingPageTiers = getPricingPageTiers;
29
+ exports.getRateLimitForTier = getRateLimitForTier;
30
+ exports.getRateLimiterTiers = getRateLimiterTiers;
31
+ exports.getSeatPricing = getSeatPricing;
32
+ exports.calculateEffectiveSeats = calculateEffectiveSeats;
33
+ exports.canAddMember = canAddMember;
34
+ exports.calculateSeatCost = calculateSeatCost;
35
+ exports.getStripeSeatPriceId = getStripeSeatPriceId;
36
+ exports.formatSeatInfo = formatSeatInfo;
37
+ exports.validateSeatReduction = validateSeatReduction;
38
+ // ============================================================================
39
+ // TIER ENUM
40
+ // ============================================================================
41
+ exports.TIERS = ['free', 'starter', 'pro', 'compliance', 'enterprise', 'unlimited'];
42
+ /** Tiers that can be purchased (excludes free and unlimited) */
43
+ exports.PURCHASABLE_TIERS = ['starter', 'pro', 'compliance', 'enterprise'];
44
+ /** Tier order for comparison (lower index = lower tier) */
45
+ exports.TIER_ORDER = ['free', 'starter', 'pro', 'compliance', 'enterprise', 'unlimited'];
46
+ // ============================================================================
47
+ // FEATURE FLAGS
48
+ // ============================================================================
49
+ exports.FEATURES = [
50
+ 'scan',
51
+ 'scan:full',
52
+ 'scan:security',
53
+ 'scan:compliance',
54
+ 'gate',
55
+ 'fix',
56
+ 'fix:auto',
57
+ 'ship',
58
+ 'reality',
59
+ 'reality:flows',
60
+ 'ai-agent',
61
+ 'ai-agent:goals',
62
+ 'autopilot',
63
+ 'context',
64
+ 'badge',
65
+ 'mcp',
66
+ 'compliance:soc2',
67
+ 'compliance:hipaa',
68
+ 'compliance:gdpr',
69
+ 'compliance:pci',
70
+ 'compliance:nist',
71
+ 'compliance:iso27001',
72
+ 'reports:html',
73
+ 'reports:pdf',
74
+ 'reports:sarif',
75
+ 'team:members',
76
+ 'team:admin',
77
+ 'api:access',
78
+ 'webhooks',
79
+ 'deploy-hooks',
80
+ ];
81
+ exports.SEAT_PRICING = {
82
+ free: {
83
+ monthlyPricePerSeat: 0,
84
+ annualPricePerSeat: 0,
85
+ maxAdditionalSeats: 0,
86
+ supportsAdditionalSeats: false,
87
+ },
88
+ starter: {
89
+ monthlyPricePerSeat: 0,
90
+ annualPricePerSeat: 0,
91
+ maxAdditionalSeats: 0,
92
+ supportsAdditionalSeats: false,
93
+ },
94
+ pro: {
95
+ monthlyPricePerSeat: 25,
96
+ annualPricePerSeat: 240, // 20% off: 25 * 12 * 0.8 = 240
97
+ maxAdditionalSeats: 45, // Base 5 + max 45 = 50 total
98
+ supportsAdditionalSeats: true,
99
+ },
100
+ compliance: {
101
+ monthlyPricePerSeat: 35,
102
+ annualPricePerSeat: 336, // 20% off: 35 * 12 * 0.8 = 336
103
+ maxAdditionalSeats: 90, // Base 10 + max 90 = 100 total
104
+ supportsAdditionalSeats: true,
105
+ },
106
+ enterprise: {
107
+ monthlyPricePerSeat: 45,
108
+ annualPricePerSeat: 432, // 20% off: 45 * 12 * 0.8 = 432
109
+ maxAdditionalSeats: -1, // Unlimited
110
+ supportsAdditionalSeats: true,
111
+ },
112
+ unlimited: {
113
+ monthlyPricePerSeat: 0,
114
+ annualPricePerSeat: 0,
115
+ maxAdditionalSeats: -1,
116
+ supportsAdditionalSeats: true,
117
+ },
118
+ };
119
+ // ============================================================================
120
+ // CANONICAL TIER DEFINITIONS
121
+ // ============================================================================
122
+ exports.TIER_CONFIG = {
123
+ free: {
124
+ id: 'free',
125
+ name: 'Free',
126
+ price: 0,
127
+ annualPrice: 0,
128
+ description: 'Get started',
129
+ features: [
130
+ 'scan',
131
+ 'gate',
132
+ 'ship',
133
+ 'context',
134
+ 'badge',
135
+ ],
136
+ limits: {
137
+ scansPerMonth: 10,
138
+ realityRunsPerMonth: 0,
139
+ aiAgentRunsPerMonth: 0,
140
+ projects: 1,
141
+ teamMembers: 1,
142
+ complianceFrameworks: 0,
143
+ },
144
+ rateLimit: {
145
+ requestsPerMinute: 100,
146
+ burstLimit: 150,
147
+ windowMs: 60 * 1000,
148
+ },
149
+ upsell: {
150
+ message: 'Upgrade to Starter for Reality Mode browser testing and 100 scans/month',
151
+ nextTier: 'starter',
152
+ },
153
+ },
154
+ starter: {
155
+ id: 'starter',
156
+ name: 'Starter',
157
+ price: 29,
158
+ annualPrice: 278, // 20% off: 29 * 12 * 0.8 = 278.40 → 278
159
+ description: 'For solo devs',
160
+ features: [
161
+ 'scan',
162
+ 'scan:full',
163
+ 'gate',
164
+ 'fix',
165
+ 'ship',
166
+ 'reality',
167
+ 'context',
168
+ 'badge',
169
+ 'reports:html',
170
+ ],
171
+ limits: {
172
+ scansPerMonth: 100,
173
+ realityRunsPerMonth: 20,
174
+ aiAgentRunsPerMonth: 0,
175
+ projects: 3,
176
+ teamMembers: 1,
177
+ complianceFrameworks: 0,
178
+ },
179
+ rateLimit: {
180
+ requestsPerMinute: 300,
181
+ burstLimit: 450,
182
+ windowMs: 60 * 1000,
183
+ },
184
+ upsell: {
185
+ message: 'Upgrade to Pro for AI Agent testing, auto-fix, and Autopilot protection',
186
+ nextTier: 'pro',
187
+ },
188
+ },
189
+ pro: {
190
+ id: 'pro',
191
+ name: 'Pro',
192
+ price: 99,
193
+ annualPrice: 950, // 20% off: 99 * 12 * 0.8 = 950.40 → 950
194
+ description: 'Full automation',
195
+ features: [
196
+ 'scan',
197
+ 'scan:full',
198
+ 'scan:security',
199
+ 'gate',
200
+ 'fix',
201
+ 'fix:auto',
202
+ 'ship',
203
+ 'reality',
204
+ 'reality:flows',
205
+ 'ai-agent',
206
+ 'ai-agent:goals',
207
+ 'autopilot',
208
+ 'context',
209
+ 'badge',
210
+ 'mcp',
211
+ 'reports:html',
212
+ 'reports:sarif',
213
+ 'api:access',
214
+ 'webhooks',
215
+ ],
216
+ limits: {
217
+ scansPerMonth: 500,
218
+ realityRunsPerMonth: 100,
219
+ aiAgentRunsPerMonth: 50,
220
+ projects: 10,
221
+ teamMembers: 5,
222
+ complianceFrameworks: 0,
223
+ },
224
+ rateLimit: {
225
+ requestsPerMinute: 1000,
226
+ burstLimit: 1500,
227
+ windowMs: 60 * 1000,
228
+ },
229
+ upsell: {
230
+ message: 'Upgrade to Compliance tier for SOC2, HIPAA, GDPR frameworks',
231
+ nextTier: 'compliance',
232
+ },
233
+ },
234
+ compliance: {
235
+ id: 'compliance',
236
+ name: 'Compliance',
237
+ price: 199,
238
+ annualPrice: 1910, // 20% off: 199 * 12 * 0.8 = 1910.40 → 1910
239
+ description: 'Enterprise ready',
240
+ features: [
241
+ 'scan',
242
+ 'scan:full',
243
+ 'scan:security',
244
+ 'scan:compliance',
245
+ 'gate',
246
+ 'fix',
247
+ 'fix:auto',
248
+ 'ship',
249
+ 'reality',
250
+ 'reality:flows',
251
+ 'ai-agent',
252
+ 'ai-agent:goals',
253
+ 'autopilot',
254
+ 'context',
255
+ 'badge',
256
+ 'mcp',
257
+ 'compliance:soc2',
258
+ 'compliance:hipaa',
259
+ 'compliance:gdpr',
260
+ 'compliance:pci',
261
+ 'compliance:nist',
262
+ 'compliance:iso27001',
263
+ 'reports:html',
264
+ 'reports:pdf',
265
+ 'reports:sarif',
266
+ 'api:access',
267
+ 'webhooks',
268
+ 'deploy-hooks',
269
+ ],
270
+ limits: {
271
+ scansPerMonth: 1000,
272
+ realityRunsPerMonth: 200,
273
+ aiAgentRunsPerMonth: 100,
274
+ projects: 25,
275
+ teamMembers: 10,
276
+ complianceFrameworks: 6,
277
+ },
278
+ rateLimit: {
279
+ requestsPerMinute: 2000,
280
+ burstLimit: 3000,
281
+ windowMs: 60 * 1000,
282
+ },
283
+ upsell: {
284
+ message: 'Contact sales for Enterprise with unlimited usage and dedicated support',
285
+ nextTier: 'enterprise',
286
+ },
287
+ },
288
+ enterprise: {
289
+ id: 'enterprise',
290
+ name: 'Enterprise',
291
+ price: 499,
292
+ annualPrice: 4790, // 20% off: 499 * 12 * 0.8 = 4790.40 → 4790
293
+ description: 'Custom solutions',
294
+ features: [
295
+ 'scan',
296
+ 'scan:full',
297
+ 'scan:security',
298
+ 'scan:compliance',
299
+ 'gate',
300
+ 'fix',
301
+ 'fix:auto',
302
+ 'ship',
303
+ 'reality',
304
+ 'reality:flows',
305
+ 'ai-agent',
306
+ 'ai-agent:goals',
307
+ 'autopilot',
308
+ 'context',
309
+ 'badge',
310
+ 'mcp',
311
+ 'compliance:soc2',
312
+ 'compliance:hipaa',
313
+ 'compliance:gdpr',
314
+ 'compliance:pci',
315
+ 'compliance:nist',
316
+ 'compliance:iso27001',
317
+ 'reports:html',
318
+ 'reports:pdf',
319
+ 'reports:sarif',
320
+ 'api:access',
321
+ 'webhooks',
322
+ 'deploy-hooks',
323
+ 'team:members',
324
+ 'team:admin',
325
+ ],
326
+ limits: {
327
+ scansPerMonth: 5000,
328
+ realityRunsPerMonth: 1000,
329
+ aiAgentRunsPerMonth: 500,
330
+ projects: 100,
331
+ teamMembers: 50,
332
+ complianceFrameworks: 6,
333
+ },
334
+ rateLimit: {
335
+ requestsPerMinute: 10000,
336
+ burstLimit: 15000,
337
+ windowMs: 60 * 1000,
338
+ },
339
+ upsell: {
340
+ message: 'You have our top tier! Contact support for custom requirements.',
341
+ nextTier: 'unlimited',
342
+ },
343
+ },
344
+ unlimited: {
345
+ id: 'unlimited',
346
+ name: 'Unlimited',
347
+ price: 0,
348
+ annualPrice: 0,
349
+ description: 'Internal/Special',
350
+ features: exports.FEATURES,
351
+ limits: {
352
+ scansPerMonth: -1, // Unlimited
353
+ realityRunsPerMonth: -1,
354
+ aiAgentRunsPerMonth: -1,
355
+ projects: -1,
356
+ teamMembers: -1,
357
+ complianceFrameworks: 6,
358
+ },
359
+ rateLimit: {
360
+ requestsPerMinute: 100000,
361
+ burstLimit: 150000,
362
+ windowMs: 60 * 1000,
363
+ },
364
+ upsell: {
365
+ message: 'You have unlimited access!',
366
+ nextTier: 'unlimited',
367
+ },
368
+ },
369
+ };
370
+ // ============================================================================
371
+ // HELPER FUNCTIONS
372
+ // ============================================================================
373
+ /**
374
+ * Check if a tier string is valid
375
+ */
376
+ function isValidTier(tier) {
377
+ return exports.TIERS.includes(tier);
378
+ }
379
+ /**
380
+ * Get tier config by tier name
381
+ */
382
+ function getTierConfig(tier) {
383
+ return exports.TIER_CONFIG[tier];
384
+ }
385
+ /**
386
+ * Get all tier configs as array (useful for iteration)
387
+ */
388
+ function getAllTierConfigs() {
389
+ return exports.TIER_ORDER.map(tier => exports.TIER_CONFIG[tier]);
390
+ }
391
+ /**
392
+ * Get purchasable tier configs (for pricing page)
393
+ */
394
+ function getPurchasableTierConfigs() {
395
+ return exports.PURCHASABLE_TIERS.map(tier => exports.TIER_CONFIG[tier]);
396
+ }
397
+ /**
398
+ * Compare two tiers (-1 if a < b, 0 if equal, 1 if a > b)
399
+ */
400
+ function compareTiers(a, b) {
401
+ const indexA = exports.TIER_ORDER.indexOf(a);
402
+ const indexB = exports.TIER_ORDER.indexOf(b);
403
+ return indexA - indexB;
404
+ }
405
+ /**
406
+ * Check if tier A is higher than tier B
407
+ */
408
+ function isTierHigher(a, b) {
409
+ return compareTiers(a, b) > 0;
410
+ }
411
+ /**
412
+ * Find the minimum tier that has a specific feature
413
+ */
414
+ function getMinimumTierForFeature(feature) {
415
+ for (const tier of exports.TIER_ORDER) {
416
+ if (exports.TIER_CONFIG[tier].features.includes(feature)) {
417
+ return tier;
418
+ }
419
+ }
420
+ return null;
421
+ }
422
+ /**
423
+ * Check if a tier has a specific feature
424
+ */
425
+ function tierHasFeature(tier, feature) {
426
+ const config = exports.TIER_CONFIG[tier];
427
+ // Unlimited tier has all features
428
+ if (tier === 'unlimited')
429
+ return true;
430
+ return config.features.includes(feature);
431
+ }
432
+ /**
433
+ * Get limit value, handling -1 as Infinity
434
+ */
435
+ function getEffectiveLimit(limit) {
436
+ return limit === -1 ? Infinity : limit;
437
+ }
438
+ /**
439
+ * Format limit for display
440
+ */
441
+ function formatLimit(limit) {
442
+ return limit === -1 ? 'Unlimited' : limit.toLocaleString();
443
+ }
444
+ /**
445
+ * Get Stripe price ID for a tier (from environment)
446
+ */
447
+ function getStripePriceId(tier, interval) {
448
+ const envKey = interval === 'year'
449
+ ? `STRIPE_PRICE_${tier.toUpperCase()}_ANNUAL`
450
+ : `STRIPE_PRICE_${tier.toUpperCase()}_MONTHLY`;
451
+ return process.env[envKey] || process.env[`STRIPE_PRICE_ID_${tier.toUpperCase()}`];
452
+ }
453
+ /**
454
+ * Map Stripe price ID back to tier
455
+ */
456
+ function getTierFromStripePriceId(priceId) {
457
+ for (const tier of exports.PURCHASABLE_TIERS) {
458
+ const monthlyId = getStripePriceId(tier, 'month');
459
+ const annualId = getStripePriceId(tier, 'year');
460
+ if (priceId === monthlyId || priceId === annualId) {
461
+ return tier;
462
+ }
463
+ }
464
+ return 'free';
465
+ }
466
+ // ============================================================================
467
+ // PRICING PAGE HELPERS
468
+ // ============================================================================
469
+ /**
470
+ * Get pricing tiers formatted for landing page display
471
+ */
472
+ function getPricingPageTiers() {
473
+ return [
474
+ {
475
+ id: 'free',
476
+ name: exports.TIER_CONFIG.free.name,
477
+ price: exports.TIER_CONFIG.free.price,
478
+ annual: exports.TIER_CONFIG.free.annualPrice,
479
+ description: exports.TIER_CONFIG.free.description,
480
+ popular: false,
481
+ features: [
482
+ 'Static code analysis',
483
+ 'AI code validation',
484
+ 'Ship badge generator',
485
+ `${formatLimit(exports.TIER_CONFIG.free.limits.scansPerMonth)} scans/month`,
486
+ ],
487
+ },
488
+ {
489
+ id: 'starter',
490
+ name: exports.TIER_CONFIG.starter.name,
491
+ price: exports.TIER_CONFIG.starter.price,
492
+ annual: exports.TIER_CONFIG.starter.annualPrice,
493
+ description: exports.TIER_CONFIG.starter.description,
494
+ popular: false,
495
+ features: [
496
+ 'Everything in Free, plus:',
497
+ 'Reality Mode browser testing',
498
+ 'CI/CD deploy blocking',
499
+ 'Mock detection',
500
+ `${formatLimit(exports.TIER_CONFIG.starter.limits.scansPerMonth)} scans, ${formatLimit(exports.TIER_CONFIG.starter.limits.realityRunsPerMonth)} Reality runs`,
501
+ ],
502
+ },
503
+ {
504
+ id: 'pro',
505
+ name: exports.TIER_CONFIG.pro.name,
506
+ price: exports.TIER_CONFIG.pro.price,
507
+ annual: exports.TIER_CONFIG.pro.annualPrice,
508
+ description: exports.TIER_CONFIG.pro.description,
509
+ popular: true,
510
+ features: [
511
+ 'Everything in Starter, plus:',
512
+ 'AI Agent autonomous testing',
513
+ 'Auto-fix with generated prompts',
514
+ 'Autopilot continuous protection',
515
+ 'MCP plugin for your IDE',
516
+ `${formatLimit(exports.TIER_CONFIG.pro.limits.realityRunsPerMonth)} Reality, ${formatLimit(exports.TIER_CONFIG.pro.limits.aiAgentRunsPerMonth)} AI Agent runs`,
517
+ ],
518
+ },
519
+ {
520
+ id: 'compliance',
521
+ name: exports.TIER_CONFIG.compliance.name,
522
+ price: exports.TIER_CONFIG.compliance.price,
523
+ annual: exports.TIER_CONFIG.compliance.annualPrice,
524
+ description: exports.TIER_CONFIG.compliance.description,
525
+ popular: false,
526
+ features: [
527
+ 'Everything in Pro, plus:',
528
+ 'SOC2, HIPAA, GDPR, PCI-DSS',
529
+ 'NIST and ISO 27001 frameworks',
530
+ 'Audit-ready PDF reports',
531
+ `${formatLimit(exports.TIER_CONFIG.compliance.limits.realityRunsPerMonth)} Reality, ${formatLimit(exports.TIER_CONFIG.compliance.limits.aiAgentRunsPerMonth)} AI Agent runs`,
532
+ ],
533
+ },
534
+ ];
535
+ }
536
+ // ============================================================================
537
+ // RATE LIMIT HELPERS
538
+ // ============================================================================
539
+ /**
540
+ * Get rate limit config for a tier (for rate-limiter middleware)
541
+ */
542
+ function getRateLimitForTier(tier) {
543
+ return exports.TIER_CONFIG[tier].rateLimit;
544
+ }
545
+ /**
546
+ * Get user tiers formatted for rate limiter
547
+ */
548
+ function getRateLimiterTiers() {
549
+ const result = {};
550
+ for (const tier of exports.TIERS) {
551
+ const config = exports.TIER_CONFIG[tier];
552
+ result[tier] = {
553
+ name: config.name,
554
+ baseLimit: config.rateLimit.requestsPerMinute,
555
+ burstLimit: config.rateLimit.burstLimit,
556
+ windowMs: config.rateLimit.windowMs,
557
+ };
558
+ }
559
+ return result;
560
+ }
561
+ // ============================================================================
562
+ // SEAT MANAGEMENT HELPERS
563
+ // ============================================================================
564
+ /**
565
+ * Get seat pricing for a tier
566
+ */
567
+ function getSeatPricing(tier) {
568
+ return exports.SEAT_PRICING[tier];
569
+ }
570
+ /**
571
+ * Calculate effective team seats (base + purchased extras)
572
+ */
573
+ function calculateEffectiveSeats(tier, purchasedExtraSeats) {
574
+ const baseSeats = exports.TIER_CONFIG[tier].limits.teamMembers;
575
+ const seatConfig = exports.SEAT_PRICING[tier];
576
+ // If tier doesn't support additional seats, return base only
577
+ if (!seatConfig.supportsAdditionalSeats) {
578
+ return baseSeats === -1 ? Infinity : baseSeats;
579
+ }
580
+ // Handle unlimited base seats
581
+ if (baseSeats === -1) {
582
+ return Infinity;
583
+ }
584
+ // Cap purchased seats at max allowed (if not unlimited)
585
+ let effectiveExtras = purchasedExtraSeats;
586
+ if (seatConfig.maxAdditionalSeats !== -1) {
587
+ effectiveExtras = Math.min(purchasedExtraSeats, seatConfig.maxAdditionalSeats);
588
+ }
589
+ return baseSeats + effectiveExtras;
590
+ }
591
+ /**
592
+ * Check if a member can be added given current seats and effective limit
593
+ */
594
+ function canAddMember(tier, currentMemberCount, purchasedExtraSeats) {
595
+ const effectiveSeats = calculateEffectiveSeats(tier, purchasedExtraSeats);
596
+ if (effectiveSeats === Infinity) {
597
+ return { allowed: true, effectiveSeats };
598
+ }
599
+ if (currentMemberCount >= effectiveSeats) {
600
+ const seatConfig = exports.SEAT_PRICING[tier];
601
+ const canPurchaseMore = seatConfig.supportsAdditionalSeats &&
602
+ (seatConfig.maxAdditionalSeats === -1 || purchasedExtraSeats < seatConfig.maxAdditionalSeats);
603
+ return {
604
+ allowed: false,
605
+ reason: canPurchaseMore
606
+ ? `Seat limit reached (${currentMemberCount}/${effectiveSeats}). Purchase additional seats at $${seatConfig.monthlyPricePerSeat}/seat/month.`
607
+ : `Seat limit reached (${currentMemberCount}/${effectiveSeats}). Upgrade to a higher tier for more seats.`,
608
+ effectiveSeats,
609
+ };
610
+ }
611
+ return { allowed: true, effectiveSeats };
612
+ }
613
+ /**
614
+ * Calculate cost for additional seats
615
+ */
616
+ function calculateSeatCost(tier, additionalSeats, billingInterval) {
617
+ const seatConfig = exports.SEAT_PRICING[tier];
618
+ if (!seatConfig.supportsAdditionalSeats) {
619
+ return { total: 0, perSeat: 0, supported: false };
620
+ }
621
+ const perSeat = billingInterval === 'year'
622
+ ? seatConfig.annualPricePerSeat
623
+ : seatConfig.monthlyPricePerSeat;
624
+ return {
625
+ total: perSeat * additionalSeats,
626
+ perSeat,
627
+ supported: true,
628
+ };
629
+ }
630
+ /**
631
+ * Get Stripe seat price ID for a tier
632
+ */
633
+ function getStripeSeatPriceId(tier, interval) {
634
+ const envKey = interval === 'year'
635
+ ? `STRIPE_SEAT_PRICE_${tier.toUpperCase()}_ANNUAL`
636
+ : `STRIPE_SEAT_PRICE_${tier.toUpperCase()}_MONTHLY`;
637
+ return process.env[envKey];
638
+ }
639
+ /**
640
+ * Format seat info for display
641
+ */
642
+ function formatSeatInfo(tier) {
643
+ const config = exports.TIER_CONFIG[tier];
644
+ const seatConfig = exports.SEAT_PRICING[tier];
645
+ const baseSeats = config.limits.teamMembers;
646
+ if (baseSeats === -1) {
647
+ return 'Unlimited team members';
648
+ }
649
+ if (!seatConfig.supportsAdditionalSeats) {
650
+ return `${baseSeats} team member${baseSeats !== 1 ? 's' : ''}`;
651
+ }
652
+ return `${baseSeats} seats included, +$${seatConfig.monthlyPricePerSeat}/seat/mo`;
653
+ }
654
+ /**
655
+ * Validate seat reduction (graceful handling)
656
+ * Returns info about whether reduction is safe or requires admin action
657
+ */
658
+ function validateSeatReduction(currentMemberCount, _currentEffectiveSeats, // Kept for API compatibility, may be used for logging
659
+ newEffectiveSeats) {
660
+ if (newEffectiveSeats >= currentMemberCount) {
661
+ return {
662
+ safe: true,
663
+ requiresAction: false,
664
+ excessMembers: 0,
665
+ message: 'Seat reduction is safe.',
666
+ };
667
+ }
668
+ const excessMembers = currentMemberCount - newEffectiveSeats;
669
+ return {
670
+ safe: false,
671
+ requiresAction: true,
672
+ excessMembers,
673
+ message: `Cannot reduce seats: ${excessMembers} member(s) would exceed the new limit. Remove members before reducing seats.`,
674
+ };
675
+ }