@medialane/sdk 0.129.0 → 0.129.2
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 +34 -0
- package/dist/index.cjs +28 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +27 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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";
|
|
@@ -1874,8 +1896,7 @@ function toHex(bytes) {
|
|
|
1874
1896
|
return out;
|
|
1875
1897
|
}
|
|
1876
1898
|
function sign(secret, domain, payload) {
|
|
1877
|
-
|
|
1878
|
-
return toHex(hmac_js.hmac(sha2_js.sha256, encoder.encode(secret), encoder.encode(message)));
|
|
1899
|
+
return toHex(hmac_js.hmac(sha2_js.sha256, encoder.encode(secret), encoder.encode(`${domain}.${payload}`)));
|
|
1879
1900
|
}
|
|
1880
1901
|
function constantTimeEquals(a, b) {
|
|
1881
1902
|
if (a.length !== b.length) return false;
|
|
@@ -1884,7 +1905,7 @@ function constantTimeEquals(a, b) {
|
|
|
1884
1905
|
return diff === 0;
|
|
1885
1906
|
}
|
|
1886
1907
|
function signatureMatches(secret, domain, payload, provided) {
|
|
1887
|
-
return constantTimeEquals(provided, sign(secret, domain, payload))
|
|
1908
|
+
return constantTimeEquals(provided, sign(secret, domain, payload));
|
|
1888
1909
|
}
|
|
1889
1910
|
function encodePayload(value) {
|
|
1890
1911
|
return base.base64urlnopad.encode(encoder.encode(JSON.stringify(value)));
|
|
@@ -2120,6 +2141,7 @@ exports.isPrivateHost = isPrivateHost;
|
|
|
2120
2141
|
exports.isPrivateOrInsecureUrl = isPrivateOrInsecureUrl;
|
|
2121
2142
|
exports.isSameOrigin = isSameOrigin;
|
|
2122
2143
|
exports.isServiceId = isServiceId;
|
|
2144
|
+
exports.isSpoofableForwardingHeader = isSpoofableForwardingHeader;
|
|
2123
2145
|
exports.isTransientRpcError = isTransientRpcError;
|
|
2124
2146
|
exports.isValidIpfsCidPath = isValidIpfsCidPath;
|
|
2125
2147
|
exports.issueAccountSessionToken = issueAccountSessionToken;
|
|
@@ -2137,6 +2159,7 @@ exports.resolveRemixPolicy = resolveRemixPolicy;
|
|
|
2137
2159
|
exports.resolveSafeImageContentType = resolveSafeImageContentType;
|
|
2138
2160
|
exports.shortenAddress = shortenAddress;
|
|
2139
2161
|
exports.stringifyBigInts = stringifyBigInts;
|
|
2162
|
+
exports.trustedClientIp = trustedClientIp;
|
|
2140
2163
|
exports.u256ToBigInt = u256ToBigInt;
|
|
2141
2164
|
exports.uploadDirectoryToBackend = uploadDirectoryToBackend;
|
|
2142
2165
|
exports.uploadFileToBackend = uploadFileToBackend;
|