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.
- package/dist/__tests__/autopilot.test.d.ts +7 -0
- package/dist/__tests__/autopilot.test.d.ts.map +1 -0
- package/dist/__tests__/autopilot.test.js +156 -0
- package/dist/__tests__/tier-config.test.d.ts +9 -0
- package/dist/__tests__/tier-config.test.d.ts.map +1 -0
- package/dist/__tests__/tier-config.test.js +230 -0
- package/dist/__tests__/utils/hash-inline.test.d.ts +2 -0
- package/dist/__tests__/utils/hash-inline.test.d.ts.map +1 -0
- package/dist/__tests__/utils/hash-inline.test.js +62 -0
- package/dist/__tests__/utils/hash.test.d.ts +3 -0
- package/dist/__tests__/utils/hash.test.d.ts.map +1 -0
- package/dist/__tests__/utils/hash.test.js +95 -0
- package/dist/__tests__/utils/simple.test.d.ts +1 -0
- package/dist/__tests__/utils/simple.test.d.ts.map +1 -0
- package/dist/__tests__/utils/simple.test.js +10 -0
- package/dist/__tests__/utils/utils-simple.test.d.ts +1 -0
- package/dist/__tests__/utils/utils-simple.test.d.ts.map +1 -0
- package/dist/__tests__/utils/utils-simple.test.js +6 -0
- package/dist/__tests__/utils/utils.test.d.ts +15 -0
- package/dist/__tests__/utils/utils.test.d.ts.map +1 -0
- package/dist/__tests__/utils/utils.test.js +172 -0
- package/dist/autopilot/autopilot-runner.d.ts +33 -0
- package/dist/autopilot/autopilot-runner.d.ts.map +1 -0
- package/dist/autopilot/autopilot-runner.js +479 -0
- package/dist/autopilot/index.d.ts +6 -0
- package/dist/autopilot/index.d.ts.map +1 -0
- package/dist/autopilot/index.js +25 -0
- package/dist/autopilot/types.d.ts +102 -0
- package/dist/autopilot/types.d.ts.map +1 -0
- package/dist/autopilot/types.js +18 -0
- package/dist/cache/index.d.ts +7 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +22 -0
- package/dist/cache/redis-cache.d.ts +145 -0
- package/dist/cache/redis-cache.d.ts.map +1 -0
- package/dist/cache/redis-cache.js +459 -0
- package/dist/ci/github-actions.d.ts +77 -0
- package/dist/ci/github-actions.d.ts.map +1 -0
- package/dist/ci/github-actions.js +277 -0
- package/dist/ci/index.d.ts +12 -0
- package/dist/ci/index.d.ts.map +1 -0
- package/dist/ci/index.js +27 -0
- package/dist/ci/pre-commit.d.ts +65 -0
- package/dist/ci/pre-commit.d.ts.map +1 -0
- package/dist/ci/pre-commit.js +286 -0
- package/dist/entitlements.d.ts +149 -0
- package/dist/entitlements.d.ts.map +1 -0
- package/dist/entitlements.js +464 -0
- package/dist/env.d.ts +113 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +204 -0
- package/dist/fix-packs/__tests__/generate-fix-packs.test.d.ts +7 -0
- package/dist/fix-packs/__tests__/generate-fix-packs.test.d.ts.map +1 -0
- package/dist/fix-packs/__tests__/generate-fix-packs.test.js +250 -0
- package/dist/fix-packs/generate-fix-packs.d.ts +15 -0
- package/dist/fix-packs/generate-fix-packs.d.ts.map +1 -0
- package/dist/fix-packs/generate-fix-packs.js +505 -0
- package/dist/fix-packs/index.d.ts +8 -0
- package/dist/fix-packs/index.d.ts.map +1 -0
- package/dist/fix-packs/index.js +23 -0
- package/dist/fix-packs/types.d.ts +113 -0
- package/dist/fix-packs/types.d.ts.map +1 -0
- package/dist/fix-packs/types.js +71 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/metrics/prometheus.d.ts +99 -0
- package/dist/metrics/prometheus.d.ts.map +1 -0
- package/dist/metrics/prometheus.js +306 -0
- package/dist/quota-ledger.d.ts +119 -0
- package/dist/quota-ledger.d.ts.map +1 -0
- package/dist/quota-ledger.js +462 -0
- package/dist/rbac/__tests__/permissions.test.d.ts +8 -0
- package/dist/rbac/__tests__/permissions.test.d.ts.map +1 -0
- package/dist/rbac/__tests__/permissions.test.js +350 -0
- package/dist/rbac/index.d.ts +9 -0
- package/dist/rbac/index.d.ts.map +1 -0
- package/dist/rbac/index.js +32 -0
- package/dist/rbac/permissions.d.ts +71 -0
- package/dist/rbac/permissions.d.ts.map +1 -0
- package/dist/rbac/permissions.js +247 -0
- package/dist/rbac/types.d.ts +69 -0
- package/dist/rbac/types.d.ts.map +1 -0
- package/dist/rbac/types.js +213 -0
- package/dist/tier-config.d.ts +203 -0
- package/dist/tier-config.d.ts.map +1 -0
- package/dist/tier-config.js +675 -0
- package/dist/types.d.ts +365 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/utils.d.ts +36 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +127 -0
- package/dist/verified-autofix/__tests__/format-validator.test.d.ts +11 -0
- package/dist/verified-autofix/__tests__/format-validator.test.d.ts.map +1 -0
- package/dist/verified-autofix/__tests__/format-validator.test.js +285 -0
- package/dist/verified-autofix/__tests__/pipeline.test.d.ts +11 -0
- package/dist/verified-autofix/__tests__/pipeline.test.d.ts.map +1 -0
- package/dist/verified-autofix/__tests__/pipeline.test.js +389 -0
- package/dist/verified-autofix/__tests__/repo-fingerprint.test.d.ts +11 -0
- package/dist/verified-autofix/__tests__/repo-fingerprint.test.d.ts.map +1 -0
- package/dist/verified-autofix/__tests__/repo-fingerprint.test.js +236 -0
- package/dist/verified-autofix/__tests__/workspace.test.d.ts +11 -0
- package/dist/verified-autofix/__tests__/workspace.test.d.ts.map +1 -0
- package/dist/verified-autofix/__tests__/workspace.test.js +314 -0
- package/dist/verified-autofix/format-validator.d.ts +101 -0
- package/dist/verified-autofix/format-validator.d.ts.map +1 -0
- package/dist/verified-autofix/format-validator.js +446 -0
- package/dist/verified-autofix/index.d.ts +14 -0
- package/dist/verified-autofix/index.d.ts.map +1 -0
- package/dist/verified-autofix/index.js +39 -0
- package/dist/verified-autofix/pipeline.d.ts +68 -0
- package/dist/verified-autofix/pipeline.d.ts.map +1 -0
- package/dist/verified-autofix/pipeline.js +330 -0
- package/dist/verified-autofix/repo-fingerprint.d.ts +56 -0
- package/dist/verified-autofix/repo-fingerprint.d.ts.map +1 -0
- package/dist/verified-autofix/repo-fingerprint.js +396 -0
- package/dist/verified-autofix/workspace.d.ts +83 -0
- package/dist/verified-autofix/workspace.d.ts.map +1 -0
- package/dist/verified-autofix/workspace.js +454 -0
- package/dist/verified-autofix.d.ts +182 -0
- package/dist/verified-autofix.d.ts.map +1 -0
- package/dist/verified-autofix.js +1021 -0
- package/dist/visualization/dependency-graph.d.ts +79 -0
- package/dist/visualization/dependency-graph.d.ts.map +1 -0
- package/dist/visualization/dependency-graph.js +399 -0
- package/dist/visualization/index.d.ts +5 -0
- package/dist/visualization/index.d.ts.map +1 -0
- package/dist/visualization/index.js +20 -0
- package/package.json +29 -0
- package/src/__tests__/autopilot.test.ts +196 -0
- package/src/__tests__/tier-config.test.ts +289 -0
- package/src/__tests__/utils/hash-inline.test.ts +76 -0
- package/src/__tests__/utils/hash.test.ts +119 -0
- package/src/__tests__/utils/simple.test.ts +10 -0
- package/src/__tests__/utils/utils-simple.test.ts +5 -0
- package/src/__tests__/utils/utils.test.ts +203 -0
- package/src/autopilot/autopilot-runner.ts +503 -0
- package/src/autopilot/index.ts +6 -0
- package/src/autopilot/types.ts +119 -0
- package/src/cache/index.ts +7 -0
- package/src/cache/redis-cache.d.ts +155 -0
- package/src/cache/redis-cache.d.ts.map +1 -0
- package/src/cache/redis-cache.ts +517 -0
- package/src/ci/github-actions.ts +335 -0
- package/src/ci/index.ts +12 -0
- package/src/ci/pre-commit.ts +338 -0
- package/src/db/usage-schema.prisma +114 -0
- package/src/entitlements.ts +570 -0
- package/src/env.d.ts +68 -0
- package/src/env.d.ts.map +1 -0
- package/src/env.ts +247 -0
- package/src/fix-packs/__tests__/generate-fix-packs.test.ts +317 -0
- package/src/fix-packs/generate-fix-packs.ts +577 -0
- package/src/fix-packs/index.ts +8 -0
- package/src/fix-packs/types.ts +206 -0
- package/src/index.d.ts +7 -0
- package/src/index.d.ts.map +1 -0
- package/src/index.ts +12 -0
- package/src/metrics/prometheus.d.ts +104 -0
- package/src/metrics/prometheus.d.ts.map +1 -0
- package/src/metrics/prometheus.ts +446 -0
- package/src/quota-ledger.ts +548 -0
- package/src/rbac/__tests__/permissions.test.ts +446 -0
- package/src/rbac/index.ts +46 -0
- package/src/rbac/permissions.ts +301 -0
- package/src/rbac/types.ts +298 -0
- package/src/tier-config.json +157 -0
- package/src/tier-config.ts +815 -0
- package/src/types.d.ts +365 -0
- package/src/types.d.ts.map +1 -0
- package/src/types.ts +441 -0
- package/src/utils.d.ts +36 -0
- package/src/utils.d.ts.map +1 -0
- package/src/utils.ts +140 -0
- package/src/verified-autofix/__tests__/format-validator.test.ts +335 -0
- package/src/verified-autofix/__tests__/pipeline.test.ts +419 -0
- package/src/verified-autofix/__tests__/repo-fingerprint.test.ts +241 -0
- package/src/verified-autofix/__tests__/workspace.test.ts +373 -0
- package/src/verified-autofix/format-validator.ts +517 -0
- package/src/verified-autofix/index.ts +63 -0
- package/src/verified-autofix/pipeline.ts +403 -0
- package/src/verified-autofix/repo-fingerprint.ts +459 -0
- package/src/verified-autofix/workspace.ts +531 -0
- package/src/verified-autofix.ts +1187 -0
- package/src/visualization/dependency-graph.d.ts +85 -0
- package/src/visualization/dependency-graph.d.ts.map +1 -0
- package/src/visualization/dependency-graph.ts +495 -0
- 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
|
+
}
|