@scalemule/nextjs 0.0.28 → 0.0.30
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/dist/client.d.mts +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/{index-DewTyMF2.d.mts → index-BoENfro3.d.mts} +1 -1
- package/dist/{index-DewTyMF2.d.ts → index-BoENfro3.d.ts} +1 -1
- package/dist/index.d.mts +57 -3
- package/dist/index.d.ts +57 -3
- package/dist/index.js +262 -0
- package/dist/index.mjs +261 -1
- package/dist/server/index.d.mts +46 -9
- package/dist/server/index.d.ts +46 -9
- package/dist/server/index.js +137 -1
- package/dist/server/index.mjs +137 -2
- package/dist/server/webhook-handler.d.mts +2 -2
- package/dist/server/webhook-handler.d.ts +2 -2
- package/dist/testing.d.mts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/{webhook-handler-CQ2lZgy0.d.ts → webhook-handler-CjSSCdvI.d.ts} +2 -2
- package/dist/{webhook-handler-DS0r_w4M.d.mts → webhook-handler-Cz9jtet2.d.mts} +2 -2
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createContext, useState, useMemo, useEffect, useCallback, useContext, useRef } from 'react';
|
|
2
2
|
import { jsx } from 'react/jsx-runtime';
|
|
3
|
+
import { WebPushManager } from '@scalemule/sdk';
|
|
3
4
|
|
|
4
5
|
// src/provider.tsx
|
|
5
6
|
|
|
@@ -3051,6 +3052,265 @@ function useFeatureFlags(options = {}) {
|
|
|
3051
3052
|
getFlag
|
|
3052
3053
|
};
|
|
3053
3054
|
}
|
|
3055
|
+
function getCsrfToken() {
|
|
3056
|
+
if (typeof document === "undefined") return "";
|
|
3057
|
+
const match = document.cookie.match(/(?:^|;\s*)sm_csrf=([^;]*)/);
|
|
3058
|
+
return match ? decodeURIComponent(match[1]) : "";
|
|
3059
|
+
}
|
|
3060
|
+
function usePushNotifications(options = {}) {
|
|
3061
|
+
const { serviceWorkerUrl = "/sw.js", pushProxyUrl = "/api/push", onNotification } = options;
|
|
3062
|
+
const { user } = useScaleMule();
|
|
3063
|
+
const [isSupported, setIsSupported] = useState(false);
|
|
3064
|
+
const [permission, setPermission] = useState("unsupported");
|
|
3065
|
+
const [isSubscribed, setIsSubscribed] = useState(false);
|
|
3066
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
3067
|
+
const [error, setError] = useState(null);
|
|
3068
|
+
const [tokenId, setTokenId] = useState(null);
|
|
3069
|
+
const onNotificationRef = useRef(onNotification);
|
|
3070
|
+
useEffect(() => {
|
|
3071
|
+
onNotificationRef.current = onNotification;
|
|
3072
|
+
}, [onNotification]);
|
|
3073
|
+
const prevUserRef = useRef(null);
|
|
3074
|
+
const fetcher = useMemo(() => {
|
|
3075
|
+
async function proxyGet(path) {
|
|
3076
|
+
const res = await fetch(`${pushProxyUrl}/${path}`);
|
|
3077
|
+
const json = await res.json();
|
|
3078
|
+
if (!json.success) throw new Error(json.error?.message || "Request failed");
|
|
3079
|
+
return json.data;
|
|
3080
|
+
}
|
|
3081
|
+
async function proxyPost(path, body) {
|
|
3082
|
+
const res = await fetch(`${pushProxyUrl}/${path}`, {
|
|
3083
|
+
method: "POST",
|
|
3084
|
+
headers: {
|
|
3085
|
+
"Content-Type": "application/json",
|
|
3086
|
+
"x-csrf-token": getCsrfToken()
|
|
3087
|
+
},
|
|
3088
|
+
body: body ? JSON.stringify(body) : void 0
|
|
3089
|
+
});
|
|
3090
|
+
const json = await res.json();
|
|
3091
|
+
if (!json.success) throw new Error(json.error?.message || "Request failed");
|
|
3092
|
+
return json.data;
|
|
3093
|
+
}
|
|
3094
|
+
async function proxyPut(path, body) {
|
|
3095
|
+
const res = await fetch(`${pushProxyUrl}/${path}`, {
|
|
3096
|
+
method: "PUT",
|
|
3097
|
+
headers: {
|
|
3098
|
+
"Content-Type": "application/json",
|
|
3099
|
+
"x-csrf-token": getCsrfToken()
|
|
3100
|
+
},
|
|
3101
|
+
body: body ? JSON.stringify(body) : void 0
|
|
3102
|
+
});
|
|
3103
|
+
const json = await res.json();
|
|
3104
|
+
if (!json.success) throw new Error(json.error?.message || "Request failed");
|
|
3105
|
+
return json.data;
|
|
3106
|
+
}
|
|
3107
|
+
async function proxyDelete(path) {
|
|
3108
|
+
const res = await fetch(`${pushProxyUrl}/${path}`, {
|
|
3109
|
+
method: "DELETE",
|
|
3110
|
+
headers: {
|
|
3111
|
+
"x-csrf-token": getCsrfToken()
|
|
3112
|
+
}
|
|
3113
|
+
});
|
|
3114
|
+
const json = await res.json();
|
|
3115
|
+
if (!json.success) throw new Error(json.error?.message || "Request failed");
|
|
3116
|
+
}
|
|
3117
|
+
return {
|
|
3118
|
+
getSettings: () => proxyGet("settings/me"),
|
|
3119
|
+
registerToken: (data) => proxyPost("register", data),
|
|
3120
|
+
unregisterToken: (id) => proxyDelete(`tokens/by-id/${id}`),
|
|
3121
|
+
associateUser: (id) => proxyPut(`tokens/by-id/${id}/user`, {}),
|
|
3122
|
+
disassociateUser: (id) => proxyDelete(`tokens/by-id/${id}/user`)
|
|
3123
|
+
};
|
|
3124
|
+
}, [pushProxyUrl]);
|
|
3125
|
+
const manager = useMemo(() => {
|
|
3126
|
+
if (typeof window === "undefined") return null;
|
|
3127
|
+
try {
|
|
3128
|
+
return new WebPushManager({ fetcher, serviceWorkerUrl });
|
|
3129
|
+
} catch {
|
|
3130
|
+
return null;
|
|
3131
|
+
}
|
|
3132
|
+
}, [fetcher, serviceWorkerUrl]);
|
|
3133
|
+
useEffect(() => {
|
|
3134
|
+
if (!manager) return;
|
|
3135
|
+
const supported = manager.isSupported();
|
|
3136
|
+
setIsSupported(supported);
|
|
3137
|
+
setPermission(manager.getPermissionState());
|
|
3138
|
+
if (supported) {
|
|
3139
|
+
const storedTokenId = manager.getTokenId();
|
|
3140
|
+
manager.isSubscribed().then((sub) => {
|
|
3141
|
+
if (!sub && storedTokenId) {
|
|
3142
|
+
manager.unsubscribe().catch(() => {
|
|
3143
|
+
});
|
|
3144
|
+
setIsSubscribed(false);
|
|
3145
|
+
setTokenId(null);
|
|
3146
|
+
setPermission(manager.getPermissionState());
|
|
3147
|
+
} else {
|
|
3148
|
+
setIsSubscribed(sub);
|
|
3149
|
+
setTokenId(manager.getTokenId());
|
|
3150
|
+
}
|
|
3151
|
+
});
|
|
3152
|
+
}
|
|
3153
|
+
}, [manager]);
|
|
3154
|
+
useEffect(() => {
|
|
3155
|
+
if (typeof navigator === "undefined" || !("serviceWorker" in navigator)) return;
|
|
3156
|
+
function handleMessage(event) {
|
|
3157
|
+
if (event.data?.type === "push-received") {
|
|
3158
|
+
onNotificationRef.current?.(event.data.payload);
|
|
3159
|
+
}
|
|
3160
|
+
if (event.data?.type === "push-subscription-expired") {
|
|
3161
|
+
if (manager) {
|
|
3162
|
+
manager.unsubscribe().catch(() => {
|
|
3163
|
+
});
|
|
3164
|
+
}
|
|
3165
|
+
setIsSubscribed(false);
|
|
3166
|
+
setTokenId(null);
|
|
3167
|
+
setPermission(typeof Notification !== "undefined" ? Notification.permission : "unsupported");
|
|
3168
|
+
}
|
|
3169
|
+
}
|
|
3170
|
+
navigator.serviceWorker.addEventListener("message", handleMessage);
|
|
3171
|
+
return () => navigator.serviceWorker.removeEventListener("message", handleMessage);
|
|
3172
|
+
}, [manager]);
|
|
3173
|
+
useEffect(() => {
|
|
3174
|
+
if (!manager) return;
|
|
3175
|
+
const currentUserId = user?.id || null;
|
|
3176
|
+
const prevUserId = prevUserRef.current;
|
|
3177
|
+
if (currentUserId && !prevUserId && isSubscribed) {
|
|
3178
|
+
manager.associateUser().catch(() => {
|
|
3179
|
+
});
|
|
3180
|
+
}
|
|
3181
|
+
prevUserRef.current = currentUserId;
|
|
3182
|
+
}, [user?.id, manager, isSubscribed]);
|
|
3183
|
+
const subscribe = useCallback(async () => {
|
|
3184
|
+
if (!manager) return;
|
|
3185
|
+
setIsLoading(true);
|
|
3186
|
+
setError(null);
|
|
3187
|
+
try {
|
|
3188
|
+
const result = await manager.subscribe();
|
|
3189
|
+
if (result) {
|
|
3190
|
+
setIsSubscribed(true);
|
|
3191
|
+
setTokenId(result.tokenId);
|
|
3192
|
+
setPermission("granted");
|
|
3193
|
+
} else {
|
|
3194
|
+
setPermission(manager.getPermissionState());
|
|
3195
|
+
}
|
|
3196
|
+
} catch (e) {
|
|
3197
|
+
setError({
|
|
3198
|
+
code: "PUSH_SUBSCRIBE_ERROR",
|
|
3199
|
+
message: e instanceof Error ? e.message : "Failed to subscribe"
|
|
3200
|
+
});
|
|
3201
|
+
} finally {
|
|
3202
|
+
setIsLoading(false);
|
|
3203
|
+
}
|
|
3204
|
+
}, [manager]);
|
|
3205
|
+
const unsubscribe = useCallback(async () => {
|
|
3206
|
+
if (!manager) return;
|
|
3207
|
+
setIsLoading(true);
|
|
3208
|
+
setError(null);
|
|
3209
|
+
try {
|
|
3210
|
+
await manager.unsubscribe();
|
|
3211
|
+
setIsSubscribed(false);
|
|
3212
|
+
setTokenId(null);
|
|
3213
|
+
} catch (e) {
|
|
3214
|
+
setError({
|
|
3215
|
+
code: "PUSH_UNSUBSCRIBE_ERROR",
|
|
3216
|
+
message: e instanceof Error ? e.message : "Failed to unsubscribe"
|
|
3217
|
+
});
|
|
3218
|
+
} finally {
|
|
3219
|
+
setIsLoading(false);
|
|
3220
|
+
}
|
|
3221
|
+
}, [manager]);
|
|
3222
|
+
const disassociateUser = useCallback(async () => {
|
|
3223
|
+
if (!manager) return;
|
|
3224
|
+
try {
|
|
3225
|
+
await manager.disassociateUser();
|
|
3226
|
+
} catch {
|
|
3227
|
+
}
|
|
3228
|
+
}, [manager]);
|
|
3229
|
+
return {
|
|
3230
|
+
isSupported,
|
|
3231
|
+
permission,
|
|
3232
|
+
isSubscribed,
|
|
3233
|
+
isLoading,
|
|
3234
|
+
error,
|
|
3235
|
+
subscribe,
|
|
3236
|
+
unsubscribe,
|
|
3237
|
+
disassociateUser,
|
|
3238
|
+
tokenId
|
|
3239
|
+
};
|
|
3240
|
+
}
|
|
3241
|
+
function useShare(options) {
|
|
3242
|
+
const { client, user } = useScaleMule();
|
|
3243
|
+
const [referralCode, setReferralCode] = useState(
|
|
3244
|
+
options?.referralCode ?? null
|
|
3245
|
+
);
|
|
3246
|
+
const [loading, setLoading] = useState(false);
|
|
3247
|
+
const [copied, setCopied] = useState(false);
|
|
3248
|
+
useEffect(() => {
|
|
3249
|
+
if (!user) {
|
|
3250
|
+
setReferralCode(options?.referralCode ?? null);
|
|
3251
|
+
setLoading(false);
|
|
3252
|
+
return;
|
|
3253
|
+
}
|
|
3254
|
+
if (!options?.autoFetchReferral) {
|
|
3255
|
+
setLoading(false);
|
|
3256
|
+
return;
|
|
3257
|
+
}
|
|
3258
|
+
if (options?.referralCode) {
|
|
3259
|
+
setReferralCode(options.referralCode);
|
|
3260
|
+
setLoading(false);
|
|
3261
|
+
return;
|
|
3262
|
+
}
|
|
3263
|
+
setReferralCode(null);
|
|
3264
|
+
let cancelled = false;
|
|
3265
|
+
setLoading(true);
|
|
3266
|
+
client.get("/v1/referrals/me").then((data) => {
|
|
3267
|
+
if (!cancelled) setReferralCode(data.referral_code);
|
|
3268
|
+
}).catch(() => {
|
|
3269
|
+
if (!cancelled) setReferralCode(null);
|
|
3270
|
+
}).finally(() => {
|
|
3271
|
+
if (!cancelled) setLoading(false);
|
|
3272
|
+
});
|
|
3273
|
+
return () => {
|
|
3274
|
+
cancelled = true;
|
|
3275
|
+
};
|
|
3276
|
+
}, [user?.id, options?.url, options?.referralCode, options?.autoFetchReferral, client]);
|
|
3277
|
+
const shareUrl = useMemo(() => {
|
|
3278
|
+
const raw = options?.url || (typeof window !== "undefined" ? window.location.href : "");
|
|
3279
|
+
if (!raw) return raw;
|
|
3280
|
+
try {
|
|
3281
|
+
const base = typeof window !== "undefined" ? window.location.origin : void 0;
|
|
3282
|
+
const u = new URL(raw, base);
|
|
3283
|
+
if (referralCode) {
|
|
3284
|
+
u.searchParams.set("rc", referralCode);
|
|
3285
|
+
}
|
|
3286
|
+
return u.toString();
|
|
3287
|
+
} catch {
|
|
3288
|
+
return raw;
|
|
3289
|
+
}
|
|
3290
|
+
}, [options?.url, referralCode]);
|
|
3291
|
+
const copyLink = useCallback(async () => {
|
|
3292
|
+
try {
|
|
3293
|
+
await navigator.clipboard.writeText(shareUrl);
|
|
3294
|
+
} catch {
|
|
3295
|
+
try {
|
|
3296
|
+
const textarea = document.createElement("textarea");
|
|
3297
|
+
textarea.value = shareUrl;
|
|
3298
|
+
textarea.style.position = "fixed";
|
|
3299
|
+
textarea.style.opacity = "0";
|
|
3300
|
+
document.body.appendChild(textarea);
|
|
3301
|
+
textarea.select();
|
|
3302
|
+
document.execCommand("copy");
|
|
3303
|
+
document.body.removeChild(textarea);
|
|
3304
|
+
} catch {
|
|
3305
|
+
return false;
|
|
3306
|
+
}
|
|
3307
|
+
}
|
|
3308
|
+
setCopied(true);
|
|
3309
|
+
setTimeout(() => setCopied(false), 2e3);
|
|
3310
|
+
return true;
|
|
3311
|
+
}, [shareUrl]);
|
|
3312
|
+
return { shareUrl, referralCode, copyLink, copied, loading };
|
|
3313
|
+
}
|
|
3054
3314
|
|
|
3055
3315
|
// src/validation.ts
|
|
3056
3316
|
var phoneCountries = [
|
|
@@ -3343,4 +3603,4 @@ function createSafeLogger(prefix) {
|
|
|
3343
3603
|
};
|
|
3344
3604
|
}
|
|
3345
3605
|
|
|
3346
|
-
export { ScaleMuleApiError, ScaleMuleClient, ScaleMuleProvider, composePhone, createClient, createSafeLogger, normalizePhone, phoneCountries, sanitizeForLog, useAnalytics, useAuth, useBilling, useContent, useFeatureFlags, useRealtime, useScaleMule, useScaleMuleClient, useUser, validateForm, validators };
|
|
3606
|
+
export { ScaleMuleApiError, ScaleMuleClient, ScaleMuleProvider, composePhone, createClient, createSafeLogger, normalizePhone, phoneCountries, sanitizeForLog, useAnalytics, useAuth, useBilling, useContent, useFeatureFlags, usePushNotifications, useRealtime, useScaleMule, useScaleMuleClient, useShare, useUser, validateForm, validators };
|
package/dist/server/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { S as ServerConfig } from '../webhook-handler-
|
|
2
|
-
export { a as ScaleMuleServer,
|
|
3
|
-
import {
|
|
1
|
+
import { S as ServerConfig } from '../webhook-handler-Cz9jtet2.mjs';
|
|
2
|
+
export { a as ScaleMuleServer, V as VideoFailedEvent, b as VideoReadyEvent, c as VideoTranscodedEvent, d as VideoUploadedEvent, W as WebhookEvent, e as WebhookRoutesConfig, f as createServerClient, g as createWebhookHandler, h as createWebhookRoutes, p as parseWebhookEvent, r as registerVideoWebhook, i as resolveGatewayUrl, v as verifyWebhookSignature } from '../webhook-handler-Cz9jtet2.mjs';
|
|
3
|
+
import { p as ClientContext, A as ApiError } from '../index-BoENfro3.mjs';
|
|
4
4
|
import { NextRequest, NextResponse } from 'next/server';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -231,7 +231,7 @@ interface AuthRoutesConfig {
|
|
|
231
231
|
email: string;
|
|
232
232
|
}) => void | Promise<void>;
|
|
233
233
|
}
|
|
234
|
-
type RouteHandler = (request: Request, context: {
|
|
234
|
+
type RouteHandler$1 = (request: Request, context: {
|
|
235
235
|
params: Promise<{
|
|
236
236
|
scalemule?: string[];
|
|
237
237
|
}>;
|
|
@@ -264,10 +264,10 @@ type RouteHandler = (request: Request, context: {
|
|
|
264
264
|
* - POST /api/auth/verify-email - Verify email
|
|
265
265
|
*/
|
|
266
266
|
declare function createAuthRoutes(config?: AuthRoutesConfig): {
|
|
267
|
-
GET: RouteHandler;
|
|
268
|
-
POST: RouteHandler;
|
|
269
|
-
DELETE: RouteHandler;
|
|
270
|
-
PATCH: RouteHandler;
|
|
267
|
+
GET: RouteHandler$1;
|
|
268
|
+
POST: RouteHandler$1;
|
|
269
|
+
DELETE: RouteHandler$1;
|
|
270
|
+
PATCH: RouteHandler$1;
|
|
271
271
|
};
|
|
272
272
|
interface AnalyticsTrackingGateConfig {
|
|
273
273
|
/** Feature flag key used to decide whether to forward analytics */
|
|
@@ -346,7 +346,44 @@ interface AnalyticsRoutesConfig {
|
|
|
346
346
|
* ```
|
|
347
347
|
*/
|
|
348
348
|
declare function createAnalyticsRoutes(config?: AnalyticsRoutesConfig): {
|
|
349
|
+
POST: RouteHandler$1;
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Push Notification Proxy Routes
|
|
354
|
+
*
|
|
355
|
+
* Drop-in route handlers for Next.js App Router that proxy push notification
|
|
356
|
+
* requests to the ScaleMule gateway. Follows the same pattern as createAuthRoutes().
|
|
357
|
+
*
|
|
358
|
+
* @example
|
|
359
|
+
* ```ts
|
|
360
|
+
* // app/api/push/[...action]/route.ts
|
|
361
|
+
* import { createPushRoutes } from '@scalemule/nextjs/server'
|
|
362
|
+
*
|
|
363
|
+
* export const { GET, POST, PUT, DELETE } = createPushRoutes({
|
|
364
|
+
* apiKey: process.env.SCALEMULE_API_KEY!,
|
|
365
|
+
* gatewayUrl: process.env.SCALEMULE_API_URL!,
|
|
366
|
+
* })
|
|
367
|
+
* ```
|
|
368
|
+
*/
|
|
369
|
+
interface PushRoutesConfig {
|
|
370
|
+
/** Server-side secret API key */
|
|
371
|
+
apiKey: string;
|
|
372
|
+
/** Gateway URL (e.g., https://api.scalemule.com) */
|
|
373
|
+
gatewayUrl: string;
|
|
374
|
+
/** Enable CSRF validation on POST/PUT/DELETE (default: true) */
|
|
375
|
+
csrf?: boolean;
|
|
376
|
+
}
|
|
377
|
+
type RouteHandler = (request: Request, context: {
|
|
378
|
+
params: Promise<{
|
|
379
|
+
action?: string[];
|
|
380
|
+
}>;
|
|
381
|
+
}) => Promise<Response>;
|
|
382
|
+
declare function createPushRoutes(config: PushRoutesConfig): {
|
|
383
|
+
GET: RouteHandler;
|
|
349
384
|
POST: RouteHandler;
|
|
385
|
+
PUT: RouteHandler;
|
|
386
|
+
DELETE: RouteHandler;
|
|
350
387
|
};
|
|
351
388
|
|
|
352
389
|
/**
|
|
@@ -940,4 +977,4 @@ declare function invalidateBundleCache(key?: string): void;
|
|
|
940
977
|
*/
|
|
941
978
|
declare function prefetchBundles(keys: string[]): Promise<void>;
|
|
942
979
|
|
|
943
|
-
export { type AnalyticsRoutesConfig, type AnalyticsTrackingGateConfig, type AuthMiddlewareConfig, type AuthRoutesConfig, CSRF_COOKIE_NAME, CSRF_HEADER_NAME, type HandlerContext, type HandlerOptions, type MySqlBundle, OAUTH_STATE_COOKIE_NAME, type OAuthBundle, type PostgresBundle, type RedisBundle, type S3Bundle, SESSION_COOKIE_NAME, ScaleMuleError, ServerConfig, type SessionCookieOptions, type SessionData, type SmtpBundle, USER_ID_COOKIE_NAME, apiHandler, buildClientContextHeaders, buildFlagContext, clearOAuthState, clearSession, configureBundles, configureSecrets, createAnalyticsRoutes, createAuthMiddleware, createAuthRoutes, errorCodeToStatus, extractClientContext, extractClientContextFromReq, generateCSRFToken, getAppSecret, getAppSecretOrDefault, getBootstrapFlags, getBundle, getCSRFToken, getMySqlBundle, getOAuthBundle, getPostgresBundle, getRedisBundle, getS3Bundle, getSession, getSessionFromRequest, getSmtpBundle, invalidateBundleCache, invalidateSecretCache, prefetchBundles, prefetchSecrets, requireAppSecret, requireBundle, requireSession, setOAuthState, unwrap, validateCSRFToken, validateCSRFTokenAsync, validateOAuthState, validateOAuthStateAsync, withAuth, withCSRFProtection, withCSRFToken, withSession };
|
|
980
|
+
export { type AnalyticsRoutesConfig, type AnalyticsTrackingGateConfig, type AuthMiddlewareConfig, type AuthRoutesConfig, CSRF_COOKIE_NAME, CSRF_HEADER_NAME, type HandlerContext, type HandlerOptions, type MySqlBundle, OAUTH_STATE_COOKIE_NAME, type OAuthBundle, type PostgresBundle, type PushRoutesConfig, type RedisBundle, type S3Bundle, SESSION_COOKIE_NAME, ScaleMuleError, ServerConfig, type SessionCookieOptions, type SessionData, type SmtpBundle, USER_ID_COOKIE_NAME, apiHandler, buildClientContextHeaders, buildFlagContext, clearOAuthState, clearSession, configureBundles, configureSecrets, createAnalyticsRoutes, createAuthMiddleware, createAuthRoutes, createPushRoutes, errorCodeToStatus, extractClientContext, extractClientContextFromReq, generateCSRFToken, getAppSecret, getAppSecretOrDefault, getBootstrapFlags, getBundle, getCSRFToken, getMySqlBundle, getOAuthBundle, getPostgresBundle, getRedisBundle, getS3Bundle, getSession, getSessionFromRequest, getSmtpBundle, invalidateBundleCache, invalidateSecretCache, prefetchBundles, prefetchSecrets, requireAppSecret, requireBundle, requireSession, setOAuthState, unwrap, validateCSRFToken, validateCSRFTokenAsync, validateOAuthState, validateOAuthStateAsync, withAuth, withCSRFProtection, withCSRFToken, withSession };
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { S as ServerConfig } from '../webhook-handler-
|
|
2
|
-
export { a as ScaleMuleServer,
|
|
3
|
-
import {
|
|
1
|
+
import { S as ServerConfig } from '../webhook-handler-CjSSCdvI.js';
|
|
2
|
+
export { a as ScaleMuleServer, V as VideoFailedEvent, b as VideoReadyEvent, c as VideoTranscodedEvent, d as VideoUploadedEvent, W as WebhookEvent, e as WebhookRoutesConfig, f as createServerClient, g as createWebhookHandler, h as createWebhookRoutes, p as parseWebhookEvent, r as registerVideoWebhook, i as resolveGatewayUrl, v as verifyWebhookSignature } from '../webhook-handler-CjSSCdvI.js';
|
|
3
|
+
import { p as ClientContext, A as ApiError } from '../index-BoENfro3.js';
|
|
4
4
|
import { NextRequest, NextResponse } from 'next/server';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -231,7 +231,7 @@ interface AuthRoutesConfig {
|
|
|
231
231
|
email: string;
|
|
232
232
|
}) => void | Promise<void>;
|
|
233
233
|
}
|
|
234
|
-
type RouteHandler = (request: Request, context: {
|
|
234
|
+
type RouteHandler$1 = (request: Request, context: {
|
|
235
235
|
params: Promise<{
|
|
236
236
|
scalemule?: string[];
|
|
237
237
|
}>;
|
|
@@ -264,10 +264,10 @@ type RouteHandler = (request: Request, context: {
|
|
|
264
264
|
* - POST /api/auth/verify-email - Verify email
|
|
265
265
|
*/
|
|
266
266
|
declare function createAuthRoutes(config?: AuthRoutesConfig): {
|
|
267
|
-
GET: RouteHandler;
|
|
268
|
-
POST: RouteHandler;
|
|
269
|
-
DELETE: RouteHandler;
|
|
270
|
-
PATCH: RouteHandler;
|
|
267
|
+
GET: RouteHandler$1;
|
|
268
|
+
POST: RouteHandler$1;
|
|
269
|
+
DELETE: RouteHandler$1;
|
|
270
|
+
PATCH: RouteHandler$1;
|
|
271
271
|
};
|
|
272
272
|
interface AnalyticsTrackingGateConfig {
|
|
273
273
|
/** Feature flag key used to decide whether to forward analytics */
|
|
@@ -346,7 +346,44 @@ interface AnalyticsRoutesConfig {
|
|
|
346
346
|
* ```
|
|
347
347
|
*/
|
|
348
348
|
declare function createAnalyticsRoutes(config?: AnalyticsRoutesConfig): {
|
|
349
|
+
POST: RouteHandler$1;
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Push Notification Proxy Routes
|
|
354
|
+
*
|
|
355
|
+
* Drop-in route handlers for Next.js App Router that proxy push notification
|
|
356
|
+
* requests to the ScaleMule gateway. Follows the same pattern as createAuthRoutes().
|
|
357
|
+
*
|
|
358
|
+
* @example
|
|
359
|
+
* ```ts
|
|
360
|
+
* // app/api/push/[...action]/route.ts
|
|
361
|
+
* import { createPushRoutes } from '@scalemule/nextjs/server'
|
|
362
|
+
*
|
|
363
|
+
* export const { GET, POST, PUT, DELETE } = createPushRoutes({
|
|
364
|
+
* apiKey: process.env.SCALEMULE_API_KEY!,
|
|
365
|
+
* gatewayUrl: process.env.SCALEMULE_API_URL!,
|
|
366
|
+
* })
|
|
367
|
+
* ```
|
|
368
|
+
*/
|
|
369
|
+
interface PushRoutesConfig {
|
|
370
|
+
/** Server-side secret API key */
|
|
371
|
+
apiKey: string;
|
|
372
|
+
/** Gateway URL (e.g., https://api.scalemule.com) */
|
|
373
|
+
gatewayUrl: string;
|
|
374
|
+
/** Enable CSRF validation on POST/PUT/DELETE (default: true) */
|
|
375
|
+
csrf?: boolean;
|
|
376
|
+
}
|
|
377
|
+
type RouteHandler = (request: Request, context: {
|
|
378
|
+
params: Promise<{
|
|
379
|
+
action?: string[];
|
|
380
|
+
}>;
|
|
381
|
+
}) => Promise<Response>;
|
|
382
|
+
declare function createPushRoutes(config: PushRoutesConfig): {
|
|
383
|
+
GET: RouteHandler;
|
|
349
384
|
POST: RouteHandler;
|
|
385
|
+
PUT: RouteHandler;
|
|
386
|
+
DELETE: RouteHandler;
|
|
350
387
|
};
|
|
351
388
|
|
|
352
389
|
/**
|
|
@@ -940,4 +977,4 @@ declare function invalidateBundleCache(key?: string): void;
|
|
|
940
977
|
*/
|
|
941
978
|
declare function prefetchBundles(keys: string[]): Promise<void>;
|
|
942
979
|
|
|
943
|
-
export { type AnalyticsRoutesConfig, type AnalyticsTrackingGateConfig, type AuthMiddlewareConfig, type AuthRoutesConfig, CSRF_COOKIE_NAME, CSRF_HEADER_NAME, type HandlerContext, type HandlerOptions, type MySqlBundle, OAUTH_STATE_COOKIE_NAME, type OAuthBundle, type PostgresBundle, type RedisBundle, type S3Bundle, SESSION_COOKIE_NAME, ScaleMuleError, ServerConfig, type SessionCookieOptions, type SessionData, type SmtpBundle, USER_ID_COOKIE_NAME, apiHandler, buildClientContextHeaders, buildFlagContext, clearOAuthState, clearSession, configureBundles, configureSecrets, createAnalyticsRoutes, createAuthMiddleware, createAuthRoutes, errorCodeToStatus, extractClientContext, extractClientContextFromReq, generateCSRFToken, getAppSecret, getAppSecretOrDefault, getBootstrapFlags, getBundle, getCSRFToken, getMySqlBundle, getOAuthBundle, getPostgresBundle, getRedisBundle, getS3Bundle, getSession, getSessionFromRequest, getSmtpBundle, invalidateBundleCache, invalidateSecretCache, prefetchBundles, prefetchSecrets, requireAppSecret, requireBundle, requireSession, setOAuthState, unwrap, validateCSRFToken, validateCSRFTokenAsync, validateOAuthState, validateOAuthStateAsync, withAuth, withCSRFProtection, withCSRFToken, withSession };
|
|
980
|
+
export { type AnalyticsRoutesConfig, type AnalyticsTrackingGateConfig, type AuthMiddlewareConfig, type AuthRoutesConfig, CSRF_COOKIE_NAME, CSRF_HEADER_NAME, type HandlerContext, type HandlerOptions, type MySqlBundle, OAUTH_STATE_COOKIE_NAME, type OAuthBundle, type PostgresBundle, type PushRoutesConfig, type RedisBundle, type S3Bundle, SESSION_COOKIE_NAME, ScaleMuleError, ServerConfig, type SessionCookieOptions, type SessionData, type SmtpBundle, USER_ID_COOKIE_NAME, apiHandler, buildClientContextHeaders, buildFlagContext, clearOAuthState, clearSession, configureBundles, configureSecrets, createAnalyticsRoutes, createAuthMiddleware, createAuthRoutes, createPushRoutes, errorCodeToStatus, extractClientContext, extractClientContextFromReq, generateCSRFToken, getAppSecret, getAppSecretOrDefault, getBootstrapFlags, getBundle, getCSRFToken, getMySqlBundle, getOAuthBundle, getPostgresBundle, getRedisBundle, getS3Bundle, getSession, getSessionFromRequest, getSmtpBundle, invalidateBundleCache, invalidateSecretCache, prefetchBundles, prefetchSecrets, requireAppSecret, requireBundle, requireSession, setOAuthState, unwrap, validateCSRFToken, validateCSRFTokenAsync, validateOAuthState, validateOAuthStateAsync, withAuth, withCSRFProtection, withCSRFToken, withSession };
|
package/dist/server/index.js
CHANGED
|
@@ -1601,6 +1601,137 @@ function createAnalyticsRoutes(config = {}) {
|
|
|
1601
1601
|
return { POST };
|
|
1602
1602
|
}
|
|
1603
1603
|
|
|
1604
|
+
// src/server/push.ts
|
|
1605
|
+
function errorResponse2(code, message, status) {
|
|
1606
|
+
return new Response(
|
|
1607
|
+
JSON.stringify({ success: false, error: { code, message } }),
|
|
1608
|
+
{ status, headers: { "Content-Type": "application/json" } }
|
|
1609
|
+
);
|
|
1610
|
+
}
|
|
1611
|
+
function successResponse2(data, status = 200) {
|
|
1612
|
+
return new Response(
|
|
1613
|
+
JSON.stringify({ success: true, data }),
|
|
1614
|
+
{ status, headers: { "Content-Type": "application/json" } }
|
|
1615
|
+
);
|
|
1616
|
+
}
|
|
1617
|
+
function createPushRoutes(config) {
|
|
1618
|
+
const { apiKey, gatewayUrl, csrf = true } = config;
|
|
1619
|
+
async function proxyToGateway(request, method, subPath) {
|
|
1620
|
+
const targetUrl = `${gatewayUrl}/v1/communication/push/${subPath}`;
|
|
1621
|
+
const headers2 = {
|
|
1622
|
+
"x-api-key": apiKey
|
|
1623
|
+
};
|
|
1624
|
+
const session = getSessionFromRequest(request);
|
|
1625
|
+
if (session?.sessionToken) {
|
|
1626
|
+
headers2["Authorization"] = `Bearer ${session.sessionToken}`;
|
|
1627
|
+
}
|
|
1628
|
+
const clientContext = extractClientContext(
|
|
1629
|
+
request
|
|
1630
|
+
);
|
|
1631
|
+
const contextHeaders = buildClientContextHeaders(clientContext);
|
|
1632
|
+
Object.assign(headers2, contextHeaders);
|
|
1633
|
+
const workspaceId = request.headers.get("x-sm-workspace-id");
|
|
1634
|
+
if (workspaceId) {
|
|
1635
|
+
headers2["x-sm-workspace-id"] = workspaceId;
|
|
1636
|
+
}
|
|
1637
|
+
const fetchOptions = {
|
|
1638
|
+
method,
|
|
1639
|
+
headers: headers2
|
|
1640
|
+
};
|
|
1641
|
+
if (method !== "GET" && method !== "DELETE") {
|
|
1642
|
+
try {
|
|
1643
|
+
const body = await request.text();
|
|
1644
|
+
if (body) {
|
|
1645
|
+
headers2["Content-Type"] = "application/json";
|
|
1646
|
+
fetchOptions.body = body;
|
|
1647
|
+
}
|
|
1648
|
+
} catch {
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
const response = await fetch(targetUrl, fetchOptions);
|
|
1652
|
+
if (response.status === 204) {
|
|
1653
|
+
return successResponse2(null);
|
|
1654
|
+
}
|
|
1655
|
+
const contentType = response.headers.get("content-type") || "";
|
|
1656
|
+
if (contentType.includes("application/json")) {
|
|
1657
|
+
const json = await response.json();
|
|
1658
|
+
if (response.ok) {
|
|
1659
|
+
return new Response(JSON.stringify(json), {
|
|
1660
|
+
status: response.status,
|
|
1661
|
+
headers: { "Content-Type": "application/json" }
|
|
1662
|
+
});
|
|
1663
|
+
} else {
|
|
1664
|
+
const errMsg = json?.error?.message || json?.message || "Request failed";
|
|
1665
|
+
const errCode = json?.error?.code || json?.code || "PUSH_ERROR";
|
|
1666
|
+
return errorResponse2(errCode, errMsg, response.status);
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
const text = await response.text();
|
|
1670
|
+
if (response.ok) {
|
|
1671
|
+
return successResponse2(text);
|
|
1672
|
+
}
|
|
1673
|
+
return errorResponse2("PUSH_ERROR", text || "Request failed", response.status);
|
|
1674
|
+
}
|
|
1675
|
+
function extractSubPath(params) {
|
|
1676
|
+
return (params.action || []).join("/");
|
|
1677
|
+
}
|
|
1678
|
+
const GET = async (request, context) => {
|
|
1679
|
+
try {
|
|
1680
|
+
const params = await context?.params;
|
|
1681
|
+
const subPath = extractSubPath(params || {});
|
|
1682
|
+
return proxyToGateway(request, "GET", subPath);
|
|
1683
|
+
} catch (e) {
|
|
1684
|
+
return errorResponse2("INTERNAL_ERROR", String(e), 500);
|
|
1685
|
+
}
|
|
1686
|
+
};
|
|
1687
|
+
const POST = async (request, context) => {
|
|
1688
|
+
if (csrf) {
|
|
1689
|
+
const csrfError = validateCSRFToken(request);
|
|
1690
|
+
if (csrfError) {
|
|
1691
|
+
return errorResponse2("CSRF_ERROR", "CSRF validation failed", 403);
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
try {
|
|
1695
|
+
const params = await context?.params;
|
|
1696
|
+
const subPath = extractSubPath(params || {});
|
|
1697
|
+
return proxyToGateway(request, "POST", subPath);
|
|
1698
|
+
} catch (e) {
|
|
1699
|
+
return errorResponse2("INTERNAL_ERROR", String(e), 500);
|
|
1700
|
+
}
|
|
1701
|
+
};
|
|
1702
|
+
const PUT = async (request, context) => {
|
|
1703
|
+
if (csrf) {
|
|
1704
|
+
const csrfError = validateCSRFToken(request);
|
|
1705
|
+
if (csrfError) {
|
|
1706
|
+
return errorResponse2("CSRF_ERROR", "CSRF validation failed", 403);
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
try {
|
|
1710
|
+
const params = await context?.params;
|
|
1711
|
+
const subPath = extractSubPath(params || {});
|
|
1712
|
+
return proxyToGateway(request, "PUT", subPath);
|
|
1713
|
+
} catch (e) {
|
|
1714
|
+
return errorResponse2("INTERNAL_ERROR", String(e), 500);
|
|
1715
|
+
}
|
|
1716
|
+
};
|
|
1717
|
+
const DELETE = async (request, context) => {
|
|
1718
|
+
if (csrf) {
|
|
1719
|
+
const csrfError = validateCSRFToken(request);
|
|
1720
|
+
if (csrfError) {
|
|
1721
|
+
return errorResponse2("CSRF_ERROR", "CSRF validation failed", 403);
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
try {
|
|
1725
|
+
const params = await context?.params;
|
|
1726
|
+
const subPath = extractSubPath(params || {});
|
|
1727
|
+
return proxyToGateway(request, "DELETE", subPath);
|
|
1728
|
+
} catch (e) {
|
|
1729
|
+
return errorResponse2("INTERNAL_ERROR", String(e), 500);
|
|
1730
|
+
}
|
|
1731
|
+
};
|
|
1732
|
+
return { GET, POST, PUT, DELETE };
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1604
1735
|
// src/server/errors.ts
|
|
1605
1736
|
var ScaleMuleError = class extends Error {
|
|
1606
1737
|
constructor(code, message, status = 400, details) {
|
|
@@ -1705,8 +1836,12 @@ function apiHandler(handler, options) {
|
|
|
1705
1836
|
const custom = options.onError(error);
|
|
1706
1837
|
if (custom) return custom;
|
|
1707
1838
|
}
|
|
1839
|
+
const safeMessage = error.status >= 500 ? "An unexpected error occurred" : error.message;
|
|
1840
|
+
if (error.status >= 500) {
|
|
1841
|
+
console.error("Internal API error:", error.message);
|
|
1842
|
+
}
|
|
1708
1843
|
return Response.json(
|
|
1709
|
-
{ success: false, error: { code: error.code, message:
|
|
1844
|
+
{ success: false, error: { code: error.code, message: safeMessage } },
|
|
1710
1845
|
{ status: error.status }
|
|
1711
1846
|
);
|
|
1712
1847
|
}
|
|
@@ -2220,6 +2355,7 @@ exports.configureSecrets = configureSecrets;
|
|
|
2220
2355
|
exports.createAnalyticsRoutes = createAnalyticsRoutes;
|
|
2221
2356
|
exports.createAuthMiddleware = createAuthMiddleware;
|
|
2222
2357
|
exports.createAuthRoutes = createAuthRoutes;
|
|
2358
|
+
exports.createPushRoutes = createPushRoutes;
|
|
2223
2359
|
exports.createServerClient = createServerClient;
|
|
2224
2360
|
exports.createWebhookHandler = createWebhookHandler;
|
|
2225
2361
|
exports.createWebhookRoutes = createWebhookRoutes;
|