@scalemule/nextjs 0.1.38 → 0.1.40

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.
@@ -12,6 +12,9 @@ var ScaleMuleApiError = class extends Error {
12
12
  this.code = error.code;
13
13
  this.field = error.field;
14
14
  this.status = status;
15
+ this.requestId = error.requestId;
16
+ this.traceId = error.traceId;
17
+ this.problem = error.problem;
15
18
  }
16
19
  };
17
20
 
@@ -29,40 +32,40 @@ function validateIP(ip) {
29
32
  return void 0;
30
33
  }
31
34
  function extractClientContext(request) {
32
- const headers = request.headers;
35
+ const headers2 = request.headers;
33
36
  let ip;
34
- const cfConnectingIp = headers.get("cf-connecting-ip");
37
+ const cfConnectingIp = headers2.get("cf-connecting-ip");
35
38
  if (cfConnectingIp) {
36
39
  ip = validateIP(cfConnectingIp);
37
40
  }
38
41
  if (!ip) {
39
- const doConnectingIp = headers.get("do-connecting-ip");
42
+ const doConnectingIp = headers2.get("do-connecting-ip");
40
43
  if (doConnectingIp) {
41
44
  ip = validateIP(doConnectingIp);
42
45
  }
43
46
  }
44
47
  if (!ip) {
45
- const realIp = headers.get("x-real-ip");
48
+ const realIp = headers2.get("x-real-ip");
46
49
  if (realIp) {
47
50
  ip = validateIP(realIp);
48
51
  }
49
52
  }
50
53
  if (!ip) {
51
- const forwardedFor = headers.get("x-forwarded-for");
54
+ const forwardedFor = headers2.get("x-forwarded-for");
52
55
  if (forwardedFor) {
53
56
  const firstIp = forwardedFor.split(",")[0]?.trim();
54
57
  ip = validateIP(firstIp);
55
58
  }
56
59
  }
57
60
  if (!ip) {
58
- const vercelForwarded = headers.get("x-vercel-forwarded-for");
61
+ const vercelForwarded = headers2.get("x-vercel-forwarded-for");
59
62
  if (vercelForwarded) {
60
63
  const firstIp = vercelForwarded.split(",")[0]?.trim();
61
64
  ip = validateIP(firstIp);
62
65
  }
63
66
  }
64
67
  if (!ip) {
65
- const trueClientIp = headers.get("true-client-ip");
68
+ const trueClientIp = headers2.get("true-client-ip");
66
69
  if (trueClientIp) {
67
70
  ip = validateIP(trueClientIp);
68
71
  }
@@ -70,10 +73,10 @@ function extractClientContext(request) {
70
73
  if (!ip && request.ip) {
71
74
  ip = validateIP(request.ip);
72
75
  }
73
- const userAgent = headers.get("user-agent") || void 0;
74
- const deviceFingerprint = headers.get("x-device-fingerprint") || void 0;
75
- const referrer = headers.get("referer") || void 0;
76
- const anonymousId = headers.get("x-anonymous-id") || void 0;
76
+ const userAgent = headers2.get("user-agent") || void 0;
77
+ const deviceFingerprint = headers2.get("x-device-fingerprint") || void 0;
78
+ const referrer = headers2.get("referer") || void 0;
79
+ const anonymousId = headers2.get("x-anonymous-id") || void 0;
77
80
  return {
78
81
  ip,
79
82
  userAgent,
@@ -83,27 +86,46 @@ function extractClientContext(request) {
83
86
  };
84
87
  }
85
88
  function buildClientContextHeaders(context) {
86
- const headers = {};
89
+ const headers2 = {};
87
90
  if (!context) {
88
- return headers;
91
+ return headers2;
89
92
  }
90
93
  if (context.ip) {
91
- headers["x-sm-forwarded-client-ip"] = context.ip;
92
- headers["X-Client-IP"] = context.ip;
94
+ headers2["x-sm-forwarded-client-ip"] = context.ip;
95
+ headers2["X-Client-IP"] = context.ip;
93
96
  }
94
97
  if (context.userAgent) {
95
- headers["X-Client-User-Agent"] = context.userAgent;
98
+ headers2["X-Client-User-Agent"] = context.userAgent;
96
99
  }
97
100
  if (context.deviceFingerprint) {
98
- headers["X-Client-Device-Fingerprint"] = context.deviceFingerprint;
101
+ headers2["X-Client-Device-Fingerprint"] = context.deviceFingerprint;
99
102
  }
100
103
  if (context.referrer) {
101
- headers["X-Client-Referrer"] = context.referrer;
104
+ headers2["X-Client-Referrer"] = context.referrer;
102
105
  }
103
106
  if (context.anonymousId) {
104
- headers["x-anonymous-id"] = context.anonymousId;
107
+ headers2["x-anonymous-id"] = context.anonymousId;
105
108
  }
106
- return headers;
109
+ return headers2;
110
+ }
111
+
112
+ // src/error-context.ts
113
+ function withErrorContext(error, responseData, headers2) {
114
+ const enriched = { ...error };
115
+ const meta = responseData?.meta;
116
+ const requestId = meta?.request_id ?? headers2?.get("x-request-id") ?? void 0;
117
+ if (requestId !== void 0 && enriched.requestId === void 0) {
118
+ enriched.requestId = requestId;
119
+ }
120
+ const traceId = meta?.trace_id;
121
+ if (traceId !== void 0 && enriched.traceId === void 0) {
122
+ enriched.traceId = traceId;
123
+ }
124
+ const rawError = responseData?.error;
125
+ if (rawError !== null && typeof rawError === "object" && enriched.problem === void 0) {
126
+ enriched.problem = rawError;
127
+ }
128
+ return enriched;
107
129
  }
108
130
 
109
131
  // src/server/client.ts
@@ -114,6 +136,7 @@ var GATEWAY_URLS = {
114
136
  function resolveGatewayUrl(config) {
115
137
  if (config.gatewayUrl) return config.gatewayUrl;
116
138
  if (process.env.SCALEMULE_API_URL) return process.env.SCALEMULE_API_URL;
139
+ if (process.env.SCALEMULE_GATEWAY_URL) return process.env.SCALEMULE_GATEWAY_URL;
117
140
  return GATEWAY_URLS[config.environment || "prod"];
118
141
  }
119
142
  var ScaleMuleServer = class {
@@ -502,7 +525,7 @@ var ScaleMuleServer = class {
502
525
  formData.append("file", blob, file.filename);
503
526
  formData.append("sm_user_id", userId);
504
527
  const url = `${this.gatewayUrl}/v1/storage/upload`;
505
- const headers = {
528
+ const headers2 = {
506
529
  "x-api-key": this.apiKey,
507
530
  "x-user-id": userId,
508
531
  ...buildClientContextHeaders(options?.clientContext)
@@ -513,7 +536,7 @@ var ScaleMuleServer = class {
513
536
  try {
514
537
  const response = await fetch(url, {
515
538
  method: "POST",
516
- headers,
539
+ headers: headers2,
517
540
  body: formData
518
541
  });
519
542
  const text = await response.text();
@@ -524,7 +547,11 @@ var ScaleMuleServer = class {
524
547
  }
525
548
  if (!response.ok) {
526
549
  throw new ScaleMuleApiError(
527
- responseData?.error || { code: "UPLOAD_FAILED", message: text || "Upload failed" }
550
+ withErrorContext(
551
+ responseData?.error || { code: "UPLOAD_FAILED", message: text || "Upload failed" },
552
+ responseData,
553
+ response.headers
554
+ )
528
555
  );
529
556
  }
530
557
  const data = responseData?.data !== void 0 ? responseData.data : responseData;
@@ -735,14 +762,14 @@ var ScaleMuleServer = class {
735
762
  */
736
763
  async request(method, path, options = {}) {
737
764
  const url = `${this.gatewayUrl}${path}`;
738
- const headers = {
765
+ const headers2 = {
739
766
  "x-api-key": this.apiKey,
740
767
  "Content-Type": "application/json",
741
768
  // Forward client context headers if provided
742
769
  ...buildClientContextHeaders(options.clientContext)
743
770
  };
744
771
  if (options.sessionToken) {
745
- headers["Authorization"] = `Bearer ${options.sessionToken}`;
772
+ headers2["Authorization"] = `Bearer ${options.sessionToken}`;
746
773
  }
747
774
  if (this.debug) {
748
775
  console.log(`[ScaleMule Server] ${method} ${path}`);
@@ -753,7 +780,7 @@ var ScaleMuleServer = class {
753
780
  try {
754
781
  const response = await fetch(url, {
755
782
  method,
756
- headers,
783
+ headers: headers2,
757
784
  body: options.body ? JSON.stringify(options.body) : void 0
758
785
  });
759
786
  const rotated = response.headers.get("x-rotated-session-token");
@@ -767,10 +794,11 @@ var ScaleMuleServer = class {
767
794
  } catch {
768
795
  }
769
796
  if (!response.ok) {
770
- const error = responseData?.error || {
797
+ const baseError = responseData?.error || {
771
798
  code: `HTTP_${response.status}`,
772
799
  message: responseData?.message || text || response.statusText
773
800
  };
801
+ const error = withErrorContext(baseError, responseData, response.headers);
774
802
  if (response.status === 401 && options.sessionToken && !options.isAutoRefresh) {
775
803
  if (this.debug) console.log("[ScaleMule Server] 401 received, attempting auto-refresh...");
776
804
  try {
@@ -849,66 +877,86 @@ function createCookieHeader(name, value, options = {}) {
849
877
  }
850
878
  function createClearCookieHeader(name, options = {}) {
851
879
  const path = options.path ?? "/";
852
- let cookie = `${name}=; Path=${path}; Max-Age=0; HttpOnly`;
880
+ const secure = options.secure ?? process.env.NODE_ENV === "production";
881
+ const sameSite = options.sameSite ?? "lax";
882
+ let cookie = `${name}=; Path=${path}; Max-Age=0; HttpOnly; SameSite=${sameSite}`;
883
+ if (secure) {
884
+ cookie += "; Secure";
885
+ }
853
886
  if (options.domain) {
854
887
  cookie += `; Domain=${options.domain}`;
855
888
  }
856
889
  return cookie;
857
890
  }
858
891
  function withSession(loginResponse, responseBody, options = {}) {
859
- const headers = new Headers();
860
- headers.set("Content-Type", "application/json");
861
- headers.append(
892
+ const headers2 = new Headers();
893
+ headers2.set("Content-Type", "application/json");
894
+ headers2.append(
862
895
  "Set-Cookie",
863
896
  createCookieHeader(SESSION_COOKIE_NAME, loginResponse.session_token, options)
864
897
  );
865
- headers.append(
898
+ headers2.append(
866
899
  "Set-Cookie",
867
900
  createCookieHeader(USER_ID_COOKIE_NAME, loginResponse.user.id, options)
868
901
  );
869
902
  return new Response(JSON.stringify({ success: true, data: responseBody }), {
870
903
  status: 200,
871
- headers
904
+ headers: headers2
872
905
  });
873
906
  }
874
907
  function withRefreshedSession(sessionToken, userId, responseBody, options = {}) {
875
- const headers = new Headers();
876
- headers.set("Content-Type", "application/json");
877
- headers.append(
908
+ const headers2 = new Headers();
909
+ headers2.set("Content-Type", "application/json");
910
+ headers2.append(
878
911
  "Set-Cookie",
879
912
  createCookieHeader(SESSION_COOKIE_NAME, sessionToken, options)
880
913
  );
881
- headers.append(
914
+ headers2.append(
882
915
  "Set-Cookie",
883
916
  createCookieHeader(USER_ID_COOKIE_NAME, userId, options)
884
917
  );
885
918
  return new Response(JSON.stringify({ success: true, data: responseBody }), {
886
919
  status: 200,
887
- headers
920
+ headers: headers2
888
921
  });
889
922
  }
890
923
  function clearSession(responseBody, options = {}, status = 200) {
891
- const headers = new Headers();
892
- headers.set("Content-Type", "application/json");
893
- headers.append("Set-Cookie", createClearCookieHeader(SESSION_COOKIE_NAME, options));
894
- headers.append("Set-Cookie", createClearCookieHeader(USER_ID_COOKIE_NAME, options));
924
+ const headers2 = new Headers();
925
+ headers2.set("Content-Type", "application/json");
926
+ headers2.append("Set-Cookie", createClearCookieHeader(SESSION_COOKIE_NAME, options));
927
+ headers2.append("Set-Cookie", createClearCookieHeader(USER_ID_COOKIE_NAME, options));
895
928
  return new Response(JSON.stringify({ success: status < 300, data: responseBody }), {
896
929
  status,
897
- headers
930
+ headers: headers2
898
931
  });
899
932
  }
900
933
  async function getSession() {
901
934
  const cookieStore = await headers.cookies();
902
935
  const sessionCookie = cookieStore.get(SESSION_COOKIE_NAME);
903
936
  const userIdCookie = cookieStore.get(USER_ID_COOKIE_NAME);
904
- if (!sessionCookie?.value || !userIdCookie?.value) {
905
- return null;
937
+ if (sessionCookie?.value && userIdCookie?.value) {
938
+ return {
939
+ sessionToken: sessionCookie.value,
940
+ userId: userIdCookie.value,
941
+ expiresAt: /* @__PURE__ */ new Date()
942
+ // Note: actual expiry is managed by ScaleMule backend
943
+ };
906
944
  }
945
+ const headerStore = await headers.headers();
946
+ return sessionFromAuthHeaders(
947
+ headerStore.get("authorization"),
948
+ headerStore.get("x-sm-user-id")
949
+ );
950
+ }
951
+ function sessionFromAuthHeaders(authorization, userId) {
952
+ if (!authorization || !userId) return null;
953
+ const match = /^Bearer\s+(.+)$/i.exec(authorization.trim());
954
+ const token = match?.[1]?.trim();
955
+ if (!token) return null;
907
956
  return {
908
- sessionToken: sessionCookie.value,
909
- userId: userIdCookie.value,
957
+ sessionToken: token,
958
+ userId,
910
959
  expiresAt: /* @__PURE__ */ new Date()
911
- // Note: actual expiry is managed by ScaleMule backend
912
960
  };
913
961
  }
914
962
  var MAX_KNOWN_ACCOUNTS = 10;
@@ -950,7 +998,7 @@ function applyPrivacyToEntry(entry, privacy) {
950
998
  };
951
999
  }
952
1000
  }
953
- function appendKnownAccountCookie(headers, account, existingCookie, options = {}, privacy) {
1001
+ function appendKnownAccountCookie(headers2, account, existingCookie, options = {}, privacy) {
954
1002
  let accounts = {};
955
1003
  if (existingCookie) {
956
1004
  try {
@@ -979,9 +1027,9 @@ function appendKnownAccountCookie(headers, account, existingCookie, options = {}
979
1027
  if (options.domain) {
980
1028
  cookie += `; Domain=${options.domain}`;
981
1029
  }
982
- headers.append("Set-Cookie", cookie);
1030
+ headers2.append("Set-Cookie", cookie);
983
1031
  }
984
- function removeKnownAccountFromCookie(headers, userId, existingCookie, options = {}) {
1032
+ function removeKnownAccountFromCookie(headers2, userId, existingCookie, options = {}) {
985
1033
  let accounts = {};
986
1034
  if (existingCookie) {
987
1035
  try {
@@ -1001,15 +1049,15 @@ function removeKnownAccountFromCookie(headers, userId, existingCookie, options =
1001
1049
  if (options.domain) {
1002
1050
  cookie += `; Domain=${options.domain}`;
1003
1051
  }
1004
- headers.append("Set-Cookie", cookie);
1052
+ headers2.append("Set-Cookie", cookie);
1005
1053
  }
1006
- function clearKnownAccountsCookie(headers, options = {}) {
1054
+ function clearKnownAccountsCookie(headers2, options = {}) {
1007
1055
  const path = options.path ?? "/";
1008
1056
  let cookie = `${KNOWN_ACCOUNTS_COOKIE_NAME}=; Path=${path}; Max-Age=0`;
1009
1057
  if (options.domain) {
1010
1058
  cookie += `; Domain=${options.domain}`;
1011
1059
  }
1012
- headers.append("Set-Cookie", cookie);
1060
+ headers2.append("Set-Cookie", cookie);
1013
1061
  }
1014
1062
  function getKnownAccountsFromRequest(request) {
1015
1063
  const cookieHeader = request.headers.get("cookie");
@@ -1434,13 +1482,13 @@ function createAuthRoutes(config = {}) {
1434
1482
  if (!user_id) {
1435
1483
  return errorResponse("VALIDATION_ERROR", "user_id required", 400);
1436
1484
  }
1437
- const headers = new Headers();
1438
- headers.set("Content-Type", "application/json");
1485
+ const headers2 = new Headers();
1486
+ headers2.set("Content-Type", "application/json");
1439
1487
  const existingKnown = getKnownAccountsCookieRaw(request);
1440
- removeKnownAccountFromCookie(headers, user_id, existingKnown, cookieOptions);
1488
+ removeKnownAccountFromCookie(headers2, user_id, existingKnown, cookieOptions);
1441
1489
  return new Response(
1442
1490
  JSON.stringify({ success: true, data: { message: "Account forgotten" } }),
1443
- { status: 200, headers }
1491
+ { status: 200, headers: headers2 }
1444
1492
  );
1445
1493
  }
1446
1494
  // ==================== Forget All Accounts ====================
@@ -1448,12 +1496,12 @@ function createAuthRoutes(config = {}) {
1448
1496
  if (!config.enableAccountSwitcher) {
1449
1497
  return errorResponse("NOT_FOUND", "Account switcher not enabled", 404);
1450
1498
  }
1451
- const headers = new Headers();
1452
- headers.set("Content-Type", "application/json");
1453
- clearKnownAccountsCookie(headers, cookieOptions);
1499
+ const headers2 = new Headers();
1500
+ headers2.set("Content-Type", "application/json");
1501
+ clearKnownAccountsCookie(headers2, cookieOptions);
1454
1502
  return new Response(
1455
1503
  JSON.stringify({ success: true, data: { message: "All accounts forgotten" } }),
1456
- { status: 200, headers }
1504
+ { status: 200, headers: headers2 }
1457
1505
  );
1458
1506
  }
1459
1507
  default: