@ucptools/validator 1.0.1 → 1.2.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 (162) hide show
  1. package/dist/auth/config.d.ts +20 -0
  2. package/dist/auth/config.d.ts.map +1 -0
  3. package/dist/auth/config.js +114 -0
  4. package/dist/auth/config.js.map +1 -0
  5. package/dist/auth/index.d.ts +5 -0
  6. package/dist/auth/index.d.ts.map +1 -0
  7. package/dist/auth/index.js +17 -0
  8. package/dist/auth/index.js.map +1 -0
  9. package/dist/auth/middleware.d.ts +45 -0
  10. package/dist/auth/middleware.d.ts.map +1 -0
  11. package/dist/auth/middleware.js +170 -0
  12. package/dist/auth/middleware.js.map +1 -0
  13. package/dist/auth/service.d.ts +80 -0
  14. package/dist/auth/service.d.ts.map +1 -0
  15. package/dist/auth/service.js +298 -0
  16. package/dist/auth/service.js.map +1 -0
  17. package/dist/cli/index.js +96 -0
  18. package/dist/cli/index.js.map +1 -1
  19. package/dist/cli/mock-server.d.ts +20 -0
  20. package/dist/cli/mock-server.d.ts.map +1 -0
  21. package/dist/cli/mock-server.js +261 -0
  22. package/dist/cli/mock-server.js.map +1 -0
  23. package/dist/db/index.d.ts +8 -2
  24. package/dist/db/index.d.ts.map +1 -1
  25. package/dist/db/index.js +22 -5
  26. package/dist/db/index.js.map +1 -1
  27. package/dist/db/schema.d.ts +3570 -128
  28. package/dist/db/schema.d.ts.map +1 -1
  29. package/dist/db/schema.js +377 -17
  30. package/dist/db/schema.js.map +1 -1
  31. package/dist/db/utils.d.ts +252 -0
  32. package/dist/db/utils.d.ts.map +1 -0
  33. package/dist/db/utils.js +295 -0
  34. package/dist/db/utils.js.map +1 -0
  35. package/dist/feed-analyzer/feed-analyzer.d.ts.map +1 -1
  36. package/dist/feed-analyzer/feed-analyzer.js +218 -4
  37. package/dist/feed-analyzer/feed-analyzer.js.map +1 -1
  38. package/dist/feed-analyzer/types.d.ts +82 -1
  39. package/dist/feed-analyzer/types.d.ts.map +1 -1
  40. package/dist/feed-analyzer/types.js +13 -0
  41. package/dist/feed-analyzer/types.js.map +1 -1
  42. package/dist/generator/profile-builder.d.ts.map +1 -1
  43. package/dist/generator/profile-builder.js +158 -115
  44. package/dist/generator/profile-builder.js.map +1 -1
  45. package/dist/lib/analytics.d.ts +349 -0
  46. package/dist/lib/analytics.d.ts.map +1 -0
  47. package/dist/lib/analytics.js +198 -0
  48. package/dist/lib/analytics.js.map +1 -0
  49. package/dist/security/security-scanner.d.ts.map +1 -1
  50. package/dist/security/security-scanner.js +130 -2
  51. package/dist/security/security-scanner.js.map +1 -1
  52. package/dist/security/types.d.ts +32 -0
  53. package/dist/security/types.d.ts.map +1 -1
  54. package/dist/security/types.js.map +1 -1
  55. package/dist/services/analytics.d.ts +114 -0
  56. package/dist/services/analytics.d.ts.map +1 -0
  57. package/dist/services/analytics.js +862 -0
  58. package/dist/services/analytics.js.map +1 -0
  59. package/dist/services/badge.d.ts +31 -0
  60. package/dist/services/badge.d.ts.map +1 -0
  61. package/dist/services/badge.js +152 -0
  62. package/dist/services/badge.js.map +1 -0
  63. package/dist/services/cron.d.ts +127 -0
  64. package/dist/services/cron.d.ts.map +1 -0
  65. package/dist/services/cron.js +693 -0
  66. package/dist/services/cron.js.map +1 -0
  67. package/dist/services/directory.d.ts +2 -0
  68. package/dist/services/directory.d.ts.map +1 -1
  69. package/dist/services/directory.js +45 -27
  70. package/dist/services/directory.js.map +1 -1
  71. package/dist/services/email.d.ts +127 -0
  72. package/dist/services/email.d.ts.map +1 -0
  73. package/dist/services/email.js +876 -0
  74. package/dist/services/email.js.map +1 -0
  75. package/dist/services/hosted-profiles.d.ts +77 -0
  76. package/dist/services/hosted-profiles.d.ts.map +1 -0
  77. package/dist/services/hosted-profiles.js +433 -0
  78. package/dist/services/hosted-profiles.js.map +1 -0
  79. package/dist/services/latency.d.ts +67 -0
  80. package/dist/services/latency.d.ts.map +1 -0
  81. package/dist/services/latency.js +274 -0
  82. package/dist/services/latency.js.map +1 -0
  83. package/dist/services/manifest-compliance.d.ts +64 -0
  84. package/dist/services/manifest-compliance.d.ts.map +1 -0
  85. package/dist/services/manifest-compliance.js +271 -0
  86. package/dist/services/manifest-compliance.js.map +1 -0
  87. package/dist/services/monitoring-diff.d.ts +31 -0
  88. package/dist/services/monitoring-diff.d.ts.map +1 -0
  89. package/dist/services/monitoring-diff.js +189 -0
  90. package/dist/services/monitoring-diff.js.map +1 -0
  91. package/dist/services/notifications.d.ts +46 -0
  92. package/dist/services/notifications.d.ts.map +1 -0
  93. package/dist/services/notifications.js +88 -0
  94. package/dist/services/notifications.js.map +1 -0
  95. package/dist/services/posthog.d.ts +43 -0
  96. package/dist/services/posthog.d.ts.map +1 -0
  97. package/dist/services/posthog.js +110 -0
  98. package/dist/services/posthog.js.map +1 -0
  99. package/dist/services/stripe.d.ts +93 -0
  100. package/dist/services/stripe.d.ts.map +1 -0
  101. package/dist/services/stripe.js +490 -0
  102. package/dist/services/stripe.js.map +1 -0
  103. package/dist/services/validation-history.d.ts +99 -0
  104. package/dist/services/validation-history.d.ts.map +1 -0
  105. package/dist/services/validation-history.js +344 -0
  106. package/dist/services/validation-history.js.map +1 -0
  107. package/dist/services/validation-logging.d.ts +103 -0
  108. package/dist/services/validation-logging.d.ts.map +1 -0
  109. package/dist/services/validation-logging.js +210 -0
  110. package/dist/services/validation-logging.js.map +1 -0
  111. package/dist/services/validation.d.ts +119 -0
  112. package/dist/services/validation.d.ts.map +1 -0
  113. package/dist/services/validation.js +1185 -0
  114. package/dist/services/validation.js.map +1 -0
  115. package/dist/simulator/agent-simulator.d.ts.map +1 -1
  116. package/dist/simulator/agent-simulator.js +229 -9
  117. package/dist/simulator/agent-simulator.js.map +1 -1
  118. package/dist/simulator/types.d.ts +26 -0
  119. package/dist/simulator/types.d.ts.map +1 -1
  120. package/dist/simulator/types.js.map +1 -1
  121. package/dist/types/acp-validation.d.ts +87 -0
  122. package/dist/types/acp-validation.d.ts.map +1 -0
  123. package/dist/types/acp-validation.js +40 -0
  124. package/dist/types/acp-validation.js.map +1 -0
  125. package/dist/types/analytics.d.ts +182 -0
  126. package/dist/types/analytics.d.ts.map +1 -0
  127. package/dist/types/analytics.js +7 -0
  128. package/dist/types/analytics.js.map +1 -0
  129. package/dist/types/generator.d.ts +4 -0
  130. package/dist/types/generator.d.ts.map +1 -1
  131. package/dist/types/ucp-profile.d.ts +32 -2
  132. package/dist/types/ucp-profile.d.ts.map +1 -1
  133. package/dist/types/ucp-profile.js +31 -1
  134. package/dist/types/ucp-profile.js.map +1 -1
  135. package/dist/types/validation.d.ts +14 -0
  136. package/dist/types/validation.d.ts.map +1 -1
  137. package/dist/types/validation.js +19 -0
  138. package/dist/types/validation.js.map +1 -1
  139. package/dist/validator/acp/index.d.ts +31 -0
  140. package/dist/validator/acp/index.d.ts.map +1 -0
  141. package/dist/validator/acp/index.js +574 -0
  142. package/dist/validator/acp/index.js.map +1 -0
  143. package/dist/validator/network-validator.d.ts.map +1 -1
  144. package/dist/validator/network-validator.js +23 -13
  145. package/dist/validator/network-validator.js.map +1 -1
  146. package/dist/validator/rules-validator.d.ts +8 -0
  147. package/dist/validator/rules-validator.d.ts.map +1 -1
  148. package/dist/validator/rules-validator.js +159 -43
  149. package/dist/validator/rules-validator.js.map +1 -1
  150. package/dist/validator/structural-validator.d.ts.map +1 -1
  151. package/dist/validator/structural-validator.js +283 -53
  152. package/dist/validator/structural-validator.js.map +1 -1
  153. package/dist/validator/utils.d.ts +62 -0
  154. package/dist/validator/utils.d.ts.map +1 -0
  155. package/dist/validator/utils.js +151 -0
  156. package/dist/validator/utils.js.map +1 -0
  157. package/package.json +45 -12
  158. package/.claude/settings.local.json +0 -60
  159. package/.vercel/README.txt +0 -11
  160. package/.vercel/project.json +0 -1
  161. package/publish-output.txt +0 -0
  162. package/tsconfig.json +0 -20
@@ -0,0 +1,490 @@
1
+ "use strict";
2
+ /**
3
+ * Stripe Service
4
+ * Handles all Stripe-related operations including:
5
+ * - Checkout session creation
6
+ * - Subscription management
7
+ * - Customer portal
8
+ * - Webhook events
9
+ * Production deployment ready
10
+ */
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.STRIPE_PRICE_IDS = exports.stripe = void 0;
16
+ exports.getStripe = getStripe;
17
+ exports.priceIdToTier = priceIdToTier;
18
+ exports.createCheckoutSession = createCheckoutSession;
19
+ exports.createCustomerPortalSession = createCustomerPortalSession;
20
+ exports.startTrialWithoutPayment = startTrialWithoutPayment;
21
+ exports.handleWebhookEvent = handleWebhookEvent;
22
+ exports.verifyWebhookSignature = verifyWebhookSignature;
23
+ exports.getSubscriptionDetails = getSubscriptionDetails;
24
+ exports.cancelSubscription = cancelSubscription;
25
+ exports.resumeSubscription = resumeSubscription;
26
+ exports.updateSubscription = updateSubscription;
27
+ const stripe_1 = __importDefault(require("stripe"));
28
+ const index_js_1 = require("../db/index.js");
29
+ const utils_js_1 = require("../db/utils.js");
30
+ const email_js_1 = require("./email.js");
31
+ const notifications_js_1 = require("./notifications.js");
32
+ const drizzle_orm_1 = require("drizzle-orm");
33
+ // Initialize Stripe lazily to avoid crashing on startup
34
+ // Use test keys in development, production keys in production
35
+ const isProduction = process.env.NODE_ENV === 'production';
36
+ const stripeSecretKey = isProduction
37
+ ? process.env.STRIPE_SECRET_KEY
38
+ : (process.env.STRIPE_TEST_SECRET_KEY || process.env.STRIPE_SECRET_KEY);
39
+ // Log Stripe mode on startup
40
+ console.log(`[Stripe] Mode: ${isProduction ? 'PRODUCTION' : 'TEST'}`);
41
+ console.log(`[Stripe] Secret key present: ${!!stripeSecretKey}`);
42
+ console.log(`[Stripe] Key starts with: ${stripeSecretKey?.substring(0, 7) || 'NONE'}`);
43
+ let stripeInstance = null;
44
+ /**
45
+ * Get the Stripe instance, throwing an error if not configured.
46
+ * This allows the app to start even without Stripe keys,
47
+ * failing only when Stripe features are actually used.
48
+ */
49
+ function getStripe() {
50
+ if (!stripeInstance) {
51
+ if (!stripeSecretKey) {
52
+ throw new Error('STRIPE_SECRET_KEY environment variable is required for payment features');
53
+ }
54
+ stripeInstance = new stripe_1.default(stripeSecretKey, {});
55
+ }
56
+ return stripeInstance;
57
+ }
58
+ // Legacy export for backward compatibility - lazy getter
59
+ exports.stripe = new Proxy({}, {
60
+ get(_, prop) {
61
+ return getStripe()[prop];
62
+ }
63
+ });
64
+ // Stripe Price IDs (to be set from environment or config)
65
+ // Use test price IDs in development, production price IDs in production
66
+ const isProductionEnv = process.env.NODE_ENV === 'production';
67
+ exports.STRIPE_PRICE_IDS = {
68
+ // Starter tier ($9/mo)
69
+ starter_monthly: isProductionEnv
70
+ ? (process.env.STRIPE_PRICE_STARTER_MONTHLY || 'price_starter_monthly')
71
+ : (process.env.STRIPE_TEST_PRICE_STARTER_MONTHLY || process.env.STRIPE_PRICE_STARTER_MONTHLY || 'price_starter_monthly'),
72
+ starter_annual: isProductionEnv
73
+ ? (process.env.STRIPE_PRICE_STARTER_ANNUAL || 'price_starter_annual')
74
+ : (process.env.STRIPE_TEST_PRICE_STARTER_ANNUAL || process.env.STRIPE_PRICE_STARTER_ANNUAL || 'price_starter_annual'),
75
+ // Pro tier ($19/mo) - ready for future use
76
+ pro_monthly: isProductionEnv
77
+ ? (process.env.STRIPE_PRICE_PRO_MONTHLY || 'price_pro_monthly')
78
+ : (process.env.STRIPE_TEST_PRICE_PRO_MONTHLY || process.env.STRIPE_PRICE_PRO_MONTHLY || 'price_pro_monthly'),
79
+ pro_annual: isProductionEnv
80
+ ? (process.env.STRIPE_PRICE_PRO_ANNUAL || 'price_pro_annual')
81
+ : (process.env.STRIPE_TEST_PRICE_PRO_ANNUAL || process.env.STRIPE_PRICE_PRO_ANNUAL || 'price_pro_annual'),
82
+ // Business tier ($49/mo) - deferred until traction
83
+ business_monthly: isProductionEnv
84
+ ? (process.env.STRIPE_PRICE_BUSINESS_MONTHLY || 'price_business_monthly')
85
+ : (process.env.STRIPE_TEST_PRICE_BUSINESS_MONTHLY || process.env.STRIPE_PRICE_BUSINESS_MONTHLY || 'price_business_monthly'),
86
+ business_annual: isProductionEnv
87
+ ? (process.env.STRIPE_PRICE_BUSINESS_ANNUAL || 'price_business_annual')
88
+ : (process.env.STRIPE_TEST_PRICE_BUSINESS_ANNUAL || process.env.STRIPE_PRICE_BUSINESS_ANNUAL || 'price_business_annual'),
89
+ };
90
+ // Log price IDs on startup
91
+ console.log(`[Stripe] Price IDs:`, {
92
+ starter_monthly: exports.STRIPE_PRICE_IDS.starter_monthly,
93
+ starter_annual: exports.STRIPE_PRICE_IDS.starter_annual,
94
+ pro_monthly: exports.STRIPE_PRICE_IDS.pro_monthly,
95
+ pro_annual: exports.STRIPE_PRICE_IDS.pro_annual,
96
+ });
97
+ /**
98
+ * Map Stripe price ID to subscription tier
99
+ */
100
+ function priceIdToTier(priceId) {
101
+ // Check business tier first (most specific)
102
+ const businessPrices = [
103
+ exports.STRIPE_PRICE_IDS.business_monthly,
104
+ exports.STRIPE_PRICE_IDS.business_annual,
105
+ ];
106
+ if (businessPrices.includes(priceId) || priceId.includes('business')) {
107
+ return 'business';
108
+ }
109
+ // Check pro tier
110
+ const proPrices = [
111
+ exports.STRIPE_PRICE_IDS.pro_monthly,
112
+ exports.STRIPE_PRICE_IDS.pro_annual,
113
+ ];
114
+ if (proPrices.includes(priceId) || priceId.includes('pro')) {
115
+ return 'pro';
116
+ }
117
+ // Check starter tier
118
+ const starterPrices = [
119
+ exports.STRIPE_PRICE_IDS.starter_monthly,
120
+ exports.STRIPE_PRICE_IDS.starter_annual,
121
+ ];
122
+ if (starterPrices.includes(priceId) || priceId.includes('starter')) {
123
+ return 'starter';
124
+ }
125
+ return 'free';
126
+ }
127
+ /**
128
+ * Create a Stripe checkout session for a new subscription
129
+ */
130
+ async function createCheckoutSession(params) {
131
+ const { userId, userEmail, priceId, successUrl, cancelUrl, trialDays } = params;
132
+ // Create or retrieve Stripe customer
133
+ const customers = await exports.stripe.customers.list({
134
+ email: userEmail,
135
+ limit: 1,
136
+ });
137
+ let customerId;
138
+ if (customers.data.length > 0) {
139
+ customerId = customers.data[0].id;
140
+ }
141
+ else {
142
+ const customer = await exports.stripe.customers.create({
143
+ email: userEmail,
144
+ metadata: {
145
+ userId,
146
+ },
147
+ });
148
+ customerId = customer.id;
149
+ }
150
+ // Create checkout session
151
+ const sessionParams = {
152
+ customer: customerId,
153
+ mode: 'subscription',
154
+ payment_method_types: ['card'],
155
+ locale: 'en', // Force English locale for international users
156
+ line_items: [
157
+ {
158
+ price: priceId,
159
+ quantity: 1,
160
+ },
161
+ ],
162
+ success_url: successUrl,
163
+ cancel_url: cancelUrl,
164
+ metadata: {
165
+ userId,
166
+ },
167
+ subscription_data: {
168
+ metadata: {
169
+ userId,
170
+ },
171
+ },
172
+ };
173
+ // Add trial period if specified
174
+ if (trialDays && trialDays > 0) {
175
+ sessionParams.subscription_data = {
176
+ ...sessionParams.subscription_data,
177
+ trial_period_days: trialDays,
178
+ };
179
+ }
180
+ return await exports.stripe.checkout.sessions.create(sessionParams);
181
+ }
182
+ /**
183
+ * Create a customer portal session for managing subscription
184
+ */
185
+ async function createCustomerPortalSession(params) {
186
+ const { customerId, returnUrl } = params;
187
+ return await exports.stripe.billingPortal.sessions.create({
188
+ customer: customerId,
189
+ return_url: returnUrl,
190
+ locale: 'en', // Force English locale for portal
191
+ });
192
+ }
193
+ /**
194
+ * Start a free trial without requiring a credit card
195
+ * Creates a Stripe customer for future conversion, and a local subscription record
196
+ *
197
+ * @param userId - The user's ID in our database
198
+ * @param userEmail - The user's email address
199
+ * @param userName - Optional user name
200
+ * @param trialDays - Number of trial days (default 7)
201
+ * @returns The created Stripe customer ID and trial end date
202
+ */
203
+ async function startTrialWithoutPayment(params) {
204
+ const { userId, userEmail, userName, trialDays = 7 } = params;
205
+ const db = (0, index_js_1.getDb)();
206
+ // Check if customer already exists in Stripe
207
+ const existingCustomers = await exports.stripe.customers.list({
208
+ email: userEmail,
209
+ limit: 1,
210
+ });
211
+ let customerId;
212
+ if (existingCustomers.data.length > 0) {
213
+ customerId = existingCustomers.data[0].id;
214
+ }
215
+ else {
216
+ // Create new Stripe customer (no payment method required)
217
+ const customer = await exports.stripe.customers.create({
218
+ email: userEmail,
219
+ name: userName || undefined,
220
+ metadata: {
221
+ userId,
222
+ },
223
+ });
224
+ customerId = customer.id;
225
+ }
226
+ // Calculate trial end date
227
+ const trialEndsAt = new Date();
228
+ trialEndsAt.setDate(trialEndsAt.getDate() + trialDays);
229
+ // Update or create subscription in our database
230
+ await (0, utils_js_1.upsertSubscription)({
231
+ userId,
232
+ stripeSubscriptionId: null, // No Stripe subscription yet - will be created when they add payment
233
+ tier: 'starter',
234
+ status: 'trialing',
235
+ currentPeriodEnd: trialEndsAt,
236
+ });
237
+ // Update user with Stripe customer ID
238
+ await db.update(index_js_1.users)
239
+ .set({
240
+ stripeCustomerId: customerId,
241
+ updatedAt: new Date(),
242
+ })
243
+ .where((0, drizzle_orm_1.eq)(index_js_1.users.id, userId));
244
+ console.log(`[Stripe] Started trial for user ${userId} (customer: ${customerId}), ends: ${trialEndsAt.toISOString()}`);
245
+ return { customerId, trialEndsAt };
246
+ }
247
+ /**
248
+ * Handle Stripe webhook events
249
+ */
250
+ async function handleWebhookEvent(event) {
251
+ const db = (0, index_js_1.getDb)();
252
+ switch (event.type) {
253
+ case 'checkout.session.completed': {
254
+ const session = event.data.object;
255
+ await handleCheckoutCompleted(session);
256
+ break;
257
+ }
258
+ case 'customer.subscription.created': {
259
+ const subscription = event.data.object;
260
+ await handleSubscriptionChange(subscription, true);
261
+ break;
262
+ }
263
+ case 'customer.subscription.updated': {
264
+ const subscription = event.data.object;
265
+ await handleSubscriptionChange(subscription, false);
266
+ break;
267
+ }
268
+ case 'customer.subscription.deleted': {
269
+ const subscription = event.data.object;
270
+ await handleSubscriptionDeleted(subscription);
271
+ break;
272
+ }
273
+ case 'invoice.paid': {
274
+ const invoice = event.data.object;
275
+ await handleInvoicePaid(invoice);
276
+ break;
277
+ }
278
+ case 'invoice.payment_failed': {
279
+ const invoice = event.data.object;
280
+ await handleInvoicePaymentFailed(invoice);
281
+ break;
282
+ }
283
+ default:
284
+ console.log(`Unhandled event type: ${event.type}`);
285
+ }
286
+ }
287
+ /**
288
+ * Handle checkout.session.completed event
289
+ */
290
+ async function handleCheckoutCompleted(session) {
291
+ const userId = session.metadata?.userId;
292
+ if (!userId) {
293
+ console.error('No userId in session metadata');
294
+ return;
295
+ }
296
+ // Update user's Stripe customer ID if not already set
297
+ if (session.customer && typeof session.customer === 'string') {
298
+ const db = (0, index_js_1.getDb)();
299
+ // TODO: Update user's stripeCustomerId in database
300
+ // For now, the customer ID is available in session.customer
301
+ }
302
+ // The subscription will be created via customer.subscription.created event
303
+ console.log(`Checkout completed for user ${userId}`);
304
+ }
305
+ /**
306
+ * Handle subscription created/updated events
307
+ */
308
+ async function handleSubscriptionChange(subscription, isNew = false) {
309
+ const userId = subscription.metadata?.userId;
310
+ if (!userId) {
311
+ console.error('No userId in subscription metadata');
312
+ return;
313
+ }
314
+ // Get the price ID from the subscription
315
+ const firstItem = subscription.items.data[0];
316
+ const priceId = firstItem?.price.id;
317
+ if (!priceId) {
318
+ console.error('No price ID in subscription');
319
+ return;
320
+ }
321
+ // Determine tier from price ID
322
+ const tier = priceIdToTier(priceId);
323
+ // Determine status
324
+ let status;
325
+ switch (subscription.status) {
326
+ case 'active':
327
+ status = 'active';
328
+ break;
329
+ case 'canceled':
330
+ case 'unpaid':
331
+ status = 'canceled';
332
+ break;
333
+ case 'past_due':
334
+ status = 'past_due';
335
+ break;
336
+ case 'trialing':
337
+ status = 'trialing';
338
+ break;
339
+ default:
340
+ status = 'active';
341
+ }
342
+ // Get current_period_end - check subscription root first, then items (API version compatibility)
343
+ const currentPeriodEndTimestamp = subscription.current_period_end
344
+ ?? firstItem?.current_period_end;
345
+ if (!currentPeriodEndTimestamp) {
346
+ console.error('No current_period_end found in subscription or items');
347
+ return;
348
+ }
349
+ // Upsert subscription in database
350
+ await (0, utils_js_1.upsertSubscription)({
351
+ userId,
352
+ tier,
353
+ status,
354
+ stripeSubscriptionId: subscription.id,
355
+ stripePriceId: priceId,
356
+ currentPeriodEnd: new Date(currentPeriodEndTimestamp * 1000),
357
+ });
358
+ console.log(`Subscription ${subscription.status} for user ${userId}, tier: ${tier}`);
359
+ // Send welcome email for new subscriptions (not updates)
360
+ if (isNew && tier !== 'free') {
361
+ try {
362
+ const db = (0, index_js_1.getDb)();
363
+ const [user] = await db.select().from(index_js_1.users).where((0, drizzle_orm_1.eq)(index_js_1.users.id, userId)).limit(1);
364
+ if (user) {
365
+ const baseUrl = process.env.NEXT_PUBLIC_APP_URL || 'https://ucptools.dev';
366
+ await (0, email_js_1.sendWelcomeEmail)({
367
+ to: {
368
+ email: user.email,
369
+ name: user.name || undefined,
370
+ },
371
+ dashboardUrl: `${baseUrl}/dashboard`,
372
+ });
373
+ console.log(`Welcome email sent to ${user.email}`);
374
+ // Send Slack notification for new signup (fire-and-forget)
375
+ (0, notifications_js_1.notifySignup)({ id: userId, email: user.email, name: user.name }, { tier, status });
376
+ }
377
+ }
378
+ catch (error) {
379
+ console.error('Failed to send welcome email:', error);
380
+ // Don't throw - welcome email failure shouldn't break subscription flow
381
+ }
382
+ }
383
+ }
384
+ /**
385
+ * Handle subscription deleted event
386
+ */
387
+ async function handleSubscriptionDeleted(subscription) {
388
+ const userId = subscription.metadata?.userId;
389
+ if (!userId) {
390
+ console.error('No userId in subscription metadata');
391
+ return;
392
+ }
393
+ // Get current_period_end - check subscription root first, then items (API version compatibility)
394
+ const firstItem = subscription.items.data[0];
395
+ const currentPeriodEndTimestamp = subscription.current_period_end
396
+ ?? firstItem?.current_period_end;
397
+ // Update subscription to canceled and downgrade to free
398
+ await (0, utils_js_1.upsertSubscription)({
399
+ userId,
400
+ tier: 'free',
401
+ status: 'canceled',
402
+ stripeSubscriptionId: subscription.id,
403
+ currentPeriodEnd: currentPeriodEndTimestamp ? new Date(currentPeriodEndTimestamp * 1000) : new Date(),
404
+ });
405
+ console.log(`Subscription canceled for user ${userId}, downgraded to free`);
406
+ }
407
+ /**
408
+ * Handle invoice.paid event
409
+ */
410
+ async function handleInvoicePaid(invoice) {
411
+ const subscriptionId = invoice.subscription;
412
+ if (!subscriptionId || typeof subscriptionId !== 'string') {
413
+ return;
414
+ }
415
+ // Retrieve subscription to update status
416
+ const subscription = await exports.stripe.subscriptions.retrieve(subscriptionId);
417
+ await handleSubscriptionChange(subscription);
418
+ console.log(`Invoice paid for subscription ${subscriptionId}`);
419
+ }
420
+ /**
421
+ * Handle invoice.payment_failed event
422
+ */
423
+ async function handleInvoicePaymentFailed(invoice) {
424
+ const subscriptionId = invoice.subscription;
425
+ if (!subscriptionId || typeof subscriptionId !== 'string') {
426
+ return;
427
+ }
428
+ const userId = invoice.subscription_details?.metadata?.userId;
429
+ if (!userId) {
430
+ console.error('No userId in invoice metadata');
431
+ return;
432
+ }
433
+ // Update subscription status to past_due
434
+ await (0, utils_js_1.upsertSubscription)({
435
+ userId,
436
+ status: 'past_due',
437
+ });
438
+ console.log(`Payment failed for subscription ${subscriptionId}, user ${userId}`);
439
+ // TODO: Send email notification to user
440
+ }
441
+ /**
442
+ * Verify Stripe webhook signature
443
+ */
444
+ function verifyWebhookSignature(payload, signature, secret) {
445
+ return exports.stripe.webhooks.constructEvent(payload, signature, secret);
446
+ }
447
+ /**
448
+ * Get subscription details for a user
449
+ */
450
+ async function getSubscriptionDetails(stripeSubscriptionId) {
451
+ try {
452
+ return await exports.stripe.subscriptions.retrieve(stripeSubscriptionId);
453
+ }
454
+ catch (error) {
455
+ console.error('Error retrieving subscription:', error);
456
+ return null;
457
+ }
458
+ }
459
+ /**
460
+ * Cancel a subscription at period end
461
+ */
462
+ async function cancelSubscription(stripeSubscriptionId) {
463
+ return await exports.stripe.subscriptions.update(stripeSubscriptionId, {
464
+ cancel_at_period_end: true,
465
+ });
466
+ }
467
+ /**
468
+ * Resume a canceled subscription
469
+ */
470
+ async function resumeSubscription(stripeSubscriptionId) {
471
+ return await exports.stripe.subscriptions.update(stripeSubscriptionId, {
472
+ cancel_at_period_end: false,
473
+ });
474
+ }
475
+ /**
476
+ * Update subscription to a new price (upgrade/downgrade)
477
+ */
478
+ async function updateSubscription(stripeSubscriptionId, newPriceId) {
479
+ const subscription = await exports.stripe.subscriptions.retrieve(stripeSubscriptionId);
480
+ return await exports.stripe.subscriptions.update(stripeSubscriptionId, {
481
+ items: [
482
+ {
483
+ id: subscription.items.data[0].id,
484
+ price: newPriceId,
485
+ },
486
+ ],
487
+ proration_behavior: 'always_invoice',
488
+ });
489
+ }
490
+ //# sourceMappingURL=stripe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stripe.js","sourceRoot":"","sources":["../../src/services/stripe.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;;;;AA+BH,8BAQC;AA+CD,sCA6BC;AAKD,sDA8DC;AAKD,kEAWC;AAYD,4DAsDC;AAKD,gDA2CC;AA0LD,wDAMC;AAKD,wDAOC;AAKD,gDAIC;AAKD,gDAIC;AAKD,gDAeC;AAxiBD,oDAA4B;AAC5B,6CAA8C;AAC9C,6CAAoD;AACpD,yCAA8C;AAC9C,yDAAkD;AAClD,6CAAiC;AAKjC,wDAAwD;AACxD,8DAA8D;AAC9D,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,CAAC;AAC3D,MAAM,eAAe,GAAG,YAAY;IAChC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB;IAC/B,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;AAE5E,6BAA6B;AAC7B,OAAO,CAAC,GAAG,CAAC,kBAAkB,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;AACtE,OAAO,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC;AACjE,OAAO,CAAC,GAAG,CAAC,6BAA6B,eAAe,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,EAAE,CAAC,CAAC;AAEvF,IAAI,cAAc,GAAkB,IAAI,CAAC;AAEzC;;;;GAIG;AACH,SAAgB,SAAS;IACrB,IAAI,CAAC,cAAc,EAAE,CAAC;QAClB,IAAI,CAAC,eAAe,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CAAC,yEAAyE,CAAC,CAAC;QAC/F,CAAC;QACD,cAAc,GAAG,IAAI,gBAAM,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC;IACrD,CAAC;IACD,OAAO,cAAc,CAAC;AAC1B,CAAC;AAED,yDAAyD;AAC5C,QAAA,MAAM,GAAG,IAAI,KAAK,CAAC,EAAY,EAAE;IAC1C,GAAG,CAAC,CAAC,EAAE,IAAI;QACP,OAAO,SAAS,EAAE,CAAC,IAAoB,CAAC,CAAC;IAC7C,CAAC;CACJ,CAAC,CAAC;AAEH,0DAA0D;AAC1D,wEAAwE;AACxE,MAAM,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,CAAC;AACjD,QAAA,gBAAgB,GAAG;IAC5B,uBAAuB;IACvB,eAAe,EAAE,eAAe;QAC5B,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,4BAA4B,IAAI,uBAAuB,CAAC;QACvE,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,iCAAiC,IAAI,OAAO,CAAC,GAAG,CAAC,4BAA4B,IAAI,uBAAuB,CAAC;IAC5H,cAAc,EAAE,eAAe;QAC3B,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,2BAA2B,IAAI,sBAAsB,CAAC;QACrE,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,gCAAgC,IAAI,OAAO,CAAC,GAAG,CAAC,2BAA2B,IAAI,sBAAsB,CAAC;IACzH,2CAA2C;IAC3C,WAAW,EAAE,eAAe;QACxB,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,wBAAwB,IAAI,mBAAmB,CAAC;QAC/D,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,6BAA6B,IAAI,OAAO,CAAC,GAAG,CAAC,wBAAwB,IAAI,mBAAmB,CAAC;IAChH,UAAU,EAAE,eAAe;QACvB,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,uBAAuB,IAAI,kBAAkB,CAAC;QAC7D,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,4BAA4B,IAAI,OAAO,CAAC,GAAG,CAAC,uBAAuB,IAAI,kBAAkB,CAAC;IAC7G,mDAAmD;IACnD,gBAAgB,EAAE,eAAe;QAC7B,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,6BAA6B,IAAI,wBAAwB,CAAC;QACzE,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,kCAAkC,IAAI,OAAO,CAAC,GAAG,CAAC,6BAA6B,IAAI,wBAAwB,CAAC;IAC/H,eAAe,EAAE,eAAe;QAC5B,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,4BAA4B,IAAI,uBAAuB,CAAC;QACvE,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,iCAAiC,IAAI,OAAO,CAAC,GAAG,CAAC,4BAA4B,IAAI,uBAAuB,CAAC;CAC/H,CAAC;AAEF,2BAA2B;AAC3B,OAAO,CAAC,GAAG,CAAC,qBAAqB,EAAE;IAC/B,eAAe,EAAE,wBAAgB,CAAC,eAAe;IACjD,cAAc,EAAE,wBAAgB,CAAC,cAAc;IAC/C,WAAW,EAAE,wBAAgB,CAAC,WAAW;IACzC,UAAU,EAAE,wBAAgB,CAAC,UAAU;CAC1C,CAAC,CAAC;AAEH;;GAEG;AACH,SAAgB,aAAa,CAAC,OAAe;IACzC,4CAA4C;IAC5C,MAAM,cAAc,GAAG;QACnB,wBAAgB,CAAC,gBAAgB;QACjC,wBAAgB,CAAC,eAAe;KACnC,CAAC;IACF,IAAI,cAAc,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;QACnE,OAAO,UAAU,CAAC;IACtB,CAAC;IAED,iBAAiB;IACjB,MAAM,SAAS,GAAG;QACd,wBAAgB,CAAC,WAAW;QAC5B,wBAAgB,CAAC,UAAU;KAC9B,CAAC;IACF,IAAI,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACzD,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,qBAAqB;IACrB,MAAM,aAAa,GAAG;QAClB,wBAAgB,CAAC,eAAe;QAChC,wBAAgB,CAAC,cAAc;KAClC,CAAC;IACF,IAAI,aAAa,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QACjE,OAAO,SAAS,CAAC;IACrB,CAAC;IAED,OAAO,MAAM,CAAC;AAClB,CAAC;AAED;;GAEG;AACI,KAAK,UAAU,qBAAqB,CAAC,MAO3C;IACG,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC;IAEhF,qCAAqC;IACrC,MAAM,SAAS,GAAG,MAAM,cAAM,CAAC,SAAS,CAAC,IAAI,CAAC;QAC1C,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,CAAC;KACX,CAAC,CAAC;IAEH,IAAI,UAAkB,CAAC;IACvB,IAAI,SAAS,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,UAAU,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACtC,CAAC;SAAM,CAAC;QACJ,MAAM,QAAQ,GAAG,MAAM,cAAM,CAAC,SAAS,CAAC,MAAM,CAAC;YAC3C,KAAK,EAAE,SAAS;YAChB,QAAQ,EAAE;gBACN,MAAM;aACT;SACJ,CAAC,CAAC;QACH,UAAU,GAAG,QAAQ,CAAC,EAAE,CAAC;IAC7B,CAAC;IAED,0BAA0B;IAC1B,MAAM,aAAa,GAAwC;QACvD,QAAQ,EAAE,UAAU;QACpB,IAAI,EAAE,cAAc;QACpB,oBAAoB,EAAE,CAAC,MAAM,CAAC;QAC9B,MAAM,EAAE,IAAI,EAAG,+CAA+C;QAC9D,UAAU,EAAE;YACR;gBACI,KAAK,EAAE,OAAO;gBACd,QAAQ,EAAE,CAAC;aACd;SACJ;QACD,WAAW,EAAE,UAAU;QACvB,UAAU,EAAE,SAAS;QACrB,QAAQ,EAAE;YACN,MAAM;SACT;QACD,iBAAiB,EAAE;YACf,QAAQ,EAAE;gBACN,MAAM;aACT;SACJ;KACJ,CAAC;IAEF,gCAAgC;IAChC,IAAI,SAAS,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;QAC7B,aAAa,CAAC,iBAAiB,GAAG;YAC9B,GAAG,aAAa,CAAC,iBAAiB;YAClC,iBAAiB,EAAE,SAAS;SAC/B,CAAC;IACN,CAAC;IAED,OAAO,MAAM,cAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;AAChE,CAAC;AAED;;GAEG;AACI,KAAK,UAAU,2BAA2B,CAAC,MAGjD;IACG,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC;IAEzC,OAAO,MAAM,cAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,MAAM,CAAC;QAC9C,QAAQ,EAAE,UAAU;QACpB,UAAU,EAAE,SAAS;QACrB,MAAM,EAAE,IAAI,EAAG,kCAAkC;KACpD,CAAC,CAAC;AACP,CAAC;AAED;;;;;;;;;GASG;AACI,KAAK,UAAU,wBAAwB,CAAC,MAK9C;IACG,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,GAAG,CAAC,EAAE,GAAG,MAAM,CAAC;IAC9D,MAAM,EAAE,GAAG,IAAA,gBAAK,GAAE,CAAC;IAEnB,6CAA6C;IAC7C,MAAM,iBAAiB,GAAG,MAAM,cAAM,CAAC,SAAS,CAAC,IAAI,CAAC;QAClD,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,CAAC;KACX,CAAC,CAAC;IAEH,IAAI,UAAkB,CAAC;IACvB,IAAI,iBAAiB,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpC,UAAU,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9C,CAAC;SAAM,CAAC;QACJ,0DAA0D;QAC1D,MAAM,QAAQ,GAAG,MAAM,cAAM,CAAC,SAAS,CAAC,MAAM,CAAC;YAC3C,KAAK,EAAE,SAAS;YAChB,IAAI,EAAE,QAAQ,IAAI,SAAS;YAC3B,QAAQ,EAAE;gBACN,MAAM;aACT;SACJ,CAAC,CAAC;QACH,UAAU,GAAG,QAAQ,CAAC,EAAE,CAAC;IAC7B,CAAC;IAED,2BAA2B;IAC3B,MAAM,WAAW,GAAG,IAAI,IAAI,EAAE,CAAC;IAC/B,WAAW,CAAC,OAAO,CAAC,WAAW,CAAC,OAAO,EAAE,GAAG,SAAS,CAAC,CAAC;IAEvD,gDAAgD;IAChD,MAAM,IAAA,6BAAkB,EAAC;QACrB,MAAM;QACN,oBAAoB,EAAE,IAAI,EAAE,qEAAqE;QACjG,IAAI,EAAE,SAAS;QACf,MAAM,EAAE,UAAU;QAClB,gBAAgB,EAAE,WAAW;KAChC,CAAC,CAAC;IAEH,sCAAsC;IACtC,MAAM,EAAE,CAAC,MAAM,CAAC,gBAAK,CAAC;SACjB,GAAG,CAAC;QACD,gBAAgB,EAAE,UAAU;QAC5B,SAAS,EAAE,IAAI,IAAI,EAAE;KACxB,CAAC;SACD,KAAK,CAAC,IAAA,gBAAE,EAAC,gBAAK,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC;IAEjC,OAAO,CAAC,GAAG,CAAC,mCAAmC,MAAM,eAAe,UAAU,YAAY,WAAW,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IAEvH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC;AACvC,CAAC;AAED;;GAEG;AACI,KAAK,UAAU,kBAAkB,CAAC,KAAmB;IACxD,MAAM,EAAE,GAAG,IAAA,gBAAK,GAAE,CAAC;IAEnB,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;QACjB,KAAK,4BAA4B,CAAC,CAAC,CAAC;YAChC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,MAAiC,CAAC;YAC7D,MAAM,uBAAuB,CAAC,OAAO,CAAC,CAAC;YACvC,MAAM;QACV,CAAC;QAED,KAAK,+BAA+B,CAAC,CAAC,CAAC;YACnC,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,CAAC,MAA6B,CAAC;YAC9D,MAAM,wBAAwB,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;YACnD,MAAM;QACV,CAAC;QAED,KAAK,+BAA+B,CAAC,CAAC,CAAC;YACnC,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,CAAC,MAA6B,CAAC;YAC9D,MAAM,wBAAwB,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;YACpD,MAAM;QACV,CAAC;QAED,KAAK,+BAA+B,CAAC,CAAC,CAAC;YACnC,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,CAAC,MAA6B,CAAC;YAC9D,MAAM,yBAAyB,CAAC,YAAY,CAAC,CAAC;YAC9C,MAAM;QACV,CAAC;QAED,KAAK,cAAc,CAAC,CAAC,CAAC;YAClB,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,MAAwB,CAAC;YACpD,MAAM,iBAAiB,CAAC,OAAO,CAAC,CAAC;YACjC,MAAM;QACV,CAAC;QAED,KAAK,wBAAwB,CAAC,CAAC,CAAC;YAC5B,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,MAAwB,CAAC;YACpD,MAAM,0BAA0B,CAAC,OAAO,CAAC,CAAC;YAC1C,MAAM;QACV,CAAC;QAED;YACI,OAAO,CAAC,GAAG,CAAC,yBAAyB,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;IAC3D,CAAC;AACL,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,uBAAuB,CAAC,OAAgC;IACnE,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC;IACxC,IAAI,CAAC,MAAM,EAAE,CAAC;QACV,OAAO,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;QAC/C,OAAO;IACX,CAAC;IAED,sDAAsD;IACtD,IAAI,OAAO,CAAC,QAAQ,IAAI,OAAO,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC3D,MAAM,EAAE,GAAG,IAAA,gBAAK,GAAE,CAAC;QACnB,mDAAmD;QACnD,4DAA4D;IAChE,CAAC;IAED,2EAA2E;IAC3E,OAAO,CAAC,GAAG,CAAC,+BAA+B,MAAM,EAAE,CAAC,CAAC;AACzD,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,wBAAwB,CAAC,YAAiC,EAAE,QAAiB,KAAK;IAC7F,MAAM,MAAM,GAAG,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC7C,IAAI,CAAC,MAAM,EAAE,CAAC;QACV,OAAO,CAAC,KAAK,CAAC,oCAAoC,CAAC,CAAC;QACpD,OAAO;IACX,CAAC;IAED,yCAAyC;IACzC,MAAM,SAAS,GAAG,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC;IACpC,IAAI,CAAC,OAAO,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC;QAC7C,OAAO;IACX,CAAC;IAED,+BAA+B;IAC/B,MAAM,IAAI,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;IAEpC,mBAAmB;IACnB,IAAI,MAAuD,CAAC;IAC5D,QAAQ,YAAY,CAAC,MAAM,EAAE,CAAC;QAC1B,KAAK,QAAQ;YACT,MAAM,GAAG,QAAQ,CAAC;YAClB,MAAM;QACV,KAAK,UAAU,CAAC;QAChB,KAAK,QAAQ;YACT,MAAM,GAAG,UAAU,CAAC;YACpB,MAAM;QACV,KAAK,UAAU;YACX,MAAM,GAAG,UAAU,CAAC;YACpB,MAAM;QACV,KAAK,UAAU;YACX,MAAM,GAAG,UAAU,CAAC;YACpB,MAAM;QACV;YACI,MAAM,GAAG,QAAQ,CAAC;IAC1B,CAAC;IAED,iGAAiG;IACjG,MAAM,yBAAyB,GAAI,YAAoB,CAAC,kBAAkB;WAClE,SAAiB,EAAE,kBAAkB,CAAC;IAE9C,IAAI,CAAC,yBAAyB,EAAE,CAAC;QAC7B,OAAO,CAAC,KAAK,CAAC,sDAAsD,CAAC,CAAC;QACtE,OAAO;IACX,CAAC;IAED,kCAAkC;IAClC,MAAM,IAAA,6BAAkB,EAAC;QACrB,MAAM;QACN,IAAI;QACJ,MAAM;QACN,oBAAoB,EAAE,YAAY,CAAC,EAAE;QACrC,aAAa,EAAE,OAAO;QACtB,gBAAgB,EAAE,IAAI,IAAI,CAAC,yBAAyB,GAAG,IAAI,CAAC;KAC/D,CAAC,CAAC;IAEH,OAAO,CAAC,GAAG,CAAC,gBAAgB,YAAY,CAAC,MAAM,aAAa,MAAM,WAAW,IAAI,EAAE,CAAC,CAAC;IAErF,yDAAyD;IACzD,IAAI,KAAK,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QAC3B,IAAI,CAAC;YACD,MAAM,EAAE,GAAG,IAAA,gBAAK,GAAE,CAAC;YACnB,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,gBAAK,CAAC,CAAC,KAAK,CAAC,IAAA,gBAAE,EAAC,gBAAK,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAElF,IAAI,IAAI,EAAE,CAAC;gBACP,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,sBAAsB,CAAC;gBAC1E,MAAM,IAAA,2BAAgB,EAAC;oBACnB,EAAE,EAAE;wBACA,KAAK,EAAE,IAAI,CAAC,KAAK;wBACjB,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,SAAS;qBAC/B;oBACD,YAAY,EAAE,GAAG,OAAO,YAAY;iBACvC,CAAC,CAAC;gBACH,OAAO,CAAC,GAAG,CAAC,yBAAyB,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;gBAEnD,2DAA2D;gBAC3D,IAAA,+BAAY,EACR,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAClD,EAAE,IAAI,EAAE,MAAM,EAAE,CACnB,CAAC;YACN,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,+BAA+B,EAAE,KAAK,CAAC,CAAC;YACtD,wEAAwE;QAC5E,CAAC;IACL,CAAC;AACL,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,yBAAyB,CAAC,YAAiC;IACtE,MAAM,MAAM,GAAG,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC7C,IAAI,CAAC,MAAM,EAAE,CAAC;QACV,OAAO,CAAC,KAAK,CAAC,oCAAoC,CAAC,CAAC;QACpD,OAAO;IACX,CAAC;IAED,iGAAiG;IACjG,MAAM,SAAS,GAAG,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC7C,MAAM,yBAAyB,GAAI,YAAoB,CAAC,kBAAkB;WAClE,SAAiB,EAAE,kBAAkB,CAAC;IAE9C,wDAAwD;IACxD,MAAM,IAAA,6BAAkB,EAAC;QACrB,MAAM;QACN,IAAI,EAAE,MAAM;QACZ,MAAM,EAAE,UAAU;QAClB,oBAAoB,EAAE,YAAY,CAAC,EAAE;QACrC,gBAAgB,EAAE,yBAAyB,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,yBAAyB,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE;KACxG,CAAC,CAAC;IAEH,OAAO,CAAC,GAAG,CAAC,kCAAkC,MAAM,sBAAsB,CAAC,CAAC;AAChF,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,iBAAiB,CAAC,OAAuB;IACpD,MAAM,cAAc,GAAI,OAAe,CAAC,YAAY,CAAC;IACrD,IAAI,CAAC,cAAc,IAAI,OAAO,cAAc,KAAK,QAAQ,EAAE,CAAC;QACxD,OAAO;IACX,CAAC;IAED,yCAAyC;IACzC,MAAM,YAAY,GAAG,MAAM,cAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;IACzE,MAAM,wBAAwB,CAAC,YAAY,CAAC,CAAC;IAE7C,OAAO,CAAC,GAAG,CAAC,iCAAiC,cAAc,EAAE,CAAC,CAAC;AACnE,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,0BAA0B,CAAC,OAAuB;IAC7D,MAAM,cAAc,GAAI,OAAe,CAAC,YAAY,CAAC;IACrD,IAAI,CAAC,cAAc,IAAI,OAAO,cAAc,KAAK,QAAQ,EAAE,CAAC;QACxD,OAAO;IACX,CAAC;IAED,MAAM,MAAM,GAAI,OAAe,CAAC,oBAAoB,EAAE,QAAQ,EAAE,MAAM,CAAC;IACvE,IAAI,CAAC,MAAM,EAAE,CAAC;QACV,OAAO,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;QAC/C,OAAO;IACX,CAAC;IAED,yCAAyC;IACzC,MAAM,IAAA,6BAAkB,EAAC;QACrB,MAAM;QACN,MAAM,EAAE,UAAU;KACrB,CAAC,CAAC;IAEH,OAAO,CAAC,GAAG,CAAC,mCAAmC,cAAc,UAAU,MAAM,EAAE,CAAC,CAAC;IACjF,wCAAwC;AAC5C,CAAC;AAED;;GAEG;AACH,SAAgB,sBAAsB,CAClC,OAAwB,EACxB,SAAiB,EACjB,MAAc;IAEd,OAAO,cAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;AACtE,CAAC;AAED;;GAEG;AACI,KAAK,UAAU,sBAAsB,CAAC,oBAA4B;IACrE,IAAI,CAAC;QACD,OAAO,MAAM,cAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,oBAAoB,CAAC,CAAC;IACrE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,gCAAgC,EAAE,KAAK,CAAC,CAAC;QACvD,OAAO,IAAI,CAAC;IAChB,CAAC;AACL,CAAC;AAED;;GAEG;AACI,KAAK,UAAU,kBAAkB,CAAC,oBAA4B;IACjE,OAAO,MAAM,cAAM,CAAC,aAAa,CAAC,MAAM,CAAC,oBAAoB,EAAE;QAC3D,oBAAoB,EAAE,IAAI;KAC7B,CAAC,CAAC;AACP,CAAC;AAED;;GAEG;AACI,KAAK,UAAU,kBAAkB,CAAC,oBAA4B;IACjE,OAAO,MAAM,cAAM,CAAC,aAAa,CAAC,MAAM,CAAC,oBAAoB,EAAE;QAC3D,oBAAoB,EAAE,KAAK;KAC9B,CAAC,CAAC;AACP,CAAC;AAED;;GAEG;AACI,KAAK,UAAU,kBAAkB,CACpC,oBAA4B,EAC5B,UAAkB;IAElB,MAAM,YAAY,GAAG,MAAM,cAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,oBAAoB,CAAC,CAAC;IAE/E,OAAO,MAAM,cAAM,CAAC,aAAa,CAAC,MAAM,CAAC,oBAAoB,EAAE;QAC3D,KAAK,EAAE;YACH;gBACI,EAAE,EAAE,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE;gBACjC,KAAK,EAAE,UAAU;aACpB;SACJ;QACD,kBAAkB,EAAE,gBAAgB;KACvC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Validation History Service
3
+ *
4
+ * Manages storage and retrieval of validation history for domains.
5
+ * Supports both authenticated users (via monitoredDomains) and
6
+ * anonymous public access (with limited history).
7
+ */
8
+ import { type ValidationResult } from './validation.js';
9
+ export interface ValidationHistoryEntry {
10
+ id: string;
11
+ domainId: string;
12
+ score: number | null;
13
+ grade: string | null;
14
+ hasUcp: boolean | null;
15
+ issuesCount: number | null;
16
+ errorsCount: number | null;
17
+ warningsCount: number | null;
18
+ validatedAt: Date;
19
+ }
20
+ export interface ValidationTrend {
21
+ direction: 'improving' | 'declining' | 'stable' | 'insufficient_data';
22
+ scoreChange: number;
23
+ periodDays: number;
24
+ dataPoints: number;
25
+ }
26
+ export interface DomainHistoryResult {
27
+ domain: string;
28
+ currentScore: number | null;
29
+ currentGrade: string | null;
30
+ trend: ValidationTrend;
31
+ history: ValidationHistoryEntry[];
32
+ lastValidatedAt: Date | null;
33
+ }
34
+ export interface PublicHistoryResult {
35
+ domain: string;
36
+ history: Array<{
37
+ score: number | null;
38
+ grade: string | null;
39
+ hasUcp: boolean | null;
40
+ validatedAt: Date;
41
+ }>;
42
+ trend: ValidationTrend;
43
+ limitedAccess: boolean;
44
+ message?: string;
45
+ }
46
+ /**
47
+ * Store a validation result in history
48
+ */
49
+ export declare function storeValidationHistory(domainId: string, result: ValidationResult): Promise<ValidationHistoryEntry | null>;
50
+ /**
51
+ * Get validation history for a monitored domain (authenticated user)
52
+ */
53
+ export declare function getValidationHistory(domainId: string, limit?: number, daysBack?: number): Promise<ValidationHistoryEntry[]>;
54
+ /**
55
+ * Calculate trend from history entries
56
+ */
57
+ export declare function calculateTrend(history: ValidationHistoryEntry[]): ValidationTrend;
58
+ /**
59
+ * Get domain history with trend analysis (for authenticated users)
60
+ */
61
+ export declare function getDomainHistory(domainId: string, daysBack?: number): Promise<DomainHistoryResult | null>;
62
+ /**
63
+ * Get public history for a domain (limited to 3 entries for anonymous users)
64
+ * This looks up by domain name, not by user's monitored domain ID
65
+ */
66
+ export declare function getPublicDomainHistory(domainName: string, isAuthenticated?: boolean): Promise<PublicHistoryResult>;
67
+ /**
68
+ * Get the previous validation entry for a domain (for diffing)
69
+ * Returns the most recent entry excluding the given ID.
70
+ */
71
+ export declare function getPreviousValidation(domainId: string, excludeId?: string): Promise<{
72
+ id: string;
73
+ resultJson: string | null;
74
+ score: number | null;
75
+ grade: string | null;
76
+ } | null>;
77
+ /**
78
+ * Update a validation history entry with severity and change summary
79
+ */
80
+ export declare function updateHistorySeverity(historyId: string, severity: string, changesSummary: string): Promise<void>;
81
+ /**
82
+ * Run validation and store history for a monitored domain
83
+ */
84
+ export declare function validateAndStoreHistory(domainId: string, domainName: string): Promise<{
85
+ result: ValidationResult;
86
+ historyEntry: ValidationHistoryEntry | null;
87
+ }>;
88
+ /**
89
+ * Get validation history statistics
90
+ */
91
+ export declare function getHistoryStats(domainId: string): Promise<{
92
+ totalValidations: number;
93
+ averageScore: number;
94
+ bestScore: number;
95
+ worstScore: number;
96
+ firstValidation: Date | null;
97
+ lastValidation: Date | null;
98
+ }>;
99
+ //# sourceMappingURL=validation-history.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation-history.d.ts","sourceRoot":"","sources":["../../src/services/validation-history.ts"],"names":[],"mappings":"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"}