@sylphx/sdk 0.15.2 → 0.15.3
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/index.d.ts +7 -0
- package/dist/index.mjs.map +1 -1
- package/dist/nextjs/index.d.ts +277 -276
- package/dist/nextjs/index.mjs +198 -194
- package/dist/nextjs/index.mjs.map +1 -1
- package/dist/react/index.d.ts +11 -5
- package/dist/react/index.mjs.map +1 -1
- package/dist/server/index.d.ts +12 -0
- package/dist/server/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/nextjs/index.mjs
CHANGED
|
@@ -1,45 +1,6 @@
|
|
|
1
1
|
// src/nextjs/middleware.ts
|
|
2
2
|
import { NextResponse } from "next/server";
|
|
3
3
|
|
|
4
|
-
// src/constants.ts
|
|
5
|
-
var ENV_URL = "SYLPHX_URL";
|
|
6
|
-
var ENV_PUBLIC_URL = "NEXT_PUBLIC_SYLPHX_URL";
|
|
7
|
-
var ENV_SECRET_URL = "SYLPHX_SECRET_URL";
|
|
8
|
-
var SDK_API_PATH = `/v1`;
|
|
9
|
-
var DEFAULT_SDK_API_HOST = "api.sylphx.com";
|
|
10
|
-
function detectSdkPlatform() {
|
|
11
|
-
if (typeof window !== "undefined") return "browser";
|
|
12
|
-
const runtimeGlobal = globalThis;
|
|
13
|
-
if (typeof runtimeGlobal.EdgeRuntime !== "undefined") return "edge";
|
|
14
|
-
return "node";
|
|
15
|
-
}
|
|
16
|
-
var SDK_PLATFORM = detectSdkPlatform();
|
|
17
|
-
var TOKEN_EXPIRY_BUFFER_MS = 3e4;
|
|
18
|
-
var SESSION_TOKEN_LIFETIME_SECONDS = 5 * 60;
|
|
19
|
-
var SESSION_TOKEN_LIFETIME_MS = SESSION_TOKEN_LIFETIME_SECONDS * 1e3;
|
|
20
|
-
var REFRESH_TOKEN_LIFETIME_SECONDS = 30 * 24 * 60 * 60;
|
|
21
|
-
var FLAGS_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
22
|
-
var FLAGS_STALE_WHILE_REVALIDATE_MS = 60 * 1e3;
|
|
23
|
-
var ANALYTICS_SESSION_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
24
|
-
var WEBHOOK_MAX_AGE_MS = 5 * 60 * 1e3;
|
|
25
|
-
var WEBHOOK_CLOCK_SKEW_MS = 30 * 1e3;
|
|
26
|
-
var PKCE_CODE_TTL_MS = 10 * 60 * 1e3;
|
|
27
|
-
var JOBS_DLQ_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
28
|
-
var SESSION_REPLAY_MAX_DURATION_MS = 60 * 60 * 1e3;
|
|
29
|
-
var FLAGS_EXPOSURE_DEDUPE_WINDOW_MS = 60 * 60 * 1e3;
|
|
30
|
-
var CLICK_ID_EXPIRY_MS = 90 * 24 * 60 * 60 * 1e3;
|
|
31
|
-
var STALE_TIME_FREQUENT_MS = 60 * 1e3;
|
|
32
|
-
var STALE_TIME_MODERATE_MS = 2 * 60 * 1e3;
|
|
33
|
-
var STALE_TIME_STABLE_MS = 5 * 60 * 1e3;
|
|
34
|
-
var STALE_TIME_STATS_MS = 30 * 1e3;
|
|
35
|
-
var NEW_USER_THRESHOLD_MS = 60 * 1e3;
|
|
36
|
-
var STORAGE_MULTIPART_THRESHOLD_BYTES = 5 * 1024 * 1024;
|
|
37
|
-
var STORAGE_DEFAULT_MAX_SIZE_BYTES = 5 * 1024 * 1024;
|
|
38
|
-
var STORAGE_AVATAR_MAX_SIZE_BYTES = 2 * 1024 * 1024;
|
|
39
|
-
var STORAGE_LARGE_MAX_SIZE_BYTES = 10 * 1024 * 1024;
|
|
40
|
-
var JWK_CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
41
|
-
var ETAG_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
42
|
-
|
|
43
4
|
// src/key-validation.ts
|
|
44
5
|
var PUBLIC_KEY_PATTERN = /^pk_(dev|stg|prod|prev)(?:_[a-z0-9]{12})?_[a-f0-9]{32}$/;
|
|
45
6
|
var APP_ID_PATTERN = /^(app|pk)_(dev|stg|prod|prev)_[a-z0-9_-]+$/;
|
|
@@ -196,6 +157,47 @@ function getCookieNamespace(secretKey) {
|
|
|
196
157
|
|
|
197
158
|
// src/nextjs/cookies.ts
|
|
198
159
|
import { cookies } from "next/headers";
|
|
160
|
+
|
|
161
|
+
// src/constants.ts
|
|
162
|
+
var ENV_URL = "SYLPHX_URL";
|
|
163
|
+
var ENV_PUBLIC_URL = "NEXT_PUBLIC_SYLPHX_URL";
|
|
164
|
+
var ENV_SECRET_URL = "SYLPHX_SECRET_URL";
|
|
165
|
+
var SDK_API_PATH = `/v1`;
|
|
166
|
+
var DEFAULT_SDK_API_HOST = "api.sylphx.com";
|
|
167
|
+
function detectSdkPlatform() {
|
|
168
|
+
if (typeof window !== "undefined") return "browser";
|
|
169
|
+
const runtimeGlobal = globalThis;
|
|
170
|
+
if (typeof runtimeGlobal.EdgeRuntime !== "undefined") return "edge";
|
|
171
|
+
return "node";
|
|
172
|
+
}
|
|
173
|
+
var SDK_PLATFORM = detectSdkPlatform();
|
|
174
|
+
var TOKEN_EXPIRY_BUFFER_MS = 3e4;
|
|
175
|
+
var SESSION_TOKEN_LIFETIME_SECONDS = 5 * 60;
|
|
176
|
+
var SESSION_TOKEN_LIFETIME_MS = SESSION_TOKEN_LIFETIME_SECONDS * 1e3;
|
|
177
|
+
var REFRESH_TOKEN_LIFETIME_SECONDS = 30 * 24 * 60 * 60;
|
|
178
|
+
var FLAGS_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
179
|
+
var FLAGS_STALE_WHILE_REVALIDATE_MS = 60 * 1e3;
|
|
180
|
+
var ANALYTICS_SESSION_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
181
|
+
var WEBHOOK_MAX_AGE_MS = 5 * 60 * 1e3;
|
|
182
|
+
var WEBHOOK_CLOCK_SKEW_MS = 30 * 1e3;
|
|
183
|
+
var PKCE_CODE_TTL_MS = 10 * 60 * 1e3;
|
|
184
|
+
var JOBS_DLQ_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
185
|
+
var SESSION_REPLAY_MAX_DURATION_MS = 60 * 60 * 1e3;
|
|
186
|
+
var FLAGS_EXPOSURE_DEDUPE_WINDOW_MS = 60 * 60 * 1e3;
|
|
187
|
+
var CLICK_ID_EXPIRY_MS = 90 * 24 * 60 * 60 * 1e3;
|
|
188
|
+
var STALE_TIME_FREQUENT_MS = 60 * 1e3;
|
|
189
|
+
var STALE_TIME_MODERATE_MS = 2 * 60 * 1e3;
|
|
190
|
+
var STALE_TIME_STABLE_MS = 5 * 60 * 1e3;
|
|
191
|
+
var STALE_TIME_STATS_MS = 30 * 1e3;
|
|
192
|
+
var NEW_USER_THRESHOLD_MS = 60 * 1e3;
|
|
193
|
+
var STORAGE_MULTIPART_THRESHOLD_BYTES = 5 * 1024 * 1024;
|
|
194
|
+
var STORAGE_DEFAULT_MAX_SIZE_BYTES = 5 * 1024 * 1024;
|
|
195
|
+
var STORAGE_AVATAR_MAX_SIZE_BYTES = 2 * 1024 * 1024;
|
|
196
|
+
var STORAGE_LARGE_MAX_SIZE_BYTES = 10 * 1024 * 1024;
|
|
197
|
+
var JWK_CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
198
|
+
var ETAG_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
199
|
+
|
|
200
|
+
// src/nextjs/cookies.ts
|
|
199
201
|
function getCookieNames(namespace) {
|
|
200
202
|
return {
|
|
201
203
|
/** HttpOnly JWT access token (5 min) */
|
|
@@ -346,6 +348,163 @@ function parseUserCookie(value) {
|
|
|
346
348
|
}
|
|
347
349
|
}
|
|
348
350
|
|
|
351
|
+
// src/nextjs/middleware-helpers.ts
|
|
352
|
+
var OAUTH_PKCE_TTL_SECONDS = 10 * 60;
|
|
353
|
+
var OAUTH_PKCE_TTL_MS = OAUTH_PKCE_TTL_SECONDS * 1e3;
|
|
354
|
+
var DEFAULT_BAAS_PREFIX = "/sylphx";
|
|
355
|
+
var BAAS_PROXY_AUTH_REQUIRED_PATHS = ["/challenge/", "/security/"];
|
|
356
|
+
var BAAS_PROXY_PUBLIC_PATHS = ["/app"];
|
|
357
|
+
var BODYLESS_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD"]);
|
|
358
|
+
var RESPONSE_HEADER_ALLOWLIST = [
|
|
359
|
+
"cache-control",
|
|
360
|
+
"content-language",
|
|
361
|
+
"content-type",
|
|
362
|
+
"etag",
|
|
363
|
+
"expires",
|
|
364
|
+
"last-modified",
|
|
365
|
+
"retry-after"
|
|
366
|
+
];
|
|
367
|
+
function isTokenResponse(data) {
|
|
368
|
+
return typeof data === "object" && data !== null && "accessToken" in data && "refreshToken" in data && "user" in data && typeof data.accessToken === "string" && typeof data.refreshToken === "string";
|
|
369
|
+
}
|
|
370
|
+
function decodeJwtPayload(token) {
|
|
371
|
+
try {
|
|
372
|
+
const parts = token.split(".");
|
|
373
|
+
if (parts.length !== 3) return null;
|
|
374
|
+
const payload = parts[1];
|
|
375
|
+
const base64 = payload.replace(/-/g, "+").replace(/_/g, "/");
|
|
376
|
+
const padded = base64.padEnd(base64.length + (4 - base64.length % 4) % 4, "=");
|
|
377
|
+
const jsonPayload = atob(padded);
|
|
378
|
+
return JSON.parse(jsonPayload);
|
|
379
|
+
} catch {
|
|
380
|
+
return null;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
function isTokenExpired(token) {
|
|
384
|
+
const payload = decodeJwtPayload(token);
|
|
385
|
+
if (!payload?.exp) return true;
|
|
386
|
+
return payload.exp * 1e3 < Date.now() + TOKEN_EXPIRY_BUFFER_MS;
|
|
387
|
+
}
|
|
388
|
+
function matchesPattern(pathname, pattern) {
|
|
389
|
+
if (pattern === pathname) return true;
|
|
390
|
+
if (pattern.endsWith("/*")) {
|
|
391
|
+
const base = pattern.slice(0, -2);
|
|
392
|
+
return pathname === base || pathname.startsWith(`${base}/`);
|
|
393
|
+
}
|
|
394
|
+
if (pattern.endsWith("/**")) {
|
|
395
|
+
const base = pattern.slice(0, -3);
|
|
396
|
+
return pathname === base || pathname.startsWith(`${base}/`);
|
|
397
|
+
}
|
|
398
|
+
return false;
|
|
399
|
+
}
|
|
400
|
+
function matchesAny(pathname, patterns) {
|
|
401
|
+
return patterns.some((p) => matchesPattern(pathname, p));
|
|
402
|
+
}
|
|
403
|
+
function normalizeRoutePrefix(prefix) {
|
|
404
|
+
const normalized = prefix.trim().replace(/\/+$/u, "");
|
|
405
|
+
return normalized.startsWith("/") ? normalized : `/${normalized}`;
|
|
406
|
+
}
|
|
407
|
+
function stripRoutePrefix(pathname, prefix) {
|
|
408
|
+
if (pathname === prefix) return "/";
|
|
409
|
+
if (!pathname.startsWith(`${prefix}/`)) return null;
|
|
410
|
+
return pathname.slice(prefix.length);
|
|
411
|
+
}
|
|
412
|
+
function isPublicBaasProxyPath(pathname) {
|
|
413
|
+
return BAAS_PROXY_PUBLIC_PATHS.some((path) => pathname === path);
|
|
414
|
+
}
|
|
415
|
+
function isAuthenticatedBaasProxyPath(pathname) {
|
|
416
|
+
return BAAS_PROXY_AUTH_REQUIRED_PATHS.some((prefix) => pathname.startsWith(prefix));
|
|
417
|
+
}
|
|
418
|
+
function isAllowedBaasProxyPath(pathname) {
|
|
419
|
+
return isPublicBaasProxyPath(pathname) || isAuthenticatedBaasProxyPath(pathname);
|
|
420
|
+
}
|
|
421
|
+
function copyRequestHeader(source, target, name) {
|
|
422
|
+
const value = source.get(name);
|
|
423
|
+
if (value) target.set(name, value);
|
|
424
|
+
}
|
|
425
|
+
function buildUpstreamProxyHeaders(request, ctx, sessionToken) {
|
|
426
|
+
const headers = new Headers();
|
|
427
|
+
copyRequestHeader(request.headers, headers, "accept");
|
|
428
|
+
copyRequestHeader(request.headers, headers, "content-type");
|
|
429
|
+
copyRequestHeader(request.headers, headers, "user-agent");
|
|
430
|
+
copyRequestHeader(request.headers, headers, "x-correlation-id");
|
|
431
|
+
headers.set("x-app-secret", ctx.secretKey);
|
|
432
|
+
if (sessionToken) headers.set("authorization", `Bearer ${sessionToken}`);
|
|
433
|
+
return headers;
|
|
434
|
+
}
|
|
435
|
+
function copyResponseHeaders(source) {
|
|
436
|
+
const headers = new Headers();
|
|
437
|
+
for (const name of RESPONSE_HEADER_ALLOWLIST) {
|
|
438
|
+
const value = source.get(name);
|
|
439
|
+
if (value) headers.set(name, value);
|
|
440
|
+
}
|
|
441
|
+
return headers;
|
|
442
|
+
}
|
|
443
|
+
function requestPathWithSearch(request) {
|
|
444
|
+
const { pathname, search } = request.nextUrl;
|
|
445
|
+
return `${pathname}${search}`;
|
|
446
|
+
}
|
|
447
|
+
function isSafeRelativeRedirectPath(value) {
|
|
448
|
+
return typeof value === "string" && value.startsWith("/") && !value.startsWith("//");
|
|
449
|
+
}
|
|
450
|
+
function resolveSafeRelativeRedirectPath(value, fallback) {
|
|
451
|
+
if (isSafeRelativeRedirectPath(value)) return value;
|
|
452
|
+
if (isSafeRelativeRedirectPath(fallback)) return fallback;
|
|
453
|
+
return "/";
|
|
454
|
+
}
|
|
455
|
+
function resolveSameOriginUrl(request, value, fallbackPath) {
|
|
456
|
+
if (!value) return new URL(fallbackPath, request.url).toString();
|
|
457
|
+
if (isSafeRelativeRedirectPath(value)) {
|
|
458
|
+
const url = new URL(value, request.url);
|
|
459
|
+
if (url.hash) return null;
|
|
460
|
+
return url.toString();
|
|
461
|
+
}
|
|
462
|
+
try {
|
|
463
|
+
const url = new URL(value);
|
|
464
|
+
if (url.origin !== new URL(request.url).origin) return null;
|
|
465
|
+
if (url.hash) return null;
|
|
466
|
+
return url.toString();
|
|
467
|
+
} catch {
|
|
468
|
+
return null;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
function normalizeAppUrl(value) {
|
|
472
|
+
if (!value?.trim()) return null;
|
|
473
|
+
try {
|
|
474
|
+
const url = new URL(value);
|
|
475
|
+
return url.origin;
|
|
476
|
+
} catch {
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
function resolveOAuthCallbackUrl(request, ctx) {
|
|
481
|
+
const origin = ctx.config.appUrl ?? new URL(request.url).origin;
|
|
482
|
+
return new URL(`${ctx.config.authPrefix}/callback`, origin);
|
|
483
|
+
}
|
|
484
|
+
function bytesToBase64Url(bytes) {
|
|
485
|
+
let binary = "";
|
|
486
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
487
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/u, "");
|
|
488
|
+
}
|
|
489
|
+
function randomBase64Url(byteLength) {
|
|
490
|
+
const bytes = new Uint8Array(byteLength);
|
|
491
|
+
crypto.getRandomValues(bytes);
|
|
492
|
+
return bytesToBase64Url(bytes);
|
|
493
|
+
}
|
|
494
|
+
async function sha256Base64Url(value) {
|
|
495
|
+
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(value));
|
|
496
|
+
return bytesToBase64Url(new Uint8Array(digest));
|
|
497
|
+
}
|
|
498
|
+
async function parseJsonObject(request) {
|
|
499
|
+
try {
|
|
500
|
+
const body = await request.json();
|
|
501
|
+
if (typeof body === "object" && body !== null && !Array.isArray(body)) return body;
|
|
502
|
+
return null;
|
|
503
|
+
} catch {
|
|
504
|
+
return null;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
349
508
|
// src/connection-url.ts
|
|
350
509
|
var SYLPHX_PROTOCOL = "sylphx:";
|
|
351
510
|
var DEFAULT_VERSION = "v1";
|
|
@@ -502,161 +661,6 @@ function resolveNextjsPublishableKey(options) {
|
|
|
502
661
|
}
|
|
503
662
|
|
|
504
663
|
// src/nextjs/middleware.ts
|
|
505
|
-
var OAUTH_PKCE_TTL_SECONDS = 10 * 60;
|
|
506
|
-
var OAUTH_PKCE_TTL_MS = OAUTH_PKCE_TTL_SECONDS * 1e3;
|
|
507
|
-
var DEFAULT_BAAS_PREFIX = "/sylphx";
|
|
508
|
-
var BAAS_PROXY_AUTH_REQUIRED_PATHS = ["/challenge/", "/security/"];
|
|
509
|
-
var BAAS_PROXY_PUBLIC_PATHS = ["/app"];
|
|
510
|
-
var BODYLESS_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD"]);
|
|
511
|
-
var RESPONSE_HEADER_ALLOWLIST = [
|
|
512
|
-
"cache-control",
|
|
513
|
-
"content-language",
|
|
514
|
-
"content-type",
|
|
515
|
-
"etag",
|
|
516
|
-
"expires",
|
|
517
|
-
"last-modified",
|
|
518
|
-
"retry-after"
|
|
519
|
-
];
|
|
520
|
-
function isTokenResponse(data) {
|
|
521
|
-
return typeof data === "object" && data !== null && "accessToken" in data && "refreshToken" in data && "user" in data && typeof data.accessToken === "string" && typeof data.refreshToken === "string";
|
|
522
|
-
}
|
|
523
|
-
function decodeJwtPayload(token) {
|
|
524
|
-
try {
|
|
525
|
-
const parts = token.split(".");
|
|
526
|
-
if (parts.length !== 3) return null;
|
|
527
|
-
const payload = parts[1];
|
|
528
|
-
const base64 = payload.replace(/-/g, "+").replace(/_/g, "/");
|
|
529
|
-
const padded = base64.padEnd(base64.length + (4 - base64.length % 4) % 4, "=");
|
|
530
|
-
const jsonPayload = atob(padded);
|
|
531
|
-
return JSON.parse(jsonPayload);
|
|
532
|
-
} catch {
|
|
533
|
-
return null;
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
function isTokenExpired(token) {
|
|
537
|
-
const payload = decodeJwtPayload(token);
|
|
538
|
-
if (!payload?.exp) return true;
|
|
539
|
-
return payload.exp * 1e3 < Date.now() + TOKEN_EXPIRY_BUFFER_MS;
|
|
540
|
-
}
|
|
541
|
-
function matchesPattern(pathname, pattern) {
|
|
542
|
-
if (pattern === pathname) return true;
|
|
543
|
-
if (pattern.endsWith("/*")) {
|
|
544
|
-
const base = pattern.slice(0, -2);
|
|
545
|
-
return pathname === base || pathname.startsWith(`${base}/`);
|
|
546
|
-
}
|
|
547
|
-
if (pattern.endsWith("/**")) {
|
|
548
|
-
const base = pattern.slice(0, -3);
|
|
549
|
-
return pathname === base || pathname.startsWith(`${base}/`);
|
|
550
|
-
}
|
|
551
|
-
return false;
|
|
552
|
-
}
|
|
553
|
-
function matchesAny(pathname, patterns) {
|
|
554
|
-
return patterns.some((p) => matchesPattern(pathname, p));
|
|
555
|
-
}
|
|
556
|
-
function normalizeRoutePrefix(prefix) {
|
|
557
|
-
const normalized = prefix.trim().replace(/\/+$/u, "");
|
|
558
|
-
return normalized.startsWith("/") ? normalized : `/${normalized}`;
|
|
559
|
-
}
|
|
560
|
-
function stripRoutePrefix(pathname, prefix) {
|
|
561
|
-
if (pathname === prefix) return "/";
|
|
562
|
-
if (!pathname.startsWith(`${prefix}/`)) return null;
|
|
563
|
-
return pathname.slice(prefix.length);
|
|
564
|
-
}
|
|
565
|
-
function isPublicBaasProxyPath(pathname) {
|
|
566
|
-
return BAAS_PROXY_PUBLIC_PATHS.some((path) => pathname === path);
|
|
567
|
-
}
|
|
568
|
-
function isAuthenticatedBaasProxyPath(pathname) {
|
|
569
|
-
return BAAS_PROXY_AUTH_REQUIRED_PATHS.some((prefix) => pathname.startsWith(prefix));
|
|
570
|
-
}
|
|
571
|
-
function isAllowedBaasProxyPath(pathname) {
|
|
572
|
-
return isPublicBaasProxyPath(pathname) || isAuthenticatedBaasProxyPath(pathname);
|
|
573
|
-
}
|
|
574
|
-
function copyRequestHeader(source, target, name) {
|
|
575
|
-
const value = source.get(name);
|
|
576
|
-
if (value) target.set(name, value);
|
|
577
|
-
}
|
|
578
|
-
function buildUpstreamProxyHeaders(request, ctx, sessionToken) {
|
|
579
|
-
const headers = new Headers();
|
|
580
|
-
copyRequestHeader(request.headers, headers, "accept");
|
|
581
|
-
copyRequestHeader(request.headers, headers, "content-type");
|
|
582
|
-
copyRequestHeader(request.headers, headers, "user-agent");
|
|
583
|
-
copyRequestHeader(request.headers, headers, "x-correlation-id");
|
|
584
|
-
headers.set("x-app-secret", ctx.secretKey);
|
|
585
|
-
if (sessionToken) headers.set("authorization", `Bearer ${sessionToken}`);
|
|
586
|
-
return headers;
|
|
587
|
-
}
|
|
588
|
-
function copyResponseHeaders(source) {
|
|
589
|
-
const headers = new Headers();
|
|
590
|
-
for (const name of RESPONSE_HEADER_ALLOWLIST) {
|
|
591
|
-
const value = source.get(name);
|
|
592
|
-
if (value) headers.set(name, value);
|
|
593
|
-
}
|
|
594
|
-
return headers;
|
|
595
|
-
}
|
|
596
|
-
function requestPathWithSearch(request) {
|
|
597
|
-
const { pathname, search } = request.nextUrl;
|
|
598
|
-
return `${pathname}${search}`;
|
|
599
|
-
}
|
|
600
|
-
function isSafeRelativeRedirectPath(value) {
|
|
601
|
-
return typeof value === "string" && value.startsWith("/") && !value.startsWith("//");
|
|
602
|
-
}
|
|
603
|
-
function resolveSafeRelativeRedirectPath(value, fallback) {
|
|
604
|
-
if (isSafeRelativeRedirectPath(value)) return value;
|
|
605
|
-
if (isSafeRelativeRedirectPath(fallback)) return fallback;
|
|
606
|
-
return "/";
|
|
607
|
-
}
|
|
608
|
-
function resolveSameOriginUrl(request, value, fallbackPath) {
|
|
609
|
-
if (!value) return new URL(fallbackPath, request.url).toString();
|
|
610
|
-
if (isSafeRelativeRedirectPath(value)) {
|
|
611
|
-
const url = new URL(value, request.url);
|
|
612
|
-
if (url.hash) return null;
|
|
613
|
-
return url.toString();
|
|
614
|
-
}
|
|
615
|
-
try {
|
|
616
|
-
const url = new URL(value);
|
|
617
|
-
if (url.origin !== new URL(request.url).origin) return null;
|
|
618
|
-
if (url.hash) return null;
|
|
619
|
-
return url.toString();
|
|
620
|
-
} catch {
|
|
621
|
-
return null;
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
function normalizeAppUrl(value) {
|
|
625
|
-
if (!value?.trim()) return null;
|
|
626
|
-
try {
|
|
627
|
-
const url = new URL(value);
|
|
628
|
-
return url.origin;
|
|
629
|
-
} catch {
|
|
630
|
-
return null;
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
function resolveOAuthCallbackUrl(request, ctx) {
|
|
634
|
-
const origin = ctx.config.appUrl ?? new URL(request.url).origin;
|
|
635
|
-
return new URL(`${ctx.config.authPrefix}/callback`, origin);
|
|
636
|
-
}
|
|
637
|
-
function bytesToBase64Url(bytes) {
|
|
638
|
-
let binary = "";
|
|
639
|
-
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
640
|
-
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/u, "");
|
|
641
|
-
}
|
|
642
|
-
function randomBase64Url(byteLength) {
|
|
643
|
-
const bytes = new Uint8Array(byteLength);
|
|
644
|
-
crypto.getRandomValues(bytes);
|
|
645
|
-
return bytesToBase64Url(bytes);
|
|
646
|
-
}
|
|
647
|
-
async function sha256Base64Url(value) {
|
|
648
|
-
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(value));
|
|
649
|
-
return bytesToBase64Url(new Uint8Array(digest));
|
|
650
|
-
}
|
|
651
|
-
async function parseJsonObject(request) {
|
|
652
|
-
try {
|
|
653
|
-
const body = await request.json();
|
|
654
|
-
if (typeof body === "object" && body !== null && !Array.isArray(body)) return body;
|
|
655
|
-
return null;
|
|
656
|
-
} catch {
|
|
657
|
-
return null;
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
664
|
function getOAuthPkceCookieName(ctx) {
|
|
661
665
|
return `__${ctx.namespace}_oauth_pkce`;
|
|
662
666
|
}
|