@revealui/services 0.0.3 → 0.1.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 (108) hide show
  1. package/LICENSE +22 -0
  2. package/LICENSE.commercial +112 -0
  3. package/README.md +177 -0
  4. package/dist/api/create-checkout-session/index.d.ts +2 -0
  5. package/dist/api/create-checkout-session/index.d.ts.map +1 -0
  6. package/dist/api/create-checkout-session/index.js +61 -0
  7. package/dist/api/create-checkout-session/index.js.map +1 -0
  8. package/dist/api/create-portal-link/index.d.ts +2 -0
  9. package/dist/api/create-portal-link/index.d.ts.map +1 -0
  10. package/dist/api/create-portal-link/index.js +37 -0
  11. package/dist/api/create-portal-link/index.js.map +1 -0
  12. package/dist/api/handlers/customer-handlers.d.ts +27 -0
  13. package/dist/api/handlers/customer-handlers.d.ts.map +1 -0
  14. package/dist/api/handlers/customer-handlers.js +86 -0
  15. package/dist/api/handlers/customer-handlers.js.map +1 -0
  16. package/dist/api/handlers/index.d.ts +18 -0
  17. package/dist/api/handlers/index.d.ts.map +1 -0
  18. package/dist/api/handlers/index.js +18 -0
  19. package/dist/api/handlers/index.js.map +1 -0
  20. package/dist/api/handlers/invoice-handlers.d.ts +9 -0
  21. package/dist/api/handlers/invoice-handlers.d.ts.map +1 -0
  22. package/dist/api/handlers/invoice-handlers.js +52 -0
  23. package/dist/api/handlers/invoice-handlers.js.map +1 -0
  24. package/dist/api/handlers/payment-handlers.d.ts +13 -0
  25. package/dist/api/handlers/payment-handlers.d.ts.map +1 -0
  26. package/dist/api/handlers/payment-handlers.js +133 -0
  27. package/dist/api/handlers/payment-handlers.js.map +1 -0
  28. package/dist/api/handlers/payment-intent.d.ts +21 -0
  29. package/dist/api/handlers/payment-intent.d.ts.map +1 -0
  30. package/dist/api/handlers/payment-intent.js +87 -0
  31. package/dist/api/handlers/payment-intent.js.map +1 -0
  32. package/dist/api/handlers/product-handlers.d.ts +11 -0
  33. package/dist/api/handlers/product-handlers.d.ts.map +1 -0
  34. package/dist/api/handlers/product-handlers.js +43 -0
  35. package/dist/api/handlers/product-handlers.js.map +1 -0
  36. package/dist/api/handlers/subscription-handlers.d.ts +13 -0
  37. package/dist/api/handlers/subscription-handlers.d.ts.map +1 -0
  38. package/dist/api/handlers/subscription-handlers.js +115 -0
  39. package/dist/api/handlers/subscription-handlers.js.map +1 -0
  40. package/dist/api/index.d.ts +8 -0
  41. package/dist/api/index.d.ts.map +1 -0
  42. package/dist/api/index.js +8 -0
  43. package/dist/api/index.js.map +1 -0
  44. package/dist/api/types/stripe.d.ts +42 -0
  45. package/dist/api/types/stripe.d.ts.map +1 -0
  46. package/dist/api/types/stripe.js +72 -0
  47. package/dist/api/types/stripe.js.map +1 -0
  48. package/dist/api/update-price/index.d.ts +42 -0
  49. package/dist/api/update-price/index.d.ts.map +1 -0
  50. package/dist/api/update-price/index.js +78 -0
  51. package/dist/api/update-price/index.js.map +1 -0
  52. package/dist/api/update-product/index.d.ts +44 -0
  53. package/dist/api/update-product/index.d.ts.map +1 -0
  54. package/dist/api/update-product/index.js +85 -0
  55. package/dist/api/update-product/index.js.map +1 -0
  56. package/dist/api/utils.d.ts +34 -0
  57. package/dist/api/utils.d.ts.map +1 -0
  58. package/dist/api/utils.js +66 -0
  59. package/dist/api/utils.js.map +1 -0
  60. package/dist/api/webhooks/index.d.ts +2 -0
  61. package/dist/api/webhooks/index.d.ts.map +1 -0
  62. package/dist/api/webhooks/index.js +270 -0
  63. package/dist/api/webhooks/index.js.map +1 -0
  64. package/dist/client/index.d.ts +11 -0
  65. package/dist/client/index.d.ts.map +1 -0
  66. package/dist/client/index.js +12 -0
  67. package/dist/client/index.js.map +1 -0
  68. package/dist/index.d.ts +32 -24
  69. package/dist/index.d.ts.map +1 -0
  70. package/dist/index.js +45 -41
  71. package/dist/index.js.map +1 -0
  72. package/dist/stripe/db-circuit-breaker.d.ts +47 -0
  73. package/dist/stripe/db-circuit-breaker.d.ts.map +1 -0
  74. package/dist/stripe/db-circuit-breaker.js +223 -0
  75. package/dist/stripe/db-circuit-breaker.js.map +1 -0
  76. package/dist/stripe/index.d.ts +2 -0
  77. package/dist/stripe/index.d.ts.map +1 -0
  78. package/dist/stripe/index.js +2 -0
  79. package/dist/stripe/index.js.map +1 -0
  80. package/dist/stripe/stripeClient.d.ts +126 -0
  81. package/dist/stripe/stripeClient.d.ts.map +1 -0
  82. package/dist/stripe/stripeClient.js +226 -0
  83. package/dist/stripe/stripeClient.js.map +1 -0
  84. package/dist/supabase/index.d.ts +6 -0
  85. package/dist/supabase/index.d.ts.map +1 -0
  86. package/dist/supabase/index.js +5 -0
  87. package/dist/supabase/index.js.map +1 -0
  88. package/dist/supabase/resilience.d.ts +50 -0
  89. package/dist/supabase/resilience.d.ts.map +1 -0
  90. package/dist/supabase/resilience.js +166 -0
  91. package/dist/supabase/resilience.js.map +1 -0
  92. package/dist/supabase/types.d.ts +206 -0
  93. package/dist/supabase/types.d.ts.map +1 -0
  94. package/dist/supabase/types.js +19 -0
  95. package/dist/supabase/types.js.map +1 -0
  96. package/dist/supabase/utils/client.d.ts +4 -0
  97. package/dist/supabase/utils/client.d.ts.map +1 -0
  98. package/dist/supabase/utils/client.js +12 -0
  99. package/dist/supabase/utils/client.js.map +1 -0
  100. package/dist/supabase/utils/server.d.ts +10 -0
  101. package/dist/supabase/utils/server.d.ts.map +1 -0
  102. package/dist/supabase/utils/server.js +49 -0
  103. package/dist/supabase/utils/server.js.map +1 -0
  104. package/dist/supabase/utils/web.d.ts +4 -0
  105. package/dist/supabase/utils/web.d.ts.map +1 -0
  106. package/dist/supabase/utils/web.js +37 -0
  107. package/dist/supabase/utils/web.js.map +1 -0
  108. package/package.json +73 -16
@@ -0,0 +1,270 @@
1
+ import { generateLicenseKey } from '@revealui/core/license';
2
+ import { logger } from '@revealui/core/utils/logger';
3
+ import { getClient } from '@revealui/db/client';
4
+ import { processedWebhookEvents } from '@revealui/db/schema';
5
+ import { eq } from 'drizzle-orm';
6
+ import { protectedStripe } from '../../stripe/stripeClient.js';
7
+ import { createServerClientFromRequest } from '../../supabase/index.js';
8
+ import { handleInvoicePaymentFailed, handleInvoicePaymentSucceeded, handlePaymentMethodAttached, manageSubscriptionStatusChange, upsertPriceRecord, upsertProductRecord, } from '../utils.js';
9
+ const relevantEvents = new Set([
10
+ 'product.created',
11
+ 'product.updated',
12
+ 'price.created',
13
+ 'price.updated',
14
+ 'checkout.session.completed',
15
+ 'customer.subscription.created',
16
+ 'customer.subscription.updated',
17
+ 'customer.subscription.deleted',
18
+ 'invoice.payment_succeeded',
19
+ 'invoice.payment_failed',
20
+ 'payment_method.attached',
21
+ ]);
22
+ /** Maximum webhook payload size (10 MB). Stripe payloads are tiny; this guards against DoS. */
23
+ const MAX_BODY_BYTES = 10 * 1024 * 1024;
24
+ /**
25
+ * Resolve the webhook secret from environment variables.
26
+ * Throws at startup/request time if no secret is configured, preventing
27
+ * unsigned webhook acceptance.
28
+ */
29
+ function getWebhookSecret() {
30
+ const secret = (typeof import.meta.env.STRIPE_WEBHOOK_SECRET_LIVE === 'string'
31
+ ? import.meta.env.STRIPE_WEBHOOK_SECRET_LIVE
32
+ : undefined) ??
33
+ (typeof import.meta.env.STRIPE_WEBHOOK_SECRET === 'string'
34
+ ? import.meta.env.STRIPE_WEBHOOK_SECRET
35
+ : undefined);
36
+ if (!secret) {
37
+ throw new Error('STRIPE_WEBHOOK_SECRET or STRIPE_WEBHOOK_SECRET_LIVE must be configured. ' +
38
+ 'Refusing to process webhooks without signature verification.');
39
+ }
40
+ return secret;
41
+ }
42
+ /**
43
+ * Database-backed idempotency check. Prevents duplicate webhook processing
44
+ * across Vercel cold starts, multi-region deployments, and server restarts.
45
+ */
46
+ async function isEventAlreadyProcessed(eventId) {
47
+ const db = getClient();
48
+ const [existing] = await db
49
+ .select({ id: processedWebhookEvents.id })
50
+ .from(processedWebhookEvents)
51
+ .where(eq(processedWebhookEvents.id, eventId))
52
+ .limit(1);
53
+ return !!existing;
54
+ }
55
+ async function markEventProcessed(eventId, eventType) {
56
+ const db = getClient();
57
+ await db.insert(processedWebhookEvents).values({ id: eventId, eventType }).onConflictDoNothing();
58
+ }
59
+ export async function POST(request) {
60
+ const supabase = createServerClientFromRequest(request);
61
+ if (!supabase) {
62
+ return new Response('Supabase client not available', { status: 500 });
63
+ }
64
+ let webhookSecret;
65
+ try {
66
+ webhookSecret = getWebhookSecret();
67
+ }
68
+ catch (err) {
69
+ const msg = err instanceof Error ? err.message : 'Unknown error';
70
+ logger.error('Webhook secret not configured', { error: msg });
71
+ return new Response('Webhook endpoint not configured', { status: 500 });
72
+ }
73
+ // Reject oversized payloads before reading into memory (DoS protection).
74
+ // Content-Length is advisory; we still enforce after reading.
75
+ const contentLength = request.headers.get('content-length');
76
+ if (contentLength !== null && Number(contentLength) > MAX_BODY_BYTES) {
77
+ return new Response('Payload too large', { status: 413 });
78
+ }
79
+ const body = await request.text();
80
+ if (body.length > MAX_BODY_BYTES) {
81
+ return new Response('Payload too large', { status: 413 });
82
+ }
83
+ const sig = request.headers.get('Stripe-Signature');
84
+ if (!sig) {
85
+ return new Response('Missing Stripe-Signature header', { status: 400 });
86
+ }
87
+ let event;
88
+ try {
89
+ event = protectedStripe.webhooks.constructEvent(body, sig, webhookSecret);
90
+ }
91
+ catch (err) {
92
+ const errorMessage = err instanceof Error ? err.message : 'Unknown error';
93
+ logger.error('Webhook signature verification failed', {
94
+ error: errorMessage,
95
+ });
96
+ return new Response(`Webhook Error: ${errorMessage}`, { status: 400 });
97
+ }
98
+ // Idempotency check: skip already-processed events
99
+ if (await isEventAlreadyProcessed(event.id)) {
100
+ logger.debug('Skipping duplicate webhook event', { eventId: event.id });
101
+ return new Response(JSON.stringify({ received: true, duplicate: true }), { status: 200 });
102
+ }
103
+ if (relevantEvents.has(event.type)) {
104
+ try {
105
+ // Mark event as processing BEFORE handling — prevents duplicate processing
106
+ // on Stripe retries even if the handler throws below.
107
+ await markEventProcessed(event.id, event.type);
108
+ switch (event.type) {
109
+ case 'product.created':
110
+ case 'product.updated': {
111
+ const product = event.data.object;
112
+ if (product.object === 'product') {
113
+ await upsertProductRecord(supabase, product);
114
+ }
115
+ break;
116
+ }
117
+ case 'price.created':
118
+ case 'price.updated': {
119
+ const price = event.data.object;
120
+ if (price.object === 'price') {
121
+ await upsertPriceRecord(supabase, price);
122
+ }
123
+ break;
124
+ }
125
+ case 'customer.subscription.created':
126
+ case 'customer.subscription.updated':
127
+ case 'customer.subscription.deleted': {
128
+ const subscription = event.data.object;
129
+ if (subscription.object === 'subscription') {
130
+ const customerId = typeof subscription.customer === 'string'
131
+ ? subscription.customer
132
+ : subscription.customer?.id;
133
+ if (!customerId) {
134
+ throw new Error('Subscription missing customer');
135
+ }
136
+ await manageSubscriptionStatusChange(subscription.id, customerId, event.type === 'customer.subscription.created', supabase);
137
+ // Flag license for revocation on subscription cancellation
138
+ if (event.type === 'customer.subscription.deleted') {
139
+ logger.info('Subscription deleted — license should be revoked', {
140
+ customerId,
141
+ subscriptionId: subscription.id,
142
+ });
143
+ }
144
+ }
145
+ break;
146
+ }
147
+ case 'checkout.session.completed': {
148
+ const checkoutSession = event.data.object;
149
+ if (checkoutSession.object === 'checkout.session') {
150
+ const session = checkoutSession;
151
+ if (session.mode === 'subscription' && session.subscription) {
152
+ const subscriptionId = typeof session.subscription === 'string'
153
+ ? session.subscription
154
+ : session.subscription.id;
155
+ const customerId = typeof session.customer === 'string' ? session.customer : session.customer?.id;
156
+ if (subscriptionId && customerId) {
157
+ await manageSubscriptionStatusChange(subscriptionId, customerId, true, supabase);
158
+ // Retrieve the subscription to:
159
+ // (a) check if a license key was already generated (idempotency across retries)
160
+ // (b) get the actual price ID for authoritative tier resolution
161
+ const subscription = await protectedStripe.subscriptions.retrieve(subscriptionId);
162
+ if (subscription.metadata?.license_key) {
163
+ // Already generated — Stripe retried the webhook. Safe to skip.
164
+ logger.info('License key already present, skipping generation (idempotent)', {
165
+ customerId,
166
+ subscriptionId,
167
+ });
168
+ }
169
+ else {
170
+ const priceId = subscription.items.data[0]?.price?.id ?? null;
171
+ const tier = resolveTierFromMetadata(session.metadata, priceId);
172
+ const privateKey = process.env.REVEALUI_LICENSE_PRIVATE_KEY;
173
+ if (!privateKey) {
174
+ // This is a configuration error, not a customer error.
175
+ // The subscription is active but no license can be issued.
176
+ logger.error('CRITICAL: REVEALUI_LICENSE_PRIVATE_KEY not set — license not generated. ' +
177
+ 'Manual intervention required.', { customerId, subscriptionId, tier });
178
+ }
179
+ else if (tier !== 'free') {
180
+ try {
181
+ const licenseKey = await generateLicenseKey({ tier, customerId }, privateKey);
182
+ await protectedStripe.subscriptions.update(subscriptionId, {
183
+ metadata: { license_key: licenseKey, license_tier: tier },
184
+ });
185
+ logger.info('License key generated for checkout', { tier, customerId });
186
+ }
187
+ catch (licenseErr) {
188
+ logger.error('CRITICAL: License generation failed after successful checkout. ' +
189
+ 'Manual intervention required.', {
190
+ error: licenseErr instanceof Error ? licenseErr.message : 'Unknown error',
191
+ customerId,
192
+ subscriptionId,
193
+ tier,
194
+ });
195
+ }
196
+ }
197
+ }
198
+ logger.info('Subscription session completed');
199
+ }
200
+ }
201
+ }
202
+ break;
203
+ }
204
+ case 'invoice.payment_succeeded': {
205
+ const invoiceEvent = event;
206
+ await handleInvoicePaymentSucceeded(invoiceEvent, supabase);
207
+ break;
208
+ }
209
+ case 'invoice.payment_failed': {
210
+ const invoiceEvent = event;
211
+ await handleInvoicePaymentFailed(invoiceEvent, supabase);
212
+ break;
213
+ }
214
+ case 'payment_method.attached': {
215
+ const pmEvent = event;
216
+ await handlePaymentMethodAttached(pmEvent, supabase);
217
+ break;
218
+ }
219
+ default:
220
+ throw new Error('Unhandled relevant event!');
221
+ }
222
+ }
223
+ catch (error) {
224
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
225
+ logger.error('Webhook handler error', { error: errorMessage });
226
+ return new Response(`Webhook error: "Webhook handler failed. View logs." Error: ${errorMessage}`, {
227
+ status: 400,
228
+ });
229
+ }
230
+ }
231
+ return new Response(JSON.stringify({ received: true }), { status: 200 });
232
+ }
233
+ /**
234
+ * Resolve license tier from the subscription's actual price ID and checkout metadata.
235
+ *
236
+ * Price ID takes precedence — it cannot be spoofed because it comes from
237
+ * Stripe's own subscription record, not from client-supplied data.
238
+ * Metadata is a fallback for products that predate the env-var tier mapping.
239
+ */
240
+ function resolveTierFromMetadata(metadata, priceId) {
241
+ // Primary: validate against known price IDs from environment
242
+ if (priceId) {
243
+ const proPriceId = process.env.STRIPE_PRO_PRICE_ID;
244
+ const maxPriceId = process.env.STRIPE_MAX_PRICE_ID;
245
+ const enterprisePriceId = process.env.STRIPE_ENTERPRISE_PRICE_ID;
246
+ if (proPriceId && priceId === proPriceId)
247
+ return 'pro';
248
+ if (maxPriceId && priceId === maxPriceId)
249
+ return 'max';
250
+ if (enterprisePriceId && priceId === enterprisePriceId)
251
+ return 'enterprise';
252
+ }
253
+ // Fallback: metadata set server-side during checkout session creation (not customer-supplied)
254
+ const tier = metadata?.tier;
255
+ if (tier === 'enterprise')
256
+ return 'enterprise';
257
+ if (tier === 'max')
258
+ return 'max';
259
+ if (tier === 'pro')
260
+ return 'pro';
261
+ // No tier could be resolved — this is a configuration error.
262
+ // Throw so Stripe retries (500) and the team can investigate.
263
+ logger.error('Cannot resolve license tier — unknown price ID and no metadata', {
264
+ priceId,
265
+ metadata,
266
+ });
267
+ throw new Error(`Cannot resolve license tier: price ID "${priceId}" does not match any configured tier. ` +
268
+ 'Check STRIPE_PRO_PRICE_ID, STRIPE_MAX_PRICE_ID, and STRIPE_ENTERPRISE_PRICE_ID env vars.');
269
+ }
270
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/api/webhooks/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * services/client - Client-side Services
3
+ *
4
+ * Client-side service integrations for browser environments:
5
+ * - Browser clients
6
+ *
7
+ * These are client-side implementations for use in React
8
+ * components and browser environments.
9
+ */
10
+ export { createBrowserClient } from '../supabase/index.js';
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAA"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * services/client - Client-side Services
3
+ *
4
+ * Client-side service integrations for browser environments:
5
+ * - Browser clients
6
+ *
7
+ * These are client-side implementations for use in React
8
+ * components and browser environments.
9
+ */
10
+ // Export browser client creation functions
11
+ export { createBrowserClient } from '../supabase/index.js';
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,2CAA2C;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAA"}
package/dist/index.d.ts CHANGED
@@ -1,27 +1,35 @@
1
1
  /**
2
- * @revealui/services type stubs for public repo typecheck
2
+ * services - Shared Services Package
3
3
  *
4
- * The full implementation is in the Pro package.
5
- * These stubs satisfy TypeScript while the Pro package awaits
6
- * its first proper npm publish.
4
+ * Provides server-side and client-side service integrations:
5
+ * - Stripe payment processing
6
+ * - Supabase database and auth
7
+ * - API routes
8
+ *
9
+ * ## Usage
10
+ *
11
+ * ### Full Package (Recommended)
12
+ * ```typescript
13
+ * import { protectedStripe, createServerClient, createBrowserClient } from 'services'
14
+ * ```
15
+ *
16
+ * ### Core (Server-side)
17
+ * ```typescript
18
+ * import { createServerClient, protectedStripe } from 'services/server'
19
+ * ```
20
+ *
21
+ * ### Client (Browser)
22
+ * ```typescript
23
+ * import { createBrowserClient } from 'services/client'
24
+ * ```
25
+ */
26
+ /**
27
+ * Check if payment services are licensed for use.
28
+ * Returns false with a warning log if no Pro/Enterprise license is active.
7
29
  */
8
- import type Stripe from 'stripe'
9
-
10
- // ---------------------------------------------------------------------------
11
- // Stripe
12
- // ---------------------------------------------------------------------------
13
-
14
- /** Stripe client wrapped with circuit-breaker protection */
15
- export declare const protectedStripe: Stripe
16
-
17
- // ---------------------------------------------------------------------------
18
- // Supabase
19
- // ---------------------------------------------------------------------------
20
-
21
- export declare function createServerClient(...args: unknown[]): unknown
22
-
23
- // ---------------------------------------------------------------------------
24
- // Payments
25
- // ---------------------------------------------------------------------------
26
-
27
- export declare function createPaymentIntent(...args: unknown[]): Promise<unknown>
30
+ export declare function checkServicesLicense(): boolean;
31
+ export * from './api/index.js';
32
+ export * from './client/index.js';
33
+ export * from './stripe/index.js';
34
+ export * from './supabase/index.js';
35
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAKH;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,OAAO,CAS9C;AAGD,cAAc,gBAAgB,CAAA;AAE9B,cAAc,mBAAmB,CAAA;AACjC,cAAc,mBAAmB,CAAA;AACjC,cAAc,qBAAqB,CAAA"}
package/dist/index.js CHANGED
@@ -1,42 +1,46 @@
1
- // @revealui/services — stub for public repo builds
2
- // Full implementation lives in the Pro package.
3
- // These stubs export real named values so bundlers can resolve them.
4
- // Methods throw at call time if the Pro package is not installed.
5
-
6
- function notImplemented(name) {
7
- return function () {
8
- throw new Error(
9
- '@revealui/services: ' + name + ' requires the Pro package or STRIPE_SECRET_KEY.'
10
- )
11
- }
1
+ /**
2
+ * services - Shared Services Package
3
+ *
4
+ * Provides server-side and client-side service integrations:
5
+ * - Stripe payment processing
6
+ * - Supabase database and auth
7
+ * - API routes
8
+ *
9
+ * ## Usage
10
+ *
11
+ * ### Full Package (Recommended)
12
+ * ```typescript
13
+ * import { protectedStripe, createServerClient, createBrowserClient } from 'services'
14
+ * ```
15
+ *
16
+ * ### Core (Server-side)
17
+ * ```typescript
18
+ * import { createServerClient, protectedStripe } from 'services/server'
19
+ * ```
20
+ *
21
+ * ### Client (Browser)
22
+ * ```typescript
23
+ * import { createBrowserClient } from 'services/client'
24
+ * ```
25
+ */
26
+ import { isFeatureEnabled } from '@revealui/core/features';
27
+ import { logger } from '@revealui/core/observability/logger';
28
+ /**
29
+ * Check if payment services are licensed for use.
30
+ * Returns false with a warning log if no Pro/Enterprise license is active.
31
+ */
32
+ export function checkServicesLicense() {
33
+ if (!isFeatureEnabled('payments')) {
34
+ logger.warn('[@revealui/services] Payment and service integrations require a Pro or Enterprise license. ' +
35
+ 'Visit https://revealui.com/pricing for details.');
36
+ return false;
37
+ }
38
+ return true;
12
39
  }
13
-
14
- export const protectedStripe = {
15
- billingPortal: { sessions: { create: notImplemented('billingPortal.sessions.create') } },
16
- customers: {
17
- create: notImplemented('customers.create'),
18
- retrieve: notImplemented('customers.retrieve'),
19
- update: notImplemented('customers.update'),
20
- del: notImplemented('customers.del'),
21
- list: notImplemented('customers.list'),
22
- },
23
- paymentIntents: {
24
- create: notImplemented('paymentIntents.create'),
25
- retrieve: notImplemented('paymentIntents.retrieve'),
26
- update: notImplemented('paymentIntents.update'),
27
- },
28
- checkout: {
29
- sessions: {
30
- create: notImplemented('checkout.sessions.create'),
31
- retrieve: notImplemented('checkout.sessions.retrieve'),
32
- },
33
- },
34
- products: { create: notImplemented('products.create'), retrieve: notImplemented('products.retrieve'), update: notImplemented('products.update'), list: notImplemented('products.list') },
35
- prices: { create: notImplemented('prices.create'), retrieve: notImplemented('prices.retrieve'), update: notImplemented('prices.update'), list: notImplemented('prices.list') },
36
- subscriptions: { retrieve: notImplemented('subscriptions.retrieve'), update: notImplemented('subscriptions.update'), cancel: notImplemented('subscriptions.cancel') },
37
- webhooks: { constructEvent: notImplemented('webhooks.constructEvent') },
38
- balance: { retrieve: notImplemented('balance.retrieve') },
39
- }
40
-
41
- export function createServerClient() { return null }
42
- export function createPaymentIntent() { return Promise.reject(new Error('@revealui/services stub')) }
40
+ // Re-export core (server-side) exports
41
+ export * from './api/index.js';
42
+ // Re-export client (client-side) exports
43
+ export * from './client/index.js';
44
+ export * from './stripe/index.js';
45
+ export * from './supabase/index.js';
46
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAC1D,OAAO,EAAE,MAAM,EAAE,MAAM,qCAAqC,CAAA;AAE5D;;;GAGG;AACH,MAAM,UAAU,oBAAoB;IAClC,IAAI,CAAC,gBAAgB,CAAC,UAAU,CAAC,EAAE,CAAC;QAClC,MAAM,CAAC,IAAI,CACT,6FAA6F;YAC3F,iDAAiD,CACpD,CAAA;QACD,OAAO,KAAK,CAAA;IACd,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,uCAAuC;AACvC,cAAc,gBAAgB,CAAA;AAC9B,yCAAyC;AACzC,cAAc,mBAAmB,CAAA;AACjC,cAAc,mBAAmB,CAAA;AACjC,cAAc,qBAAqB,CAAA"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * DB-Backed Circuit Breaker
3
+ *
4
+ * Stores circuit state in NeonDB so all API instances share the same view.
5
+ *
6
+ * Architecture:
7
+ * - Local in-memory cache (5s TTL) — fast read path, no DB hit per request
8
+ * - DB write only on state transitions (open/closed/half-open changes)
9
+ * - Fail-open on DB errors: if we can't read state, we let the call through
10
+ * rather than blocking all traffic because the circuit state store is down
11
+ */
12
+ export interface DbCircuitBreakerConfig {
13
+ /** Number of consecutive failures before tripping. Default: 5 */
14
+ failureThreshold: number;
15
+ /** Consecutive successes in half-open to close the circuit. Default: 2 */
16
+ successThreshold: number;
17
+ /** Milliseconds to wait in open state before probing. Default: 30_000 */
18
+ resetTimeout: number;
19
+ /** Local cache TTL in milliseconds. Default: 5_000 */
20
+ cacheTtlMs: number;
21
+ }
22
+ export declare class DbCircuitBreaker {
23
+ private readonly serviceName;
24
+ private readonly config;
25
+ constructor(serviceName: string, config?: Partial<DbCircuitBreakerConfig>);
26
+ /**
27
+ * Returns true if the circuit is open (requests should be blocked).
28
+ * Automatically transitions open→half-open when resetTimeout elapses.
29
+ */
30
+ isOpen(): Promise<boolean>;
31
+ /**
32
+ * Record a successful call. Closes the circuit if enough successes in half-open.
33
+ */
34
+ recordSuccess(): Promise<void>;
35
+ /**
36
+ * Record a failed call. Trips the circuit when the failure threshold is reached.
37
+ */
38
+ recordFailure(): Promise<void>;
39
+ /** Force-reset state and clear local cache. Primarily for testing. */
40
+ reset(): Promise<void>;
41
+ /** Clear only the local cache (forces next read to hit DB). For testing. */
42
+ clearLocalCache(): void;
43
+ private readState;
44
+ private readFromDb;
45
+ private writeState;
46
+ }
47
+ //# sourceMappingURL=db-circuit-breaker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db-circuit-breaker.d.ts","sourceRoot":"","sources":["../../src/stripe/db-circuit-breaker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAoBH,MAAM,WAAW,sBAAsB;IACrC,iEAAiE;IACjE,gBAAgB,EAAE,MAAM,CAAC;IACzB,0EAA0E;IAC1E,gBAAgB,EAAE,MAAM,CAAC;IACzB,yEAAyE;IACzE,YAAY,EAAE,MAAM,CAAC;IACrB,sDAAsD;IACtD,UAAU,EAAE,MAAM,CAAC;CACpB;AAYD,qBAAa,gBAAgB;IAIzB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAH9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAyB;gBAG7B,WAAW,EAAE,MAAM,EACpC,MAAM,GAAE,OAAO,CAAC,sBAAsB,CAAM;IAK9C;;;OAGG;IACG,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;IAsBhC;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IA8BpC;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAyBpC,sEAAsE;IAChE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAY5B,4EAA4E;IAC5E,eAAe,IAAI,IAAI;YAMT,SAAS;YAQT,UAAU;YA+CV,UAAU;CA2CzB"}