perspectapi-ts-sdk 6.5.9 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -1011
- package/dist/chunk-K3T2AFYA.mjs +1393 -0
- package/dist/index-CWvUyMt3.d.mts +2224 -0
- package/dist/index-CWvUyMt3.d.ts +2224 -0
- package/dist/index.d.mts +130 -2221
- package/dist/index.d.ts +130 -2221
- package/dist/index.js +8 -2
- package/dist/index.mjs +13 -1364
- package/dist/v2/index.d.mts +1 -0
- package/dist/v2/index.d.ts +1 -0
- package/dist/v2/index.js +1419 -0
- package/dist/v2/index.mjs +40 -0
- package/docs/README.md +15 -0
- package/docs/v1-deprecated/README.md +9 -0
- package/docs/v1-deprecated/examples/README.md +324 -0
- package/docs/v1-deprecated/examples/basic-usage.ts +258 -0
- package/docs/v1-deprecated/examples/cloudflare-worker.ts +274 -0
- package/docs/v1-deprecated/examples/content-query-with-slug-prefix.ts +237 -0
- package/docs/v1-deprecated/examples/image-transforms.ts +200 -0
- package/docs/v1-deprecated/examples/site-user-checkout.ts +186 -0
- package/docs/v1-deprecated/examples/slug-prefix-examples.ts +491 -0
- package/docs/v1-deprecated/legacy-docs/caching.md +667 -0
- package/docs/v1-deprecated/legacy-docs/contact.md +1396 -0
- package/docs/v1-deprecated/legacy-docs/csrf-protection.md +664 -0
- package/docs/v1-deprecated/legacy-docs/image-transforms.md +523 -0
- package/docs/v1-deprecated/legacy-docs/loaders.md +304 -0
- package/docs/v1-deprecated/legacy-docs/newsletter.md +811 -0
- package/docs/v1-deprecated/legacy-docs/site-users.md +817 -0
- package/docs/v1-deprecated/legacy-notes/CHANGELOG-CHECKOUT.md +143 -0
- package/docs/v1-deprecated/legacy-notes/CSRF-CHECKOUT.md +271 -0
- package/docs/v1-deprecated/legacy-notes/IMAGE_TRANSFORMS_PORT.md +298 -0
- package/docs/v1-deprecated/sdk-readme.md +1076 -0
- package/examples/README.md +19 -0
- package/examples/basic-v2.ts +37 -0
- package/llms.txt +25 -0
- package/package.json +18 -7
- package/src/client/api-keys-client.ts +4 -0
- package/src/client/auth-client.ts +4 -0
- package/src/client/base-client.ts +7 -0
- package/src/client/bundles-client.ts +4 -0
- package/src/client/categories-client.ts +4 -0
- package/src/client/checkout-client.ts +4 -0
- package/src/client/contact-client.ts +4 -0
- package/src/client/content-client.ts +4 -0
- package/src/client/newsletter-client.ts +4 -0
- package/src/client/newsletter-management-client.ts +4 -0
- package/src/client/organizations-client.ts +4 -0
- package/src/client/products-client.ts +4 -0
- package/src/client/site-users-client.ts +10 -1
- package/src/client/sites-client.ts +4 -0
- package/src/client/webhooks-client.ts +4 -0
- package/src/deprecation.ts +2 -1
- package/src/index.ts +2 -1
- package/src/loaders.ts +59 -0
- package/src/perspect-api-client.ts +2 -2
- package/src/v2/client/orders-client.ts +6 -1
- package/src/v2/types.ts +3 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Site User Profile & Checkout Prefill
|
|
3
|
+
*
|
|
4
|
+
* @deprecated Historical v1 example. Do not copy into new code. v1 sunsets on
|
|
5
|
+
* 2026-06-01; use createPerspectApiV2Client from perspectapi-ts-sdk/v2.
|
|
6
|
+
*
|
|
7
|
+
* Shows how to:
|
|
8
|
+
* 1. Authenticate a site user via OTP
|
|
9
|
+
* 2. Read and write profile data (shipping address, phone, etc.)
|
|
10
|
+
* 3. Create a checkout session that prefills Stripe with the user's details
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { createPerspectApiClient } from '../../../src';
|
|
14
|
+
|
|
15
|
+
const SITE_NAME = 'my-store';
|
|
16
|
+
|
|
17
|
+
// The SDK needs both an API key (for checkout) and a site user JWT (for profile).
|
|
18
|
+
// In a real app the API key lives server-side and the JWT is set after OTP login.
|
|
19
|
+
const client = createPerspectApiClient({
|
|
20
|
+
baseUrl: 'https://api.perspect.commm',
|
|
21
|
+
apiKey: 'pk_live_xxx',
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// 1. OTP Authentication
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
async function loginSiteUser(email: string, otpCode: string) {
|
|
29
|
+
// Request the OTP (sends an email with a 6-digit code)
|
|
30
|
+
await client.siteUsers.requestOtp(SITE_NAME, { email });
|
|
31
|
+
|
|
32
|
+
// Verify the code and set the JWT on the client in one call
|
|
33
|
+
const { data } = await client.siteUsers.verifyOtpAndSetAuth(SITE_NAME, {
|
|
34
|
+
email,
|
|
35
|
+
code: otpCode,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// data.user.id is the site_user_id you'll pass to checkout later
|
|
39
|
+
console.log('Logged in as', data.user.email, '— id:', data.user.id);
|
|
40
|
+
return data.user;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
// 2. Profile: Read & Write
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
|
|
47
|
+
async function showProfile() {
|
|
48
|
+
const { data } = await client.siteUsers.getMe(SITE_NAME);
|
|
49
|
+
|
|
50
|
+
console.log('Name:', data.user.first_name, data.user.last_name);
|
|
51
|
+
console.log('Email:', data.user.email);
|
|
52
|
+
console.log('Profile keys:', Object.keys(data.profile));
|
|
53
|
+
|
|
54
|
+
return data;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function saveShippingAddress(address: {
|
|
58
|
+
line1: string;
|
|
59
|
+
line2?: string;
|
|
60
|
+
city: string;
|
|
61
|
+
state: string;
|
|
62
|
+
postal_code: string;
|
|
63
|
+
country: string;
|
|
64
|
+
}) {
|
|
65
|
+
// Profile values are stored as strings. Serialize objects as JSON.
|
|
66
|
+
await client.siteUsers.setProfileValue(
|
|
67
|
+
SITE_NAME,
|
|
68
|
+
'shipping_address',
|
|
69
|
+
JSON.stringify(address),
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function getShippingAddress() {
|
|
74
|
+
const { data } = await client.siteUsers.getProfile(SITE_NAME);
|
|
75
|
+
const raw = data.profile['shipping_address'];
|
|
76
|
+
if (!raw) return null;
|
|
77
|
+
return JSON.parse(raw.value);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function savePhone(phone: string) {
|
|
81
|
+
await client.siteUsers.setProfileValue(SITE_NAME, 'phone', phone);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
// 3. Checkout with Prefill
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Create a checkout session for the logged-in site user.
|
|
90
|
+
*
|
|
91
|
+
* Passing `site_user_id` tells the backend to create (or reuse) a gateway
|
|
92
|
+
* customer object. Stripe then prefills the checkout page with the
|
|
93
|
+
* customer's email, name, saved addresses, and payment methods.
|
|
94
|
+
*
|
|
95
|
+
* On the first checkout a new Stripe Customer is created automatically.
|
|
96
|
+
* Subsequent checkouts reuse the same Customer, so returning buyers see
|
|
97
|
+
* their saved details without re-entering anything.
|
|
98
|
+
*/
|
|
99
|
+
async function createCheckout(siteUserId: string) {
|
|
100
|
+
const { data: session } = await client.checkout.createCheckoutSession(
|
|
101
|
+
SITE_NAME,
|
|
102
|
+
{
|
|
103
|
+
line_items: [
|
|
104
|
+
{ price: 'prod_abc123', quantity: 1 },
|
|
105
|
+
],
|
|
106
|
+
success_url: 'https://my-store.com/thank-you',
|
|
107
|
+
cancel_url: 'https://my-store.com/cart',
|
|
108
|
+
site_user_id: siteUserId,
|
|
109
|
+
},
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
console.log('Checkout URL:', session.url);
|
|
113
|
+
return session;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Checkout with a variant product (SKU).
|
|
118
|
+
*/
|
|
119
|
+
async function createVariantCheckout(siteUserId: string) {
|
|
120
|
+
const { data: session } = await client.checkout.createCheckoutSession(
|
|
121
|
+
SITE_NAME,
|
|
122
|
+
{
|
|
123
|
+
line_items: [
|
|
124
|
+
{ sku_id: 42, quantity: 2 },
|
|
125
|
+
],
|
|
126
|
+
success_url: 'https://my-store.com/thank-you',
|
|
127
|
+
cancel_url: 'https://my-store.com/cart',
|
|
128
|
+
site_user_id: siteUserId,
|
|
129
|
+
},
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
console.log('Variant checkout URL:', session.url);
|
|
133
|
+
return session;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ---------------------------------------------------------------------------
|
|
137
|
+
// 4. Order History
|
|
138
|
+
// ---------------------------------------------------------------------------
|
|
139
|
+
|
|
140
|
+
async function showOrders() {
|
|
141
|
+
const { data } = await client.siteUsers.getOrders(SITE_NAME);
|
|
142
|
+
|
|
143
|
+
for (const order of data.orders) {
|
|
144
|
+
console.log(
|
|
145
|
+
`${order.order_id ?? order.session_id} ${order.currency.toUpperCase()} ${(order.amount_total / 100).toFixed(2)} ${order.status}`,
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// ---------------------------------------------------------------------------
|
|
151
|
+
// Full flow
|
|
152
|
+
// ---------------------------------------------------------------------------
|
|
153
|
+
|
|
154
|
+
async function main() {
|
|
155
|
+
// Step 1 — Log in (in a real app the code comes from the user's email)
|
|
156
|
+
const user = await loginSiteUser('jane@example.com', '123456');
|
|
157
|
+
|
|
158
|
+
// Step 2 — Save profile data (only needed once; persists across sessions)
|
|
159
|
+
await client.siteUsers.updateMe(SITE_NAME, {
|
|
160
|
+
first_name: 'Jane',
|
|
161
|
+
last_name: 'Doe',
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
await saveShippingAddress({
|
|
165
|
+
line1: '123 Main St',
|
|
166
|
+
city: 'Portland',
|
|
167
|
+
state: 'OR',
|
|
168
|
+
postal_code: '97201',
|
|
169
|
+
country: 'US',
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
await savePhone('+15035551234');
|
|
173
|
+
|
|
174
|
+
// Step 3 — Read it back
|
|
175
|
+
await showProfile();
|
|
176
|
+
const address = await getShippingAddress();
|
|
177
|
+
console.log('Saved shipping address:', address);
|
|
178
|
+
|
|
179
|
+
// Step 4 — Create a checkout session (Stripe prefills from the gateway customer)
|
|
180
|
+
await createCheckout(user.id);
|
|
181
|
+
|
|
182
|
+
// Step 5 — After purchase, view order history
|
|
183
|
+
await showOrders();
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
main();
|
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Examples demonstrating slug_prefix filtering with perspectapi-ts-sdk
|
|
3
|
+
*
|
|
4
|
+
* @deprecated Historical v1 example. Do not copy into new code. v1 sunsets on
|
|
5
|
+
* 2026-06-01; use createPerspectApiV2Client from perspectapi-ts-sdk/v2.
|
|
6
|
+
*
|
|
7
|
+
* These examples show how to use the slug_prefix parameter to filter
|
|
8
|
+
* content and products by their URL prefix.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { createPerspectApiClient } from '../../../src';
|
|
12
|
+
|
|
13
|
+
// Initialize the client
|
|
14
|
+
const client = createPerspectApiClient({
|
|
15
|
+
baseUrl: 'https://api.example.com',
|
|
16
|
+
apiKey: 'your-api-key-here'
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const siteName = 'mysite';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* CONTENT FILTERING EXAMPLES
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
// Example 1: Get all blog posts
|
|
26
|
+
async function getBlogPosts() {
|
|
27
|
+
const response = await client.content.getContent(siteName, {
|
|
28
|
+
slug_prefix: 'blog',
|
|
29
|
+
page_status: 'publish',
|
|
30
|
+
page_type: 'post',
|
|
31
|
+
page: 1,
|
|
32
|
+
limit: 20
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
console.log('Blog Posts:', response.data);
|
|
36
|
+
console.log('Total:', response.pagination?.total);
|
|
37
|
+
// URLs will be: /blog/post-1, /blog/post-2, etc.
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Example 2: Get all news articles
|
|
41
|
+
async function getNewsArticles() {
|
|
42
|
+
const response = await client.content.getContent(siteName, {
|
|
43
|
+
slug_prefix: 'news',
|
|
44
|
+
page_status: 'publish',
|
|
45
|
+
page: 1,
|
|
46
|
+
limit: 10
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
console.log('News Articles:', response.data);
|
|
50
|
+
// URLs will be: /news/article-1, /news/article-2, etc.
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Example 3: Get documentation pages
|
|
54
|
+
async function getDocumentation() {
|
|
55
|
+
const response = await client.content.getContent(siteName, {
|
|
56
|
+
slug_prefix: 'docs',
|
|
57
|
+
page_type: 'page',
|
|
58
|
+
page_status: 'publish'
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
console.log('Documentation Pages:', response.data);
|
|
62
|
+
// URLs will be: /docs/getting-started, /docs/api-reference, etc.
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Example 4: Search within blog posts only
|
|
66
|
+
async function searchBlogPosts(searchTerm: string) {
|
|
67
|
+
const response = await client.content.getContent(siteName, {
|
|
68
|
+
slug_prefix: 'blog',
|
|
69
|
+
search: searchTerm,
|
|
70
|
+
page_status: 'publish'
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
console.log(`Blog posts matching "${searchTerm}":`, response.data);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Example 5: Get draft blog posts (for admin)
|
|
77
|
+
async function getDraftBlogPosts() {
|
|
78
|
+
const response = await client.content.getContent(siteName, {
|
|
79
|
+
slug_prefix: 'blog',
|
|
80
|
+
page_status: 'draft',
|
|
81
|
+
page_type: 'post'
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
console.log('Draft Blog Posts:', response.data);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Example 6: Get blog posts by specific author
|
|
88
|
+
async function getBlogPostsByAuthor(userId: number) {
|
|
89
|
+
const response = await client.content.getContent(siteName, {
|
|
90
|
+
slug_prefix: 'blog',
|
|
91
|
+
user_id: userId,
|
|
92
|
+
page_status: 'publish'
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
console.log(`Blog posts by user ${userId}:`, response.data);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* PRODUCT FILTERING EXAMPLES
|
|
100
|
+
*/
|
|
101
|
+
|
|
102
|
+
// Example 7: Get all shop products
|
|
103
|
+
async function getShopProducts() {
|
|
104
|
+
const response = await client.products.getProducts(siteName, {
|
|
105
|
+
slug_prefix: 'shop',
|
|
106
|
+
isActive: true,
|
|
107
|
+
page: 1,
|
|
108
|
+
limit: 20
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
console.log('Shop Products:', response.data);
|
|
112
|
+
// URLs will be: /shop/product-1, /shop/product-2, etc.
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Example 8: Get artwork products
|
|
116
|
+
async function getArtworkProducts() {
|
|
117
|
+
const response = await client.products.getProducts(siteName, {
|
|
118
|
+
slug_prefix: 'artwork',
|
|
119
|
+
isActive: true
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
console.log('Artwork Products:', response.data);
|
|
123
|
+
// URLs will be: /artwork/painting-1, /artwork/sculpture-1, etc.
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Example 9: Search within shop products only
|
|
127
|
+
async function searchShopProducts(searchTerm: string) {
|
|
128
|
+
const response = await client.products.getProducts(siteName, {
|
|
129
|
+
slug_prefix: 'shop',
|
|
130
|
+
search: searchTerm,
|
|
131
|
+
isActive: true
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
console.log(`Shop products matching "${searchTerm}":`, response.data);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Example 10: Get shop products in specific category
|
|
138
|
+
async function getShopProductsByCategory(categorySlug: string) {
|
|
139
|
+
const response = await client.products.getProducts(siteName, {
|
|
140
|
+
slug_prefix: 'shop',
|
|
141
|
+
category: categorySlug,
|
|
142
|
+
isActive: true
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
console.log(`Shop products in ${categorySlug}:`, response.data);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Example 11: Get shop electronics
|
|
149
|
+
async function getShopElectronics() {
|
|
150
|
+
const response = await client.products.getProducts(siteName, {
|
|
151
|
+
slug_prefix: 'shop',
|
|
152
|
+
category: 'electronics',
|
|
153
|
+
isActive: true,
|
|
154
|
+
page: 1,
|
|
155
|
+
limit: 50
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
console.log('Shop Electronics:', response.data);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Example 12: Get premium products (different prefix)
|
|
162
|
+
async function getPremiumProducts() {
|
|
163
|
+
const response = await client.products.getProducts(siteName, {
|
|
164
|
+
slug_prefix: 'premium',
|
|
165
|
+
isActive: true
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
console.log('Premium Products:', response.data);
|
|
169
|
+
// URLs will be: /premium/product-1, /premium/product-2, etc.
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* ADVANCED FILTERING EXAMPLES
|
|
174
|
+
*/
|
|
175
|
+
|
|
176
|
+
// Example 13: Multi-section content aggregation
|
|
177
|
+
async function getRecentContentFromAllSections() {
|
|
178
|
+
// Get recent posts from different sections
|
|
179
|
+
const [blogPosts, newsArticles, guides] = await Promise.all([
|
|
180
|
+
client.content.getContent(siteName, {
|
|
181
|
+
slug_prefix: 'blog',
|
|
182
|
+
page_status: 'publish',
|
|
183
|
+
limit: 5
|
|
184
|
+
}),
|
|
185
|
+
client.content.getContent(siteName, {
|
|
186
|
+
slug_prefix: 'news',
|
|
187
|
+
page_status: 'publish',
|
|
188
|
+
limit: 5
|
|
189
|
+
}),
|
|
190
|
+
client.content.getContent(siteName, {
|
|
191
|
+
slug_prefix: 'guides',
|
|
192
|
+
page_status: 'publish',
|
|
193
|
+
limit: 5
|
|
194
|
+
})
|
|
195
|
+
]);
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
blog: blogPosts.data || [],
|
|
199
|
+
news: newsArticles.data || [],
|
|
200
|
+
guides: guides.data || []
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Example 14: Product catalog organization
|
|
205
|
+
async function getProductCatalog() {
|
|
206
|
+
// Get products from different sections
|
|
207
|
+
const [shopProducts, artworks, services] = await Promise.all([
|
|
208
|
+
client.products.getProducts(siteName, {
|
|
209
|
+
slug_prefix: 'shop',
|
|
210
|
+
isActive: true,
|
|
211
|
+
limit: 20
|
|
212
|
+
}),
|
|
213
|
+
client.products.getProducts(siteName, {
|
|
214
|
+
slug_prefix: 'artwork',
|
|
215
|
+
isActive: true,
|
|
216
|
+
limit: 20
|
|
217
|
+
}),
|
|
218
|
+
client.products.getProducts(siteName, {
|
|
219
|
+
slug_prefix: 'services',
|
|
220
|
+
isActive: true,
|
|
221
|
+
limit: 20
|
|
222
|
+
})
|
|
223
|
+
]);
|
|
224
|
+
|
|
225
|
+
return {
|
|
226
|
+
shop: shopProducts.data || [],
|
|
227
|
+
artwork: artworks.data || [],
|
|
228
|
+
services: services.data || []
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Example 15: Paginated blog listing
|
|
233
|
+
async function getPaginatedBlogPosts(page: number = 1, limit: number = 20) {
|
|
234
|
+
const response = await client.content.getContent(siteName, {
|
|
235
|
+
slug_prefix: 'blog',
|
|
236
|
+
page_status: 'publish',
|
|
237
|
+
page_type: 'post',
|
|
238
|
+
page,
|
|
239
|
+
limit
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
return {
|
|
243
|
+
posts: response.data,
|
|
244
|
+
currentPage: page,
|
|
245
|
+
totalPages: response.pagination?.totalPages || 0,
|
|
246
|
+
totalPosts: response.pagination?.total || 0,
|
|
247
|
+
hasNextPage: page < (response.pagination?.totalPages || 0),
|
|
248
|
+
hasPrevPage: page > 1
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Example 16: Search across specific product categories with prefix
|
|
253
|
+
async function searchProductsInSection(
|
|
254
|
+
prefix: string,
|
|
255
|
+
searchTerm: string,
|
|
256
|
+
category?: string
|
|
257
|
+
) {
|
|
258
|
+
const response = await client.products.getProducts(siteName, {
|
|
259
|
+
slug_prefix: prefix,
|
|
260
|
+
search: searchTerm,
|
|
261
|
+
category: category,
|
|
262
|
+
isActive: true
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
return response.data;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* REAL-WORLD USE CASES
|
|
270
|
+
*/
|
|
271
|
+
|
|
272
|
+
// Example 17: Blog homepage - recent posts
|
|
273
|
+
async function getBlogHomepage() {
|
|
274
|
+
const recentPosts = await client.content.getContent(siteName, {
|
|
275
|
+
slug_prefix: 'blog',
|
|
276
|
+
page_status: 'publish',
|
|
277
|
+
page_type: 'post',
|
|
278
|
+
limit: 10
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
return {
|
|
282
|
+
title: 'Blog',
|
|
283
|
+
posts: recentPosts.data || [],
|
|
284
|
+
total: recentPosts.pagination?.total
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Example 18: Shop homepage - featured products
|
|
289
|
+
async function getShopHomepage() {
|
|
290
|
+
const products = await client.products.getProducts(siteName, {
|
|
291
|
+
slug_prefix: 'shop',
|
|
292
|
+
isActive: true,
|
|
293
|
+
limit: 12
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
return {
|
|
297
|
+
title: 'Shop',
|
|
298
|
+
products: products.data || []
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Example 19: Documentation site structure
|
|
303
|
+
async function getDocumentationStructure() {
|
|
304
|
+
const [gettingStarted, apiDocs, guides, tutorials] = await Promise.all([
|
|
305
|
+
client.content.getContent(siteName, {
|
|
306
|
+
slug_prefix: 'docs',
|
|
307
|
+
search: 'getting started',
|
|
308
|
+
page_status: 'publish'
|
|
309
|
+
}),
|
|
310
|
+
client.content.getContent(siteName, {
|
|
311
|
+
slug_prefix: 'api',
|
|
312
|
+
page_status: 'publish'
|
|
313
|
+
}),
|
|
314
|
+
client.content.getContent(siteName, {
|
|
315
|
+
slug_prefix: 'guides',
|
|
316
|
+
page_status: 'publish'
|
|
317
|
+
}),
|
|
318
|
+
client.content.getContent(siteName, {
|
|
319
|
+
slug_prefix: 'tutorials',
|
|
320
|
+
page_status: 'publish'
|
|
321
|
+
})
|
|
322
|
+
]);
|
|
323
|
+
|
|
324
|
+
return {
|
|
325
|
+
gettingStarted: gettingStarted.data || [],
|
|
326
|
+
api: apiDocs.data || [],
|
|
327
|
+
guides: guides.data || [],
|
|
328
|
+
tutorials: tutorials.data || []
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Example 20: E-commerce with multiple product types
|
|
333
|
+
async function getEcommerceProducts() {
|
|
334
|
+
const [physical, digital, services, subscriptions] = await Promise.all([
|
|
335
|
+
client.products.getProducts(siteName, {
|
|
336
|
+
slug_prefix: 'shop',
|
|
337
|
+
isActive: true
|
|
338
|
+
}),
|
|
339
|
+
client.products.getProducts(siteName, {
|
|
340
|
+
slug_prefix: 'downloads',
|
|
341
|
+
isActive: true
|
|
342
|
+
}),
|
|
343
|
+
client.products.getProducts(siteName, {
|
|
344
|
+
slug_prefix: 'services',
|
|
345
|
+
isActive: true
|
|
346
|
+
}),
|
|
347
|
+
client.products.getProducts(siteName, {
|
|
348
|
+
slug_prefix: 'subscriptions',
|
|
349
|
+
isActive: true
|
|
350
|
+
})
|
|
351
|
+
]);
|
|
352
|
+
|
|
353
|
+
return {
|
|
354
|
+
physical: physical.data || [],
|
|
355
|
+
digital: digital.data || [],
|
|
356
|
+
services: services.data || [],
|
|
357
|
+
subscriptions: subscriptions.data || []
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* UTILITY FUNCTIONS
|
|
363
|
+
*/
|
|
364
|
+
|
|
365
|
+
// Example 21: Get all available slug prefixes (for debugging)
|
|
366
|
+
async function getAvailableContentPrefixes() {
|
|
367
|
+
const allContent = await client.content.getContent(siteName, {
|
|
368
|
+
limit: 100
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
const prefixes = new Set(
|
|
372
|
+
(allContent.data || [])
|
|
373
|
+
.map(item => item.slug_prefix)
|
|
374
|
+
.filter(prefix => prefix !== null && prefix !== undefined)
|
|
375
|
+
);
|
|
376
|
+
|
|
377
|
+
return Array.from(prefixes);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Example 22: Get all available product prefixes (for debugging)
|
|
381
|
+
async function getAvailableProductPrefixes() {
|
|
382
|
+
const allProducts = await client.products.getProducts(siteName, {
|
|
383
|
+
limit: 100
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
const prefixes = new Set(
|
|
387
|
+
(allProducts.data || [])
|
|
388
|
+
.map(item => item.slug_prefix)
|
|
389
|
+
.filter(prefix => prefix !== null && prefix !== undefined)
|
|
390
|
+
);
|
|
391
|
+
|
|
392
|
+
return Array.from(prefixes);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* ERROR HANDLING EXAMPLES
|
|
397
|
+
*/
|
|
398
|
+
|
|
399
|
+
// Example 23: Handle no results gracefully
|
|
400
|
+
async function safeGetBlogPosts() {
|
|
401
|
+
try {
|
|
402
|
+
const response = await client.content.getContent(siteName, {
|
|
403
|
+
slug_prefix: 'blog',
|
|
404
|
+
page_status: 'publish'
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
if (!response.data || response.data.length === 0) {
|
|
408
|
+
console.log('No blog posts found');
|
|
409
|
+
return [];
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
return response.data;
|
|
413
|
+
} catch (error) {
|
|
414
|
+
console.error('Error fetching blog posts:', error);
|
|
415
|
+
return [];
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Example 24: Validate prefix exists before querying
|
|
420
|
+
async function getContentByPrefix(prefix: string) {
|
|
421
|
+
const availablePrefixes = await getAvailableContentPrefixes();
|
|
422
|
+
|
|
423
|
+
if (!availablePrefixes.includes(prefix)) {
|
|
424
|
+
console.warn(`Prefix "${prefix}" not found. Available: ${availablePrefixes.join(', ')}`);
|
|
425
|
+
return [];
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
const response = await client.content.getContent(siteName, {
|
|
429
|
+
slug_prefix: prefix,
|
|
430
|
+
page_status: 'publish'
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
return response.data;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* EXPORT ALL EXAMPLES
|
|
438
|
+
*/
|
|
439
|
+
export {
|
|
440
|
+
// Content examples
|
|
441
|
+
getBlogPosts,
|
|
442
|
+
getNewsArticles,
|
|
443
|
+
getDocumentation,
|
|
444
|
+
searchBlogPosts,
|
|
445
|
+
getDraftBlogPosts,
|
|
446
|
+
getBlogPostsByAuthor,
|
|
447
|
+
|
|
448
|
+
// Product examples
|
|
449
|
+
getShopProducts,
|
|
450
|
+
getArtworkProducts,
|
|
451
|
+
searchShopProducts,
|
|
452
|
+
getShopProductsByCategory,
|
|
453
|
+
getShopElectronics,
|
|
454
|
+
getPremiumProducts,
|
|
455
|
+
|
|
456
|
+
// Advanced examples
|
|
457
|
+
getRecentContentFromAllSections,
|
|
458
|
+
getProductCatalog,
|
|
459
|
+
getPaginatedBlogPosts,
|
|
460
|
+
searchProductsInSection,
|
|
461
|
+
|
|
462
|
+
// Real-world use cases
|
|
463
|
+
getBlogHomepage,
|
|
464
|
+
getShopHomepage,
|
|
465
|
+
getDocumentationStructure,
|
|
466
|
+
getEcommerceProducts,
|
|
467
|
+
|
|
468
|
+
// Utility functions
|
|
469
|
+
getAvailableContentPrefixes,
|
|
470
|
+
getAvailableProductPrefixes,
|
|
471
|
+
|
|
472
|
+
// Error handling
|
|
473
|
+
safeGetBlogPosts,
|
|
474
|
+
getContentByPrefix
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* USAGE NOTES:
|
|
479
|
+
*
|
|
480
|
+
* 1. The slug_prefix parameter is optional - omit it to get all content/products
|
|
481
|
+
* 2. slug_prefix performs exact match (case-sensitive)
|
|
482
|
+
* 3. Combine slug_prefix with other filters for precise queries
|
|
483
|
+
* 4. Use consistent naming: 'blog', 'news', 'shop', 'artwork' (lowercase)
|
|
484
|
+
* 5. Plan your URL structure before setting prefixes
|
|
485
|
+
*
|
|
486
|
+
* URL STRUCTURE EXAMPLES:
|
|
487
|
+
* - slug_prefix: 'blog', slug: 'my-post' → /blog/my-post
|
|
488
|
+
* - slug_prefix: 'shop', slug: 'laptop' → /shop/laptop
|
|
489
|
+
* - slug_prefix: 'artwork', slug: 'painting' → /artwork/painting
|
|
490
|
+
* - slug_prefix: null, slug: 'about' → /about
|
|
491
|
+
*/
|