@proveanything/smartlinks 1.2.4 → 1.3.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 +85 -1
- package/{API_SUMMARY.md → dist/API_SUMMARY.md} +225 -1
- package/dist/README.md +569 -0
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.js +2 -0
- package/dist/api/realtime.d.ts +103 -0
- package/dist/api/realtime.js +113 -0
- package/dist/docs/API_SUMMARY.md +3230 -0
- package/dist/docs/i18n.md +287 -0
- package/dist/docs/liquid-templates.md +484 -0
- package/dist/docs/realtime.md +764 -0
- package/dist/docs/theme-defaults.md +100 -0
- package/dist/docs/theme.system.md +338 -0
- package/dist/docs/widgets.md +510 -0
- package/dist/http.js +132 -19
- package/dist/i18n.md +287 -0
- package/dist/liquid-templates.md +484 -0
- package/dist/realtime.md +764 -0
- package/dist/theme-defaults.md +100 -0
- package/dist/theme.system.md +338 -0
- package/dist/types/error.d.ts +69 -3
- package/dist/types/error.js +98 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/realtime.d.ts +44 -0
- package/dist/types/realtime.js +2 -0
- package/dist/widgets.md +510 -0
- package/docs/API_SUMMARY.md +3230 -0
- package/docs/i18n.md +287 -0
- package/docs/liquid-templates.md +484 -0
- package/docs/realtime.md +764 -0
- package/docs/theme-defaults.md +100 -0
- package/docs/theme.system.md +338 -0
- package/docs/widgets.md +510 -0
- package/package.json +4 -4
- package/dist/api/actions.d.ts +0 -32
- package/dist/api/actions.js +0 -99
- package/dist/build-docs.js +0 -61
- package/dist/types/actions.d.ts +0 -123
- package/dist/types/actions.js +0 -2
package/README.md
CHANGED
|
@@ -80,6 +80,79 @@ const jwt = await auth.requestAdminJWT('collectionId')
|
|
|
80
80
|
const publicJwt = await auth.requestPublicJWT('collectionId', 'productId', 'proofId')
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
+
## Error Handling
|
|
84
|
+
|
|
85
|
+
The SDK throws `SmartlinksApiError` for all API errors, providing structured access to:
|
|
86
|
+
- **HTTP status code** (`statusCode` / `code`) - Numeric HTTP status (400, 401, 500, etc.)
|
|
87
|
+
- **Server error code** (`errorCode`) - String identifier ("NOT_AUTHORIZED", "broadcasts.topic.invalid", etc.)
|
|
88
|
+
- **Error message** (`message`) - Human-readable description
|
|
89
|
+
- **Additional details** (`details`) - Server-specific fields
|
|
90
|
+
|
|
91
|
+
### Automatic Error Normalization
|
|
92
|
+
|
|
93
|
+
The SDK automatically normalizes various server error response formats into a consistent structure:
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
// Server may return errors in different formats:
|
|
97
|
+
// { errorText: "...", errorCode: "..." }
|
|
98
|
+
// { error: "...", message: "..." }
|
|
99
|
+
// { ok: false, error: "..." }
|
|
100
|
+
// { error: "..." }
|
|
101
|
+
|
|
102
|
+
// All are normalized to SmartlinksApiError with consistent access:
|
|
103
|
+
import { SmartlinksApiError, product } from '@proveanything/smartlinks'
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
const item = await product.get('collectionId', 'productId', false)
|
|
107
|
+
} catch (error) {
|
|
108
|
+
if (error instanceof SmartlinksApiError) {
|
|
109
|
+
console.error({
|
|
110
|
+
message: error.message, // "Error 404: Product not found"
|
|
111
|
+
statusCode: error.statusCode, // 404 (HTTP status)
|
|
112
|
+
code: error.code, // 404 (same as statusCode)
|
|
113
|
+
errorCode: error.errorCode, // "NOT_FOUND" (server error code string)
|
|
114
|
+
details: error.details, // Additional server details
|
|
115
|
+
url: error.url, // Failed URL
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
// Handle specific server error codes (primary identifier)
|
|
119
|
+
switch (error.errorCode) {
|
|
120
|
+
case 'NOT_AUTHORIZED':
|
|
121
|
+
// Invalid credentials
|
|
122
|
+
break
|
|
123
|
+
case 'broadcasts.topic.invalid':
|
|
124
|
+
// Invalid broadcast topic
|
|
125
|
+
break
|
|
126
|
+
default:
|
|
127
|
+
// Fall back to HTTP status code
|
|
128
|
+
if (error.isNotFound()) {
|
|
129
|
+
// Handle 404
|
|
130
|
+
} else if (error.isAuthError()) {
|
|
131
|
+
// Handle 401/403 - redirect to login
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Use helper methods for HTTP status-based handling
|
|
136
|
+
if (error.isRateLimited()) {
|
|
137
|
+
// Handle 429 - implement retry logic
|
|
138
|
+
} else if (error.isServerError()) {
|
|
139
|
+
// Handle 5xx - show maintenance message
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Helper Methods
|
|
146
|
+
|
|
147
|
+
- `error.isClientError()` - 4xx status codes
|
|
148
|
+
- `error.isServerError()` - 5xx status codes
|
|
149
|
+
- `error.isAuthError()` - 401 or 403
|
|
150
|
+
- `error.isNotFound()` - 404
|
|
151
|
+
- `error.isRateLimited()` - 429
|
|
152
|
+
- `error.toJSON()` - Serializable object for logging
|
|
153
|
+
|
|
154
|
+
For comprehensive error handling examples and migration guidance, see [examples/error-handling-demo.ts](examples/error-handling-demo.ts).
|
|
155
|
+
|
|
83
156
|
## Common tasks
|
|
84
157
|
|
|
85
158
|
### Products
|
|
@@ -481,7 +554,18 @@ setExtraHeaders({ 'X-Debug': '1' })
|
|
|
481
554
|
|
|
482
555
|
Explore every function, parameter, and type here:
|
|
483
556
|
|
|
484
|
-
-
|
|
557
|
+
- [API_SUMMARY.md](docs/API_SUMMARY.md) - Complete API reference
|
|
558
|
+
|
|
559
|
+
## Additional Documentation
|
|
560
|
+
|
|
561
|
+
The SDK includes comprehensive guides for advanced features:
|
|
562
|
+
|
|
563
|
+
- **[Liquid Templates](docs/liquid-templates.md)** - Dynamic templating for emails and notifications with personalized data
|
|
564
|
+
- **[Real-Time Messaging](docs/realtime.md)** - Ably integration for live chat, presence, and real-time updates
|
|
565
|
+
- **[Theme System](docs/theme.system.md)** - Dynamic theming for iframe apps with CSS variables and postMessage
|
|
566
|
+
- **[Theme Defaults](docs/theme-defaults.md)** - Default theme configuration reference
|
|
567
|
+
- **[Widgets](docs/widgets.md)** - React widget system for embeddable components
|
|
568
|
+
- **[Internationalization](docs/i18n.md)** - Multi-language support and localization
|
|
485
569
|
|
|
486
570
|
## Requirements
|
|
487
571
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Smartlinks API Summary
|
|
2
2
|
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.1 | Generated: 2026-01-30T18:16:59.130Z
|
|
4
4
|
|
|
5
5
|
This is a concise summary of all available API functions and types.
|
|
6
6
|
|
|
@@ -51,6 +51,7 @@ The Smartlinks SDK is organized into the following namespaces:
|
|
|
51
51
|
- **jobs** - Functions for jobs operations
|
|
52
52
|
- **journeysAnalytics** - Functions for journeysAnalytics operations
|
|
53
53
|
- **location** - Functions for location operations
|
|
54
|
+
- **realtime** - Functions for realtime operations
|
|
54
55
|
- **template** - Functions for template operations
|
|
55
56
|
|
|
56
57
|
## HTTP Utilities
|
|
@@ -118,6 +119,194 @@ Returns the common headers used for API requests, including apiKey and bearerTok
|
|
|
118
119
|
**sendCustomProxyMessage**(request: string, params: any) → `Promise<T>`
|
|
119
120
|
Sends a custom proxy message to the parent Smartlinks application when running in an iframe. This function is used to communicate with the parent window when the SDK is embedded in an iframe and proxyMode is enabled. It sends a message to the parent and waits for a response.
|
|
120
121
|
|
|
122
|
+
## Error Handling
|
|
123
|
+
|
|
124
|
+
All API functions throw `SmartlinksApiError` when requests fail. This error class provides structured access to HTTP status codes, server error codes, and additional context.
|
|
125
|
+
|
|
126
|
+
### SmartlinksApiError
|
|
127
|
+
|
|
128
|
+
**Properties:**
|
|
129
|
+
- **message** `string` - Human-readable error message in English (e.g., "Error 400: Not Authorized")
|
|
130
|
+
- **statusCode** `number` - HTTP status code (400, 401, 404, 500, etc.)
|
|
131
|
+
- **code** `number` - Numeric error code (same as statusCode)
|
|
132
|
+
- **details** `Record<string, any> | undefined` - Additional server response data, including string error codes
|
|
133
|
+
- **url** `string | undefined` - The URL that was requested
|
|
134
|
+
|
|
135
|
+
**Helper Methods:**
|
|
136
|
+
- **isAuthError()** `boolean` - Returns true for 401 or 403 status codes
|
|
137
|
+
- **isNotFound()** `boolean` - Returns true for 404 status code
|
|
138
|
+
- **isRateLimited()** `boolean` - Returns true for 429 status code
|
|
139
|
+
- **isClientError()** `boolean` - Returns true for 4xx status codes
|
|
140
|
+
- **isServerError()** `boolean` - Returns true for 5xx status codes
|
|
141
|
+
- **toJSON()** `object` - Returns a serializable object for logging
|
|
142
|
+
|
|
143
|
+
### Error Format Normalization
|
|
144
|
+
|
|
145
|
+
The SDK automatically normalizes various server error response formats into a consistent structure. The server may return errors in different formats, but they are all accessible through the same properties.
|
|
146
|
+
|
|
147
|
+
**Server String Error Codes:**
|
|
148
|
+
Server-specific error identifiers are preserved in `error.details`:
|
|
149
|
+
- Access via: `error.details?.errorCode` or `error.details?.error`
|
|
150
|
+
- Format examples: `"NOT_AUTHORIZED"`, `"broadcasts.topic.invalid"`, `"sendgrid.provision.failed"`
|
|
151
|
+
- Use these for programmatic error handling (switch statements, conditional logic)
|
|
152
|
+
|
|
153
|
+
### Usage Examples
|
|
154
|
+
|
|
155
|
+
**Basic Error Handling:**
|
|
156
|
+
```typescript
|
|
157
|
+
import { SmartlinksApiError, product } from '@proveanything/smartlinks'
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
const item = await product.get('collectionId', 'productId')
|
|
161
|
+
} catch (error) {
|
|
162
|
+
if (error instanceof SmartlinksApiError) {
|
|
163
|
+
console.error('Status:', error.statusCode) // 404
|
|
164
|
+
console.error('Message:', error.message) // "Error 404: Not found"
|
|
165
|
+
console.error('URL:', error.url) // "/public/collection/..."
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
**Using Helper Methods:**
|
|
171
|
+
```typescript
|
|
172
|
+
try {
|
|
173
|
+
await product.create('collectionId', data)
|
|
174
|
+
} catch (error) {
|
|
175
|
+
if (error instanceof SmartlinksApiError) {
|
|
176
|
+
if (error.isAuthError()) {
|
|
177
|
+
// Handle 401/403 - redirect to login
|
|
178
|
+
redirectToLogin()
|
|
179
|
+
} else if (error.isNotFound()) {
|
|
180
|
+
// Handle 404
|
|
181
|
+
showNotFound()
|
|
182
|
+
} else if (error.isRateLimited()) {
|
|
183
|
+
// Handle 429 - implement retry with backoff
|
|
184
|
+
await retryAfterDelay()
|
|
185
|
+
} else if (error.isServerError()) {
|
|
186
|
+
// Handle 5xx
|
|
187
|
+
showMaintenanceMessage()
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
**Accessing Server Error Codes:**
|
|
194
|
+
```typescript
|
|
195
|
+
try {
|
|
196
|
+
await broadcasts.send('collectionId', 'broadcastId', options)
|
|
197
|
+
} catch (error) {
|
|
198
|
+
if (error instanceof SmartlinksApiError) {
|
|
199
|
+
// Extract server-defined string error code
|
|
200
|
+
const serverCode = error.details?.errorCode || error.details?.error
|
|
201
|
+
|
|
202
|
+
switch (serverCode) {
|
|
203
|
+
case 'NOT_AUTHORIZED':
|
|
204
|
+
redirectToLogin()
|
|
205
|
+
break
|
|
206
|
+
case 'broadcasts.topic.invalid':
|
|
207
|
+
showTopicSelector()
|
|
208
|
+
break
|
|
209
|
+
case 'sendgrid.provision.failed':
|
|
210
|
+
alertAdmin('Email service error')
|
|
211
|
+
break
|
|
212
|
+
default:
|
|
213
|
+
showError(error.message)
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**Error Logging for Monitoring:**
|
|
220
|
+
```typescript
|
|
221
|
+
try {
|
|
222
|
+
await api.someMethod()
|
|
223
|
+
} catch (error) {
|
|
224
|
+
if (error instanceof SmartlinksApiError) {
|
|
225
|
+
// Log structured error data
|
|
226
|
+
logger.error('API Error', error.toJSON())
|
|
227
|
+
|
|
228
|
+
// Send to monitoring service
|
|
229
|
+
Sentry.captureException(error, {
|
|
230
|
+
extra: error.toJSON(),
|
|
231
|
+
tags: {
|
|
232
|
+
statusCode: error.statusCode,
|
|
233
|
+
serverErrorCode: error.details?.errorCode || error.details?.error,
|
|
234
|
+
}
|
|
235
|
+
})
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
**Handling Validation Errors:**
|
|
241
|
+
```typescript
|
|
242
|
+
try {
|
|
243
|
+
await product.create('collectionId', formData)
|
|
244
|
+
} catch (error) {
|
|
245
|
+
if (error instanceof SmartlinksApiError && error.statusCode === 400) {
|
|
246
|
+
// Access field-specific validation errors if available
|
|
247
|
+
if (error.details?.fields) {
|
|
248
|
+
Object.entries(error.details.fields).forEach(([field, message]) => {
|
|
249
|
+
showFieldError(field, String(message))
|
|
250
|
+
})
|
|
251
|
+
} else {
|
|
252
|
+
showError(error.message)
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
**Retry Logic for Transient Errors:**
|
|
259
|
+
```typescript
|
|
260
|
+
async function withRetry<T>(fn: () => Promise<T>, maxRetries = 3): Promise<T> {
|
|
261
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
262
|
+
try {
|
|
263
|
+
return await fn()
|
|
264
|
+
} catch (error) {
|
|
265
|
+
if (error instanceof SmartlinksApiError) {
|
|
266
|
+
// Only retry server errors and rate limiting
|
|
267
|
+
const shouldRetry = error.isServerError() || error.isRateLimited()
|
|
268
|
+
|
|
269
|
+
if (!shouldRetry || attempt === maxRetries - 1) {
|
|
270
|
+
throw error
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Exponential backoff
|
|
274
|
+
const delay = 1000 * Math.pow(2, attempt)
|
|
275
|
+
await new Promise(resolve => setTimeout(resolve, delay))
|
|
276
|
+
} else {
|
|
277
|
+
throw error
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
throw new Error('Max retries exceeded')
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Usage
|
|
285
|
+
const collections = await withRetry(() => collection.list())
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Error Code Reference
|
|
289
|
+
|
|
290
|
+
**HTTP Status Codes (numeric):**
|
|
291
|
+
- `400` - Bad Request (invalid input)
|
|
292
|
+
- `401` - Unauthorized (authentication required)
|
|
293
|
+
- `403` - Forbidden (insufficient permissions)
|
|
294
|
+
- `404` - Not Found (resource doesn't exist)
|
|
295
|
+
- `429` - Too Many Requests (rate limited)
|
|
296
|
+
- `500` - Internal Server Error
|
|
297
|
+
- `502` - Bad Gateway
|
|
298
|
+
- `503` - Service Unavailable
|
|
299
|
+
|
|
300
|
+
**Server Error Codes (strings in `details.errorCode` or `details.error`):**
|
|
301
|
+
Examples include:
|
|
302
|
+
- `"NOT_AUTHORIZED"` - Not authorized for this action
|
|
303
|
+
- `"broadcasts.topic.invalid"` - Invalid communication topic
|
|
304
|
+
- `"broadcasts.manual.segment.missing"` - Missing required segment
|
|
305
|
+
- `"sendgrid.provision.failed"` - Email service provisioning failed
|
|
306
|
+
- `"validation.failed"` - Request validation failed
|
|
307
|
+
|
|
308
|
+
*Note: Server error codes use either `UPPERCASE_UNDERSCORE` or `dotted.notation` format. Both are supported.*
|
|
309
|
+
|
|
121
310
|
## Types
|
|
122
311
|
|
|
123
312
|
### appConfiguration
|
|
@@ -1250,7 +1439,9 @@ interface ContactSchema {
|
|
|
1250
1439
|
```typescript
|
|
1251
1440
|
interface ErrorResponse {
|
|
1252
1441
|
code: number
|
|
1442
|
+
errorCode?: string
|
|
1253
1443
|
message: string
|
|
1444
|
+
details?: Record<string, any>
|
|
1254
1445
|
}
|
|
1255
1446
|
```
|
|
1256
1447
|
|
|
@@ -1830,6 +2021,31 @@ interface QrShortCodeLookupResponse {
|
|
|
1830
2021
|
}
|
|
1831
2022
|
```
|
|
1832
2023
|
|
|
2024
|
+
### realtime
|
|
2025
|
+
|
|
2026
|
+
**RealtimeTokenRequest** (interface)
|
|
2027
|
+
```typescript
|
|
2028
|
+
interface RealtimeTokenRequest {
|
|
2029
|
+
collectionId: string
|
|
2030
|
+
appId?: string
|
|
2031
|
+
}
|
|
2032
|
+
```
|
|
2033
|
+
|
|
2034
|
+
**AblyTokenRequest** (interface)
|
|
2035
|
+
```typescript
|
|
2036
|
+
interface AblyTokenRequest {
|
|
2037
|
+
keyName: string
|
|
2038
|
+
ttl: number
|
|
2039
|
+
timestamp: number
|
|
2040
|
+
capability: string
|
|
2041
|
+
nonce: string
|
|
2042
|
+
mac: string
|
|
2043
|
+
clientId: string
|
|
2044
|
+
}
|
|
2045
|
+
```
|
|
2046
|
+
|
|
2047
|
+
**RealtimeChannelPattern** = `string`
|
|
2048
|
+
|
|
1833
2049
|
### segments
|
|
1834
2050
|
|
|
1835
2051
|
**SegmentRecord** (interface)
|
|
@@ -2901,6 +3117,14 @@ Get proofs for a batch (admin only). GET /admin/collection/:collectionId/product
|
|
|
2901
3117
|
**lookupShortCode**(shortId: string, code: string) → `Promise<QrShortCodeLookupResponse>`
|
|
2902
3118
|
Resolve a short code to related resource identifiers.
|
|
2903
3119
|
|
|
3120
|
+
### realtime
|
|
3121
|
+
|
|
3122
|
+
**getPublicToken**(params: RealtimeTokenRequest) → `Promise<AblyTokenRequest>`
|
|
3123
|
+
Get an Ably token for public (user-scoped) real-time communication. This endpoint returns an Ably TokenRequest that can be used to initialize an Ably client with appropriate permissions for the specified collection and optional app. Requires user authentication (Bearer token). ```ts const tokenRequest = await realtime.getPublicToken({ collectionId: 'my-collection-id', appId: 'my-app-id' }) // Use with Ably const ably = new Ably.Realtime.Promise({ authCallback: async (data, callback) => { callback(null, tokenRequest) } }) // Subscribe to channels matching the pattern const channel = ably.channels.get('collection:my-collection-id:app:my-app-id:chat:general') await channel.subscribe('message', (msg) => console.log(msg.data)) ```
|
|
3124
|
+
|
|
3125
|
+
**getAdminToken**() → `Promise<AblyTokenRequest>`
|
|
3126
|
+
Get an Ably token for admin real-time communication. This endpoint returns an Ably TokenRequest that can be used to initialize an Ably client with admin permissions to receive system notifications and alerts. Admin users get subscribe-only (read-only) access to the interaction:{userId} channel pattern. Requires admin authentication (Bearer token). ```ts const tokenRequest = await realtime.getAdminToken() // Use with Ably const ably = new Ably.Realtime.Promise({ authCallback: async (data, callback) => { callback(null, tokenRequest) } }) // Subscribe to admin interaction channel const userId = 'my-user-id' const channel = ably.channels.get(`interaction:${userId}`) await channel.subscribe((message) => { console.log('Admin notification:', message.data) }) ```
|
|
3127
|
+
|
|
2904
3128
|
### segments
|
|
2905
3129
|
|
|
2906
3130
|
**create**(collectionId: string,
|