perspectapi-ts-sdk 1.1.1

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,249 @@
1
+ /**
2
+ * Checkout/Stripe client for PerspectAPI SDK
3
+ */
4
+
5
+ import { BaseClient } from './base-client';
6
+ import type {
7
+ CreateCheckoutSessionRequest,
8
+ CheckoutSession,
9
+ ApiResponse,
10
+ } from '../types';
11
+
12
+ export class CheckoutClient extends BaseClient {
13
+ constructor(http: any) {
14
+ super(http, '/api/v1');
15
+ }
16
+
17
+ /**
18
+ * Get CSRF token for a specific site
19
+ * @param siteName - The site name to get CSRF token for
20
+ */
21
+ async getCsrfToken(siteName: string): Promise<ApiResponse<{
22
+ csrf_token: string;
23
+ site_id: string;
24
+ site_name: string;
25
+ }>> {
26
+ return this.http.get(`/api/v1/csrf/token/${siteName}`);
27
+ }
28
+
29
+ /**
30
+ * Create Stripe checkout session with CSRF protection
31
+ * @param siteName - The site name for the checkout session
32
+ * @param data - Checkout session data (can include priceId for single item or line_items for multiple)
33
+ * @param csrfToken - Optional CSRF token (if not provided, will fetch automatically)
34
+ */
35
+ async createCheckoutSession(
36
+ siteName: string,
37
+ data: CreateCheckoutSessionRequest,
38
+ csrfToken?: string
39
+ ): Promise<ApiResponse<CheckoutSession>> {
40
+ // If no CSRF token provided, fetch it first
41
+ let token = csrfToken;
42
+ if (!token) {
43
+ const csrfResponse = await this.getCsrfToken(siteName);
44
+ const csrfToken = csrfResponse.data?.csrf_token;
45
+
46
+ if (!csrfToken) {
47
+ throw new Error('Failed to obtain CSRF token');
48
+ }
49
+
50
+ token = csrfToken;
51
+ }
52
+
53
+ // Make the checkout request with CSRF token in headers
54
+ return this.http.request(this.buildPath(
55
+ this.siteScopedEndpoint(siteName, '/checkout/create-session')
56
+ ), {
57
+ method: 'POST',
58
+ body: data,
59
+ headers: {
60
+ 'X-CSRF-Token': token
61
+ }
62
+ });
63
+ }
64
+
65
+ /**
66
+ * Get checkout session by ID
67
+ */
68
+ async getCheckoutSession(sessionId: string): Promise<ApiResponse<{
69
+ id: string;
70
+ status: string;
71
+ paymentStatus: string;
72
+ customerEmail?: string;
73
+ amountTotal: number;
74
+ currency: string;
75
+ metadata?: Record<string, string>;
76
+ createdAt: string;
77
+ expiresAt: string;
78
+ }>> {
79
+ return this.getSingle(`/checkout/sessions/${sessionId}`);
80
+ }
81
+
82
+ /**
83
+ * Get checkout sessions for organization
84
+ */
85
+ async getCheckoutSessions(params?: {
86
+ page?: number;
87
+ limit?: number;
88
+ status?: string;
89
+ customerEmail?: string;
90
+ startDate?: string;
91
+ endDate?: string;
92
+ }): Promise<ApiResponse<Array<{
93
+ id: string;
94
+ status: string;
95
+ paymentStatus: string;
96
+ customerEmail?: string;
97
+ amountTotal: number;
98
+ currency: string;
99
+ createdAt: string;
100
+ }>>> {
101
+ return this.getPaginated('/checkout/sessions', params);
102
+ }
103
+
104
+ /**
105
+ * Cancel checkout session
106
+ */
107
+ async cancelCheckoutSession(sessionId: string): Promise<ApiResponse<{
108
+ success: boolean;
109
+ message: string;
110
+ }>> {
111
+ return this.create(`/checkout/sessions/${sessionId}/cancel`, {});
112
+ }
113
+
114
+ /**
115
+ * Expire checkout session
116
+ */
117
+ async expireCheckoutSession(sessionId: string): Promise<ApiResponse<{
118
+ success: boolean;
119
+ message: string;
120
+ }>> {
121
+ return this.create(`/checkout/sessions/${sessionId}/expire`, {});
122
+ }
123
+
124
+ /**
125
+ * Get Stripe publishable key
126
+ */
127
+ async getStripePublishableKey(): Promise<ApiResponse<{
128
+ publishableKey: string;
129
+ mode: 'test' | 'live';
130
+ }>> {
131
+ return this.getSingle('/checkout/stripe/config');
132
+ }
133
+
134
+ /**
135
+ * Create payment intent (for custom checkout flows)
136
+ */
137
+ async createPaymentIntent(data: {
138
+ amount: number;
139
+ currency: string;
140
+ customerEmail?: string;
141
+ metadata?: Record<string, string>;
142
+ paymentMethodTypes?: string[];
143
+ }): Promise<ApiResponse<{
144
+ clientSecret: string;
145
+ id: string;
146
+ status: string;
147
+ }>> {
148
+ return this.create('/checkout/payment-intent', data);
149
+ }
150
+
151
+ /**
152
+ * Confirm payment intent
153
+ */
154
+ async confirmPaymentIntent(paymentIntentId: string, data: {
155
+ paymentMethodId: string;
156
+ returnUrl?: string;
157
+ }): Promise<ApiResponse<{
158
+ success: boolean;
159
+ status: string;
160
+ nextAction?: any;
161
+ }>> {
162
+ return this.create(`/checkout/payment-intent/${paymentIntentId}/confirm`, data);
163
+ }
164
+
165
+ /**
166
+ * Get payment methods for customer
167
+ */
168
+ async getPaymentMethods(customerId: string): Promise<ApiResponse<Array<{
169
+ id: string;
170
+ type: string;
171
+ card?: {
172
+ brand: string;
173
+ last4: string;
174
+ expMonth: number;
175
+ expYear: number;
176
+ };
177
+ createdAt: string;
178
+ }>>> {
179
+ return this.getSingle(`/checkout/customers/${customerId}/payment-methods`);
180
+ }
181
+
182
+ /**
183
+ * Create customer
184
+ */
185
+ async createCustomer(data: {
186
+ email: string;
187
+ name?: string;
188
+ phone?: string;
189
+ metadata?: Record<string, string>;
190
+ }): Promise<ApiResponse<{
191
+ id: string;
192
+ email: string;
193
+ name?: string;
194
+ createdAt: string;
195
+ }>> {
196
+ return this.create('/checkout/customers', data);
197
+ }
198
+
199
+ /**
200
+ * Get customer by ID
201
+ */
202
+ async getCustomer(customerId: string): Promise<ApiResponse<{
203
+ id: string;
204
+ email: string;
205
+ name?: string;
206
+ phone?: string;
207
+ metadata?: Record<string, string>;
208
+ createdAt: string;
209
+ }>> {
210
+ return this.getSingle(`/checkout/customers/${customerId}`);
211
+ }
212
+
213
+ /**
214
+ * Update customer
215
+ */
216
+ async updateCustomer(customerId: string, data: {
217
+ email?: string;
218
+ name?: string;
219
+ phone?: string;
220
+ metadata?: Record<string, string>;
221
+ }): Promise<ApiResponse<{
222
+ id: string;
223
+ email: string;
224
+ name?: string;
225
+ updatedAt: string;
226
+ }>> {
227
+ return this.update(`/checkout/customers/${customerId}`, data);
228
+ }
229
+
230
+ /**
231
+ * Get invoices for customer
232
+ */
233
+ async getCustomerInvoices(customerId: string, params?: {
234
+ page?: number;
235
+ limit?: number;
236
+ status?: string;
237
+ }): Promise<ApiResponse<Array<{
238
+ id: string;
239
+ number: string;
240
+ status: string;
241
+ amountPaid: number;
242
+ amountDue: number;
243
+ currency: string;
244
+ dueDate?: string;
245
+ createdAt: string;
246
+ }>>> {
247
+ return this.getPaginated(`/checkout/customers/${customerId}/invoices`, params);
248
+ }
249
+ }
@@ -0,0 +1,203 @@
1
+ /**
2
+ * Contact Forms client for PerspectAPI SDK
3
+ */
4
+
5
+ import { BaseClient } from './base-client';
6
+ import type {
7
+ ContactSubmission,
8
+ CreateContactRequest,
9
+ PaginatedResponse,
10
+ ApiResponse,
11
+ } from '../types';
12
+
13
+ export class ContactClient extends BaseClient {
14
+ constructor(http: any) {
15
+ super(http, '/api/v1');
16
+ }
17
+
18
+ /**
19
+ * Build a contact endpoint scoped to a site (without /sites prefix)
20
+ */
21
+ private contactEndpoint(siteName: string, endpoint: string): string {
22
+ return this.siteScopedEndpoint(siteName, endpoint, { includeSitesSegment: false });
23
+ }
24
+
25
+ /**
26
+ * Submit contact form
27
+ */
28
+ async submitContact(siteName: string, data: CreateContactRequest): Promise<ApiResponse<{
29
+ id: string;
30
+ message: string;
31
+ status: string;
32
+ }>> {
33
+ return this.create<CreateContactRequest, {
34
+ id: string;
35
+ message: string;
36
+ status: string;
37
+ }>(this.contactEndpoint(siteName, '/contact/submit'), data);
38
+ }
39
+
40
+ /**
41
+ * Get contact submission status
42
+ */
43
+ async getContactStatus(siteName: string, id: string): Promise<ApiResponse<{
44
+ id: string;
45
+ status: string;
46
+ submittedAt: string;
47
+ processedAt?: string;
48
+ }>> {
49
+ return this.getSingle(this.contactEndpoint(siteName, `/contact/status/${encodeURIComponent(id)}`));
50
+ }
51
+
52
+ /**
53
+ * Get all contact submissions (admin only)
54
+ */
55
+ async getContactSubmissions(siteName: string, params?: {
56
+ page?: number;
57
+ limit?: number;
58
+ status?: string;
59
+ startDate?: string;
60
+ endDate?: string;
61
+ search?: string;
62
+ }): Promise<PaginatedResponse<ContactSubmission>> {
63
+ return this.getPaginated<ContactSubmission>(this.contactEndpoint(siteName, '/contact/list'), params);
64
+ }
65
+
66
+ /**
67
+ * Get contact submission by ID (admin only)
68
+ */
69
+ async getContactSubmissionById(siteName: string, id: string): Promise<ApiResponse<ContactSubmission & {
70
+ ipAddress?: string;
71
+ userAgent?: string;
72
+ metadata?: Record<string, any>;
73
+ }>> {
74
+ return this.getSingle(this.contactEndpoint(siteName, `/contact/${encodeURIComponent(id)}`));
75
+ }
76
+
77
+ /**
78
+ * Update contact submission status (admin only)
79
+ */
80
+ async updateContactStatus(siteName: string, id: string, status: string, notes?: string): Promise<ApiResponse<{
81
+ message: string;
82
+ status: string;
83
+ }>> {
84
+ return this.patch(this.contactEndpoint(siteName, `/contact/${encodeURIComponent(id)}`), { status, notes });
85
+ }
86
+
87
+ /**
88
+ * Mark contact as read (admin only)
89
+ */
90
+ async markContactAsRead(siteName: string, id: string): Promise<ApiResponse<{ message: string }>> {
91
+ return this.patch(this.contactEndpoint(siteName, `/contact/${encodeURIComponent(id)}`), { status: 'read' });
92
+ }
93
+
94
+ /**
95
+ * Mark contact as unread (admin only)
96
+ */
97
+ async markContactAsUnread(siteName: string, id: string): Promise<ApiResponse<{ message: string }>> {
98
+ return this.patch(this.contactEndpoint(siteName, `/contact/${encodeURIComponent(id)}`), { status: 'unread' });
99
+ }
100
+
101
+ /**
102
+ * Archive contact submission (admin only)
103
+ */
104
+ async archiveContact(siteName: string, id: string): Promise<ApiResponse<{ message: string }>> {
105
+ return this.patch(this.contactEndpoint(siteName, `/contact/${encodeURIComponent(id)}`), { status: 'archived' });
106
+ }
107
+
108
+ /**
109
+ * Delete contact submission (admin only)
110
+ */
111
+ async deleteContact(siteName: string, id: string): Promise<ApiResponse<{ message: string }>> {
112
+ return this.delete<{ message: string }>(this.contactEndpoint(siteName, `/contact/${encodeURIComponent(id)}`));
113
+ }
114
+
115
+ /**
116
+ * Bulk update contact submissions (admin only)
117
+ */
118
+ async bulkUpdateContacts(siteName: string, data: {
119
+ ids: string[];
120
+ status?: string;
121
+ action: 'mark_read' | 'mark_unread' | 'archive' | 'delete';
122
+ }): Promise<ApiResponse<{
123
+ success: boolean;
124
+ updatedCount: number;
125
+ failedCount: number;
126
+ }>> {
127
+ return this.create(this.contactEndpoint(siteName, '/contact/bulk-update'), data);
128
+ }
129
+
130
+ /**
131
+ * Get contact form statistics (admin only)
132
+ */
133
+ async getContactStats(siteName: string, params?: {
134
+ startDate?: string;
135
+ endDate?: string;
136
+ }): Promise<ApiResponse<{
137
+ totalSubmissions: number;
138
+ unreadSubmissions: number;
139
+ readSubmissions: number;
140
+ archivedSubmissions: number;
141
+ submissionsByDay: Array<{
142
+ date: string;
143
+ count: number;
144
+ }>;
145
+ submissionsByStatus: Record<string, number>;
146
+ averageResponseTime?: number;
147
+ }>> {
148
+ return this.http.get(this.buildPath(this.contactEndpoint(siteName, '/contact/stats')), params);
149
+ }
150
+
151
+ /**
152
+ * Export contact submissions (admin only)
153
+ */
154
+ async exportContacts(siteName: string, params?: {
155
+ format?: 'csv' | 'json' | 'xlsx';
156
+ startDate?: string;
157
+ endDate?: string;
158
+ status?: string;
159
+ }): Promise<ApiResponse<{
160
+ downloadUrl: string;
161
+ expiresAt: string;
162
+ }>> {
163
+ return this.create(this.contactEndpoint(siteName, '/contact/export'), params || {});
164
+ }
165
+
166
+ /**
167
+ * Get contact form configuration
168
+ */
169
+ async getContactConfig(siteName: string): Promise<ApiResponse<{
170
+ turnstileEnabled: boolean;
171
+ rateLimitEnabled: boolean;
172
+ rateLimitWindow: number;
173
+ rateLimitMax: number;
174
+ requiredFields: string[];
175
+ customFields?: Array<{
176
+ name: string;
177
+ type: string;
178
+ required: boolean;
179
+ label: string;
180
+ }>;
181
+ }>> {
182
+ return this.getSingle(this.contactEndpoint(siteName, '/contact/config'));
183
+ }
184
+
185
+ /**
186
+ * Update contact form configuration (admin only)
187
+ */
188
+ async updateContactConfig(siteName: string, data: {
189
+ turnstileEnabled?: boolean;
190
+ rateLimitEnabled?: boolean;
191
+ rateLimitWindow?: number;
192
+ rateLimitMax?: number;
193
+ requiredFields?: string[];
194
+ customFields?: Array<{
195
+ name: string;
196
+ type: string;
197
+ required: boolean;
198
+ label: string;
199
+ }>;
200
+ }): Promise<ApiResponse<{ message: string }>> {
201
+ return this.update(this.contactEndpoint(siteName, '/contact/config'), data);
202
+ }
203
+ }
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Content Management client for PerspectAPI SDK
3
+ */
4
+
5
+ import { BaseClient } from './base-client';
6
+ import type {
7
+ Content,
8
+ CreateContentRequest,
9
+ UpdateContentRequest,
10
+ ContentQueryParams,
11
+ PaginatedResponse,
12
+ ApiResponse,
13
+ } from '../types';
14
+
15
+ export class ContentClient extends BaseClient {
16
+ constructor(http: any) {
17
+ super(http, '/api/v1');
18
+ }
19
+
20
+ /**
21
+ * Get all content with pagination and filtering for a site
22
+ */
23
+ async getContent(siteName: string, params?: ContentQueryParams): Promise<PaginatedResponse<Content>> {
24
+ return this.http.get(this.buildPath(this.siteScopedEndpoint(siteName)), params);
25
+ }
26
+
27
+ /**
28
+ * Get content by ID
29
+ */
30
+ async getContentById(id: number): Promise<ApiResponse<Content>> {
31
+ return this.getSingle<Content>(`/content/${id}`);
32
+ }
33
+
34
+ /**
35
+ * Get content by slug for a site
36
+ */
37
+ async getContentBySlug(siteName: string, slug: string): Promise<ApiResponse<Content>> {
38
+ return this.http.get(this.buildPath(
39
+ this.siteScopedEndpoint(siteName, `/slug/${encodeURIComponent(slug)}`)
40
+ ));
41
+ }
42
+
43
+ /**
44
+ * Create new content
45
+ */
46
+ async createContent(data: CreateContentRequest): Promise<ApiResponse<Content>> {
47
+ return this.create<CreateContentRequest, Content>('/content', data);
48
+ }
49
+
50
+ /**
51
+ * Update content
52
+ */
53
+ async updateContent(id: number, data: UpdateContentRequest): Promise<ApiResponse<Content>> {
54
+ return this.update<UpdateContentRequest, Content>(`/content/${id}`, data);
55
+ }
56
+
57
+ /**
58
+ * Partially update content
59
+ */
60
+ async patchContent(id: number, data: Partial<UpdateContentRequest>): Promise<ApiResponse<Content>> {
61
+ return this.patch<Partial<UpdateContentRequest>, Content>(`/content/${id}`, data);
62
+ }
63
+
64
+ /**
65
+ * Delete content
66
+ */
67
+ async deleteContent(id: number): Promise<ApiResponse<{ message: string }>> {
68
+ return this.delete<{ message: string }>(`/content/${id}`);
69
+ }
70
+
71
+ /**
72
+ * Publish content
73
+ */
74
+ async publishContent(id: number): Promise<ApiResponse<Content>> {
75
+ return this.create<{ action: string }, Content>(`/content/${id}/publish`, { action: 'publish' });
76
+ }
77
+
78
+ /**
79
+ * Unpublish content
80
+ */
81
+ async unpublishContent(id: number): Promise<ApiResponse<Content>> {
82
+ return this.create<{ action: string }, Content>(`/content/${id}/publish`, { action: 'unpublish' });
83
+ }
84
+
85
+ /**
86
+ * Move content to trash
87
+ */
88
+ async trashContent(id: number): Promise<ApiResponse<Content>> {
89
+ return this.create<{ action: string }, Content>(`/content/${id}/publish`, { action: 'trash' });
90
+ }
91
+
92
+ /**
93
+ * Restore content from trash
94
+ */
95
+ async restoreContent(id: number): Promise<ApiResponse<Content>> {
96
+ return this.create<{ action: string }, Content>(`/content/${id}/publish`, { action: 'restore' });
97
+ }
98
+
99
+ /**
100
+ * Get content revisions
101
+ */
102
+ async getContentRevisions(id: number): Promise<ApiResponse<Content[]>> {
103
+ return this.getSingle<Content[]>(`/content/${id}/revisions`);
104
+ }
105
+
106
+ /**
107
+ * Duplicate content
108
+ */
109
+ async duplicateContent(id: number): Promise<ApiResponse<Content>> {
110
+ return this.create<Record<string, never>, Content>(`/content/${id}/duplicate`, {});
111
+ }
112
+ }
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Organizations Management client for PerspectAPI SDK
3
+ */
4
+
5
+ import { BaseClient } from './base-client';
6
+ import type {
7
+ Organization,
8
+ CreateOrganizationRequest,
9
+ PaginatedResponse,
10
+ ApiResponse,
11
+ } from '../types';
12
+
13
+ export class OrganizationsClient extends BaseClient {
14
+ constructor(http: any) {
15
+ super(http, '/api/v1');
16
+ }
17
+
18
+ /**
19
+ * Get all organizations
20
+ */
21
+ async getOrganizations(params?: { page?: number; limit?: number }): Promise<PaginatedResponse<Organization>> {
22
+ return this.getPaginated<Organization>('/organizations', params);
23
+ }
24
+
25
+ /**
26
+ * Get organization by ID
27
+ */
28
+ async getOrganizationById(id: number): Promise<ApiResponse<Organization>> {
29
+ return this.getSingle<Organization>(`/organizations/${id}`);
30
+ }
31
+
32
+ /**
33
+ * Create new organization
34
+ */
35
+ async createOrganization(data: CreateOrganizationRequest): Promise<ApiResponse<Organization>> {
36
+ return this.create<CreateOrganizationRequest, Organization>('/organizations', data);
37
+ }
38
+
39
+ /**
40
+ * Update organization
41
+ */
42
+ async updateOrganization(id: number, data: Partial<CreateOrganizationRequest>): Promise<ApiResponse<Organization>> {
43
+ return this.update<Partial<CreateOrganizationRequest>, Organization>(`/organizations/${id}`, data);
44
+ }
45
+
46
+ /**
47
+ * Delete organization
48
+ */
49
+ async deleteOrganization(id: number): Promise<ApiResponse<{ message: string }>> {
50
+ return this.delete<{ message: string }>(`/organizations/${id}`);
51
+ }
52
+
53
+ /**
54
+ * Get organization members
55
+ */
56
+ async getOrganizationMembers(id: number): Promise<ApiResponse<Array<{
57
+ id: string;
58
+ email: string;
59
+ firstName?: string;
60
+ lastName?: string;
61
+ role: string;
62
+ joinedAt: string;
63
+ }>>> {
64
+ return this.getSingle(`/organizations/${id}/members`);
65
+ }
66
+
67
+ /**
68
+ * Add member to organization
69
+ */
70
+ async addOrganizationMember(id: number, data: {
71
+ email: string;
72
+ role: string;
73
+ }): Promise<ApiResponse<{ message: string }>> {
74
+ return this.create(`/organizations/${id}/members`, data);
75
+ }
76
+
77
+ /**
78
+ * Update organization member role
79
+ */
80
+ async updateOrganizationMember(id: number, userId: string, data: {
81
+ role: string;
82
+ }): Promise<ApiResponse<{ message: string }>> {
83
+ return this.update(`/organizations/${id}/members/${userId}`, data);
84
+ }
85
+
86
+ /**
87
+ * Remove member from organization
88
+ */
89
+ async removeOrganizationMember(id: number, userId: string): Promise<ApiResponse<{ message: string }>> {
90
+ return this.delete<{ message: string }>(`/organizations/${id}/members/${userId}`);
91
+ }
92
+
93
+ /**
94
+ * Get organization settings
95
+ */
96
+ async getOrganizationSettings(id: number): Promise<ApiResponse<Record<string, any>>> {
97
+ return this.getSingle<Record<string, any>>(`/organizations/${id}/settings`);
98
+ }
99
+
100
+ /**
101
+ * Update organization settings
102
+ */
103
+ async updateOrganizationSettings(id: number, settings: Record<string, any>): Promise<ApiResponse<Record<string, any>>> {
104
+ return this.update<Record<string, any>>(`/organizations/${id}/settings`, settings);
105
+ }
106
+ }