@omnibase/core-js 0.5.0 → 0.5.2

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.
@@ -0,0 +1,556 @@
1
+ // src/payments/checkout.ts
2
+ var CheckoutManager = class {
3
+ /**
4
+ * Initialize the checkout manager
5
+ *
6
+ * @param paymentHandler - Payment handler instance for API communication
7
+ *
8
+ * @group Checkout
9
+ */
10
+ constructor(omnibaseClient) {
11
+ this.omnibaseClient = omnibaseClient;
12
+ }
13
+ /**
14
+ * Create a new Stripe checkout session
15
+ *
16
+ * Creates a checkout session with the specified options and returns
17
+ * the session URL for redirecting the user to complete payment.
18
+ * The session will be configured for either one-time payment or
19
+ * subscription based on the mode parameter.
20
+ *
21
+ * @param options - Configuration options for the checkout session
22
+ * @param options.price_id - Stripe price ID for the product/service
23
+ * @param options.mode - Payment mode ('payment' for one-time, 'subscription' for recurring)
24
+ * @param options.success_url - URL to redirect after successful payment
25
+ * @param options.cancel_url - URL to redirect if user cancels
26
+ * @param options.customer_id - Optional existing Stripe customer ID
27
+ *
28
+ * @returns Promise resolving to checkout session response with URL and session ID
29
+ *
30
+ * @throws {Error} When the API request fails due to network issues
31
+ * @throws {Error} When the server returns an error response (invalid price_id, etc.)
32
+ * @throws {ValidationError} When required parameters are missing or invalid
33
+ *
34
+ * @example
35
+ * One-time payment checkout:
36
+ * ```typescript
37
+ * const session = await checkoutManager.createSession({
38
+ * price_id: 'price_one_time_product',
39
+ * mode: 'payment',
40
+ * success_url: 'https://app.com/success',
41
+ * cancel_url: 'https://app.com/cancel'
42
+ * });
43
+ *
44
+ * // Redirect to Stripe checkout
45
+ * window.location.href = session.data.url;
46
+ * ```
47
+ *
48
+ * @example
49
+ * Subscription checkout with existing customer:
50
+ * ```typescript
51
+ * const session = await checkoutManager.createSession({
52
+ * price_id: 'price_monthly_plan',
53
+ * mode: 'subscription',
54
+ * success_url: 'https://app.com/dashboard?welcome=true',
55
+ * cancel_url: 'https://app.com/pricing',
56
+ * customer_id: 'cus_12345'
57
+ * });
58
+ *
59
+ * console.log(`Session created: ${session.data.sessionId}`);
60
+ * ```
61
+ *
62
+ * @since 1.0.0
63
+ * @group Checkout
64
+ */
65
+ async createSession(options) {
66
+ const response = await this.omnibaseClient.fetch(
67
+ "/api/v1/payments/checkout",
68
+ {
69
+ method: "POST",
70
+ headers: {
71
+ "Content-Type": "application/json"
72
+ },
73
+ body: JSON.stringify(options)
74
+ }
75
+ );
76
+ if (!response.ok) {
77
+ const errorData = await response.text();
78
+ throw new Error(
79
+ `Failed to create checkout session: ${response.status} - ${errorData}`
80
+ );
81
+ }
82
+ const result = await response.json();
83
+ return result;
84
+ }
85
+ };
86
+
87
+ // src/payments/config.ts
88
+ var ConfigManager = class {
89
+ constructor(omnibaseClient) {
90
+ this.omnibaseClient = omnibaseClient;
91
+ }
92
+ /**
93
+ * Get the current Stripe configuration from the database
94
+ *
95
+ * Retrieves the latest Stripe configuration including products, prices,
96
+ * and UI customization data. This configuration represents the current
97
+ * active pricing structure with all UI elements for pricing table rendering.
98
+ *
99
+ * @returns Promise resolving to the current Stripe configuration
100
+ *
101
+ * @throws {Error} When the API request fails due to network issues
102
+ * @throws {Error} When the server returns an error response (4xx, 5xx status codes)
103
+ *
104
+ * @example
105
+ * Basic usage:
106
+ * ```typescript
107
+ * const config = await getStripeConfig();
108
+ * console.log(`Found ${config.data.config.products.length} products`);
109
+ *
110
+ * // Access product UI configuration
111
+ * config.data.config.products.forEach(product => {
112
+ * console.log(`${product.name}: ${product.ui?.tagline || 'No tagline'}`);
113
+ * });
114
+ * ```
115
+ */
116
+ async getStripeConfig() {
117
+ try {
118
+ const response = await this.omnibaseClient.fetch(
119
+ `/api/v1/stripe/config`,
120
+ {
121
+ method: "GET",
122
+ headers: {
123
+ "Content-Type": "application/json"
124
+ },
125
+ credentials: "include"
126
+ }
127
+ );
128
+ if (!response.ok) {
129
+ const errorData = await response.text();
130
+ throw new Error(
131
+ `Failed to get Stripe config: ${response.status} - ${errorData}`
132
+ );
133
+ }
134
+ const data = await response.json();
135
+ return data;
136
+ } catch (error) {
137
+ console.error("Error getting Stripe config:", error);
138
+ throw error;
139
+ }
140
+ }
141
+ /**
142
+ * Get available products with UI-ready pricing data
143
+ *
144
+ * Transforms the raw Stripe configuration into UI-ready format for pricing
145
+ * table rendering. Includes formatted pricing, features, limits, and all
146
+ * display customizations needed for marketing pages.
147
+ *
148
+ * @returns Promise resolving to products ready for UI consumption
149
+ *
150
+ * @throws {Error} When the API request fails or configuration is invalid
151
+ *
152
+ * @example
153
+ * Pricing table rendering:
154
+ * ```typescript
155
+ * const products = await getAvailableProducts();
156
+ *
157
+ * products.forEach(product => {
158
+ * const display = product.pricing_display;
159
+ * console.log(`${display.name} - ${display.tagline}`);
160
+ *
161
+ * display.prices.forEach(price => {
162
+ * console.log(` ${price.display_name}: ${price.formatted_price}`);
163
+ * });
164
+ * });
165
+ * ```
166
+ */
167
+ async getAvailableProducts() {
168
+ const configResponse = await this.getStripeConfig();
169
+ if (!configResponse.data?.config) {
170
+ throw new Error("No Stripe configuration found");
171
+ }
172
+ const products = configResponse.data.config.products;
173
+ return products.map(transformProductToUIReady).sort(
174
+ (a, b) => a.pricing_display.sort_order - b.pricing_display.sort_order
175
+ );
176
+ }
177
+ /**
178
+ * Get a specific product by ID
179
+ *
180
+ * Retrieves a single product configuration by its ID from the current
181
+ * Stripe configuration. Useful for product-specific operations.
182
+ *
183
+ * @param productId - The configuration product ID to retrieve
184
+ * @returns Promise resolving to the product or null if not found
185
+ *
186
+ * @example
187
+ * ```typescript
188
+ * const product = await getProduct('starter_plan');
189
+ * if (product) {
190
+ * console.log(`Found product: ${product.name}`);
191
+ * }
192
+ * ```
193
+ */
194
+ async getProduct(productId) {
195
+ const configResponse = await this.getStripeConfig();
196
+ if (!configResponse.data?.config) {
197
+ return null;
198
+ }
199
+ const product = configResponse.data.config.products.find(
200
+ (p) => p.id === productId
201
+ );
202
+ return product || null;
203
+ }
204
+ };
205
+ function transformProductToUIReady(product) {
206
+ const ui = product.ui || {};
207
+ return {
208
+ ...product,
209
+ pricing_display: {
210
+ name: ui.display_name || product.name,
211
+ tagline: ui.tagline,
212
+ features: ui.features || [],
213
+ badge: ui.badge,
214
+ cta_text: ui.cta_text || "Choose Plan",
215
+ highlighted: ui.highlighted || false,
216
+ sort_order: ui.sort_order || 0,
217
+ prices: product.prices.map((price) => {
218
+ const priceUI = price.ui || {};
219
+ return {
220
+ id: price.id,
221
+ display_name: priceUI.display_name || formatDefaultPriceName(price),
222
+ formatted_price: formatPrice(price, priceUI),
223
+ billing_period: priceUI.billing_period || formatDefaultBillingPeriod(price),
224
+ features: priceUI.features || [],
225
+ limits: priceUI.limits || []
226
+ };
227
+ })
228
+ }
229
+ };
230
+ }
231
+ function formatPrice(price, priceUI) {
232
+ if (priceUI.price_display?.custom_text) {
233
+ return priceUI.price_display.custom_text;
234
+ }
235
+ if (!price.amount || price.amount === 0) {
236
+ return "Free";
237
+ }
238
+ const amount = price.amount / 100;
239
+ const currency = price.currency.toUpperCase();
240
+ let formattedPrice = "";
241
+ if (priceUI.price_display?.show_currency !== false) {
242
+ const currencySymbol = getCurrencySymbol(currency);
243
+ formattedPrice = `${currencySymbol}${amount.toFixed(2)}`;
244
+ } else {
245
+ formattedPrice = amount.toFixed(2);
246
+ }
247
+ if (priceUI.price_display?.suffix) {
248
+ formattedPrice += ` ${priceUI.price_display.suffix}`;
249
+ }
250
+ return formattedPrice;
251
+ }
252
+ function getCurrencySymbol(currency) {
253
+ const symbols = {
254
+ USD: "$",
255
+ EUR: "\u20AC",
256
+ GBP: "\xA3",
257
+ JPY: "\xA5",
258
+ CAD: "C$",
259
+ AUD: "A$"
260
+ };
261
+ return symbols[currency] || currency;
262
+ }
263
+ function formatDefaultPriceName(price) {
264
+ if (price.interval) {
265
+ return price.interval.charAt(0).toUpperCase() + price.interval.slice(1);
266
+ }
267
+ return "One-time";
268
+ }
269
+ function formatDefaultBillingPeriod(price) {
270
+ if (price.interval) {
271
+ const count = price.interval_count || 1;
272
+ const period = count === 1 ? price.interval : `${count} ${price.interval}s`;
273
+ return `per ${period}`;
274
+ }
275
+ return "one-time";
276
+ }
277
+
278
+ // src/payments/portal.ts
279
+ var PortalManager = class {
280
+ /**
281
+ * Initialize the portal manager
282
+ *
283
+ * @param paymentHandler - Payment handler instance for API communication
284
+ *
285
+ * @group Portal
286
+ */
287
+ constructor(omnibaseClient) {
288
+ this.omnibaseClient = omnibaseClient;
289
+ }
290
+ /**
291
+ * Create a new customer portal session
292
+ *
293
+ * Creates a portal session that allows the specified customer to
294
+ * manage their billing information, subscriptions, and payment methods.
295
+ * Returns a URL that the customer should be redirected to.
296
+ *
297
+ * The portal session is temporary and expires after a short period
298
+ * for security. Each access requires creating a new session.
299
+ *
300
+ * @param options - Configuration options for the portal session
301
+ * @param options.customer_id - Stripe customer ID for the user
302
+ * @param options.return_url - URL to redirect to when exiting the portal
303
+ *
304
+ * @returns Promise resolving to portal session response with access URL
305
+ *
306
+ * @throws {Error} When the API request fails due to network issues
307
+ * @throws {Error} When the server returns an error response (invalid customer_id, etc.)
308
+ * @throws {ValidationError} When required parameters are missing or invalid
309
+ *
310
+ * @example
311
+ * Basic portal creation:
312
+ * ```typescript
313
+ * const portal = await portalManager.create({
314
+ * customer_id: 'cus_abc123',
315
+ * return_url: 'https://myapp.com/account/billing'
316
+ * });
317
+ *
318
+ * // Redirect user to portal
319
+ * window.location.href = portal.data.url;
320
+ * ```
321
+ *
322
+ * @example
323
+ * With error handling:
324
+ * ```typescript
325
+ * try {
326
+ * const portal = await portalManager.create({
327
+ * customer_id: currentUser.stripeCustomerId,
328
+ * return_url: window.location.origin + '/billing'
329
+ * });
330
+ *
331
+ * window.location.href = portal.data.url;
332
+ * } catch (error) {
333
+ * console.error('Failed to create portal session:', error);
334
+ * showErrorMessage('Unable to access billing portal. Please try again.');
335
+ * }
336
+ * ```
337
+ *
338
+ * @since 1.0.0
339
+ * @group Portal
340
+ */
341
+ async create(options) {
342
+ const response = await this.omnibaseClient.fetch(
343
+ "/api/v1/payments/portal",
344
+ {
345
+ method: "POST",
346
+ headers: {
347
+ "Content-Type": "application/json"
348
+ },
349
+ body: JSON.stringify(options)
350
+ }
351
+ );
352
+ if (!response.ok) {
353
+ const errorData = await response.text();
354
+ throw new Error(
355
+ `Failed to create customer portal: ${response.status} - ${errorData}`
356
+ );
357
+ }
358
+ const result = await response.json();
359
+ return result;
360
+ }
361
+ };
362
+
363
+ // src/payments/usage.ts
364
+ var UsageManager = class {
365
+ /**
366
+ * Initialize the usage manager
367
+ *
368
+ * @param paymentHandler - Payment handler instance for API communication
369
+ *
370
+ * @group Usage
371
+ */
372
+ constructor(omnibaseClient) {
373
+ this.omnibaseClient = omnibaseClient;
374
+ }
375
+ /**
376
+ * Record a usage event for metered billing
377
+ *
378
+ * Records a usage event against a specific meter for billing calculation.
379
+ * The event will be aggregated with other usage events for the billing period
380
+ * to determine the customer's charges for metered products.
381
+ *
382
+ * Usage events should be recorded in real-time or as close to real-time as
383
+ * possible to ensure accurate billing and provide up-to-date usage visibility
384
+ * to customers.
385
+ *
386
+ * @param options - Usage recording options
387
+ * @param options.meter_event_name - Name of the meter to record against
388
+ * @param options.customer_id - Stripe customer ID
389
+ * @param options.value - Usage quantity as string
390
+ *
391
+ * @returns Promise resolving to API response confirmation
392
+ *
393
+ * @throws {Error} When the API request fails due to network issues
394
+ * @throws {Error} When the server returns an error response (invalid meter name, customer, etc.)
395
+ * @throws {ValidationError} When required parameters are missing or invalid
396
+ *
397
+ * @example
398
+ * API call tracking:
399
+ * ```typescript
400
+ * // Record each API call
401
+ * await usageManager.recordUsage({
402
+ * meter_event_name: 'api_requests',
403
+ * customer_id: user.stripeCustomerId,
404
+ * value: '1'
405
+ * });
406
+ * ```
407
+ *
408
+ * @example
409
+ * Batch usage recording:
410
+ * ```typescript
411
+ * // Record multiple operations at once
412
+ * const usageEvents = [
413
+ * { meter_event_name: 'compute_hours', customer_id: 'cus_123', value: '0.5' },
414
+ * { meter_event_name: 'storage_gb', customer_id: 'cus_123', value: '10' },
415
+ * { meter_event_name: 'api_calls', customer_id: 'cus_123', value: '50' }
416
+ * ];
417
+ *
418
+ * for (const event of usageEvents) {
419
+ * await usageManager.recordUsage(event);
420
+ * }
421
+ * ```
422
+ *
423
+ * @example
424
+ * With error handling:
425
+ * ```typescript
426
+ * try {
427
+ * await usageManager.recordUsage({
428
+ * meter_event_name: 'file_uploads',
429
+ * customer_id: currentUser.stripeCustomerId,
430
+ * value: String(uploadedFiles.length)
431
+ * });
432
+ * } catch (error) {
433
+ * console.error('Failed to record usage:', error);
434
+ * // Usage recording failure shouldn't block user operations
435
+ * // but should be logged for billing accuracy
436
+ * }
437
+ * ```
438
+ *
439
+ * @since 1.0.0
440
+ * @group Usage
441
+ */
442
+ async recordUsage(options) {
443
+ const response = await this.omnibaseClient.fetch("/api/v1/payments/usage", {
444
+ method: "POST",
445
+ headers: {
446
+ "Content-Type": "application/json"
447
+ },
448
+ body: JSON.stringify(options)
449
+ });
450
+ if (!response.ok) {
451
+ const errorData = await response.text();
452
+ throw new Error(
453
+ `Failed to record usage: ${response.status} - ${errorData}`
454
+ );
455
+ }
456
+ const result = await response.json();
457
+ return result;
458
+ }
459
+ };
460
+
461
+ // src/payments/handler.ts
462
+ var PaymentHandler = class {
463
+ /**
464
+ * Initialize the payment handler with API configuration
465
+ *
466
+ * Creates a new payment handler instance that will communicate with
467
+ * the specified API endpoint. The handler automatically handles
468
+ * request formatting and authentication headers.
469
+ *
470
+ * @param apiUrl - Base URL for the payment API endpoint
471
+ *
472
+ * @example
473
+ * ```typescript
474
+ * const paymentHandler = new PaymentHandler('https://api.myapp.com');
475
+ * ```
476
+ *
477
+ * @since 1.0.0
478
+ * @group Client
479
+ */
480
+ constructor(omnibaseClient) {
481
+ this.omnibaseClient = omnibaseClient;
482
+ this.checkout = new CheckoutManager(this.omnibaseClient);
483
+ this.config = new ConfigManager(this.omnibaseClient);
484
+ this.portal = new PortalManager(this.omnibaseClient);
485
+ this.usage = new UsageManager(this.omnibaseClient);
486
+ }
487
+ /**
488
+ * Checkout session management
489
+ *
490
+ * Provides functionality for creating and managing Stripe checkout sessions
491
+ * for both one-time payments and subscription billing.
492
+ *
493
+ * @example
494
+ * ```typescript
495
+ * const session = await paymentHandler.checkout.createSession({
496
+ * price_id: 'price_monthly',
497
+ * mode: 'subscription',
498
+ * success_url: window.location.origin + '/success',
499
+ * cancel_url: window.location.origin + '/pricing'
500
+ * });
501
+ * ```
502
+ */
503
+ checkout;
504
+ /**
505
+ * Stripe configuration management
506
+ *
507
+ * Handles retrieval and processing of database-backed Stripe configurations,
508
+ * providing UI-ready product and pricing data for rendering pricing tables.
509
+ *
510
+ * @example
511
+ * ```typescript
512
+ * const products = await paymentHandler.config.getAvailableProducts();
513
+ * const config = await paymentHandler.config.getStripeConfig();
514
+ * ```
515
+ */
516
+ config;
517
+ /**
518
+ * Customer portal management
519
+ *
520
+ * Creates customer portal sessions for subscription management,
521
+ * billing history, and payment method updates.
522
+ *
523
+ * @example
524
+ * ```typescript
525
+ * const portal = await paymentHandler.portal.create({
526
+ * customer_id: 'cus_123',
527
+ * return_url: 'https://app.com/billing'
528
+ * });
529
+ * ```
530
+ */
531
+ portal;
532
+ /**
533
+ * Usage tracking and metered billing
534
+ *
535
+ * Records usage events for metered billing products and manages
536
+ * usage-based pricing calculations.
537
+ *
538
+ * @example
539
+ * ```typescript
540
+ * await paymentHandler.usage.recordUsage({
541
+ * meter_event_name: 'api_calls',
542
+ * customer_id: 'cus_123',
543
+ * value: '1'
544
+ * });
545
+ * ```
546
+ */
547
+ usage;
548
+ };
549
+
550
+ export {
551
+ CheckoutManager,
552
+ ConfigManager,
553
+ PortalManager,
554
+ UsageManager,
555
+ PaymentHandler
556
+ };
@@ -0,0 +1,106 @@
1
+ // src/permissions/handler.ts
2
+ import { RelationshipApi, PermissionApi } from "@ory/client";
3
+ var PermissionsClient = class {
4
+ /**
5
+ * Ory Keto RelationshipApi for managing subject-object relationships
6
+ *
7
+ * Provides methods for creating, updating, and deleting relationships between
8
+ * subjects (users, groups) and objects (tenants, resources). This API handles
9
+ * write operations and is used to establish permission structures.
10
+ *
11
+ * Key methods:
12
+ * - `createRelationship()` - Creates a new relationship tuple
13
+ * - `deleteRelationships()` - Removes existing relationship tuples
14
+ * - `getRelationships()` - Queries existing relationships
15
+ * - `patchRelationships()` - Updates multiple relationships atomically
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * // Create a relationship
20
+ * await client.relationships.createRelationship(
21
+ * undefined,
22
+ * {
23
+ * namespace: 'Tenant',
24
+ * object: 'tenant_123',
25
+ * relation: 'members',
26
+ * subjectId: 'user_456'
27
+ * }
28
+ * );
29
+ * ```
30
+ *
31
+ * @since 1.0.0
32
+ * @group Relationships
33
+ */
34
+ relationships;
35
+ /**
36
+ * Ory Keto PermissionApi for checking permissions
37
+ *
38
+ * Provides methods for querying whether a subject has a specific permission
39
+ * on an object. This API handles read operations and is optimized for fast
40
+ * permission checks in your application logic.
41
+ *
42
+ * Key methods:
43
+ * - `checkPermission()` - Checks if a subject has permission on an object
44
+ * - `checkPermissionOrError()` - Same as above but throws error if denied
45
+ * - `expandPermissions()` - Expands relationships to show all granted permissions
46
+ *
47
+ * @example
48
+ * ```typescript
49
+ * // Check permission
50
+ * const result = await client.permissions.checkPermission(
51
+ * undefined,
52
+ * {
53
+ * namespace: 'Tenant',
54
+ * object: 'tenant_123',
55
+ * relation: 'view',
56
+ * subjectId: 'user_456'
57
+ * }
58
+ * );
59
+ *
60
+ * console.log('Has permission:', result.data.allowed);
61
+ * ```
62
+ *
63
+ * @since 1.0.0
64
+ * @group Permissions
65
+ */
66
+ permissions;
67
+ /**
68
+ * Creates a new PermissionsClient instance
69
+ *
70
+ * Initializes the client with separate endpoints for read and write operations.
71
+ * The client automatically appends the appropriate Keto API paths to the base URL
72
+ * for optimal performance and security separation.
73
+ *
74
+ * @param apiBaseUrl - The base URL for your Omnibase API instance
75
+ *
76
+ * @throws {Error} When the base URL is invalid or cannot be reached
77
+ *
78
+ * @example
79
+ * ```typescript
80
+ * const client = new PermissionsClient('https://api.example.com');
81
+ * ```
82
+ *
83
+ * @example
84
+ * Local development:
85
+ * ```typescript
86
+ * const client = new PermissionsClient('http://localhost:8080');
87
+ * ```
88
+ *
89
+ * @since 1.0.0
90
+ * @group Client
91
+ */
92
+ constructor(apiBaseUrl) {
93
+ this.relationships = new RelationshipApi(
94
+ void 0,
95
+ `${apiBaseUrl}/api/v1/permissions/write`
96
+ );
97
+ this.permissions = new PermissionApi(
98
+ void 0,
99
+ `${apiBaseUrl}/api/v1/permissions/read`
100
+ );
101
+ }
102
+ };
103
+
104
+ export {
105
+ PermissionsClient
106
+ };