@stackbe/sdk 0.6.5 → 0.7.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.
- package/README.md +23 -9
- package/dist/index.d.mts +254 -4
- package/dist/index.d.ts +254 -4
- package/dist/index.js +227 -4
- package/dist/index.mjs +226 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -549,9 +549,22 @@ stackbe.auth.invalidateSession(token);
|
|
|
549
549
|
stackbe.auth.clearCache(); // Clear all
|
|
550
550
|
```
|
|
551
551
|
|
|
552
|
-
###
|
|
552
|
+
### Magic Link Callback URLs
|
|
553
553
|
|
|
554
|
-
|
|
554
|
+
**Recommended approach**: Pass the callback URL explicitly for maximum reliability:
|
|
555
|
+
|
|
556
|
+
```typescript
|
|
557
|
+
// Determine callback URL based on your environment
|
|
558
|
+
const callbackUrl = process.env.NODE_ENV === 'production'
|
|
559
|
+
? 'https://myapp.com/auth/callback'
|
|
560
|
+
: 'http://localhost:3000/auth/callback';
|
|
561
|
+
|
|
562
|
+
await stackbe.auth.sendMagicLink('user@example.com', {
|
|
563
|
+
redirectUrl: callbackUrl,
|
|
564
|
+
});
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
**Alternative**: Use `devCallbackUrl` for automatic detection (uses `devCallbackUrl` when `NODE_ENV !== 'production'`):
|
|
555
568
|
|
|
556
569
|
```typescript
|
|
557
570
|
const stackbe = new StackBE({
|
|
@@ -560,17 +573,18 @@ const stackbe = new StackBE({
|
|
|
560
573
|
devCallbackUrl: 'http://localhost:3000/auth/callback',
|
|
561
574
|
});
|
|
562
575
|
|
|
563
|
-
// In development
|
|
576
|
+
// In development, uses devCallbackUrl automatically
|
|
564
577
|
await stackbe.auth.sendMagicLink('user@example.com');
|
|
565
|
-
// Redirects to: http://localhost:3000/auth/callback
|
|
566
578
|
|
|
567
|
-
// In production,
|
|
568
|
-
//
|
|
569
|
-
await stackbe.auth.sendMagicLink('user@example.com', {
|
|
570
|
-
redirectUrl: 'https://myapp.com/custom-callback',
|
|
571
|
-
});
|
|
579
|
+
// In production, falls back to app settings callback URL
|
|
580
|
+
// Explicit redirectUrl always takes priority if provided
|
|
572
581
|
```
|
|
573
582
|
|
|
583
|
+
**Notes:**
|
|
584
|
+
- `devCallbackUrl` only activates when `NODE_ENV !== 'production'`
|
|
585
|
+
- Explicit `redirectUrl` parameter always takes priority
|
|
586
|
+
- For predictable behavior, use the explicit approach above
|
|
587
|
+
|
|
574
588
|
## Webhooks
|
|
575
589
|
|
|
576
590
|
Typed webhook payloads for handling StackBE events:
|
package/dist/index.d.mts
CHANGED
|
@@ -13,7 +13,7 @@ declare class HttpClient {
|
|
|
13
13
|
post<T>(path: string, body?: unknown, params?: Record<string, string | number | undefined>): Promise<T>;
|
|
14
14
|
put<T>(path: string, body?: unknown, params?: Record<string, string | number | undefined>): Promise<T>;
|
|
15
15
|
patch<T>(path: string, body?: unknown): Promise<T>;
|
|
16
|
-
delete<T>(path: string): Promise<T>;
|
|
16
|
+
delete<T>(path: string, body?: unknown): Promise<T>;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
interface StackBEConfig {
|
|
@@ -340,7 +340,7 @@ interface ListProductsOptions {
|
|
|
340
340
|
appId?: string;
|
|
341
341
|
}
|
|
342
342
|
interface CreateCheckoutOptions {
|
|
343
|
-
/** Customer ID or email */
|
|
343
|
+
/** Customer ID or object with email. Use string for existing customer ID, object for email-based lookup/creation */
|
|
344
344
|
customer: string | {
|
|
345
345
|
email: string;
|
|
346
346
|
name?: string;
|
|
@@ -351,6 +351,8 @@ interface CreateCheckoutOptions {
|
|
|
351
351
|
successUrl: string;
|
|
352
352
|
/** URL to redirect if checkout is canceled */
|
|
353
353
|
cancelUrl?: string;
|
|
354
|
+
/** Organization ID for org-level subscriptions. Customer must be owner/admin of the org. */
|
|
355
|
+
organizationId?: string;
|
|
354
356
|
/** Allow promotion codes */
|
|
355
357
|
allowPromotionCodes?: boolean;
|
|
356
358
|
/** Trial period in days */
|
|
@@ -825,7 +827,7 @@ declare class CheckoutClient {
|
|
|
825
827
|
*
|
|
826
828
|
* @example
|
|
827
829
|
* ```typescript
|
|
828
|
-
* // With
|
|
830
|
+
* // With customer email (looks up or creates customer)
|
|
829
831
|
* const { url } = await stackbe.checkout.createSession({
|
|
830
832
|
* customer: { email: 'user@example.com', name: 'John' },
|
|
831
833
|
* planId: 'plan_pro_monthly',
|
|
@@ -833,6 +835,18 @@ declare class CheckoutClient {
|
|
|
833
835
|
* trialDays: 14,
|
|
834
836
|
* });
|
|
835
837
|
* ```
|
|
838
|
+
*
|
|
839
|
+
* @example
|
|
840
|
+
* ```typescript
|
|
841
|
+
* // Organization-level subscription (B2B)
|
|
842
|
+
* const { url } = await stackbe.checkout.createSession({
|
|
843
|
+
* customer: 'cust_123',
|
|
844
|
+
* organizationId: 'org_456', // Customer must be owner/admin
|
|
845
|
+
* planId: 'plan_team',
|
|
846
|
+
* successUrl: 'https://myapp.com/success',
|
|
847
|
+
* cancelUrl: 'https://myapp.com/pricing',
|
|
848
|
+
* });
|
|
849
|
+
* ```
|
|
836
850
|
*/
|
|
837
851
|
createSession(options: CreateCheckoutOptions): Promise<CheckoutSessionResponse>;
|
|
838
852
|
/**
|
|
@@ -1375,6 +1389,240 @@ declare class ProductsClient {
|
|
|
1375
1389
|
get(productId: string): Promise<Product>;
|
|
1376
1390
|
}
|
|
1377
1391
|
|
|
1392
|
+
interface FeatureRequest {
|
|
1393
|
+
id: string;
|
|
1394
|
+
title: string;
|
|
1395
|
+
description?: string;
|
|
1396
|
+
status: 'new' | 'under_review' | 'planned' | 'in_progress' | 'completed' | 'declined';
|
|
1397
|
+
category?: string;
|
|
1398
|
+
authorId: string;
|
|
1399
|
+
authorEmail: string;
|
|
1400
|
+
voteCount: number;
|
|
1401
|
+
hasVoted?: boolean;
|
|
1402
|
+
createdAt: string;
|
|
1403
|
+
updatedAt: string;
|
|
1404
|
+
}
|
|
1405
|
+
interface FeatureRequestComment {
|
|
1406
|
+
id: string;
|
|
1407
|
+
customerEmail: string;
|
|
1408
|
+
content: string;
|
|
1409
|
+
isAdminReply: boolean;
|
|
1410
|
+
createdAt: string;
|
|
1411
|
+
}
|
|
1412
|
+
interface CreateFeatureRequestOptions {
|
|
1413
|
+
title: string;
|
|
1414
|
+
description?: string;
|
|
1415
|
+
category?: string;
|
|
1416
|
+
}
|
|
1417
|
+
interface ListFeatureRequestsOptions {
|
|
1418
|
+
status?: 'new' | 'under_review' | 'planned' | 'in_progress' | 'completed' | 'declined';
|
|
1419
|
+
category?: string;
|
|
1420
|
+
sortBy?: 'newest' | 'votes' | 'recent_activity';
|
|
1421
|
+
limit?: number;
|
|
1422
|
+
offset?: number;
|
|
1423
|
+
}
|
|
1424
|
+
interface FeatureRequestListResponse {
|
|
1425
|
+
data: FeatureRequest[];
|
|
1426
|
+
total: number;
|
|
1427
|
+
limit: number;
|
|
1428
|
+
offset: number;
|
|
1429
|
+
}
|
|
1430
|
+
interface InterestedCustomer {
|
|
1431
|
+
customerId: string;
|
|
1432
|
+
email: string;
|
|
1433
|
+
votedAt?: string;
|
|
1434
|
+
submittedAt?: string;
|
|
1435
|
+
}
|
|
1436
|
+
interface InterestedCustomersResponse {
|
|
1437
|
+
featureRequestId: string;
|
|
1438
|
+
title: string;
|
|
1439
|
+
status: string;
|
|
1440
|
+
author: {
|
|
1441
|
+
customerId: string;
|
|
1442
|
+
email: string;
|
|
1443
|
+
submittedAt: string;
|
|
1444
|
+
};
|
|
1445
|
+
voters: InterestedCustomer[];
|
|
1446
|
+
totalInterested: number;
|
|
1447
|
+
}
|
|
1448
|
+
interface CustomerFeatureActivity {
|
|
1449
|
+
customerId: string;
|
|
1450
|
+
submitted: Array<{
|
|
1451
|
+
id: string;
|
|
1452
|
+
title: string;
|
|
1453
|
+
status: string;
|
|
1454
|
+
voteCount: number;
|
|
1455
|
+
submittedAt: string;
|
|
1456
|
+
}>;
|
|
1457
|
+
votedOn: Array<{
|
|
1458
|
+
id: string;
|
|
1459
|
+
title: string;
|
|
1460
|
+
status: string;
|
|
1461
|
+
voteCount: number;
|
|
1462
|
+
votedAt: string;
|
|
1463
|
+
}>;
|
|
1464
|
+
totalSubmitted: number;
|
|
1465
|
+
totalVotes: number;
|
|
1466
|
+
}
|
|
1467
|
+
declare class FeatureRequestsClient {
|
|
1468
|
+
private http;
|
|
1469
|
+
private appId;
|
|
1470
|
+
constructor(http: HttpClient, appId: string);
|
|
1471
|
+
/**
|
|
1472
|
+
* List feature requests for the app.
|
|
1473
|
+
*
|
|
1474
|
+
* @example
|
|
1475
|
+
* ```typescript
|
|
1476
|
+
* // Get all feature requests
|
|
1477
|
+
* const { data, total } = await stackbe.featureRequests.list();
|
|
1478
|
+
*
|
|
1479
|
+
* // Filter by status
|
|
1480
|
+
* const planned = await stackbe.featureRequests.list({ status: 'planned' });
|
|
1481
|
+
*
|
|
1482
|
+
* // Sort by votes
|
|
1483
|
+
* const popular = await stackbe.featureRequests.list({ sortBy: 'votes' });
|
|
1484
|
+
* ```
|
|
1485
|
+
*/
|
|
1486
|
+
list(options?: ListFeatureRequestsOptions): Promise<FeatureRequestListResponse>;
|
|
1487
|
+
/**
|
|
1488
|
+
* Submit a new feature request.
|
|
1489
|
+
* Requires customer session token.
|
|
1490
|
+
*
|
|
1491
|
+
* @example
|
|
1492
|
+
* ```typescript
|
|
1493
|
+
* const request = await stackbe.featureRequests.create({
|
|
1494
|
+
* title: 'Dark mode support',
|
|
1495
|
+
* description: 'Would love to have a dark theme option',
|
|
1496
|
+
* category: 'UI',
|
|
1497
|
+
* });
|
|
1498
|
+
* ```
|
|
1499
|
+
*/
|
|
1500
|
+
create(options: CreateFeatureRequestOptions): Promise<FeatureRequest>;
|
|
1501
|
+
/**
|
|
1502
|
+
* Get a specific feature request by ID.
|
|
1503
|
+
*
|
|
1504
|
+
* @example
|
|
1505
|
+
* ```typescript
|
|
1506
|
+
* const request = await stackbe.featureRequests.get('req_123');
|
|
1507
|
+
* console.log(`${request.title} has ${request.voteCount} votes`);
|
|
1508
|
+
* ```
|
|
1509
|
+
*/
|
|
1510
|
+
get(requestId: string): Promise<FeatureRequest & {
|
|
1511
|
+
comments: FeatureRequestComment[];
|
|
1512
|
+
}>;
|
|
1513
|
+
/**
|
|
1514
|
+
* Upvote a feature request.
|
|
1515
|
+
* Requires customer session token, OR API key with customerId.
|
|
1516
|
+
*
|
|
1517
|
+
* @example
|
|
1518
|
+
* ```typescript
|
|
1519
|
+
* // With customer session token
|
|
1520
|
+
* await stackbe.featureRequests.vote('req_123');
|
|
1521
|
+
*
|
|
1522
|
+
* // With API key (server-side)
|
|
1523
|
+
* await stackbe.featureRequests.vote('req_123', 'cust_456');
|
|
1524
|
+
* ```
|
|
1525
|
+
*/
|
|
1526
|
+
vote(requestId: string, customerId?: string): Promise<{
|
|
1527
|
+
success?: boolean;
|
|
1528
|
+
alreadyVoted?: boolean;
|
|
1529
|
+
voteCount: number;
|
|
1530
|
+
}>;
|
|
1531
|
+
/**
|
|
1532
|
+
* Remove your vote from a feature request.
|
|
1533
|
+
* Requires customer session token, OR API key with customerId.
|
|
1534
|
+
*
|
|
1535
|
+
* @example
|
|
1536
|
+
* ```typescript
|
|
1537
|
+
* // With customer session token
|
|
1538
|
+
* await stackbe.featureRequests.removeVote('req_123');
|
|
1539
|
+
*
|
|
1540
|
+
* // With API key (server-side)
|
|
1541
|
+
* await stackbe.featureRequests.removeVote('req_123', 'cust_456');
|
|
1542
|
+
* ```
|
|
1543
|
+
*/
|
|
1544
|
+
removeVote(requestId: string, customerId?: string): Promise<{
|
|
1545
|
+
success: boolean;
|
|
1546
|
+
voteCount: number;
|
|
1547
|
+
}>;
|
|
1548
|
+
/**
|
|
1549
|
+
* Add a comment to a feature request.
|
|
1550
|
+
* Requires customer session token.
|
|
1551
|
+
*
|
|
1552
|
+
* @example
|
|
1553
|
+
* ```typescript
|
|
1554
|
+
* await stackbe.featureRequests.addComment('req_123', {
|
|
1555
|
+
* content: 'This would be really helpful for my workflow!',
|
|
1556
|
+
* });
|
|
1557
|
+
* ```
|
|
1558
|
+
*/
|
|
1559
|
+
addComment(requestId: string, options: {
|
|
1560
|
+
content: string;
|
|
1561
|
+
}): Promise<FeatureRequestComment>;
|
|
1562
|
+
/**
|
|
1563
|
+
* Update a feature request status or category.
|
|
1564
|
+
* Requires API key (admin).
|
|
1565
|
+
*
|
|
1566
|
+
* @example
|
|
1567
|
+
* ```typescript
|
|
1568
|
+
* await stackbe.featureRequests.update('req_123', {
|
|
1569
|
+
* status: 'planned',
|
|
1570
|
+
* category: 'Core Features',
|
|
1571
|
+
* });
|
|
1572
|
+
* ```
|
|
1573
|
+
*/
|
|
1574
|
+
update(requestId: string, options: {
|
|
1575
|
+
status?: string;
|
|
1576
|
+
category?: string;
|
|
1577
|
+
}): Promise<FeatureRequest>;
|
|
1578
|
+
/**
|
|
1579
|
+
* Delete a feature request.
|
|
1580
|
+
* Requires API key (admin).
|
|
1581
|
+
*
|
|
1582
|
+
* @example
|
|
1583
|
+
* ```typescript
|
|
1584
|
+
* await stackbe.featureRequests.delete('req_123');
|
|
1585
|
+
* ```
|
|
1586
|
+
*/
|
|
1587
|
+
delete(requestId: string): Promise<{
|
|
1588
|
+
success: boolean;
|
|
1589
|
+
}>;
|
|
1590
|
+
/**
|
|
1591
|
+
* Get all customers interested in a feature request (author + voters).
|
|
1592
|
+
* Useful for user research or notifying users when the feature ships.
|
|
1593
|
+
* Requires API key (admin).
|
|
1594
|
+
*
|
|
1595
|
+
* @example
|
|
1596
|
+
* ```typescript
|
|
1597
|
+
* const { author, voters, totalInterested } = await stackbe.featureRequests.getInterestedCustomers('req_123');
|
|
1598
|
+
*
|
|
1599
|
+
* // Notify all interested users
|
|
1600
|
+
* const emails = [author.email, ...voters.map(v => v.email)];
|
|
1601
|
+
* await sendNotification(emails, 'Your requested feature is now live!');
|
|
1602
|
+
* ```
|
|
1603
|
+
*/
|
|
1604
|
+
getInterestedCustomers(requestId: string): Promise<InterestedCustomersResponse>;
|
|
1605
|
+
/**
|
|
1606
|
+
* Get a customer's feature request activity (requests submitted + votes).
|
|
1607
|
+
* Useful for displaying on customer profile.
|
|
1608
|
+
* Requires API key (admin).
|
|
1609
|
+
*
|
|
1610
|
+
* @example
|
|
1611
|
+
* ```typescript
|
|
1612
|
+
* const activity = await stackbe.featureRequests.getCustomerActivity('cust_123');
|
|
1613
|
+
*
|
|
1614
|
+
* console.log(`Submitted ${activity.totalSubmitted} requests`);
|
|
1615
|
+
* console.log(`Voted on ${activity.totalVotes} requests`);
|
|
1616
|
+
*
|
|
1617
|
+
* // Show their most wanted features
|
|
1618
|
+
* activity.votedOn.forEach(r => {
|
|
1619
|
+
* console.log(`- ${r.title} (${r.status})`);
|
|
1620
|
+
* });
|
|
1621
|
+
* ```
|
|
1622
|
+
*/
|
|
1623
|
+
getCustomerActivity(customerId: string): Promise<CustomerFeatureActivity>;
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1378
1626
|
declare class StackBE {
|
|
1379
1627
|
private http;
|
|
1380
1628
|
private appId;
|
|
@@ -1396,6 +1644,8 @@ declare class StackBE {
|
|
|
1396
1644
|
readonly plans: PlansClient;
|
|
1397
1645
|
/** Products */
|
|
1398
1646
|
readonly products: ProductsClient;
|
|
1647
|
+
/** Feature requests and voting */
|
|
1648
|
+
readonly featureRequests: FeatureRequestsClient;
|
|
1399
1649
|
/**
|
|
1400
1650
|
* Create a new StackBE client.
|
|
1401
1651
|
*
|
|
@@ -1496,4 +1746,4 @@ declare class StackBE {
|
|
|
1496
1746
|
}): (req: any, res: any, next: any) => Promise<any>;
|
|
1497
1747
|
}
|
|
1498
1748
|
|
|
1499
|
-
export { type AddMemberOptions, type AnyWebhookEvent, AuthClient, type CancelSubscriptionOptions, type CancelSubscriptionResponse, type CheckEntitlementResponse, type CheckUsageResponse, CheckoutClient, type CheckoutSessionResponse, type CreateCheckoutOptions, type CreateCustomerOptions, type CreateOrganizationOptions, type Customer, type CustomerCreatedEvent, type CustomerUpdatedEvent, type CustomerUsageResponse, type CustomerWebhookPayload, type CustomerWithSubscription, CustomersClient, EntitlementsClient, type EntitlementsResponse, type InviteMemberOptions, type ListPlansOptions, type ListProductsOptions, type MagicLinkOptions, type MagicLinkResponse, type Organization, type OrganizationInvite, type OrganizationMember, OrganizationsClient, type PaymentFailedEvent, type PaymentSucceededEvent, type PaymentWebhookPayload, type Plan, PlansClient, type Product, ProductsClient, type SessionResponse, StackBE, type StackBEConfig, StackBEError, type StackBEErrorCode, type StackBEErrorResponse, type Subscription, type SubscriptionCancelledEvent, type SubscriptionCreatedEvent, type SubscriptionRenewedEvent, type SubscriptionUpdatedEvent, type SubscriptionWebhookPayload, type SubscriptionWithPlan, SubscriptionsClient, type TrackUsageOptions, type TrackUsageResponse, type TrialEndedEvent, type TrialStartedEvent, type UpdateCustomerOptions, type UpdateOrganizationOptions, type UpdateSubscriptionOptions, UsageClient, type UsageMetric, type VerifyTokenResponse, type WebhookEvent, type WebhookEventType };
|
|
1749
|
+
export { type AddMemberOptions, type AnyWebhookEvent, AuthClient, type CancelSubscriptionOptions, type CancelSubscriptionResponse, type CheckEntitlementResponse, type CheckUsageResponse, CheckoutClient, type CheckoutSessionResponse, type CreateCheckoutOptions, type CreateCustomerOptions, type CreateFeatureRequestOptions, type CreateOrganizationOptions, type Customer, type CustomerCreatedEvent, type CustomerFeatureActivity, type CustomerUpdatedEvent, type CustomerUsageResponse, type CustomerWebhookPayload, type CustomerWithSubscription, CustomersClient, EntitlementsClient, type EntitlementsResponse, type FeatureRequest, type FeatureRequestComment, type FeatureRequestListResponse, FeatureRequestsClient, type InterestedCustomer, type InterestedCustomersResponse, type InviteMemberOptions, type ListFeatureRequestsOptions, type ListPlansOptions, type ListProductsOptions, type MagicLinkOptions, type MagicLinkResponse, type Organization, type OrganizationInvite, type OrganizationMember, OrganizationsClient, type PaymentFailedEvent, type PaymentSucceededEvent, type PaymentWebhookPayload, type Plan, PlansClient, type Product, ProductsClient, type SessionResponse, StackBE, type StackBEConfig, StackBEError, type StackBEErrorCode, type StackBEErrorResponse, type Subscription, type SubscriptionCancelledEvent, type SubscriptionCreatedEvent, type SubscriptionRenewedEvent, type SubscriptionUpdatedEvent, type SubscriptionWebhookPayload, type SubscriptionWithPlan, SubscriptionsClient, type TrackUsageOptions, type TrackUsageResponse, type TrialEndedEvent, type TrialStartedEvent, type UpdateCustomerOptions, type UpdateOrganizationOptions, type UpdateSubscriptionOptions, UsageClient, type UsageMetric, type VerifyTokenResponse, type WebhookEvent, type WebhookEventType };
|
package/dist/index.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ declare class HttpClient {
|
|
|
13
13
|
post<T>(path: string, body?: unknown, params?: Record<string, string | number | undefined>): Promise<T>;
|
|
14
14
|
put<T>(path: string, body?: unknown, params?: Record<string, string | number | undefined>): Promise<T>;
|
|
15
15
|
patch<T>(path: string, body?: unknown): Promise<T>;
|
|
16
|
-
delete<T>(path: string): Promise<T>;
|
|
16
|
+
delete<T>(path: string, body?: unknown): Promise<T>;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
interface StackBEConfig {
|
|
@@ -340,7 +340,7 @@ interface ListProductsOptions {
|
|
|
340
340
|
appId?: string;
|
|
341
341
|
}
|
|
342
342
|
interface CreateCheckoutOptions {
|
|
343
|
-
/** Customer ID or email */
|
|
343
|
+
/** Customer ID or object with email. Use string for existing customer ID, object for email-based lookup/creation */
|
|
344
344
|
customer: string | {
|
|
345
345
|
email: string;
|
|
346
346
|
name?: string;
|
|
@@ -351,6 +351,8 @@ interface CreateCheckoutOptions {
|
|
|
351
351
|
successUrl: string;
|
|
352
352
|
/** URL to redirect if checkout is canceled */
|
|
353
353
|
cancelUrl?: string;
|
|
354
|
+
/** Organization ID for org-level subscriptions. Customer must be owner/admin of the org. */
|
|
355
|
+
organizationId?: string;
|
|
354
356
|
/** Allow promotion codes */
|
|
355
357
|
allowPromotionCodes?: boolean;
|
|
356
358
|
/** Trial period in days */
|
|
@@ -825,7 +827,7 @@ declare class CheckoutClient {
|
|
|
825
827
|
*
|
|
826
828
|
* @example
|
|
827
829
|
* ```typescript
|
|
828
|
-
* // With
|
|
830
|
+
* // With customer email (looks up or creates customer)
|
|
829
831
|
* const { url } = await stackbe.checkout.createSession({
|
|
830
832
|
* customer: { email: 'user@example.com', name: 'John' },
|
|
831
833
|
* planId: 'plan_pro_monthly',
|
|
@@ -833,6 +835,18 @@ declare class CheckoutClient {
|
|
|
833
835
|
* trialDays: 14,
|
|
834
836
|
* });
|
|
835
837
|
* ```
|
|
838
|
+
*
|
|
839
|
+
* @example
|
|
840
|
+
* ```typescript
|
|
841
|
+
* // Organization-level subscription (B2B)
|
|
842
|
+
* const { url } = await stackbe.checkout.createSession({
|
|
843
|
+
* customer: 'cust_123',
|
|
844
|
+
* organizationId: 'org_456', // Customer must be owner/admin
|
|
845
|
+
* planId: 'plan_team',
|
|
846
|
+
* successUrl: 'https://myapp.com/success',
|
|
847
|
+
* cancelUrl: 'https://myapp.com/pricing',
|
|
848
|
+
* });
|
|
849
|
+
* ```
|
|
836
850
|
*/
|
|
837
851
|
createSession(options: CreateCheckoutOptions): Promise<CheckoutSessionResponse>;
|
|
838
852
|
/**
|
|
@@ -1375,6 +1389,240 @@ declare class ProductsClient {
|
|
|
1375
1389
|
get(productId: string): Promise<Product>;
|
|
1376
1390
|
}
|
|
1377
1391
|
|
|
1392
|
+
interface FeatureRequest {
|
|
1393
|
+
id: string;
|
|
1394
|
+
title: string;
|
|
1395
|
+
description?: string;
|
|
1396
|
+
status: 'new' | 'under_review' | 'planned' | 'in_progress' | 'completed' | 'declined';
|
|
1397
|
+
category?: string;
|
|
1398
|
+
authorId: string;
|
|
1399
|
+
authorEmail: string;
|
|
1400
|
+
voteCount: number;
|
|
1401
|
+
hasVoted?: boolean;
|
|
1402
|
+
createdAt: string;
|
|
1403
|
+
updatedAt: string;
|
|
1404
|
+
}
|
|
1405
|
+
interface FeatureRequestComment {
|
|
1406
|
+
id: string;
|
|
1407
|
+
customerEmail: string;
|
|
1408
|
+
content: string;
|
|
1409
|
+
isAdminReply: boolean;
|
|
1410
|
+
createdAt: string;
|
|
1411
|
+
}
|
|
1412
|
+
interface CreateFeatureRequestOptions {
|
|
1413
|
+
title: string;
|
|
1414
|
+
description?: string;
|
|
1415
|
+
category?: string;
|
|
1416
|
+
}
|
|
1417
|
+
interface ListFeatureRequestsOptions {
|
|
1418
|
+
status?: 'new' | 'under_review' | 'planned' | 'in_progress' | 'completed' | 'declined';
|
|
1419
|
+
category?: string;
|
|
1420
|
+
sortBy?: 'newest' | 'votes' | 'recent_activity';
|
|
1421
|
+
limit?: number;
|
|
1422
|
+
offset?: number;
|
|
1423
|
+
}
|
|
1424
|
+
interface FeatureRequestListResponse {
|
|
1425
|
+
data: FeatureRequest[];
|
|
1426
|
+
total: number;
|
|
1427
|
+
limit: number;
|
|
1428
|
+
offset: number;
|
|
1429
|
+
}
|
|
1430
|
+
interface InterestedCustomer {
|
|
1431
|
+
customerId: string;
|
|
1432
|
+
email: string;
|
|
1433
|
+
votedAt?: string;
|
|
1434
|
+
submittedAt?: string;
|
|
1435
|
+
}
|
|
1436
|
+
interface InterestedCustomersResponse {
|
|
1437
|
+
featureRequestId: string;
|
|
1438
|
+
title: string;
|
|
1439
|
+
status: string;
|
|
1440
|
+
author: {
|
|
1441
|
+
customerId: string;
|
|
1442
|
+
email: string;
|
|
1443
|
+
submittedAt: string;
|
|
1444
|
+
};
|
|
1445
|
+
voters: InterestedCustomer[];
|
|
1446
|
+
totalInterested: number;
|
|
1447
|
+
}
|
|
1448
|
+
interface CustomerFeatureActivity {
|
|
1449
|
+
customerId: string;
|
|
1450
|
+
submitted: Array<{
|
|
1451
|
+
id: string;
|
|
1452
|
+
title: string;
|
|
1453
|
+
status: string;
|
|
1454
|
+
voteCount: number;
|
|
1455
|
+
submittedAt: string;
|
|
1456
|
+
}>;
|
|
1457
|
+
votedOn: Array<{
|
|
1458
|
+
id: string;
|
|
1459
|
+
title: string;
|
|
1460
|
+
status: string;
|
|
1461
|
+
voteCount: number;
|
|
1462
|
+
votedAt: string;
|
|
1463
|
+
}>;
|
|
1464
|
+
totalSubmitted: number;
|
|
1465
|
+
totalVotes: number;
|
|
1466
|
+
}
|
|
1467
|
+
declare class FeatureRequestsClient {
|
|
1468
|
+
private http;
|
|
1469
|
+
private appId;
|
|
1470
|
+
constructor(http: HttpClient, appId: string);
|
|
1471
|
+
/**
|
|
1472
|
+
* List feature requests for the app.
|
|
1473
|
+
*
|
|
1474
|
+
* @example
|
|
1475
|
+
* ```typescript
|
|
1476
|
+
* // Get all feature requests
|
|
1477
|
+
* const { data, total } = await stackbe.featureRequests.list();
|
|
1478
|
+
*
|
|
1479
|
+
* // Filter by status
|
|
1480
|
+
* const planned = await stackbe.featureRequests.list({ status: 'planned' });
|
|
1481
|
+
*
|
|
1482
|
+
* // Sort by votes
|
|
1483
|
+
* const popular = await stackbe.featureRequests.list({ sortBy: 'votes' });
|
|
1484
|
+
* ```
|
|
1485
|
+
*/
|
|
1486
|
+
list(options?: ListFeatureRequestsOptions): Promise<FeatureRequestListResponse>;
|
|
1487
|
+
/**
|
|
1488
|
+
* Submit a new feature request.
|
|
1489
|
+
* Requires customer session token.
|
|
1490
|
+
*
|
|
1491
|
+
* @example
|
|
1492
|
+
* ```typescript
|
|
1493
|
+
* const request = await stackbe.featureRequests.create({
|
|
1494
|
+
* title: 'Dark mode support',
|
|
1495
|
+
* description: 'Would love to have a dark theme option',
|
|
1496
|
+
* category: 'UI',
|
|
1497
|
+
* });
|
|
1498
|
+
* ```
|
|
1499
|
+
*/
|
|
1500
|
+
create(options: CreateFeatureRequestOptions): Promise<FeatureRequest>;
|
|
1501
|
+
/**
|
|
1502
|
+
* Get a specific feature request by ID.
|
|
1503
|
+
*
|
|
1504
|
+
* @example
|
|
1505
|
+
* ```typescript
|
|
1506
|
+
* const request = await stackbe.featureRequests.get('req_123');
|
|
1507
|
+
* console.log(`${request.title} has ${request.voteCount} votes`);
|
|
1508
|
+
* ```
|
|
1509
|
+
*/
|
|
1510
|
+
get(requestId: string): Promise<FeatureRequest & {
|
|
1511
|
+
comments: FeatureRequestComment[];
|
|
1512
|
+
}>;
|
|
1513
|
+
/**
|
|
1514
|
+
* Upvote a feature request.
|
|
1515
|
+
* Requires customer session token, OR API key with customerId.
|
|
1516
|
+
*
|
|
1517
|
+
* @example
|
|
1518
|
+
* ```typescript
|
|
1519
|
+
* // With customer session token
|
|
1520
|
+
* await stackbe.featureRequests.vote('req_123');
|
|
1521
|
+
*
|
|
1522
|
+
* // With API key (server-side)
|
|
1523
|
+
* await stackbe.featureRequests.vote('req_123', 'cust_456');
|
|
1524
|
+
* ```
|
|
1525
|
+
*/
|
|
1526
|
+
vote(requestId: string, customerId?: string): Promise<{
|
|
1527
|
+
success?: boolean;
|
|
1528
|
+
alreadyVoted?: boolean;
|
|
1529
|
+
voteCount: number;
|
|
1530
|
+
}>;
|
|
1531
|
+
/**
|
|
1532
|
+
* Remove your vote from a feature request.
|
|
1533
|
+
* Requires customer session token, OR API key with customerId.
|
|
1534
|
+
*
|
|
1535
|
+
* @example
|
|
1536
|
+
* ```typescript
|
|
1537
|
+
* // With customer session token
|
|
1538
|
+
* await stackbe.featureRequests.removeVote('req_123');
|
|
1539
|
+
*
|
|
1540
|
+
* // With API key (server-side)
|
|
1541
|
+
* await stackbe.featureRequests.removeVote('req_123', 'cust_456');
|
|
1542
|
+
* ```
|
|
1543
|
+
*/
|
|
1544
|
+
removeVote(requestId: string, customerId?: string): Promise<{
|
|
1545
|
+
success: boolean;
|
|
1546
|
+
voteCount: number;
|
|
1547
|
+
}>;
|
|
1548
|
+
/**
|
|
1549
|
+
* Add a comment to a feature request.
|
|
1550
|
+
* Requires customer session token.
|
|
1551
|
+
*
|
|
1552
|
+
* @example
|
|
1553
|
+
* ```typescript
|
|
1554
|
+
* await stackbe.featureRequests.addComment('req_123', {
|
|
1555
|
+
* content: 'This would be really helpful for my workflow!',
|
|
1556
|
+
* });
|
|
1557
|
+
* ```
|
|
1558
|
+
*/
|
|
1559
|
+
addComment(requestId: string, options: {
|
|
1560
|
+
content: string;
|
|
1561
|
+
}): Promise<FeatureRequestComment>;
|
|
1562
|
+
/**
|
|
1563
|
+
* Update a feature request status or category.
|
|
1564
|
+
* Requires API key (admin).
|
|
1565
|
+
*
|
|
1566
|
+
* @example
|
|
1567
|
+
* ```typescript
|
|
1568
|
+
* await stackbe.featureRequests.update('req_123', {
|
|
1569
|
+
* status: 'planned',
|
|
1570
|
+
* category: 'Core Features',
|
|
1571
|
+
* });
|
|
1572
|
+
* ```
|
|
1573
|
+
*/
|
|
1574
|
+
update(requestId: string, options: {
|
|
1575
|
+
status?: string;
|
|
1576
|
+
category?: string;
|
|
1577
|
+
}): Promise<FeatureRequest>;
|
|
1578
|
+
/**
|
|
1579
|
+
* Delete a feature request.
|
|
1580
|
+
* Requires API key (admin).
|
|
1581
|
+
*
|
|
1582
|
+
* @example
|
|
1583
|
+
* ```typescript
|
|
1584
|
+
* await stackbe.featureRequests.delete('req_123');
|
|
1585
|
+
* ```
|
|
1586
|
+
*/
|
|
1587
|
+
delete(requestId: string): Promise<{
|
|
1588
|
+
success: boolean;
|
|
1589
|
+
}>;
|
|
1590
|
+
/**
|
|
1591
|
+
* Get all customers interested in a feature request (author + voters).
|
|
1592
|
+
* Useful for user research or notifying users when the feature ships.
|
|
1593
|
+
* Requires API key (admin).
|
|
1594
|
+
*
|
|
1595
|
+
* @example
|
|
1596
|
+
* ```typescript
|
|
1597
|
+
* const { author, voters, totalInterested } = await stackbe.featureRequests.getInterestedCustomers('req_123');
|
|
1598
|
+
*
|
|
1599
|
+
* // Notify all interested users
|
|
1600
|
+
* const emails = [author.email, ...voters.map(v => v.email)];
|
|
1601
|
+
* await sendNotification(emails, 'Your requested feature is now live!');
|
|
1602
|
+
* ```
|
|
1603
|
+
*/
|
|
1604
|
+
getInterestedCustomers(requestId: string): Promise<InterestedCustomersResponse>;
|
|
1605
|
+
/**
|
|
1606
|
+
* Get a customer's feature request activity (requests submitted + votes).
|
|
1607
|
+
* Useful for displaying on customer profile.
|
|
1608
|
+
* Requires API key (admin).
|
|
1609
|
+
*
|
|
1610
|
+
* @example
|
|
1611
|
+
* ```typescript
|
|
1612
|
+
* const activity = await stackbe.featureRequests.getCustomerActivity('cust_123');
|
|
1613
|
+
*
|
|
1614
|
+
* console.log(`Submitted ${activity.totalSubmitted} requests`);
|
|
1615
|
+
* console.log(`Voted on ${activity.totalVotes} requests`);
|
|
1616
|
+
*
|
|
1617
|
+
* // Show their most wanted features
|
|
1618
|
+
* activity.votedOn.forEach(r => {
|
|
1619
|
+
* console.log(`- ${r.title} (${r.status})`);
|
|
1620
|
+
* });
|
|
1621
|
+
* ```
|
|
1622
|
+
*/
|
|
1623
|
+
getCustomerActivity(customerId: string): Promise<CustomerFeatureActivity>;
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1378
1626
|
declare class StackBE {
|
|
1379
1627
|
private http;
|
|
1380
1628
|
private appId;
|
|
@@ -1396,6 +1644,8 @@ declare class StackBE {
|
|
|
1396
1644
|
readonly plans: PlansClient;
|
|
1397
1645
|
/** Products */
|
|
1398
1646
|
readonly products: ProductsClient;
|
|
1647
|
+
/** Feature requests and voting */
|
|
1648
|
+
readonly featureRequests: FeatureRequestsClient;
|
|
1399
1649
|
/**
|
|
1400
1650
|
* Create a new StackBE client.
|
|
1401
1651
|
*
|
|
@@ -1496,4 +1746,4 @@ declare class StackBE {
|
|
|
1496
1746
|
}): (req: any, res: any, next: any) => Promise<any>;
|
|
1497
1747
|
}
|
|
1498
1748
|
|
|
1499
|
-
export { type AddMemberOptions, type AnyWebhookEvent, AuthClient, type CancelSubscriptionOptions, type CancelSubscriptionResponse, type CheckEntitlementResponse, type CheckUsageResponse, CheckoutClient, type CheckoutSessionResponse, type CreateCheckoutOptions, type CreateCustomerOptions, type CreateOrganizationOptions, type Customer, type CustomerCreatedEvent, type CustomerUpdatedEvent, type CustomerUsageResponse, type CustomerWebhookPayload, type CustomerWithSubscription, CustomersClient, EntitlementsClient, type EntitlementsResponse, type InviteMemberOptions, type ListPlansOptions, type ListProductsOptions, type MagicLinkOptions, type MagicLinkResponse, type Organization, type OrganizationInvite, type OrganizationMember, OrganizationsClient, type PaymentFailedEvent, type PaymentSucceededEvent, type PaymentWebhookPayload, type Plan, PlansClient, type Product, ProductsClient, type SessionResponse, StackBE, type StackBEConfig, StackBEError, type StackBEErrorCode, type StackBEErrorResponse, type Subscription, type SubscriptionCancelledEvent, type SubscriptionCreatedEvent, type SubscriptionRenewedEvent, type SubscriptionUpdatedEvent, type SubscriptionWebhookPayload, type SubscriptionWithPlan, SubscriptionsClient, type TrackUsageOptions, type TrackUsageResponse, type TrialEndedEvent, type TrialStartedEvent, type UpdateCustomerOptions, type UpdateOrganizationOptions, type UpdateSubscriptionOptions, UsageClient, type UsageMetric, type VerifyTokenResponse, type WebhookEvent, type WebhookEventType };
|
|
1749
|
+
export { type AddMemberOptions, type AnyWebhookEvent, AuthClient, type CancelSubscriptionOptions, type CancelSubscriptionResponse, type CheckEntitlementResponse, type CheckUsageResponse, CheckoutClient, type CheckoutSessionResponse, type CreateCheckoutOptions, type CreateCustomerOptions, type CreateFeatureRequestOptions, type CreateOrganizationOptions, type Customer, type CustomerCreatedEvent, type CustomerFeatureActivity, type CustomerUpdatedEvent, type CustomerUsageResponse, type CustomerWebhookPayload, type CustomerWithSubscription, CustomersClient, EntitlementsClient, type EntitlementsResponse, type FeatureRequest, type FeatureRequestComment, type FeatureRequestListResponse, FeatureRequestsClient, type InterestedCustomer, type InterestedCustomersResponse, type InviteMemberOptions, type ListFeatureRequestsOptions, type ListPlansOptions, type ListProductsOptions, type MagicLinkOptions, type MagicLinkResponse, type Organization, type OrganizationInvite, type OrganizationMember, OrganizationsClient, type PaymentFailedEvent, type PaymentSucceededEvent, type PaymentWebhookPayload, type Plan, PlansClient, type Product, ProductsClient, type SessionResponse, StackBE, type StackBEConfig, StackBEError, type StackBEErrorCode, type StackBEErrorResponse, type Subscription, type SubscriptionCancelledEvent, type SubscriptionCreatedEvent, type SubscriptionRenewedEvent, type SubscriptionUpdatedEvent, type SubscriptionWebhookPayload, type SubscriptionWithPlan, SubscriptionsClient, type TrackUsageOptions, type TrackUsageResponse, type TrialEndedEvent, type TrialStartedEvent, type UpdateCustomerOptions, type UpdateOrganizationOptions, type UpdateSubscriptionOptions, UsageClient, type UsageMetric, type VerifyTokenResponse, type WebhookEvent, type WebhookEventType };
|
package/dist/index.js
CHANGED
|
@@ -24,6 +24,7 @@ __export(index_exports, {
|
|
|
24
24
|
CheckoutClient: () => CheckoutClient,
|
|
25
25
|
CustomersClient: () => CustomersClient,
|
|
26
26
|
EntitlementsClient: () => EntitlementsClient,
|
|
27
|
+
FeatureRequestsClient: () => FeatureRequestsClient,
|
|
27
28
|
OrganizationsClient: () => OrganizationsClient,
|
|
28
29
|
PlansClient: () => PlansClient,
|
|
29
30
|
ProductsClient: () => ProductsClient,
|
|
@@ -168,8 +169,8 @@ var HttpClient = class {
|
|
|
168
169
|
async patch(path, body) {
|
|
169
170
|
return this.request("PATCH", path, { body });
|
|
170
171
|
}
|
|
171
|
-
async delete(path) {
|
|
172
|
-
return this.request("DELETE", path);
|
|
172
|
+
async delete(path, body) {
|
|
173
|
+
return this.request("DELETE", path, body ? { body } : void 0);
|
|
173
174
|
}
|
|
174
175
|
};
|
|
175
176
|
|
|
@@ -620,7 +621,7 @@ var CheckoutClient = class {
|
|
|
620
621
|
*
|
|
621
622
|
* @example
|
|
622
623
|
* ```typescript
|
|
623
|
-
* // With
|
|
624
|
+
* // With customer email (looks up or creates customer)
|
|
624
625
|
* const { url } = await stackbe.checkout.createSession({
|
|
625
626
|
* customer: { email: 'user@example.com', name: 'John' },
|
|
626
627
|
* planId: 'plan_pro_monthly',
|
|
@@ -628,6 +629,18 @@ var CheckoutClient = class {
|
|
|
628
629
|
* trialDays: 14,
|
|
629
630
|
* });
|
|
630
631
|
* ```
|
|
632
|
+
*
|
|
633
|
+
* @example
|
|
634
|
+
* ```typescript
|
|
635
|
+
* // Organization-level subscription (B2B)
|
|
636
|
+
* const { url } = await stackbe.checkout.createSession({
|
|
637
|
+
* customer: 'cust_123',
|
|
638
|
+
* organizationId: 'org_456', // Customer must be owner/admin
|
|
639
|
+
* planId: 'plan_team',
|
|
640
|
+
* successUrl: 'https://myapp.com/success',
|
|
641
|
+
* cancelUrl: 'https://myapp.com/pricing',
|
|
642
|
+
* });
|
|
643
|
+
* ```
|
|
631
644
|
*/
|
|
632
645
|
async createSession(options) {
|
|
633
646
|
const body = {
|
|
@@ -635,15 +648,25 @@ var CheckoutClient = class {
|
|
|
635
648
|
planId: options.planId,
|
|
636
649
|
successUrl: options.successUrl,
|
|
637
650
|
cancelUrl: options.cancelUrl,
|
|
651
|
+
organizationId: options.organizationId,
|
|
638
652
|
allowPromotionCodes: options.allowPromotionCodes,
|
|
639
653
|
trialDays: options.trialDays,
|
|
640
654
|
metadata: options.metadata
|
|
641
655
|
};
|
|
656
|
+
if (!options.customer) {
|
|
657
|
+
throw new Error(
|
|
658
|
+
'customer is required. Use customer: "cust_xxx" for existing customer ID, or customer: { email: "user@example.com" } for email-based lookup.'
|
|
659
|
+
);
|
|
660
|
+
}
|
|
642
661
|
if (typeof options.customer === "string") {
|
|
643
662
|
body.customerId = options.customer;
|
|
644
|
-
} else {
|
|
663
|
+
} else if (options.customer.email) {
|
|
645
664
|
body.customerEmail = options.customer.email;
|
|
646
665
|
body.customerName = options.customer.name;
|
|
666
|
+
} else {
|
|
667
|
+
throw new Error(
|
|
668
|
+
'customer.email is required when using object form. Use customer: { email: "user@example.com" }'
|
|
669
|
+
);
|
|
647
670
|
}
|
|
648
671
|
return this.http.post("/v1/checkout/session", body);
|
|
649
672
|
}
|
|
@@ -1438,6 +1461,204 @@ var ProductsClient = class {
|
|
|
1438
1461
|
}
|
|
1439
1462
|
};
|
|
1440
1463
|
|
|
1464
|
+
// src/feature-requests.ts
|
|
1465
|
+
var FeatureRequestsClient = class {
|
|
1466
|
+
constructor(http, appId) {
|
|
1467
|
+
this.http = http;
|
|
1468
|
+
this.appId = appId;
|
|
1469
|
+
}
|
|
1470
|
+
/**
|
|
1471
|
+
* List feature requests for the app.
|
|
1472
|
+
*
|
|
1473
|
+
* @example
|
|
1474
|
+
* ```typescript
|
|
1475
|
+
* // Get all feature requests
|
|
1476
|
+
* const { data, total } = await stackbe.featureRequests.list();
|
|
1477
|
+
*
|
|
1478
|
+
* // Filter by status
|
|
1479
|
+
* const planned = await stackbe.featureRequests.list({ status: 'planned' });
|
|
1480
|
+
*
|
|
1481
|
+
* // Sort by votes
|
|
1482
|
+
* const popular = await stackbe.featureRequests.list({ sortBy: 'votes' });
|
|
1483
|
+
* ```
|
|
1484
|
+
*/
|
|
1485
|
+
async list(options = {}) {
|
|
1486
|
+
const params = {};
|
|
1487
|
+
if (options.status) params.status = options.status;
|
|
1488
|
+
if (options.category) params.category = options.category;
|
|
1489
|
+
if (options.sortBy) params.sortBy = options.sortBy;
|
|
1490
|
+
if (options.limit) params.limit = options.limit;
|
|
1491
|
+
if (options.offset) params.offset = options.offset;
|
|
1492
|
+
return this.http.get(
|
|
1493
|
+
`/v1/apps/${this.appId}/feature-requests`,
|
|
1494
|
+
params
|
|
1495
|
+
);
|
|
1496
|
+
}
|
|
1497
|
+
/**
|
|
1498
|
+
* Submit a new feature request.
|
|
1499
|
+
* Requires customer session token.
|
|
1500
|
+
*
|
|
1501
|
+
* @example
|
|
1502
|
+
* ```typescript
|
|
1503
|
+
* const request = await stackbe.featureRequests.create({
|
|
1504
|
+
* title: 'Dark mode support',
|
|
1505
|
+
* description: 'Would love to have a dark theme option',
|
|
1506
|
+
* category: 'UI',
|
|
1507
|
+
* });
|
|
1508
|
+
* ```
|
|
1509
|
+
*/
|
|
1510
|
+
async create(options) {
|
|
1511
|
+
return this.http.post(
|
|
1512
|
+
`/v1/apps/${this.appId}/feature-requests`,
|
|
1513
|
+
options
|
|
1514
|
+
);
|
|
1515
|
+
}
|
|
1516
|
+
/**
|
|
1517
|
+
* Get a specific feature request by ID.
|
|
1518
|
+
*
|
|
1519
|
+
* @example
|
|
1520
|
+
* ```typescript
|
|
1521
|
+
* const request = await stackbe.featureRequests.get('req_123');
|
|
1522
|
+
* console.log(`${request.title} has ${request.voteCount} votes`);
|
|
1523
|
+
* ```
|
|
1524
|
+
*/
|
|
1525
|
+
async get(requestId) {
|
|
1526
|
+
return this.http.get(
|
|
1527
|
+
`/v1/apps/${this.appId}/feature-requests/${requestId}`
|
|
1528
|
+
);
|
|
1529
|
+
}
|
|
1530
|
+
/**
|
|
1531
|
+
* Upvote a feature request.
|
|
1532
|
+
* Requires customer session token, OR API key with customerId.
|
|
1533
|
+
*
|
|
1534
|
+
* @example
|
|
1535
|
+
* ```typescript
|
|
1536
|
+
* // With customer session token
|
|
1537
|
+
* await stackbe.featureRequests.vote('req_123');
|
|
1538
|
+
*
|
|
1539
|
+
* // With API key (server-side)
|
|
1540
|
+
* await stackbe.featureRequests.vote('req_123', 'cust_456');
|
|
1541
|
+
* ```
|
|
1542
|
+
*/
|
|
1543
|
+
async vote(requestId, customerId) {
|
|
1544
|
+
return this.http.post(
|
|
1545
|
+
`/v1/apps/${this.appId}/feature-requests/${requestId}/vote`,
|
|
1546
|
+
customerId ? { customerId } : void 0
|
|
1547
|
+
);
|
|
1548
|
+
}
|
|
1549
|
+
/**
|
|
1550
|
+
* Remove your vote from a feature request.
|
|
1551
|
+
* Requires customer session token, OR API key with customerId.
|
|
1552
|
+
*
|
|
1553
|
+
* @example
|
|
1554
|
+
* ```typescript
|
|
1555
|
+
* // With customer session token
|
|
1556
|
+
* await stackbe.featureRequests.removeVote('req_123');
|
|
1557
|
+
*
|
|
1558
|
+
* // With API key (server-side)
|
|
1559
|
+
* await stackbe.featureRequests.removeVote('req_123', 'cust_456');
|
|
1560
|
+
* ```
|
|
1561
|
+
*/
|
|
1562
|
+
async removeVote(requestId, customerId) {
|
|
1563
|
+
return this.http.delete(
|
|
1564
|
+
`/v1/apps/${this.appId}/feature-requests/${requestId}/vote`,
|
|
1565
|
+
customerId ? { customerId } : void 0
|
|
1566
|
+
);
|
|
1567
|
+
}
|
|
1568
|
+
/**
|
|
1569
|
+
* Add a comment to a feature request.
|
|
1570
|
+
* Requires customer session token.
|
|
1571
|
+
*
|
|
1572
|
+
* @example
|
|
1573
|
+
* ```typescript
|
|
1574
|
+
* await stackbe.featureRequests.addComment('req_123', {
|
|
1575
|
+
* content: 'This would be really helpful for my workflow!',
|
|
1576
|
+
* });
|
|
1577
|
+
* ```
|
|
1578
|
+
*/
|
|
1579
|
+
async addComment(requestId, options) {
|
|
1580
|
+
return this.http.post(
|
|
1581
|
+
`/v1/apps/${this.appId}/feature-requests/${requestId}/comments`,
|
|
1582
|
+
options
|
|
1583
|
+
);
|
|
1584
|
+
}
|
|
1585
|
+
// ==================== Admin Methods ====================
|
|
1586
|
+
/**
|
|
1587
|
+
* Update a feature request status or category.
|
|
1588
|
+
* Requires API key (admin).
|
|
1589
|
+
*
|
|
1590
|
+
* @example
|
|
1591
|
+
* ```typescript
|
|
1592
|
+
* await stackbe.featureRequests.update('req_123', {
|
|
1593
|
+
* status: 'planned',
|
|
1594
|
+
* category: 'Core Features',
|
|
1595
|
+
* });
|
|
1596
|
+
* ```
|
|
1597
|
+
*/
|
|
1598
|
+
async update(requestId, options) {
|
|
1599
|
+
return this.http.patch(
|
|
1600
|
+
`/v1/apps/${this.appId}/feature-requests/${requestId}`,
|
|
1601
|
+
options
|
|
1602
|
+
);
|
|
1603
|
+
}
|
|
1604
|
+
/**
|
|
1605
|
+
* Delete a feature request.
|
|
1606
|
+
* Requires API key (admin).
|
|
1607
|
+
*
|
|
1608
|
+
* @example
|
|
1609
|
+
* ```typescript
|
|
1610
|
+
* await stackbe.featureRequests.delete('req_123');
|
|
1611
|
+
* ```
|
|
1612
|
+
*/
|
|
1613
|
+
async delete(requestId) {
|
|
1614
|
+
return this.http.delete(
|
|
1615
|
+
`/v1/apps/${this.appId}/feature-requests/${requestId}`
|
|
1616
|
+
);
|
|
1617
|
+
}
|
|
1618
|
+
/**
|
|
1619
|
+
* Get all customers interested in a feature request (author + voters).
|
|
1620
|
+
* Useful for user research or notifying users when the feature ships.
|
|
1621
|
+
* Requires API key (admin).
|
|
1622
|
+
*
|
|
1623
|
+
* @example
|
|
1624
|
+
* ```typescript
|
|
1625
|
+
* const { author, voters, totalInterested } = await stackbe.featureRequests.getInterestedCustomers('req_123');
|
|
1626
|
+
*
|
|
1627
|
+
* // Notify all interested users
|
|
1628
|
+
* const emails = [author.email, ...voters.map(v => v.email)];
|
|
1629
|
+
* await sendNotification(emails, 'Your requested feature is now live!');
|
|
1630
|
+
* ```
|
|
1631
|
+
*/
|
|
1632
|
+
async getInterestedCustomers(requestId) {
|
|
1633
|
+
return this.http.get(
|
|
1634
|
+
`/v1/apps/${this.appId}/feature-requests/${requestId}/interested`
|
|
1635
|
+
);
|
|
1636
|
+
}
|
|
1637
|
+
/**
|
|
1638
|
+
* Get a customer's feature request activity (requests submitted + votes).
|
|
1639
|
+
* Useful for displaying on customer profile.
|
|
1640
|
+
* Requires API key (admin).
|
|
1641
|
+
*
|
|
1642
|
+
* @example
|
|
1643
|
+
* ```typescript
|
|
1644
|
+
* const activity = await stackbe.featureRequests.getCustomerActivity('cust_123');
|
|
1645
|
+
*
|
|
1646
|
+
* console.log(`Submitted ${activity.totalSubmitted} requests`);
|
|
1647
|
+
* console.log(`Voted on ${activity.totalVotes} requests`);
|
|
1648
|
+
*
|
|
1649
|
+
* // Show their most wanted features
|
|
1650
|
+
* activity.votedOn.forEach(r => {
|
|
1651
|
+
* console.log(`- ${r.title} (${r.status})`);
|
|
1652
|
+
* });
|
|
1653
|
+
* ```
|
|
1654
|
+
*/
|
|
1655
|
+
async getCustomerActivity(customerId) {
|
|
1656
|
+
return this.http.get(
|
|
1657
|
+
`/v1/apps/${this.appId}/feature-requests/customer/${customerId}/activity`
|
|
1658
|
+
);
|
|
1659
|
+
}
|
|
1660
|
+
};
|
|
1661
|
+
|
|
1441
1662
|
// src/client.ts
|
|
1442
1663
|
var DEFAULT_BASE_URL = "https://api.stackbe.io";
|
|
1443
1664
|
var DEFAULT_TIMEOUT = 3e4;
|
|
@@ -1500,6 +1721,7 @@ var StackBE = class {
|
|
|
1500
1721
|
this.organizations = new OrganizationsClient(this.http, config.appId);
|
|
1501
1722
|
this.plans = new PlansClient(this.http);
|
|
1502
1723
|
this.products = new ProductsClient(this.http, config.appId);
|
|
1724
|
+
this.featureRequests = new FeatureRequestsClient(this.http, config.appId);
|
|
1503
1725
|
}
|
|
1504
1726
|
/**
|
|
1505
1727
|
* Create a middleware for Express that tracks usage automatically.
|
|
@@ -1637,6 +1859,7 @@ var StackBE = class {
|
|
|
1637
1859
|
CheckoutClient,
|
|
1638
1860
|
CustomersClient,
|
|
1639
1861
|
EntitlementsClient,
|
|
1862
|
+
FeatureRequestsClient,
|
|
1640
1863
|
OrganizationsClient,
|
|
1641
1864
|
PlansClient,
|
|
1642
1865
|
ProductsClient,
|
package/dist/index.mjs
CHANGED
|
@@ -132,8 +132,8 @@ var HttpClient = class {
|
|
|
132
132
|
async patch(path, body) {
|
|
133
133
|
return this.request("PATCH", path, { body });
|
|
134
134
|
}
|
|
135
|
-
async delete(path) {
|
|
136
|
-
return this.request("DELETE", path);
|
|
135
|
+
async delete(path, body) {
|
|
136
|
+
return this.request("DELETE", path, body ? { body } : void 0);
|
|
137
137
|
}
|
|
138
138
|
};
|
|
139
139
|
|
|
@@ -584,7 +584,7 @@ var CheckoutClient = class {
|
|
|
584
584
|
*
|
|
585
585
|
* @example
|
|
586
586
|
* ```typescript
|
|
587
|
-
* // With
|
|
587
|
+
* // With customer email (looks up or creates customer)
|
|
588
588
|
* const { url } = await stackbe.checkout.createSession({
|
|
589
589
|
* customer: { email: 'user@example.com', name: 'John' },
|
|
590
590
|
* planId: 'plan_pro_monthly',
|
|
@@ -592,6 +592,18 @@ var CheckoutClient = class {
|
|
|
592
592
|
* trialDays: 14,
|
|
593
593
|
* });
|
|
594
594
|
* ```
|
|
595
|
+
*
|
|
596
|
+
* @example
|
|
597
|
+
* ```typescript
|
|
598
|
+
* // Organization-level subscription (B2B)
|
|
599
|
+
* const { url } = await stackbe.checkout.createSession({
|
|
600
|
+
* customer: 'cust_123',
|
|
601
|
+
* organizationId: 'org_456', // Customer must be owner/admin
|
|
602
|
+
* planId: 'plan_team',
|
|
603
|
+
* successUrl: 'https://myapp.com/success',
|
|
604
|
+
* cancelUrl: 'https://myapp.com/pricing',
|
|
605
|
+
* });
|
|
606
|
+
* ```
|
|
595
607
|
*/
|
|
596
608
|
async createSession(options) {
|
|
597
609
|
const body = {
|
|
@@ -599,15 +611,25 @@ var CheckoutClient = class {
|
|
|
599
611
|
planId: options.planId,
|
|
600
612
|
successUrl: options.successUrl,
|
|
601
613
|
cancelUrl: options.cancelUrl,
|
|
614
|
+
organizationId: options.organizationId,
|
|
602
615
|
allowPromotionCodes: options.allowPromotionCodes,
|
|
603
616
|
trialDays: options.trialDays,
|
|
604
617
|
metadata: options.metadata
|
|
605
618
|
};
|
|
619
|
+
if (!options.customer) {
|
|
620
|
+
throw new Error(
|
|
621
|
+
'customer is required. Use customer: "cust_xxx" for existing customer ID, or customer: { email: "user@example.com" } for email-based lookup.'
|
|
622
|
+
);
|
|
623
|
+
}
|
|
606
624
|
if (typeof options.customer === "string") {
|
|
607
625
|
body.customerId = options.customer;
|
|
608
|
-
} else {
|
|
626
|
+
} else if (options.customer.email) {
|
|
609
627
|
body.customerEmail = options.customer.email;
|
|
610
628
|
body.customerName = options.customer.name;
|
|
629
|
+
} else {
|
|
630
|
+
throw new Error(
|
|
631
|
+
'customer.email is required when using object form. Use customer: { email: "user@example.com" }'
|
|
632
|
+
);
|
|
611
633
|
}
|
|
612
634
|
return this.http.post("/v1/checkout/session", body);
|
|
613
635
|
}
|
|
@@ -1402,6 +1424,204 @@ var ProductsClient = class {
|
|
|
1402
1424
|
}
|
|
1403
1425
|
};
|
|
1404
1426
|
|
|
1427
|
+
// src/feature-requests.ts
|
|
1428
|
+
var FeatureRequestsClient = class {
|
|
1429
|
+
constructor(http, appId) {
|
|
1430
|
+
this.http = http;
|
|
1431
|
+
this.appId = appId;
|
|
1432
|
+
}
|
|
1433
|
+
/**
|
|
1434
|
+
* List feature requests for the app.
|
|
1435
|
+
*
|
|
1436
|
+
* @example
|
|
1437
|
+
* ```typescript
|
|
1438
|
+
* // Get all feature requests
|
|
1439
|
+
* const { data, total } = await stackbe.featureRequests.list();
|
|
1440
|
+
*
|
|
1441
|
+
* // Filter by status
|
|
1442
|
+
* const planned = await stackbe.featureRequests.list({ status: 'planned' });
|
|
1443
|
+
*
|
|
1444
|
+
* // Sort by votes
|
|
1445
|
+
* const popular = await stackbe.featureRequests.list({ sortBy: 'votes' });
|
|
1446
|
+
* ```
|
|
1447
|
+
*/
|
|
1448
|
+
async list(options = {}) {
|
|
1449
|
+
const params = {};
|
|
1450
|
+
if (options.status) params.status = options.status;
|
|
1451
|
+
if (options.category) params.category = options.category;
|
|
1452
|
+
if (options.sortBy) params.sortBy = options.sortBy;
|
|
1453
|
+
if (options.limit) params.limit = options.limit;
|
|
1454
|
+
if (options.offset) params.offset = options.offset;
|
|
1455
|
+
return this.http.get(
|
|
1456
|
+
`/v1/apps/${this.appId}/feature-requests`,
|
|
1457
|
+
params
|
|
1458
|
+
);
|
|
1459
|
+
}
|
|
1460
|
+
/**
|
|
1461
|
+
* Submit a new feature request.
|
|
1462
|
+
* Requires customer session token.
|
|
1463
|
+
*
|
|
1464
|
+
* @example
|
|
1465
|
+
* ```typescript
|
|
1466
|
+
* const request = await stackbe.featureRequests.create({
|
|
1467
|
+
* title: 'Dark mode support',
|
|
1468
|
+
* description: 'Would love to have a dark theme option',
|
|
1469
|
+
* category: 'UI',
|
|
1470
|
+
* });
|
|
1471
|
+
* ```
|
|
1472
|
+
*/
|
|
1473
|
+
async create(options) {
|
|
1474
|
+
return this.http.post(
|
|
1475
|
+
`/v1/apps/${this.appId}/feature-requests`,
|
|
1476
|
+
options
|
|
1477
|
+
);
|
|
1478
|
+
}
|
|
1479
|
+
/**
|
|
1480
|
+
* Get a specific feature request by ID.
|
|
1481
|
+
*
|
|
1482
|
+
* @example
|
|
1483
|
+
* ```typescript
|
|
1484
|
+
* const request = await stackbe.featureRequests.get('req_123');
|
|
1485
|
+
* console.log(`${request.title} has ${request.voteCount} votes`);
|
|
1486
|
+
* ```
|
|
1487
|
+
*/
|
|
1488
|
+
async get(requestId) {
|
|
1489
|
+
return this.http.get(
|
|
1490
|
+
`/v1/apps/${this.appId}/feature-requests/${requestId}`
|
|
1491
|
+
);
|
|
1492
|
+
}
|
|
1493
|
+
/**
|
|
1494
|
+
* Upvote a feature request.
|
|
1495
|
+
* Requires customer session token, OR API key with customerId.
|
|
1496
|
+
*
|
|
1497
|
+
* @example
|
|
1498
|
+
* ```typescript
|
|
1499
|
+
* // With customer session token
|
|
1500
|
+
* await stackbe.featureRequests.vote('req_123');
|
|
1501
|
+
*
|
|
1502
|
+
* // With API key (server-side)
|
|
1503
|
+
* await stackbe.featureRequests.vote('req_123', 'cust_456');
|
|
1504
|
+
* ```
|
|
1505
|
+
*/
|
|
1506
|
+
async vote(requestId, customerId) {
|
|
1507
|
+
return this.http.post(
|
|
1508
|
+
`/v1/apps/${this.appId}/feature-requests/${requestId}/vote`,
|
|
1509
|
+
customerId ? { customerId } : void 0
|
|
1510
|
+
);
|
|
1511
|
+
}
|
|
1512
|
+
/**
|
|
1513
|
+
* Remove your vote from a feature request.
|
|
1514
|
+
* Requires customer session token, OR API key with customerId.
|
|
1515
|
+
*
|
|
1516
|
+
* @example
|
|
1517
|
+
* ```typescript
|
|
1518
|
+
* // With customer session token
|
|
1519
|
+
* await stackbe.featureRequests.removeVote('req_123');
|
|
1520
|
+
*
|
|
1521
|
+
* // With API key (server-side)
|
|
1522
|
+
* await stackbe.featureRequests.removeVote('req_123', 'cust_456');
|
|
1523
|
+
* ```
|
|
1524
|
+
*/
|
|
1525
|
+
async removeVote(requestId, customerId) {
|
|
1526
|
+
return this.http.delete(
|
|
1527
|
+
`/v1/apps/${this.appId}/feature-requests/${requestId}/vote`,
|
|
1528
|
+
customerId ? { customerId } : void 0
|
|
1529
|
+
);
|
|
1530
|
+
}
|
|
1531
|
+
/**
|
|
1532
|
+
* Add a comment to a feature request.
|
|
1533
|
+
* Requires customer session token.
|
|
1534
|
+
*
|
|
1535
|
+
* @example
|
|
1536
|
+
* ```typescript
|
|
1537
|
+
* await stackbe.featureRequests.addComment('req_123', {
|
|
1538
|
+
* content: 'This would be really helpful for my workflow!',
|
|
1539
|
+
* });
|
|
1540
|
+
* ```
|
|
1541
|
+
*/
|
|
1542
|
+
async addComment(requestId, options) {
|
|
1543
|
+
return this.http.post(
|
|
1544
|
+
`/v1/apps/${this.appId}/feature-requests/${requestId}/comments`,
|
|
1545
|
+
options
|
|
1546
|
+
);
|
|
1547
|
+
}
|
|
1548
|
+
// ==================== Admin Methods ====================
|
|
1549
|
+
/**
|
|
1550
|
+
* Update a feature request status or category.
|
|
1551
|
+
* Requires API key (admin).
|
|
1552
|
+
*
|
|
1553
|
+
* @example
|
|
1554
|
+
* ```typescript
|
|
1555
|
+
* await stackbe.featureRequests.update('req_123', {
|
|
1556
|
+
* status: 'planned',
|
|
1557
|
+
* category: 'Core Features',
|
|
1558
|
+
* });
|
|
1559
|
+
* ```
|
|
1560
|
+
*/
|
|
1561
|
+
async update(requestId, options) {
|
|
1562
|
+
return this.http.patch(
|
|
1563
|
+
`/v1/apps/${this.appId}/feature-requests/${requestId}`,
|
|
1564
|
+
options
|
|
1565
|
+
);
|
|
1566
|
+
}
|
|
1567
|
+
/**
|
|
1568
|
+
* Delete a feature request.
|
|
1569
|
+
* Requires API key (admin).
|
|
1570
|
+
*
|
|
1571
|
+
* @example
|
|
1572
|
+
* ```typescript
|
|
1573
|
+
* await stackbe.featureRequests.delete('req_123');
|
|
1574
|
+
* ```
|
|
1575
|
+
*/
|
|
1576
|
+
async delete(requestId) {
|
|
1577
|
+
return this.http.delete(
|
|
1578
|
+
`/v1/apps/${this.appId}/feature-requests/${requestId}`
|
|
1579
|
+
);
|
|
1580
|
+
}
|
|
1581
|
+
/**
|
|
1582
|
+
* Get all customers interested in a feature request (author + voters).
|
|
1583
|
+
* Useful for user research or notifying users when the feature ships.
|
|
1584
|
+
* Requires API key (admin).
|
|
1585
|
+
*
|
|
1586
|
+
* @example
|
|
1587
|
+
* ```typescript
|
|
1588
|
+
* const { author, voters, totalInterested } = await stackbe.featureRequests.getInterestedCustomers('req_123');
|
|
1589
|
+
*
|
|
1590
|
+
* // Notify all interested users
|
|
1591
|
+
* const emails = [author.email, ...voters.map(v => v.email)];
|
|
1592
|
+
* await sendNotification(emails, 'Your requested feature is now live!');
|
|
1593
|
+
* ```
|
|
1594
|
+
*/
|
|
1595
|
+
async getInterestedCustomers(requestId) {
|
|
1596
|
+
return this.http.get(
|
|
1597
|
+
`/v1/apps/${this.appId}/feature-requests/${requestId}/interested`
|
|
1598
|
+
);
|
|
1599
|
+
}
|
|
1600
|
+
/**
|
|
1601
|
+
* Get a customer's feature request activity (requests submitted + votes).
|
|
1602
|
+
* Useful for displaying on customer profile.
|
|
1603
|
+
* Requires API key (admin).
|
|
1604
|
+
*
|
|
1605
|
+
* @example
|
|
1606
|
+
* ```typescript
|
|
1607
|
+
* const activity = await stackbe.featureRequests.getCustomerActivity('cust_123');
|
|
1608
|
+
*
|
|
1609
|
+
* console.log(`Submitted ${activity.totalSubmitted} requests`);
|
|
1610
|
+
* console.log(`Voted on ${activity.totalVotes} requests`);
|
|
1611
|
+
*
|
|
1612
|
+
* // Show their most wanted features
|
|
1613
|
+
* activity.votedOn.forEach(r => {
|
|
1614
|
+
* console.log(`- ${r.title} (${r.status})`);
|
|
1615
|
+
* });
|
|
1616
|
+
* ```
|
|
1617
|
+
*/
|
|
1618
|
+
async getCustomerActivity(customerId) {
|
|
1619
|
+
return this.http.get(
|
|
1620
|
+
`/v1/apps/${this.appId}/feature-requests/customer/${customerId}/activity`
|
|
1621
|
+
);
|
|
1622
|
+
}
|
|
1623
|
+
};
|
|
1624
|
+
|
|
1405
1625
|
// src/client.ts
|
|
1406
1626
|
var DEFAULT_BASE_URL = "https://api.stackbe.io";
|
|
1407
1627
|
var DEFAULT_TIMEOUT = 3e4;
|
|
@@ -1464,6 +1684,7 @@ var StackBE = class {
|
|
|
1464
1684
|
this.organizations = new OrganizationsClient(this.http, config.appId);
|
|
1465
1685
|
this.plans = new PlansClient(this.http);
|
|
1466
1686
|
this.products = new ProductsClient(this.http, config.appId);
|
|
1687
|
+
this.featureRequests = new FeatureRequestsClient(this.http, config.appId);
|
|
1467
1688
|
}
|
|
1468
1689
|
/**
|
|
1469
1690
|
* Create a middleware for Express that tracks usage automatically.
|
|
@@ -1600,6 +1821,7 @@ export {
|
|
|
1600
1821
|
CheckoutClient,
|
|
1601
1822
|
CustomersClient,
|
|
1602
1823
|
EntitlementsClient,
|
|
1824
|
+
FeatureRequestsClient,
|
|
1603
1825
|
OrganizationsClient,
|
|
1604
1826
|
PlansClient,
|
|
1605
1827
|
ProductsClient,
|
package/package.json
CHANGED