@scalemule/nextjs 0.0.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.
- package/LICENSE +21 -0
- package/README.md +297 -0
- package/dist/client.d.mts +163 -0
- package/dist/client.d.ts +163 -0
- package/dist/client.js +700 -0
- package/dist/client.mjs +697 -0
- package/dist/index-BkacIKdu.d.mts +807 -0
- package/dist/index-BkacIKdu.d.ts +807 -0
- package/dist/index.d.mts +418 -0
- package/dist/index.d.ts +418 -0
- package/dist/index.js +3103 -0
- package/dist/index.mjs +3084 -0
- package/dist/server/auth.d.mts +38 -0
- package/dist/server/auth.d.ts +38 -0
- package/dist/server/auth.js +1088 -0
- package/dist/server/auth.mjs +1083 -0
- package/dist/server/index.d.mts +868 -0
- package/dist/server/index.d.ts +868 -0
- package/dist/server/index.js +2028 -0
- package/dist/server/index.mjs +1972 -0
- package/dist/server/webhook-handler.d.mts +2 -0
- package/dist/server/webhook-handler.d.ts +2 -0
- package/dist/server/webhook-handler.js +56 -0
- package/dist/server/webhook-handler.mjs +54 -0
- package/dist/testing.d.mts +109 -0
- package/dist/testing.d.ts +109 -0
- package/dist/testing.js +134 -0
- package/dist/testing.mjs +128 -0
- package/dist/webhook-handler-BPNqhuwL.d.ts +728 -0
- package/dist/webhook-handler-C-5_Ey1T.d.mts +728 -0
- package/package.json +99 -0
|
@@ -0,0 +1,728 @@
|
|
|
1
|
+
import { R as RegisterRequest, j as ApiResponse, U as User, k as LoginRequest, L as LoginResponse, c as ListFilesParams, Q as ListFilesResponse, K as StorageFile, _ as ClientContext, T as UploadResponse } from './index-BkacIKdu.mjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Server-Side ScaleMule Client
|
|
5
|
+
*
|
|
6
|
+
* Stateless client for use in Next.js API routes.
|
|
7
|
+
* Does not manage sessions - that's handled by cookies.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
type ScaleMuleEnvironment = 'dev' | 'prod';
|
|
11
|
+
interface ServerConfig {
|
|
12
|
+
/** Your ScaleMule API key (use env var, never hardcode) */
|
|
13
|
+
apiKey: string;
|
|
14
|
+
/** Environment: 'dev' or 'prod' - automatically sets gateway URL */
|
|
15
|
+
environment?: ScaleMuleEnvironment;
|
|
16
|
+
/** Custom gateway URL (overrides environment preset) */
|
|
17
|
+
gatewayUrl?: string;
|
|
18
|
+
/** Enable debug logging */
|
|
19
|
+
debug?: boolean;
|
|
20
|
+
}
|
|
21
|
+
declare class ScaleMuleServer {
|
|
22
|
+
private apiKey;
|
|
23
|
+
private gatewayUrl;
|
|
24
|
+
private debug;
|
|
25
|
+
constructor(config: ServerConfig);
|
|
26
|
+
/**
|
|
27
|
+
* Make a request to the ScaleMule API
|
|
28
|
+
*
|
|
29
|
+
* @param method - HTTP method
|
|
30
|
+
* @param path - API path (e.g., /v1/auth/login)
|
|
31
|
+
* @param options - Request options
|
|
32
|
+
* @param options.body - Request body (will be JSON stringified)
|
|
33
|
+
* @param options.userId - User ID (passed through for storage operations)
|
|
34
|
+
* @param options.sessionToken - Session token sent as Authorization: Bearer header
|
|
35
|
+
* @param options.clientContext - End user context to forward (IP, user agent, etc.)
|
|
36
|
+
*/
|
|
37
|
+
private request;
|
|
38
|
+
auth: {
|
|
39
|
+
/**
|
|
40
|
+
* Register a new user
|
|
41
|
+
*/
|
|
42
|
+
register: (data: RegisterRequest) => Promise<ApiResponse<User>>;
|
|
43
|
+
/**
|
|
44
|
+
* Login user - returns session token (store in HTTP-only cookie)
|
|
45
|
+
*/
|
|
46
|
+
login: (data: LoginRequest) => Promise<ApiResponse<LoginResponse>>;
|
|
47
|
+
/**
|
|
48
|
+
* Logout user
|
|
49
|
+
*/
|
|
50
|
+
logout: (sessionToken: string) => Promise<ApiResponse<void>>;
|
|
51
|
+
/**
|
|
52
|
+
* Get current user from session token
|
|
53
|
+
*/
|
|
54
|
+
me: (sessionToken: string) => Promise<ApiResponse<User>>;
|
|
55
|
+
/**
|
|
56
|
+
* Refresh session token
|
|
57
|
+
*/
|
|
58
|
+
refresh: (sessionToken: string) => Promise<ApiResponse<{
|
|
59
|
+
session_token: string;
|
|
60
|
+
expires_at: string;
|
|
61
|
+
}>>;
|
|
62
|
+
/**
|
|
63
|
+
* Request password reset email
|
|
64
|
+
*/
|
|
65
|
+
forgotPassword: (email: string) => Promise<ApiResponse<{
|
|
66
|
+
message: string;
|
|
67
|
+
}>>;
|
|
68
|
+
/**
|
|
69
|
+
* Reset password with token
|
|
70
|
+
*/
|
|
71
|
+
resetPassword: (token: string, newPassword: string) => Promise<ApiResponse<{
|
|
72
|
+
message: string;
|
|
73
|
+
}>>;
|
|
74
|
+
/**
|
|
75
|
+
* Verify email with token
|
|
76
|
+
*/
|
|
77
|
+
verifyEmail: (token: string) => Promise<ApiResponse<{
|
|
78
|
+
message: string;
|
|
79
|
+
}>>;
|
|
80
|
+
/**
|
|
81
|
+
* Resend verification email.
|
|
82
|
+
* Can be called with a session token (authenticated) or email (unauthenticated).
|
|
83
|
+
*/
|
|
84
|
+
resendVerification: (sessionTokenOrEmail: string, options?: {
|
|
85
|
+
email?: string;
|
|
86
|
+
}) => Promise<ApiResponse<{
|
|
87
|
+
message: string;
|
|
88
|
+
}>>;
|
|
89
|
+
};
|
|
90
|
+
user: {
|
|
91
|
+
/**
|
|
92
|
+
* Update user profile
|
|
93
|
+
*/
|
|
94
|
+
update: (sessionToken: string, data: {
|
|
95
|
+
full_name?: string;
|
|
96
|
+
avatar_url?: string;
|
|
97
|
+
}) => Promise<ApiResponse<User>>;
|
|
98
|
+
/**
|
|
99
|
+
* Change password
|
|
100
|
+
*/
|
|
101
|
+
changePassword: (sessionToken: string, currentPassword: string, newPassword: string) => Promise<ApiResponse<{
|
|
102
|
+
message: string;
|
|
103
|
+
}>>;
|
|
104
|
+
/**
|
|
105
|
+
* Change email
|
|
106
|
+
*/
|
|
107
|
+
changeEmail: (sessionToken: string, newEmail: string, password: string) => Promise<ApiResponse<{
|
|
108
|
+
message: string;
|
|
109
|
+
}>>;
|
|
110
|
+
/**
|
|
111
|
+
* Delete account
|
|
112
|
+
*/
|
|
113
|
+
deleteAccount: (sessionToken: string, password: string) => Promise<ApiResponse<{
|
|
114
|
+
message: string;
|
|
115
|
+
}>>;
|
|
116
|
+
};
|
|
117
|
+
secrets: {
|
|
118
|
+
/**
|
|
119
|
+
* Get a secret from the tenant vault
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* ```typescript
|
|
123
|
+
* const result = await scalemule.secrets.get('ANONYMOUS_USER_SALT')
|
|
124
|
+
* if (result.success) {
|
|
125
|
+
* console.log('Salt:', result.data.value)
|
|
126
|
+
* }
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
get: (key: string) => Promise<ApiResponse<{
|
|
130
|
+
value: string;
|
|
131
|
+
version: number;
|
|
132
|
+
}>>;
|
|
133
|
+
/**
|
|
134
|
+
* Set a secret in the tenant vault
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```typescript
|
|
138
|
+
* await scalemule.secrets.set('ANONYMOUS_USER_SALT', 'my-secret-salt')
|
|
139
|
+
* ```
|
|
140
|
+
*/
|
|
141
|
+
set: (key: string, value: string) => Promise<ApiResponse<{
|
|
142
|
+
value: string;
|
|
143
|
+
version: number;
|
|
144
|
+
}>>;
|
|
145
|
+
/**
|
|
146
|
+
* Delete a secret from the tenant vault
|
|
147
|
+
*/
|
|
148
|
+
delete: (key: string) => Promise<ApiResponse<void>>;
|
|
149
|
+
/**
|
|
150
|
+
* List all secrets in the tenant vault
|
|
151
|
+
*/
|
|
152
|
+
list: () => Promise<ApiResponse<{
|
|
153
|
+
secrets: Array<{
|
|
154
|
+
path: string;
|
|
155
|
+
version: number;
|
|
156
|
+
}>;
|
|
157
|
+
}>>;
|
|
158
|
+
/**
|
|
159
|
+
* Get secret version history
|
|
160
|
+
*/
|
|
161
|
+
versions: (key: string) => Promise<ApiResponse<{
|
|
162
|
+
versions: Array<{
|
|
163
|
+
version: number;
|
|
164
|
+
created_at: string;
|
|
165
|
+
}>;
|
|
166
|
+
}>>;
|
|
167
|
+
/**
|
|
168
|
+
* Rollback to a specific version
|
|
169
|
+
*/
|
|
170
|
+
rollback: (key: string, version: number) => Promise<ApiResponse<{
|
|
171
|
+
value: string;
|
|
172
|
+
version: number;
|
|
173
|
+
}>>;
|
|
174
|
+
/**
|
|
175
|
+
* Rotate a secret (copy current version as new version)
|
|
176
|
+
*/
|
|
177
|
+
rotate: (key: string, newValue: string) => Promise<ApiResponse<{
|
|
178
|
+
value: string;
|
|
179
|
+
version: number;
|
|
180
|
+
}>>;
|
|
181
|
+
};
|
|
182
|
+
bundles: {
|
|
183
|
+
/**
|
|
184
|
+
* Get a bundle (structured secret like database credentials)
|
|
185
|
+
*
|
|
186
|
+
* @param key - Bundle key (e.g., 'database/prod')
|
|
187
|
+
* @param resolve - Whether to resolve inheritance (default: true)
|
|
188
|
+
*
|
|
189
|
+
* @example
|
|
190
|
+
* ```typescript
|
|
191
|
+
* const result = await scalemule.bundles.get('database/prod')
|
|
192
|
+
* if (result.success) {
|
|
193
|
+
* console.log('DB Host:', result.data.data.host)
|
|
194
|
+
* }
|
|
195
|
+
* ```
|
|
196
|
+
*/
|
|
197
|
+
get: <T = Record<string, unknown>>(key: string, resolve?: boolean) => Promise<ApiResponse<{
|
|
198
|
+
type: string;
|
|
199
|
+
data: T;
|
|
200
|
+
version: number;
|
|
201
|
+
inherits_from?: string;
|
|
202
|
+
}>>;
|
|
203
|
+
/**
|
|
204
|
+
* Set a bundle (structured secret)
|
|
205
|
+
*
|
|
206
|
+
* @param key - Bundle key
|
|
207
|
+
* @param type - Bundle type: 'mysql', 'postgres', 'redis', 's3', 'oauth', 'smtp', 'generic'
|
|
208
|
+
* @param data - Bundle data (structure depends on type)
|
|
209
|
+
* @param inheritsFrom - Optional parent bundle key for inheritance
|
|
210
|
+
*
|
|
211
|
+
* @example
|
|
212
|
+
* ```typescript
|
|
213
|
+
* // Create a MySQL bundle
|
|
214
|
+
* await scalemule.bundles.set('database/prod', 'mysql', {
|
|
215
|
+
* host: 'db.example.com',
|
|
216
|
+
* port: 3306,
|
|
217
|
+
* username: 'app',
|
|
218
|
+
* password: 'secret',
|
|
219
|
+
* database: 'myapp'
|
|
220
|
+
* })
|
|
221
|
+
*
|
|
222
|
+
* // Create a bundle that inherits from another
|
|
223
|
+
* await scalemule.bundles.set('database/staging', 'mysql', {
|
|
224
|
+
* host: 'staging-db.example.com', // Override just the host
|
|
225
|
+
* }, 'database/prod')
|
|
226
|
+
* ```
|
|
227
|
+
*/
|
|
228
|
+
set: <T = Record<string, unknown>>(key: string, type: string, data: T, inheritsFrom?: string) => Promise<ApiResponse<{
|
|
229
|
+
type: string;
|
|
230
|
+
data: T;
|
|
231
|
+
version: number;
|
|
232
|
+
}>>;
|
|
233
|
+
/**
|
|
234
|
+
* Delete a bundle
|
|
235
|
+
*/
|
|
236
|
+
delete: (key: string) => Promise<ApiResponse<void>>;
|
|
237
|
+
/**
|
|
238
|
+
* List all bundles
|
|
239
|
+
*/
|
|
240
|
+
list: () => Promise<ApiResponse<{
|
|
241
|
+
bundles: Array<{
|
|
242
|
+
path: string;
|
|
243
|
+
type: string;
|
|
244
|
+
version: number;
|
|
245
|
+
inherits_from?: string;
|
|
246
|
+
}>;
|
|
247
|
+
}>>;
|
|
248
|
+
/**
|
|
249
|
+
* Get connection URL for a database bundle
|
|
250
|
+
*
|
|
251
|
+
* @example
|
|
252
|
+
* ```typescript
|
|
253
|
+
* const result = await scalemule.bundles.connectionUrl('database/prod')
|
|
254
|
+
* if (result.success) {
|
|
255
|
+
* const client = mysql.createConnection(result.data.url)
|
|
256
|
+
* }
|
|
257
|
+
* ```
|
|
258
|
+
*/
|
|
259
|
+
connectionUrl: (key: string) => Promise<ApiResponse<{
|
|
260
|
+
url: string;
|
|
261
|
+
}>>;
|
|
262
|
+
};
|
|
263
|
+
vaultAudit: {
|
|
264
|
+
/**
|
|
265
|
+
* Query audit logs for your tenant's vault operations
|
|
266
|
+
*
|
|
267
|
+
* @example
|
|
268
|
+
* ```typescript
|
|
269
|
+
* const result = await scalemule.vaultAudit.query({
|
|
270
|
+
* action: 'read',
|
|
271
|
+
* path: 'database/*',
|
|
272
|
+
* since: '2026-01-01'
|
|
273
|
+
* })
|
|
274
|
+
* ```
|
|
275
|
+
*/
|
|
276
|
+
query: (options?: {
|
|
277
|
+
action?: "read" | "write" | "delete" | "list";
|
|
278
|
+
path?: string;
|
|
279
|
+
since?: string;
|
|
280
|
+
until?: string;
|
|
281
|
+
limit?: number;
|
|
282
|
+
}) => Promise<ApiResponse<{
|
|
283
|
+
logs: Array<{
|
|
284
|
+
timestamp: string;
|
|
285
|
+
action: string;
|
|
286
|
+
resource_path: string;
|
|
287
|
+
success: boolean;
|
|
288
|
+
error_message?: string;
|
|
289
|
+
}>;
|
|
290
|
+
}>>;
|
|
291
|
+
};
|
|
292
|
+
storage: {
|
|
293
|
+
/**
|
|
294
|
+
* List user's files
|
|
295
|
+
*/
|
|
296
|
+
list: (userId: string, params?: ListFilesParams) => Promise<ApiResponse<ListFilesResponse>>;
|
|
297
|
+
/**
|
|
298
|
+
* Get file info
|
|
299
|
+
*/
|
|
300
|
+
get: (fileId: string) => Promise<ApiResponse<StorageFile>>;
|
|
301
|
+
/**
|
|
302
|
+
* Delete file
|
|
303
|
+
*/
|
|
304
|
+
delete: (userId: string, fileId: string) => Promise<ApiResponse<void>>;
|
|
305
|
+
/**
|
|
306
|
+
* Upload file (from server - use FormData)
|
|
307
|
+
*
|
|
308
|
+
* @param userId - The user ID who owns this file
|
|
309
|
+
* @param file - File data to upload
|
|
310
|
+
* @param options - Upload options
|
|
311
|
+
* @param options.clientContext - End user context to forward (IP, user agent, etc.)
|
|
312
|
+
*
|
|
313
|
+
* @example
|
|
314
|
+
* ```typescript
|
|
315
|
+
* // Forward end user context for proper attribution
|
|
316
|
+
* const result = await scalemule.storage.upload(
|
|
317
|
+
* userId,
|
|
318
|
+
* { buffer, filename, contentType },
|
|
319
|
+
* { clientContext: extractClientContext(request) }
|
|
320
|
+
* )
|
|
321
|
+
* ```
|
|
322
|
+
*/
|
|
323
|
+
upload: (userId: string, file: {
|
|
324
|
+
buffer: BlobPart;
|
|
325
|
+
filename: string;
|
|
326
|
+
contentType: string;
|
|
327
|
+
}, options?: {
|
|
328
|
+
clientContext?: ClientContext;
|
|
329
|
+
}) => Promise<ApiResponse<UploadResponse>>;
|
|
330
|
+
};
|
|
331
|
+
webhooks: {
|
|
332
|
+
/**
|
|
333
|
+
* Create a new webhook subscription
|
|
334
|
+
*
|
|
335
|
+
* @example
|
|
336
|
+
* ```typescript
|
|
337
|
+
* const result = await scalemule.webhooks.create({
|
|
338
|
+
* webhook_name: 'Video Status Webhook',
|
|
339
|
+
* url: 'https://myapp.com/api/webhooks/scalemule',
|
|
340
|
+
* events: ['video.ready', 'video.failed']
|
|
341
|
+
* })
|
|
342
|
+
*
|
|
343
|
+
* // Store the secret for signature verification
|
|
344
|
+
* console.log('Webhook secret:', result.data.secret)
|
|
345
|
+
* ```
|
|
346
|
+
*/
|
|
347
|
+
create: (data: {
|
|
348
|
+
webhook_name: string;
|
|
349
|
+
url: string;
|
|
350
|
+
events: string[];
|
|
351
|
+
}) => Promise<ApiResponse<{
|
|
352
|
+
id: string;
|
|
353
|
+
secret: string;
|
|
354
|
+
url: string;
|
|
355
|
+
events: string[];
|
|
356
|
+
}>>;
|
|
357
|
+
/**
|
|
358
|
+
* List all webhook subscriptions
|
|
359
|
+
*/
|
|
360
|
+
list: () => Promise<ApiResponse<{
|
|
361
|
+
webhooks: Array<{
|
|
362
|
+
id: string;
|
|
363
|
+
webhook_name: string;
|
|
364
|
+
url: string;
|
|
365
|
+
events: string[];
|
|
366
|
+
is_enabled: boolean;
|
|
367
|
+
}>;
|
|
368
|
+
}>>;
|
|
369
|
+
/**
|
|
370
|
+
* Delete a webhook subscription
|
|
371
|
+
*/
|
|
372
|
+
delete: (id: string) => Promise<ApiResponse<void>>;
|
|
373
|
+
/**
|
|
374
|
+
* Update a webhook subscription
|
|
375
|
+
*/
|
|
376
|
+
update: (id: string, data: {
|
|
377
|
+
url?: string;
|
|
378
|
+
events?: string[];
|
|
379
|
+
is_enabled?: boolean;
|
|
380
|
+
}) => Promise<ApiResponse<{
|
|
381
|
+
id: string;
|
|
382
|
+
url: string;
|
|
383
|
+
events: string[];
|
|
384
|
+
}>>;
|
|
385
|
+
/**
|
|
386
|
+
* Get available webhook event types
|
|
387
|
+
*/
|
|
388
|
+
eventTypes: () => Promise<ApiResponse<{
|
|
389
|
+
events: Array<{
|
|
390
|
+
event_name: string;
|
|
391
|
+
event_description: string;
|
|
392
|
+
payload_schema: Record<string, unknown>;
|
|
393
|
+
}>;
|
|
394
|
+
}>>;
|
|
395
|
+
};
|
|
396
|
+
analytics: {
|
|
397
|
+
/**
|
|
398
|
+
* Track an analytics event
|
|
399
|
+
*
|
|
400
|
+
* IMPORTANT: When calling from server-side code (API routes), always pass
|
|
401
|
+
* clientContext to ensure the real end user's IP is recorded, not the server's IP.
|
|
402
|
+
*
|
|
403
|
+
* @example
|
|
404
|
+
* ```typescript
|
|
405
|
+
* // In an API route
|
|
406
|
+
* import { extractClientContext, createServerClient } from '@scalemule/nextjs/server'
|
|
407
|
+
*
|
|
408
|
+
* export async function POST(request: NextRequest) {
|
|
409
|
+
* const clientContext = extractClientContext(request)
|
|
410
|
+
* const scalemule = createServerClient()
|
|
411
|
+
*
|
|
412
|
+
* await scalemule.analytics.trackEvent({
|
|
413
|
+
* event_name: 'button_clicked',
|
|
414
|
+
* properties: { button_id: 'signup' }
|
|
415
|
+
* }, { clientContext })
|
|
416
|
+
* }
|
|
417
|
+
* ```
|
|
418
|
+
*/
|
|
419
|
+
trackEvent: (event: {
|
|
420
|
+
event_name: string;
|
|
421
|
+
event_category?: string;
|
|
422
|
+
properties?: Record<string, unknown>;
|
|
423
|
+
user_id?: string;
|
|
424
|
+
session_id?: string;
|
|
425
|
+
anonymous_id?: string;
|
|
426
|
+
session_duration_seconds?: number;
|
|
427
|
+
page_url?: string;
|
|
428
|
+
page_title?: string;
|
|
429
|
+
referrer?: string;
|
|
430
|
+
landing_page?: string;
|
|
431
|
+
device_type?: string;
|
|
432
|
+
device_brand?: string;
|
|
433
|
+
device_model?: string;
|
|
434
|
+
browser?: string;
|
|
435
|
+
browser_version?: string;
|
|
436
|
+
os?: string;
|
|
437
|
+
os_version?: string;
|
|
438
|
+
screen_resolution?: string;
|
|
439
|
+
viewport_size?: string;
|
|
440
|
+
utm_source?: string;
|
|
441
|
+
utm_medium?: string;
|
|
442
|
+
utm_campaign?: string;
|
|
443
|
+
utm_term?: string;
|
|
444
|
+
utm_content?: string;
|
|
445
|
+
client_timestamp?: string;
|
|
446
|
+
timestamp?: string;
|
|
447
|
+
}, options?: {
|
|
448
|
+
clientContext?: ClientContext;
|
|
449
|
+
}) => Promise<ApiResponse<{
|
|
450
|
+
tracked: number;
|
|
451
|
+
session_id?: string;
|
|
452
|
+
}>>;
|
|
453
|
+
/**
|
|
454
|
+
* Track a page view
|
|
455
|
+
*
|
|
456
|
+
* @example
|
|
457
|
+
* ```typescript
|
|
458
|
+
* await scalemule.analytics.trackPageView({
|
|
459
|
+
* page_url: 'https://example.com/products',
|
|
460
|
+
* page_title: 'Products',
|
|
461
|
+
* referrer: 'https://google.com'
|
|
462
|
+
* }, { clientContext })
|
|
463
|
+
* ```
|
|
464
|
+
*/
|
|
465
|
+
trackPageView: (data: {
|
|
466
|
+
page_url: string;
|
|
467
|
+
page_title?: string;
|
|
468
|
+
referrer?: string;
|
|
469
|
+
session_id?: string;
|
|
470
|
+
user_id?: string;
|
|
471
|
+
}, options?: {
|
|
472
|
+
clientContext?: ClientContext;
|
|
473
|
+
}) => Promise<ApiResponse<{
|
|
474
|
+
tracked: number;
|
|
475
|
+
session_id?: string;
|
|
476
|
+
}>>;
|
|
477
|
+
/**
|
|
478
|
+
* Track multiple events in a batch (max 100)
|
|
479
|
+
*
|
|
480
|
+
* @example
|
|
481
|
+
* ```typescript
|
|
482
|
+
* await scalemule.analytics.trackBatch([
|
|
483
|
+
* { event_name: 'item_viewed', properties: { item_id: '123' } },
|
|
484
|
+
* { event_name: 'item_added_to_cart', properties: { item_id: '123' } }
|
|
485
|
+
* ], { clientContext })
|
|
486
|
+
* ```
|
|
487
|
+
*/
|
|
488
|
+
trackBatch: (events: Array<{
|
|
489
|
+
event_name: string;
|
|
490
|
+
event_category?: string;
|
|
491
|
+
properties?: Record<string, unknown>;
|
|
492
|
+
user_id?: string;
|
|
493
|
+
session_id?: string;
|
|
494
|
+
anonymous_id?: string;
|
|
495
|
+
session_duration_seconds?: number;
|
|
496
|
+
page_url?: string;
|
|
497
|
+
page_title?: string;
|
|
498
|
+
referrer?: string;
|
|
499
|
+
landing_page?: string;
|
|
500
|
+
device_type?: string;
|
|
501
|
+
browser?: string;
|
|
502
|
+
os?: string;
|
|
503
|
+
screen_resolution?: string;
|
|
504
|
+
viewport_size?: string;
|
|
505
|
+
utm_source?: string;
|
|
506
|
+
utm_medium?: string;
|
|
507
|
+
utm_campaign?: string;
|
|
508
|
+
utm_term?: string;
|
|
509
|
+
utm_content?: string;
|
|
510
|
+
client_timestamp?: string;
|
|
511
|
+
timestamp?: string;
|
|
512
|
+
}>, options?: {
|
|
513
|
+
clientContext?: ClientContext;
|
|
514
|
+
}) => Promise<ApiResponse<{
|
|
515
|
+
tracked: number;
|
|
516
|
+
}>>;
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* Create a server client with environment-based defaults
|
|
521
|
+
*/
|
|
522
|
+
declare function createServerClient(config?: Partial<ServerConfig>): ScaleMuleServer;
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* ScaleMule Webhook Helpers
|
|
526
|
+
*
|
|
527
|
+
* Provides utilities for webhook signature verification and route handlers
|
|
528
|
+
* for video processing events and other ScaleMule webhooks.
|
|
529
|
+
*
|
|
530
|
+
* @example
|
|
531
|
+
* ```typescript
|
|
532
|
+
* // app/api/webhooks/scalemule/route.ts
|
|
533
|
+
* import { createWebhookRoutes } from '@scalemule/nextjs/server'
|
|
534
|
+
*
|
|
535
|
+
* export const { POST } = createWebhookRoutes({
|
|
536
|
+
* secret: process.env.SCALEMULE_WEBHOOK_SECRET,
|
|
537
|
+
* onVideoReady: async (event) => {
|
|
538
|
+
* console.log('Video ready:', event.video_id)
|
|
539
|
+
* await updateDocument('videos', event.video_id, { status: 'ready' })
|
|
540
|
+
* },
|
|
541
|
+
* onVideoFailed: async (event) => {
|
|
542
|
+
* console.log('Video failed:', event.video_id, event.reason)
|
|
543
|
+
* }
|
|
544
|
+
* })
|
|
545
|
+
* ```
|
|
546
|
+
*/
|
|
547
|
+
|
|
548
|
+
interface WebhookEvent<T = Record<string, unknown>> {
|
|
549
|
+
event: string;
|
|
550
|
+
timestamp: number;
|
|
551
|
+
data: T;
|
|
552
|
+
}
|
|
553
|
+
interface VideoReadyEvent {
|
|
554
|
+
video_id: string;
|
|
555
|
+
application_id: string;
|
|
556
|
+
duration_seconds?: number;
|
|
557
|
+
width?: number;
|
|
558
|
+
height?: number;
|
|
559
|
+
thumbnail_url?: string;
|
|
560
|
+
playlist_url?: string;
|
|
561
|
+
}
|
|
562
|
+
interface VideoFailedEvent {
|
|
563
|
+
video_id: string;
|
|
564
|
+
application_id: string;
|
|
565
|
+
reason: string;
|
|
566
|
+
}
|
|
567
|
+
interface VideoUploadedEvent {
|
|
568
|
+
video_id: string;
|
|
569
|
+
application_id: string;
|
|
570
|
+
filename?: string;
|
|
571
|
+
size_bytes?: number;
|
|
572
|
+
}
|
|
573
|
+
interface VideoTranscodedEvent {
|
|
574
|
+
video_id: string;
|
|
575
|
+
application_id: string;
|
|
576
|
+
derivative_count: number;
|
|
577
|
+
}
|
|
578
|
+
interface WebhookRoutesConfig {
|
|
579
|
+
/** ScaleMule client configuration (optional, uses env vars by default) */
|
|
580
|
+
client?: Partial<ServerConfig>;
|
|
581
|
+
/** Webhook secret for signature verification (recommended for security) */
|
|
582
|
+
secret?: string;
|
|
583
|
+
/** Handler for video.ready events */
|
|
584
|
+
onVideoReady?: (event: VideoReadyEvent) => void | Promise<void>;
|
|
585
|
+
/** Handler for video.failed events */
|
|
586
|
+
onVideoFailed?: (event: VideoFailedEvent) => void | Promise<void>;
|
|
587
|
+
/** Handler for video.uploaded events */
|
|
588
|
+
onVideoUploaded?: (event: VideoUploadedEvent) => void | Promise<void>;
|
|
589
|
+
/** Handler for video.transcoded events */
|
|
590
|
+
onVideoTranscoded?: (event: VideoTranscodedEvent) => void | Promise<void>;
|
|
591
|
+
/** Generic handler for any webhook event */
|
|
592
|
+
onEvent?: (event: WebhookEvent) => void | Promise<void>;
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Verify webhook signature using HMAC-SHA256
|
|
596
|
+
*
|
|
597
|
+
* ScaleMule webhooks include a signature header in the format: sha256=<hex_signature>
|
|
598
|
+
*
|
|
599
|
+
* @param payload - Raw request body as string
|
|
600
|
+
* @param signature - Value of X-Webhook-Signature header
|
|
601
|
+
* @param secret - Your webhook secret
|
|
602
|
+
* @returns true if signature is valid
|
|
603
|
+
*
|
|
604
|
+
* @example
|
|
605
|
+
* ```typescript
|
|
606
|
+
* const isValid = verifyWebhookSignature(body, signature, secret)
|
|
607
|
+
* if (!isValid) {
|
|
608
|
+
* return new Response('Invalid signature', { status: 401 })
|
|
609
|
+
* }
|
|
610
|
+
* ```
|
|
611
|
+
*/
|
|
612
|
+
declare function verifyWebhookSignature(payload: string, signature: string, secret: string): boolean;
|
|
613
|
+
/**
|
|
614
|
+
* Parse a webhook event from the raw payload
|
|
615
|
+
*
|
|
616
|
+
* @param payload - Raw request body as string
|
|
617
|
+
* @returns Parsed webhook event
|
|
618
|
+
*/
|
|
619
|
+
declare function parseWebhookEvent<T = Record<string, unknown>>(payload: string): WebhookEvent<T>;
|
|
620
|
+
/**
|
|
621
|
+
* Register a webhook for video events
|
|
622
|
+
*
|
|
623
|
+
* @param url - The URL to receive webhook notifications
|
|
624
|
+
* @param options - Registration options
|
|
625
|
+
* @returns The webhook ID and secret for signature verification
|
|
626
|
+
*
|
|
627
|
+
* @example
|
|
628
|
+
* ```typescript
|
|
629
|
+
* const { id, secret } = await registerVideoWebhook(
|
|
630
|
+
* 'https://myapp.com/api/webhooks/scalemule',
|
|
631
|
+
* { events: ['video.ready', 'video.failed'] }
|
|
632
|
+
* )
|
|
633
|
+
*
|
|
634
|
+
* // Store the secret securely for signature verification
|
|
635
|
+
* await saveToEnv('SCALEMULE_WEBHOOK_SECRET', secret)
|
|
636
|
+
* ```
|
|
637
|
+
*/
|
|
638
|
+
declare function registerVideoWebhook(url: string, options?: {
|
|
639
|
+
/** Events to subscribe to (defaults to video.ready and video.failed) */
|
|
640
|
+
events?: ('video.ready' | 'video.failed' | 'video.uploaded' | 'video.transcoded')[];
|
|
641
|
+
/** Human-readable webhook name */
|
|
642
|
+
name?: string;
|
|
643
|
+
/** Client configuration */
|
|
644
|
+
clientConfig?: Partial<ServerConfig>;
|
|
645
|
+
}): Promise<{
|
|
646
|
+
id: string;
|
|
647
|
+
secret: string;
|
|
648
|
+
}>;
|
|
649
|
+
type RouteHandler$1 = (request: Request, context: {
|
|
650
|
+
params: Promise<{
|
|
651
|
+
scalemule?: string[];
|
|
652
|
+
}>;
|
|
653
|
+
}) => Promise<Response>;
|
|
654
|
+
/**
|
|
655
|
+
* Create Next.js App Router route handlers for ScaleMule webhooks
|
|
656
|
+
*
|
|
657
|
+
* Returns a POST handler that:
|
|
658
|
+
* - Verifies webhook signature (if secret provided)
|
|
659
|
+
* - Parses the event payload
|
|
660
|
+
* - Calls appropriate event handlers
|
|
661
|
+
* - Returns proper responses
|
|
662
|
+
*
|
|
663
|
+
* @param config - Webhook routes configuration
|
|
664
|
+
* @returns Object with POST handler for use in route.ts
|
|
665
|
+
*
|
|
666
|
+
* @example
|
|
667
|
+
* ```typescript
|
|
668
|
+
* // app/api/webhooks/scalemule/route.ts
|
|
669
|
+
* import { createWebhookRoutes } from '@scalemule/nextjs/server'
|
|
670
|
+
*
|
|
671
|
+
* export const { POST } = createWebhookRoutes({
|
|
672
|
+
* secret: process.env.SCALEMULE_WEBHOOK_SECRET,
|
|
673
|
+
* onVideoReady: async (event) => {
|
|
674
|
+
* // Update your database when video is ready
|
|
675
|
+
* await db.videos.update({
|
|
676
|
+
* where: { id: event.video_id },
|
|
677
|
+
* data: {
|
|
678
|
+
* status: 'ready',
|
|
679
|
+
* duration: event.duration_seconds,
|
|
680
|
+
* thumbnailUrl: event.thumbnail_url,
|
|
681
|
+
* }
|
|
682
|
+
* })
|
|
683
|
+
* },
|
|
684
|
+
* onVideoFailed: async (event) => {
|
|
685
|
+
* // Handle failed video processing
|
|
686
|
+
* await notifyUser(event.video_id, event.reason)
|
|
687
|
+
* }
|
|
688
|
+
* })
|
|
689
|
+
* ```
|
|
690
|
+
*/
|
|
691
|
+
declare function createWebhookRoutes(config?: WebhookRoutesConfig): {
|
|
692
|
+
POST: RouteHandler$1;
|
|
693
|
+
};
|
|
694
|
+
|
|
695
|
+
/**
|
|
696
|
+
* Simplified webhook handler for 1-line setup.
|
|
697
|
+
*
|
|
698
|
+
* Usage in your Next.js app:
|
|
699
|
+
* ```ts
|
|
700
|
+
* // app/api/webhooks/scalemule/route.ts
|
|
701
|
+
* import { createWebhookHandler } from '@scalemule/nextjs/server/webhooks'
|
|
702
|
+
*
|
|
703
|
+
* export const POST = createWebhookHandler({
|
|
704
|
+
* secret: process.env.SCALEMULE_WEBHOOK_SECRET,
|
|
705
|
+
* onEvent: {
|
|
706
|
+
* 'video.transcoding.completed': async (event) => { ... },
|
|
707
|
+
* 'storage.file.uploaded': async (event) => { ... },
|
|
708
|
+
* }
|
|
709
|
+
* })
|
|
710
|
+
* ```
|
|
711
|
+
*/
|
|
712
|
+
|
|
713
|
+
interface WebhookHandlerConfig {
|
|
714
|
+
/** Webhook secret for signature verification */
|
|
715
|
+
secret?: string;
|
|
716
|
+
/** Map of event name → handler function */
|
|
717
|
+
onEvent?: Record<string, (event: WebhookEvent) => void | Promise<void>>;
|
|
718
|
+
}
|
|
719
|
+
type RouteHandler = (request: Request) => Promise<Response>;
|
|
720
|
+
/**
|
|
721
|
+
* Create a webhook handler for ScaleMule events.
|
|
722
|
+
*
|
|
723
|
+
* Simpler alternative to createWebhookRoutes() — uses an event map
|
|
724
|
+
* instead of separate onVideoReady, onVideoFailed, etc. callbacks.
|
|
725
|
+
*/
|
|
726
|
+
declare function createWebhookHandler(config?: WebhookHandlerConfig): RouteHandler;
|
|
727
|
+
|
|
728
|
+
export { type ServerConfig as S, type VideoReadyEvent as V, type WebhookEvent as W, ScaleMuleServer as a, createWebhookRoutes as b, createServerClient as c, type VideoFailedEvent as d, type VideoUploadedEvent as e, type VideoTranscodedEvent as f, type WebhookRoutesConfig as g, createWebhookHandler as h, parseWebhookEvent as p, registerVideoWebhook as r, verifyWebhookSignature as v };
|