@medialane/sdk 0.129.0 → 0.129.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/README.md CHANGED
@@ -465,6 +465,40 @@ try {
465
465
  }
466
466
  ```
467
467
 
468
+ ### Showing an error to a user
469
+
470
+ Three types carry text written for someone to read. Everything else is machinery, and the
471
+ message belongs to whatever your screen was trying to do.
472
+
473
+ | Type | What it means | Show |
474
+ |---|---|---|
475
+ | `MedialaneApiError` | The API refused the request | `err.message`, which is the reason the API gave or a plain statement of the status |
476
+ | `UserFacingError` | The SDK got partway and stopped, for example a transaction that reverted | `err.message` |
477
+ | `PasskeyCancelledError` | The prompt was dismissed | Nothing was submitted, so avoid wording it as a failure |
478
+
479
+ ```typescript
480
+ import { MedialaneApiError, PasskeyCancelledError, UserFacingError } from "@medialane/sdk";
481
+
482
+ function describe(err: unknown, fallback: string): string {
483
+ if (err instanceof PasskeyCancelledError) return "Request not completed. Nothing was submitted.";
484
+ if (err instanceof UserFacingError) return err.message;
485
+ if (err instanceof MedialaneApiError) return err.message;
486
+ return fallback;
487
+ }
488
+
489
+ catch (err) {
490
+ console.error(err);
491
+ setError(describe(err, "We couldn't create that listing. Please try again."));
492
+ }
493
+ ```
494
+
495
+ The fallback matters. An error carrying none of those types says nothing a reader can act on,
496
+ and its text is as likely to be a gateway response as a sentence. Your call site knows which
497
+ action was attempted, so that is the message worth showing.
498
+
499
+ A response body that is not ours stays in `err.details`, where it is available while debugging
500
+ without reaching a reader.
501
+
468
502
  ---
469
503
 
470
504
  ## Configuration Reference
package/dist/index.cjs CHANGED
@@ -1469,8 +1469,6 @@ function createRateLimiter(windowMs, max) {
1469
1469
  }
1470
1470
  var TRUSTED_APP_IP_HEADER = "x-medialane-client-ip";
1471
1471
  function requestIp(req) {
1472
- const fromApp = req.headers.get(TRUSTED_APP_IP_HEADER)?.trim();
1473
- if (fromApp) return fromApp;
1474
1472
  const fromEdge = req.headers.get("x-vercel-forwarded-for")?.split(",")[0]?.trim();
1475
1473
  if (fromEdge) return fromEdge;
1476
1474
  const forwarded = req.headers.get("x-forwarded-for");
@@ -1859,6 +1857,30 @@ async function getBackendSignedUrl(config, kind = "image") {
1859
1857
  if (!res.ok || !data.data) throw new Error(data.error ?? "Failed to create upload URL");
1860
1858
  return data.data.url;
1861
1859
  }
1860
+
1861
+ // src/server/trusted-client-ip.ts
1862
+ var SPOOFABLE_FORWARDING_HEADERS = [
1863
+ "x-forwarded-for",
1864
+ "x-real-ip",
1865
+ "x-client-ip",
1866
+ "true-client-ip",
1867
+ "cf-connecting-ip",
1868
+ TRUSTED_APP_IP_HEADER
1869
+ ];
1870
+ function isSpoofableForwardingHeader(name) {
1871
+ return SPOOFABLE_FORWARDING_HEADERS.includes(name.toLowerCase());
1872
+ }
1873
+ function trustedClientIp(req) {
1874
+ const vercel = req.headers.get("x-vercel-forwarded-for")?.split(",")[0]?.trim();
1875
+ if (vercel) return vercel;
1876
+ const forwarded = req.headers.get("x-forwarded-for");
1877
+ if (forwarded) {
1878
+ const hops = forwarded.split(",").map((hop) => hop.trim()).filter(Boolean);
1879
+ const nearest = hops[hops.length - 1];
1880
+ if (nearest) return nearest;
1881
+ }
1882
+ return "unknown";
1883
+ }
1862
1884
  var IDENTITY_PREFIX = "siws_";
1863
1885
  var ACCOUNT_SESSION_PREFIX = "account_session_";
1864
1886
  var IDENTITY_DOMAIN = "siws-identity-v1";
@@ -2120,6 +2142,7 @@ exports.isPrivateHost = isPrivateHost;
2120
2142
  exports.isPrivateOrInsecureUrl = isPrivateOrInsecureUrl;
2121
2143
  exports.isSameOrigin = isSameOrigin;
2122
2144
  exports.isServiceId = isServiceId;
2145
+ exports.isSpoofableForwardingHeader = isSpoofableForwardingHeader;
2123
2146
  exports.isTransientRpcError = isTransientRpcError;
2124
2147
  exports.isValidIpfsCidPath = isValidIpfsCidPath;
2125
2148
  exports.issueAccountSessionToken = issueAccountSessionToken;
@@ -2137,6 +2160,7 @@ exports.resolveRemixPolicy = resolveRemixPolicy;
2137
2160
  exports.resolveSafeImageContentType = resolveSafeImageContentType;
2138
2161
  exports.shortenAddress = shortenAddress;
2139
2162
  exports.stringifyBigInts = stringifyBigInts;
2163
+ exports.trustedClientIp = trustedClientIp;
2140
2164
  exports.u256ToBigInt = u256ToBigInt;
2141
2165
  exports.uploadDirectoryToBackend = uploadDirectoryToBackend;
2142
2166
  exports.uploadFileToBackend = uploadFileToBackend;