@medialane/sdk 0.104.0 → 0.106.0

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.cts CHANGED
@@ -497,12 +497,60 @@ declare const ALLOWED_IMAGE_CONTENT_TYPES: Set<string>;
497
497
  declare const MAX_IMAGE_REDIRECTS = 5;
498
498
  declare const MAX_IMAGE_PROXY_BYTES: number;
499
499
  declare function isPrivateHost(hostname: string): boolean;
500
- declare function validateUrl(raw: string): {
500
+ /**
501
+ * Validates a URL before it is fetched server-side. `requireHttps` is on by
502
+ * default; callers that legitimately accept plain http (a metadata URI already
503
+ * committed on chain, say) opt out explicitly rather than the guard being
504
+ * lenient for everyone.
505
+ */
506
+ declare function validateUrl(raw: string, options?: {
507
+ requireHttps?: boolean;
508
+ }): {
501
509
  url: URL;
502
510
  } | {
503
511
  error: string;
504
512
  status: number;
505
513
  };
514
+ /** Convenience for callers that only need a yes/no, mirroring the backend's prior helper. */
515
+ declare function isPrivateOrInsecureUrl(raw: string, requireHttps?: boolean): boolean;
516
+
517
+ /**
518
+ * Proxies a remote image, which means fetching a URL an untrusted caller chose
519
+ * — the classic SSRF shape. Guarded in four places, each of which has to hold:
520
+ *
521
+ * 1. the URL is https, credential-free and not a private host
522
+ * 2. the hostname's *resolved* addresses are re-checked, since a public name
523
+ * can resolve to a private address
524
+ * 3. redirects are followed manually, re-validating each hop, because a
525
+ * permitted host can redirect into the private range
526
+ * 4. the response must be an allowed image type and is read under a byte cap
527
+ *
528
+ * This existed as a byte-identical copy in each app, so a fix to one would not
529
+ * have reached the other. DNS resolution is injected rather than imported: it
530
+ * is runtime-specific, and this package stays isomorphic.
531
+ *
532
+ * Note the remaining exposure: between resolving a hostname and fetching it,
533
+ * the name could resolve differently (DNS rebinding). Closing that needs the
534
+ * fetch pinned to the address already checked, which the platform fetch does
535
+ * not expose. The content-type allowlist and byte cap bound what an attacker
536
+ * gets if they win that race.
537
+ */
538
+ interface ImageProxyConfig {
539
+ checkRateLimit: (ip: string) => boolean;
540
+ /** Returns every address a hostname resolves to. Injected; see above. */
541
+ resolveHostname: (hostname: string) => Promise<string[]>;
542
+ fetchImpl?: typeof fetch;
543
+ userAgent?: string;
544
+ }
545
+ interface CappedBody {
546
+ ok: boolean;
547
+ body?: Uint8Array;
548
+ error?: string;
549
+ status?: number;
550
+ }
551
+ /** Reads a response body, refusing anything past `maxBytes` rather than buffering it. */
552
+ declare function readBodyWithCap(res: Response, maxBytes: number): Promise<CappedBody>;
553
+ declare function createImageProxyHandler(config: ImageProxyConfig): (req: Request) => Promise<Response>;
506
554
 
507
555
  interface BackendMetadataConfig {
508
556
  backendUrl: string;
@@ -527,8 +575,30 @@ declare function getBackendSignedUrl(config: BackendMetadataConfig, kind?: "imag
527
575
  declare function createRateLimiter(windowMs: number, max: number): (key: string) => boolean;
528
576
  declare function requestIp(req: Request): string;
529
577
 
578
+ /**
579
+ * Same-origin guard for server routes. A missing `Origin` header is allowed,
580
+ * because same-origin navigations and server-to-server calls do not send one;
581
+ * a present `Origin` must match `Host`.
582
+ *
583
+ * Read that carefully before relying on it: this blocks cross-site *browser*
584
+ * abuse, and nothing else. A non-browser client simply omits `Origin` and
585
+ * passes. It is not a substitute for authentication, and on a route that
586
+ * spends money or credits it must be paired with a rate limit and, where the
587
+ * route touches a paid resource, real auth.
588
+ */
530
589
  declare function isSameOrigin(req: Request): boolean;
531
590
 
591
+ declare const IDENTITY_TTL_SECONDS = 86400;
592
+ declare const ACCOUNT_SESSION_TTL_SECONDS: number;
593
+ interface SiwsIdentity {
594
+ address: string;
595
+ chain: Chain;
596
+ }
597
+ declare function issueSiwsToken(secret: string, chain: Chain, wallet: string, ttlSeconds?: number): string;
598
+ declare function verifySiwsToken(secret: string, raw: string): SiwsIdentity | null;
599
+ declare function issueAccountSessionToken(secret: string, accountId: string, ttlSeconds?: number): string;
600
+ declare function verifyAccountSessionToken(secret: string, raw: string): string | null;
601
+
532
602
  type AssetAttribute = {
533
603
  trait_type: string;
534
604
  value: string;
@@ -585,4 +655,4 @@ declare const IPFS_SAFE_CONTENT_TYPE_PREFIXES: readonly ["image/jpeg", "image/pn
585
655
  declare function resolveSafeImageContentType(contentType: string): string;
586
656
  declare const MAX_IPFS_GATEWAY_RESPONSE_BYTES: number;
587
657
 
588
- export { ALLOWED_IMAGE_CONTENT_TYPES, type AssetAttribute, type AssetMetadata, type BackendMetadataConfig, type BackendProxyConfig, type BackendUploadResult, type BuildAssetMetadataInput, type CanonicalHash, Chain, DEFAULT_CURRENCY, type FailoverFetchOptions, type FeeEnv, IPFS_SAFE_CONTENT_TYPE_PREFIXES, MAX_IMAGE_PROXY_BYTES, MAX_IMAGE_REDIRECTS, MAX_IPFS_GATEWAY_RESPONSE_BYTES, PAID_UPSTREAM_MARKERS, POLICY_REFUSAL_CODES, PUBLIC_RPC_FALLBACKS, RESERVED_TRAITS, type RemixPolicy, type RemixPolicyInput, ResolvedFeeConfig, type RpcProxyConfig, STARKNET_COLLECTION_1155_CLASS_HASH, STARKNET_COLLECTION_1155_CONTRACT, STARKNET_COLLECTION_1155_FACTORY_CLASS_HASH, STARKNET_COLLECTION_1155_START_BLOCK, STARKNET_COLLECTION_721_CONTRACT, STARKNET_COLLECTION_721_START_BLOCK, STARKNET_CREATOR_COIN_CLASS_HASH, STARKNET_CREATOR_COIN_EKUBO_LAUNCHER, STARKNET_CREATOR_COIN_FACTORY_CLASS_HASH, STARKNET_CREATOR_COIN_FACTORY_CONTRACT, STARKNET_CREATOR_COIN_START_BLOCK, STARKNET_DROP_COLLECTION_CLASS_HASH, STARKNET_DROP_FACTORY_CONTRACT, STARKNET_EKUBO_CORE, STARKNET_GENESIS_MINT_BR_CONTRACT, STARKNET_GENESIS_MINT_GLOBAL_CONTRACT, STARKNET_GENESIS_MINT_LAUNCH_CONTRACT, STARKNET_IPCOLLECTION_CLASS_HASH, STARKNET_IPNFT_CLASS_HASH, STARKNET_IP_CLUB_COLLECTION_CLASS_HASH, STARKNET_IP_CLUB_FACTORY_CONTRACT, STARKNET_IP_SPONSORSHIP_CLASS_HASH, STARKNET_IP_SPONSORSHIP_CONTRACT, STARKNET_IP_TICKETS_FACTORY_CONTRACT, STARKNET_IP_TICKET_COLLECTION_CLASS_HASH, STARKNET_MARKETPLACE_1155_CLASS_HASH, STARKNET_MARKETPLACE_1155_CONTRACT, STARKNET_MARKETPLACE_1155_START_BLOCK, STARKNET_MARKETPLACE_721_CLASS_HASH, STARKNET_MARKETPLACE_721_CONTRACT, STARKNET_MARKETPLACE_721_START_BLOCK, STARKNET_NFTCOMMENTS_CONTRACT, STARKNET_POP_COLLECTION_CLASS_HASH, STARKNET_POP_FACTORY_CONTRACT, SUPPORTED_TOKENS, SUPPORTED_URL_CHAINS, ServiceCapability, ServiceDefinition, type ServiceId, type SupportedToken, type SupportedTokenSymbol, assetHref, buildAssetMetadata, chainFromSlug, chainSlug, coinHref, collectionHref, createBackendProxyHandler, createFailoverFetch, createRateLimiter, createRpcProxyHandler, encodeU256, formatAmount, getBackendSignedUrl, getDerivativesTerm, getListableTokens, getService, getServicesByCapability, getTokenByAddress, getTokenBySymbol, hasCapability, isPolicyRefusal, isPrivateHost, isSameOrigin, isServiceId, isTransientRpcError, isValidIpfsCidPath, listServices, normalizeAddress, normalizeHash, parseAmount, requestIp, resolveAppFeeConfig, resolveRemixPolicy, resolveSafeImageContentType, shortenAddress, stringifyBigInts, u256ToBigInt, uploadDirectoryToBackend, uploadFileToBackend, uploadJsonToBackend, validateUrl };
658
+ export { ACCOUNT_SESSION_TTL_SECONDS, ALLOWED_IMAGE_CONTENT_TYPES, type AssetAttribute, type AssetMetadata, type BackendMetadataConfig, type BackendProxyConfig, type BackendUploadResult, type BuildAssetMetadataInput, type CanonicalHash, type CappedBody, Chain, DEFAULT_CURRENCY, type FailoverFetchOptions, type FeeEnv, IDENTITY_TTL_SECONDS, IPFS_SAFE_CONTENT_TYPE_PREFIXES, type ImageProxyConfig, MAX_IMAGE_PROXY_BYTES, MAX_IMAGE_REDIRECTS, MAX_IPFS_GATEWAY_RESPONSE_BYTES, PAID_UPSTREAM_MARKERS, POLICY_REFUSAL_CODES, PUBLIC_RPC_FALLBACKS, RESERVED_TRAITS, type RemixPolicy, type RemixPolicyInput, ResolvedFeeConfig, type RpcProxyConfig, STARKNET_COLLECTION_1155_CLASS_HASH, STARKNET_COLLECTION_1155_CONTRACT, STARKNET_COLLECTION_1155_FACTORY_CLASS_HASH, STARKNET_COLLECTION_1155_START_BLOCK, STARKNET_COLLECTION_721_CONTRACT, STARKNET_COLLECTION_721_START_BLOCK, STARKNET_CREATOR_COIN_CLASS_HASH, STARKNET_CREATOR_COIN_EKUBO_LAUNCHER, STARKNET_CREATOR_COIN_FACTORY_CLASS_HASH, STARKNET_CREATOR_COIN_FACTORY_CONTRACT, STARKNET_CREATOR_COIN_START_BLOCK, STARKNET_DROP_COLLECTION_CLASS_HASH, STARKNET_DROP_FACTORY_CONTRACT, STARKNET_EKUBO_CORE, STARKNET_GENESIS_MINT_BR_CONTRACT, STARKNET_GENESIS_MINT_GLOBAL_CONTRACT, STARKNET_GENESIS_MINT_LAUNCH_CONTRACT, STARKNET_IPCOLLECTION_CLASS_HASH, STARKNET_IPNFT_CLASS_HASH, STARKNET_IP_CLUB_COLLECTION_CLASS_HASH, STARKNET_IP_CLUB_FACTORY_CONTRACT, STARKNET_IP_SPONSORSHIP_CLASS_HASH, STARKNET_IP_SPONSORSHIP_CONTRACT, STARKNET_IP_TICKETS_FACTORY_CONTRACT, STARKNET_IP_TICKET_COLLECTION_CLASS_HASH, STARKNET_MARKETPLACE_1155_CLASS_HASH, STARKNET_MARKETPLACE_1155_CONTRACT, STARKNET_MARKETPLACE_1155_START_BLOCK, STARKNET_MARKETPLACE_721_CLASS_HASH, STARKNET_MARKETPLACE_721_CONTRACT, STARKNET_MARKETPLACE_721_START_BLOCK, STARKNET_NFTCOMMENTS_CONTRACT, STARKNET_POP_COLLECTION_CLASS_HASH, STARKNET_POP_FACTORY_CONTRACT, SUPPORTED_TOKENS, SUPPORTED_URL_CHAINS, ServiceCapability, ServiceDefinition, type ServiceId, type SiwsIdentity, type SupportedToken, type SupportedTokenSymbol, assetHref, buildAssetMetadata, chainFromSlug, chainSlug, coinHref, collectionHref, createBackendProxyHandler, createFailoverFetch, createImageProxyHandler, createRateLimiter, createRpcProxyHandler, encodeU256, formatAmount, getBackendSignedUrl, getDerivativesTerm, getListableTokens, getService, getServicesByCapability, getTokenByAddress, getTokenBySymbol, hasCapability, isPolicyRefusal, isPrivateHost, isPrivateOrInsecureUrl, isSameOrigin, isServiceId, isTransientRpcError, isValidIpfsCidPath, issueAccountSessionToken, issueSiwsToken, listServices, normalizeAddress, normalizeHash, parseAmount, readBodyWithCap, requestIp, resolveAppFeeConfig, resolveRemixPolicy, resolveSafeImageContentType, shortenAddress, stringifyBigInts, u256ToBigInt, uploadDirectoryToBackend, uploadFileToBackend, uploadJsonToBackend, validateUrl, verifyAccountSessionToken, verifySiwsToken };
package/dist/index.d.ts CHANGED
@@ -497,12 +497,60 @@ declare const ALLOWED_IMAGE_CONTENT_TYPES: Set<string>;
497
497
  declare const MAX_IMAGE_REDIRECTS = 5;
498
498
  declare const MAX_IMAGE_PROXY_BYTES: number;
499
499
  declare function isPrivateHost(hostname: string): boolean;
500
- declare function validateUrl(raw: string): {
500
+ /**
501
+ * Validates a URL before it is fetched server-side. `requireHttps` is on by
502
+ * default; callers that legitimately accept plain http (a metadata URI already
503
+ * committed on chain, say) opt out explicitly rather than the guard being
504
+ * lenient for everyone.
505
+ */
506
+ declare function validateUrl(raw: string, options?: {
507
+ requireHttps?: boolean;
508
+ }): {
501
509
  url: URL;
502
510
  } | {
503
511
  error: string;
504
512
  status: number;
505
513
  };
514
+ /** Convenience for callers that only need a yes/no, mirroring the backend's prior helper. */
515
+ declare function isPrivateOrInsecureUrl(raw: string, requireHttps?: boolean): boolean;
516
+
517
+ /**
518
+ * Proxies a remote image, which means fetching a URL an untrusted caller chose
519
+ * — the classic SSRF shape. Guarded in four places, each of which has to hold:
520
+ *
521
+ * 1. the URL is https, credential-free and not a private host
522
+ * 2. the hostname's *resolved* addresses are re-checked, since a public name
523
+ * can resolve to a private address
524
+ * 3. redirects are followed manually, re-validating each hop, because a
525
+ * permitted host can redirect into the private range
526
+ * 4. the response must be an allowed image type and is read under a byte cap
527
+ *
528
+ * This existed as a byte-identical copy in each app, so a fix to one would not
529
+ * have reached the other. DNS resolution is injected rather than imported: it
530
+ * is runtime-specific, and this package stays isomorphic.
531
+ *
532
+ * Note the remaining exposure: between resolving a hostname and fetching it,
533
+ * the name could resolve differently (DNS rebinding). Closing that needs the
534
+ * fetch pinned to the address already checked, which the platform fetch does
535
+ * not expose. The content-type allowlist and byte cap bound what an attacker
536
+ * gets if they win that race.
537
+ */
538
+ interface ImageProxyConfig {
539
+ checkRateLimit: (ip: string) => boolean;
540
+ /** Returns every address a hostname resolves to. Injected; see above. */
541
+ resolveHostname: (hostname: string) => Promise<string[]>;
542
+ fetchImpl?: typeof fetch;
543
+ userAgent?: string;
544
+ }
545
+ interface CappedBody {
546
+ ok: boolean;
547
+ body?: Uint8Array;
548
+ error?: string;
549
+ status?: number;
550
+ }
551
+ /** Reads a response body, refusing anything past `maxBytes` rather than buffering it. */
552
+ declare function readBodyWithCap(res: Response, maxBytes: number): Promise<CappedBody>;
553
+ declare function createImageProxyHandler(config: ImageProxyConfig): (req: Request) => Promise<Response>;
506
554
 
507
555
  interface BackendMetadataConfig {
508
556
  backendUrl: string;
@@ -527,8 +575,30 @@ declare function getBackendSignedUrl(config: BackendMetadataConfig, kind?: "imag
527
575
  declare function createRateLimiter(windowMs: number, max: number): (key: string) => boolean;
528
576
  declare function requestIp(req: Request): string;
529
577
 
578
+ /**
579
+ * Same-origin guard for server routes. A missing `Origin` header is allowed,
580
+ * because same-origin navigations and server-to-server calls do not send one;
581
+ * a present `Origin` must match `Host`.
582
+ *
583
+ * Read that carefully before relying on it: this blocks cross-site *browser*
584
+ * abuse, and nothing else. A non-browser client simply omits `Origin` and
585
+ * passes. It is not a substitute for authentication, and on a route that
586
+ * spends money or credits it must be paired with a rate limit and, where the
587
+ * route touches a paid resource, real auth.
588
+ */
530
589
  declare function isSameOrigin(req: Request): boolean;
531
590
 
591
+ declare const IDENTITY_TTL_SECONDS = 86400;
592
+ declare const ACCOUNT_SESSION_TTL_SECONDS: number;
593
+ interface SiwsIdentity {
594
+ address: string;
595
+ chain: Chain;
596
+ }
597
+ declare function issueSiwsToken(secret: string, chain: Chain, wallet: string, ttlSeconds?: number): string;
598
+ declare function verifySiwsToken(secret: string, raw: string): SiwsIdentity | null;
599
+ declare function issueAccountSessionToken(secret: string, accountId: string, ttlSeconds?: number): string;
600
+ declare function verifyAccountSessionToken(secret: string, raw: string): string | null;
601
+
532
602
  type AssetAttribute = {
533
603
  trait_type: string;
534
604
  value: string;
@@ -585,4 +655,4 @@ declare const IPFS_SAFE_CONTENT_TYPE_PREFIXES: readonly ["image/jpeg", "image/pn
585
655
  declare function resolveSafeImageContentType(contentType: string): string;
586
656
  declare const MAX_IPFS_GATEWAY_RESPONSE_BYTES: number;
587
657
 
588
- export { ALLOWED_IMAGE_CONTENT_TYPES, type AssetAttribute, type AssetMetadata, type BackendMetadataConfig, type BackendProxyConfig, type BackendUploadResult, type BuildAssetMetadataInput, type CanonicalHash, Chain, DEFAULT_CURRENCY, type FailoverFetchOptions, type FeeEnv, IPFS_SAFE_CONTENT_TYPE_PREFIXES, MAX_IMAGE_PROXY_BYTES, MAX_IMAGE_REDIRECTS, MAX_IPFS_GATEWAY_RESPONSE_BYTES, PAID_UPSTREAM_MARKERS, POLICY_REFUSAL_CODES, PUBLIC_RPC_FALLBACKS, RESERVED_TRAITS, type RemixPolicy, type RemixPolicyInput, ResolvedFeeConfig, type RpcProxyConfig, STARKNET_COLLECTION_1155_CLASS_HASH, STARKNET_COLLECTION_1155_CONTRACT, STARKNET_COLLECTION_1155_FACTORY_CLASS_HASH, STARKNET_COLLECTION_1155_START_BLOCK, STARKNET_COLLECTION_721_CONTRACT, STARKNET_COLLECTION_721_START_BLOCK, STARKNET_CREATOR_COIN_CLASS_HASH, STARKNET_CREATOR_COIN_EKUBO_LAUNCHER, STARKNET_CREATOR_COIN_FACTORY_CLASS_HASH, STARKNET_CREATOR_COIN_FACTORY_CONTRACT, STARKNET_CREATOR_COIN_START_BLOCK, STARKNET_DROP_COLLECTION_CLASS_HASH, STARKNET_DROP_FACTORY_CONTRACT, STARKNET_EKUBO_CORE, STARKNET_GENESIS_MINT_BR_CONTRACT, STARKNET_GENESIS_MINT_GLOBAL_CONTRACT, STARKNET_GENESIS_MINT_LAUNCH_CONTRACT, STARKNET_IPCOLLECTION_CLASS_HASH, STARKNET_IPNFT_CLASS_HASH, STARKNET_IP_CLUB_COLLECTION_CLASS_HASH, STARKNET_IP_CLUB_FACTORY_CONTRACT, STARKNET_IP_SPONSORSHIP_CLASS_HASH, STARKNET_IP_SPONSORSHIP_CONTRACT, STARKNET_IP_TICKETS_FACTORY_CONTRACT, STARKNET_IP_TICKET_COLLECTION_CLASS_HASH, STARKNET_MARKETPLACE_1155_CLASS_HASH, STARKNET_MARKETPLACE_1155_CONTRACT, STARKNET_MARKETPLACE_1155_START_BLOCK, STARKNET_MARKETPLACE_721_CLASS_HASH, STARKNET_MARKETPLACE_721_CONTRACT, STARKNET_MARKETPLACE_721_START_BLOCK, STARKNET_NFTCOMMENTS_CONTRACT, STARKNET_POP_COLLECTION_CLASS_HASH, STARKNET_POP_FACTORY_CONTRACT, SUPPORTED_TOKENS, SUPPORTED_URL_CHAINS, ServiceCapability, ServiceDefinition, type ServiceId, type SupportedToken, type SupportedTokenSymbol, assetHref, buildAssetMetadata, chainFromSlug, chainSlug, coinHref, collectionHref, createBackendProxyHandler, createFailoverFetch, createRateLimiter, createRpcProxyHandler, encodeU256, formatAmount, getBackendSignedUrl, getDerivativesTerm, getListableTokens, getService, getServicesByCapability, getTokenByAddress, getTokenBySymbol, hasCapability, isPolicyRefusal, isPrivateHost, isSameOrigin, isServiceId, isTransientRpcError, isValidIpfsCidPath, listServices, normalizeAddress, normalizeHash, parseAmount, requestIp, resolveAppFeeConfig, resolveRemixPolicy, resolveSafeImageContentType, shortenAddress, stringifyBigInts, u256ToBigInt, uploadDirectoryToBackend, uploadFileToBackend, uploadJsonToBackend, validateUrl };
658
+ export { ACCOUNT_SESSION_TTL_SECONDS, ALLOWED_IMAGE_CONTENT_TYPES, type AssetAttribute, type AssetMetadata, type BackendMetadataConfig, type BackendProxyConfig, type BackendUploadResult, type BuildAssetMetadataInput, type CanonicalHash, type CappedBody, Chain, DEFAULT_CURRENCY, type FailoverFetchOptions, type FeeEnv, IDENTITY_TTL_SECONDS, IPFS_SAFE_CONTENT_TYPE_PREFIXES, type ImageProxyConfig, MAX_IMAGE_PROXY_BYTES, MAX_IMAGE_REDIRECTS, MAX_IPFS_GATEWAY_RESPONSE_BYTES, PAID_UPSTREAM_MARKERS, POLICY_REFUSAL_CODES, PUBLIC_RPC_FALLBACKS, RESERVED_TRAITS, type RemixPolicy, type RemixPolicyInput, ResolvedFeeConfig, type RpcProxyConfig, STARKNET_COLLECTION_1155_CLASS_HASH, STARKNET_COLLECTION_1155_CONTRACT, STARKNET_COLLECTION_1155_FACTORY_CLASS_HASH, STARKNET_COLLECTION_1155_START_BLOCK, STARKNET_COLLECTION_721_CONTRACT, STARKNET_COLLECTION_721_START_BLOCK, STARKNET_CREATOR_COIN_CLASS_HASH, STARKNET_CREATOR_COIN_EKUBO_LAUNCHER, STARKNET_CREATOR_COIN_FACTORY_CLASS_HASH, STARKNET_CREATOR_COIN_FACTORY_CONTRACT, STARKNET_CREATOR_COIN_START_BLOCK, STARKNET_DROP_COLLECTION_CLASS_HASH, STARKNET_DROP_FACTORY_CONTRACT, STARKNET_EKUBO_CORE, STARKNET_GENESIS_MINT_BR_CONTRACT, STARKNET_GENESIS_MINT_GLOBAL_CONTRACT, STARKNET_GENESIS_MINT_LAUNCH_CONTRACT, STARKNET_IPCOLLECTION_CLASS_HASH, STARKNET_IPNFT_CLASS_HASH, STARKNET_IP_CLUB_COLLECTION_CLASS_HASH, STARKNET_IP_CLUB_FACTORY_CONTRACT, STARKNET_IP_SPONSORSHIP_CLASS_HASH, STARKNET_IP_SPONSORSHIP_CONTRACT, STARKNET_IP_TICKETS_FACTORY_CONTRACT, STARKNET_IP_TICKET_COLLECTION_CLASS_HASH, STARKNET_MARKETPLACE_1155_CLASS_HASH, STARKNET_MARKETPLACE_1155_CONTRACT, STARKNET_MARKETPLACE_1155_START_BLOCK, STARKNET_MARKETPLACE_721_CLASS_HASH, STARKNET_MARKETPLACE_721_CONTRACT, STARKNET_MARKETPLACE_721_START_BLOCK, STARKNET_NFTCOMMENTS_CONTRACT, STARKNET_POP_COLLECTION_CLASS_HASH, STARKNET_POP_FACTORY_CONTRACT, SUPPORTED_TOKENS, SUPPORTED_URL_CHAINS, ServiceCapability, ServiceDefinition, type ServiceId, type SiwsIdentity, type SupportedToken, type SupportedTokenSymbol, assetHref, buildAssetMetadata, chainFromSlug, chainSlug, coinHref, collectionHref, createBackendProxyHandler, createFailoverFetch, createImageProxyHandler, createRateLimiter, createRpcProxyHandler, encodeU256, formatAmount, getBackendSignedUrl, getDerivativesTerm, getListableTokens, getService, getServicesByCapability, getTokenByAddress, getTokenBySymbol, hasCapability, isPolicyRefusal, isPrivateHost, isPrivateOrInsecureUrl, isSameOrigin, isServiceId, isTransientRpcError, isValidIpfsCidPath, issueAccountSessionToken, issueSiwsToken, listServices, normalizeAddress, normalizeHash, parseAmount, readBodyWithCap, requestIp, resolveAppFeeConfig, resolveRemixPolicy, resolveSafeImageContentType, shortenAddress, stringifyBigInts, u256ToBigInt, uploadDirectoryToBackend, uploadFileToBackend, uploadJsonToBackend, validateUrl, verifyAccountSessionToken, verifySiwsToken };
package/dist/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import { z } from 'zod';
2
2
  import { keccak_256 } from '@noble/hashes/sha3.js';
3
- import { base58, base32 } from '@scure/base';
3
+ import { base58, base32, base64urlnopad } from '@scure/base';
4
+ import { hmac } from '@noble/hashes/hmac.js';
5
+ import { sha256 } from '@noble/hashes/sha2.js';
4
6
 
5
7
  // src/config.ts
6
8
 
@@ -1463,43 +1465,135 @@ var ALLOWED_IMAGE_CONTENT_TYPES = /* @__PURE__ */ new Set([
1463
1465
  ]);
1464
1466
  var MAX_IMAGE_REDIRECTS = 5;
1465
1467
  var MAX_IMAGE_PROXY_BYTES = 15 * 1024 * 1024;
1468
+ function parseIpv4(ip) {
1469
+ const parts = ip.split(".");
1470
+ if (parts.length !== 4) return null;
1471
+ const bytes = [];
1472
+ for (const part of parts) {
1473
+ if (!/^\d{1,3}$/.test(part)) return null;
1474
+ const n = Number(part);
1475
+ if (n > 255) return null;
1476
+ bytes.push(n);
1477
+ }
1478
+ return bytes;
1479
+ }
1480
+ function isPrivateIpv4(bytes) {
1481
+ const a = bytes[0];
1482
+ const b = bytes[1];
1483
+ const c = bytes[2];
1484
+ if (a === 0) return true;
1485
+ if (a === 10) return true;
1486
+ if (a === 100 && b >= 64 && b <= 127) return true;
1487
+ if (a === 127) return true;
1488
+ if (a === 169 && b === 254) return true;
1489
+ if (a === 172 && b >= 16 && b <= 31) return true;
1490
+ if (a === 192 && b === 0 && c === 0) return true;
1491
+ if (a === 192 && b === 168) return true;
1492
+ if (a === 198 && (b === 18 || b === 19)) return true;
1493
+ if (a >= 224) return true;
1494
+ return false;
1495
+ }
1496
+ function expandIpv6(rawIp) {
1497
+ let ip = rawIp;
1498
+ let embeddedV4 = null;
1499
+ const v4Tail = ip.match(/(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/);
1500
+ if (v4Tail && ip.includes(":")) {
1501
+ embeddedV4 = parseIpv4(v4Tail[1]);
1502
+ if (!embeddedV4) return null;
1503
+ ip = ip.slice(0, ip.length - v4Tail[1].length) + "0:0";
1504
+ }
1505
+ const sides = ip.split("::");
1506
+ if (sides.length > 2) return null;
1507
+ const head = sides[0] ? sides[0].split(":").filter(Boolean) : [];
1508
+ const tail = sides.length === 2 && sides[1] ? sides[1].split(":").filter(Boolean) : [];
1509
+ let groups;
1510
+ if (sides.length === 1) {
1511
+ groups = head;
1512
+ if (groups.length !== 8) return null;
1513
+ } else {
1514
+ const missing = 8 - head.length - tail.length;
1515
+ if (missing < 0) return null;
1516
+ groups = [...head, ...Array(missing).fill("0"), ...tail];
1517
+ }
1518
+ if (groups.length !== 8) return null;
1519
+ const bytes = [];
1520
+ for (const group of groups) {
1521
+ if (!/^[0-9a-fA-F]{1,4}$/.test(group)) return null;
1522
+ const n = parseInt(group, 16);
1523
+ bytes.push(n >> 8 & 255, n & 255);
1524
+ }
1525
+ if (embeddedV4) {
1526
+ bytes[12] = embeddedV4[0];
1527
+ bytes[13] = embeddedV4[1];
1528
+ bytes[14] = embeddedV4[2];
1529
+ bytes[15] = embeddedV4[3];
1530
+ }
1531
+ return bytes;
1532
+ }
1533
+ function isPrivateIpv6(bytes) {
1534
+ if (bytes.every((b) => b === 0)) return true;
1535
+ if (bytes.slice(0, 15).every((b) => b === 0) && bytes[15] === 1) return true;
1536
+ if ((bytes[0] & 254) === 252) return true;
1537
+ if (bytes[0] === 254 && (bytes[1] & 192) === 128) return true;
1538
+ if (bytes.slice(0, 10).every((b) => b === 0) && bytes[10] === 255 && bytes[11] === 255) {
1539
+ return isPrivateIpv4(bytes.slice(12));
1540
+ }
1541
+ return false;
1542
+ }
1543
+ function parseNumber(part) {
1544
+ if (/^0x[0-9a-f]+$/i.test(part)) return parseInt(part.slice(2), 16);
1545
+ if (/^0[0-7]+$/.test(part)) return parseInt(part, 8);
1546
+ if (/^\d+$/.test(part)) return parseInt(part, 10);
1547
+ return null;
1548
+ }
1549
+ function normalizeNumericHostname(host) {
1550
+ const parts = host.trim().split(".");
1551
+ if (parts.length < 1 || parts.length > 4) return null;
1552
+ const values = [];
1553
+ for (const part of parts) {
1554
+ const value = parseNumber(part);
1555
+ if (value === null || value < 0) return null;
1556
+ values.push(value);
1557
+ }
1558
+ const leading = values.slice(0, -1);
1559
+ if (leading.some((v) => v > 255)) return null;
1560
+ const tail = values[values.length - 1];
1561
+ const tailBytes = 4 - leading.length;
1562
+ if (tail > 2 ** (8 * tailBytes) - 1) return null;
1563
+ const bytes = [...leading];
1564
+ for (let i = tailBytes - 1; i >= 0; i--) bytes.push(tail >>> 8 * i & 255);
1565
+ return bytes.join(".");
1566
+ }
1567
+ var BLOCKED_HOSTNAMES = /* @__PURE__ */ new Set([
1568
+ "localhost",
1569
+ "metadata.google.internal",
1570
+ "metadata.azure.internal"
1571
+ ]);
1466
1572
  function isPrivateHost(hostname) {
1467
- const h = hostname.toLowerCase().replace(/^\[|\]$/g, "");
1468
- if (h === "localhost" || h === "127.0.0.1" || h === "0.0.0.0") return true;
1469
- if (/^\d+$/.test(h)) {
1470
- const n = parseInt(h, 10);
1471
- if (n === 2130706433 || n === 0 || n >= 2886729728 && n <= 2887778303 || n >= 3232235520 && n <= 3232301055 || n >= 167772160 && n <= 184549375 || n >= 2851995648 && n <= 2852061183) return true;
1472
- }
1473
- if (/^0x[0-9a-f]+$/i.test(h)) {
1474
- const n = parseInt(h, 16);
1475
- if (n === 2130706433 || n === 0 || n >= 2886729728 && n <= 2887778303 || n >= 3232235520 && n <= 3232301055 || n >= 167772160 && n <= 184549375 || n >= 2851995648 && n <= 2852061183) return true;
1476
- }
1477
- if (/^0\d+\.\d+\.\d+\.\d+$/.test(h)) return true;
1478
- if (h === "::1" || /^0*:0*:0*:0*:0*:0*:0*:0*1$/.test(h)) return true;
1479
- const v4mapped = h.match(/^::ffff:(\d+\.\d+\.\d+\.\d+)$/i);
1480
- if (v4mapped) return isPrivateHost(v4mapped[1]);
1481
- if (/^10\./.test(h)) return true;
1482
- if (/^172\.(1[6-9]|2\d|3[01])\./.test(h)) return true;
1483
- if (/^192\.168\./.test(h)) return true;
1484
- if (/^169\.254\./.test(h)) return true;
1485
- if (/^100\.(6[4-9]|[7-9]\d|1[01]\d|12[0-7])\./.test(h)) return true;
1486
- if (/^fe80:/i.test(h)) return true;
1487
- if (/^f[cd][0-9a-f]{2}:/i.test(h)) return true;
1488
- if (h.endsWith(".local")) return true;
1489
- if (h === "metadata.google.internal") return true;
1490
- if (h === "metadata.azure.internal") return true;
1573
+ const host = hostname.toLowerCase().replace(/^\[|\]$/g, "");
1574
+ if (BLOCKED_HOSTNAMES.has(host)) return true;
1575
+ if (host.endsWith(".local") || host.endsWith(".internal")) return true;
1576
+ const candidate = normalizeNumericHostname(host) ?? host;
1577
+ const v4 = parseIpv4(candidate);
1578
+ if (v4) return isPrivateIpv4(v4);
1579
+ const v6 = expandIpv6(candidate);
1580
+ if (v6) return isPrivateIpv6(v6);
1491
1581
  return false;
1492
1582
  }
1493
- function validateUrl(raw) {
1583
+ function validateUrl(raw, options = {}) {
1584
+ const requireHttps = options.requireHttps ?? true;
1494
1585
  let parsed;
1495
1586
  try {
1496
1587
  parsed = new URL(raw);
1497
1588
  } catch {
1498
1589
  return { error: "Invalid url", status: 400 };
1499
1590
  }
1500
- if (parsed.protocol !== "https:") {
1591
+ if (requireHttps && parsed.protocol !== "https:") {
1501
1592
  return { error: "Only https URLs allowed", status: 400 };
1502
1593
  }
1594
+ if (!requireHttps && parsed.protocol !== "https:" && parsed.protocol !== "http:") {
1595
+ return { error: "Only http and https URLs allowed", status: 400 };
1596
+ }
1503
1597
  if (parsed.username || parsed.password) {
1504
1598
  return { error: "URL credentials not allowed", status: 400 };
1505
1599
  }
@@ -1508,6 +1602,111 @@ function validateUrl(raw) {
1508
1602
  }
1509
1603
  return { url: parsed };
1510
1604
  }
1605
+ function isPrivateOrInsecureUrl(raw, requireHttps = true) {
1606
+ return "error" in validateUrl(raw, { requireHttps });
1607
+ }
1608
+
1609
+ // src/server/image-proxy.ts
1610
+ var DEFAULT_USER_AGENT = "Mozilla/5.0 (compatible; Medialane/1.0; +https://www.medialane.io)";
1611
+ function jsonError(message, status) {
1612
+ return Response.json({ error: message }, { status });
1613
+ }
1614
+ async function readBodyWithCap(res, maxBytes) {
1615
+ const declared = Number(res.headers.get("content-length") ?? 0);
1616
+ if (declared > maxBytes) {
1617
+ return { ok: false, error: "Image too large", status: 413 };
1618
+ }
1619
+ const reader = res.body?.getReader();
1620
+ if (!reader) return { ok: false, error: "Empty response body", status: 502 };
1621
+ const chunks = [];
1622
+ let total = 0;
1623
+ for (; ; ) {
1624
+ const { done, value } = await reader.read();
1625
+ if (done) break;
1626
+ if (!value) continue;
1627
+ total += value.byteLength;
1628
+ if (total > maxBytes) {
1629
+ await reader.cancel().catch(() => {
1630
+ });
1631
+ return { ok: false, error: "Image too large", status: 413 };
1632
+ }
1633
+ chunks.push(value);
1634
+ }
1635
+ const body = new Uint8Array(total);
1636
+ let offset = 0;
1637
+ for (const chunk of chunks) {
1638
+ body.set(chunk, offset);
1639
+ offset += chunk.byteLength;
1640
+ }
1641
+ return { ok: true, body };
1642
+ }
1643
+ function createImageProxyHandler(config) {
1644
+ const doFetch = config.fetchImpl ?? fetch;
1645
+ const userAgent = config.userAgent ?? DEFAULT_USER_AGENT;
1646
+ async function resolvesToPrivateHost(hostname) {
1647
+ try {
1648
+ const addresses = await config.resolveHostname(hostname);
1649
+ if (addresses.length === 0) return true;
1650
+ return addresses.some((address) => isPrivateHost(address));
1651
+ } catch {
1652
+ return true;
1653
+ }
1654
+ }
1655
+ async function safeFetch(url, hopsLeft) {
1656
+ if (hopsLeft < 0) throw new Error("Too many redirects");
1657
+ if (await resolvesToPrivateHost(url.hostname)) {
1658
+ throw new Error("Blocked: hostname resolves to a private address");
1659
+ }
1660
+ const res = await doFetch(url.toString(), {
1661
+ redirect: "manual",
1662
+ headers: { "User-Agent": userAgent }
1663
+ });
1664
+ if (res.status >= 300 && res.status < 400) {
1665
+ const location = res.headers.get("location");
1666
+ if (!location) throw new Error("Redirect with no Location header");
1667
+ const next = new URL(location, url);
1668
+ const validated = validateUrl(next.toString());
1669
+ if ("error" in validated) throw new Error(`Redirect blocked: ${validated.error}`);
1670
+ return safeFetch(validated.url, hopsLeft - 1);
1671
+ }
1672
+ return res;
1673
+ }
1674
+ return async function handleImageProxy(req) {
1675
+ if (!config.checkRateLimit(requestIp(req))) {
1676
+ return jsonError("Too many requests", 429);
1677
+ }
1678
+ const raw = new URL(req.url).searchParams.get("url");
1679
+ if (!raw) return jsonError("Missing url", 400);
1680
+ const validated = validateUrl(raw);
1681
+ if ("error" in validated) return jsonError(validated.error, validated.status);
1682
+ let upstream;
1683
+ try {
1684
+ upstream = await safeFetch(validated.url, MAX_IMAGE_REDIRECTS);
1685
+ } catch {
1686
+ return jsonError("Failed to fetch image", 502);
1687
+ }
1688
+ if (!upstream.ok) {
1689
+ return jsonError(`Upstream returned ${upstream.status}`, upstream.status);
1690
+ }
1691
+ const contentType = upstream.headers.get("content-type") ?? "";
1692
+ const baseType = contentType.split(";")[0].trim().toLowerCase();
1693
+ if (!ALLOWED_IMAGE_CONTENT_TYPES.has(baseType)) {
1694
+ return jsonError("Not an image", 400);
1695
+ }
1696
+ const capped = await readBodyWithCap(upstream, MAX_IMAGE_PROXY_BYTES);
1697
+ if (!capped.ok) return jsonError(capped.error, capped.status);
1698
+ return new Response(capped.body, {
1699
+ status: 200,
1700
+ headers: {
1701
+ "Content-Type": contentType,
1702
+ "X-Content-Type-Options": "nosniff",
1703
+ "Content-Security-Policy": "default-src 'none'; img-src 'self' data:; style-src 'unsafe-inline'; sandbox",
1704
+ "Cache-Control": "public, max-age=86400, s-maxage=86400, stale-while-revalidate=604800",
1705
+ "Access-Control-Allow-Origin": "*"
1706
+ }
1707
+ });
1708
+ };
1709
+ }
1511
1710
 
1512
1711
  // src/server/backend-metadata.ts
1513
1712
  function endpoint(config, path) {
@@ -1557,6 +1756,81 @@ async function getBackendSignedUrl(config, kind = "image") {
1557
1756
  if (!res.ok || !data.data) throw new Error(data.error ?? "Failed to create upload URL");
1558
1757
  return data.data.url;
1559
1758
  }
1759
+ var IDENTITY_PREFIX = "siws_";
1760
+ var ACCOUNT_SESSION_PREFIX = "account_session_";
1761
+ var IDENTITY_DOMAIN = "siws-identity-v1";
1762
+ var ACCOUNT_SESSION_DOMAIN = "account-session-v1";
1763
+ var IDENTITY_TTL_SECONDS = 86400;
1764
+ var ACCOUNT_SESSION_TTL_SECONDS = 30 * 24 * 60 * 60;
1765
+ var CLOCK_SKEW_SECONDS = 60;
1766
+ var encoder = new TextEncoder();
1767
+ var decoder = new TextDecoder();
1768
+ function toHex(bytes) {
1769
+ let out = "";
1770
+ for (const b of bytes) out += b.toString(16).padStart(2, "0");
1771
+ return out;
1772
+ }
1773
+ function sign(secret, domain, payload) {
1774
+ const message = domain === null ? payload : `${domain}.${payload}`;
1775
+ return toHex(hmac(sha256, encoder.encode(secret), encoder.encode(message)));
1776
+ }
1777
+ function constantTimeEquals(a, b) {
1778
+ if (a.length !== b.length) return false;
1779
+ let diff = 0;
1780
+ for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
1781
+ return diff === 0;
1782
+ }
1783
+ function signatureMatches(secret, domain, payload, provided) {
1784
+ return constantTimeEquals(provided, sign(secret, domain, payload)) || constantTimeEquals(provided, sign(secret, null, payload));
1785
+ }
1786
+ function encodePayload(value) {
1787
+ return base64urlnopad.encode(encoder.encode(JSON.stringify(value)));
1788
+ }
1789
+ function decodePayload(payload) {
1790
+ try {
1791
+ return JSON.parse(decoder.decode(base64urlnopad.decode(payload)));
1792
+ } catch {
1793
+ return null;
1794
+ }
1795
+ }
1796
+ function splitToken(raw, prefix) {
1797
+ if (!raw.startsWith(prefix)) return null;
1798
+ const inner = raw.slice(prefix.length);
1799
+ const dot = inner.lastIndexOf(".");
1800
+ if (dot === -1) return null;
1801
+ return { payload: inner.slice(0, dot), signature: inner.slice(dot + 1) };
1802
+ }
1803
+ function withinLifetime(iat, exp) {
1804
+ if (!iat || !exp) return false;
1805
+ const now = Math.floor(Date.now() / 1e3);
1806
+ return exp >= now && iat <= now + CLOCK_SKEW_SECONDS;
1807
+ }
1808
+ function issueSiwsToken(secret, chain, wallet, ttlSeconds = IDENTITY_TTL_SECONDS) {
1809
+ const iat = Math.floor(Date.now() / 1e3);
1810
+ const payload = encodePayload({ sub: wallet, chain, iat, exp: iat + ttlSeconds });
1811
+ return `${IDENTITY_PREFIX}${payload}.${sign(secret, IDENTITY_DOMAIN, payload)}`;
1812
+ }
1813
+ function verifySiwsToken(secret, raw) {
1814
+ const parts = splitToken(raw, IDENTITY_PREFIX);
1815
+ if (!parts) return null;
1816
+ if (!signatureMatches(secret, IDENTITY_DOMAIN, parts.payload, parts.signature)) return null;
1817
+ const data = decodePayload(parts.payload);
1818
+ if (!data?.sub || !withinLifetime(data.iat, data.exp)) return null;
1819
+ return { address: data.sub, chain: data.chain ?? "STARKNET" };
1820
+ }
1821
+ function issueAccountSessionToken(secret, accountId, ttlSeconds = ACCOUNT_SESSION_TTL_SECONDS) {
1822
+ const iat = Math.floor(Date.now() / 1e3);
1823
+ const payload = encodePayload({ accountId, iat, exp: iat + ttlSeconds });
1824
+ return `${ACCOUNT_SESSION_PREFIX}${payload}.${sign(secret, ACCOUNT_SESSION_DOMAIN, payload)}`;
1825
+ }
1826
+ function verifyAccountSessionToken(secret, raw) {
1827
+ const parts = splitToken(raw, ACCOUNT_SESSION_PREFIX);
1828
+ if (!parts) return null;
1829
+ if (!signatureMatches(secret, ACCOUNT_SESSION_DOMAIN, parts.payload, parts.signature)) return null;
1830
+ const data = decodePayload(parts.payload);
1831
+ if (!data?.accountId || !withinLifetime(data.iat, data.exp)) return null;
1832
+ return data.accountId;
1833
+ }
1560
1834
 
1561
1835
  // src/metadata.ts
1562
1836
  var RESERVED_TRAITS = /* @__PURE__ */ new Set([
@@ -1651,6 +1925,6 @@ function resolveSafeImageContentType(contentType) {
1651
1925
  }
1652
1926
  var MAX_IPFS_GATEWAY_RESPONSE_BYTES = 25 * 1024 * 1024;
1653
1927
 
1654
- export { ALLOWED_IMAGE_CONTENT_TYPES, ApiClient, CHAINS, DEFAULT_CHAIN, DEFAULT_CURRENCY, FeeConfigSchema, IPFS_SAFE_CONTENT_TYPE_PREFIXES, MAX_IMAGE_PROXY_BYTES, MAX_IMAGE_REDIRECTS, MAX_IPFS_GATEWAY_RESPONSE_BYTES, MedialaneApiError, OPEN_LICENSES, PAID_UPSTREAM_MARKERS, POLICY_REFUSAL_CODES, PUBLIC_RPC_FALLBACKS, RESERVED_TRAITS, STARKNET_COLLECTION_1155_CLASS_HASH, STARKNET_COLLECTION_1155_CONTRACT, STARKNET_COLLECTION_1155_FACTORY_CLASS_HASH, STARKNET_COLLECTION_1155_START_BLOCK, STARKNET_COLLECTION_721_CONTRACT, STARKNET_COLLECTION_721_START_BLOCK, STARKNET_CREATOR_COIN_CLASS_HASH, STARKNET_CREATOR_COIN_EKUBO_LAUNCHER, STARKNET_CREATOR_COIN_FACTORY_CLASS_HASH, STARKNET_CREATOR_COIN_FACTORY_CONTRACT, STARKNET_CREATOR_COIN_START_BLOCK, STARKNET_DROP_COLLECTION_CLASS_HASH, STARKNET_DROP_FACTORY_CONTRACT, STARKNET_EKUBO_CORE, STARKNET_GENESIS_MINT_BR_CONTRACT, STARKNET_GENESIS_MINT_GLOBAL_CONTRACT, STARKNET_GENESIS_MINT_LAUNCH_CONTRACT, STARKNET_IPCOLLECTION_CLASS_HASH, STARKNET_IPNFT_CLASS_HASH, STARKNET_IP_CLUB_COLLECTION_CLASS_HASH, STARKNET_IP_CLUB_FACTORY_CONTRACT, STARKNET_IP_SPONSORSHIP_CLASS_HASH, STARKNET_IP_SPONSORSHIP_CONTRACT, STARKNET_IP_TICKETS_FACTORY_CONTRACT, STARKNET_IP_TICKET_COLLECTION_CLASS_HASH, STARKNET_MARKETPLACE_1155_CLASS_HASH, STARKNET_MARKETPLACE_1155_CONTRACT, STARKNET_MARKETPLACE_1155_START_BLOCK, STARKNET_MARKETPLACE_721_CLASS_HASH, STARKNET_MARKETPLACE_721_CONTRACT, STARKNET_MARKETPLACE_721_START_BLOCK, STARKNET_NFTCOMMENTS_CONTRACT, STARKNET_POP_COLLECTION_CLASS_HASH, STARKNET_POP_FACTORY_CONTRACT, SUPPORTED_TOKENS, SUPPORTED_URL_CHAINS, assetHref, buildAssetMetadata, chainFromSlug, chainSlug, coinHref, collectionHref, createBackendProxyHandler, createFailoverFetch, createRateLimiter, createRpcProxyHandler, encodeU256, formatAmount, getBackendSignedUrl, getCoordinates, getDerivativesTerm, getListableTokens, getService, getServicesByCapability, getStarknetCoordinates, getTokenByAddress, getTokenBySymbol, hasCapability, isPolicyRefusal, isPrivateHost, isSameOrigin, isServiceId, isTransientRpcError, isValidIpfsCidPath, listServices, normalizeAddress, normalizeHash, parseAmount, requestIp, resolveAppFeeConfig, resolveConfig, resolveFeeConfig, resolveRemixPolicy, resolveSafeImageContentType, shortenAddress, stringifyBigInts, u256ToBigInt, uploadDirectoryToBackend, uploadFileToBackend, uploadJsonToBackend, validateUrl };
1928
+ export { ACCOUNT_SESSION_TTL_SECONDS, ALLOWED_IMAGE_CONTENT_TYPES, ApiClient, CHAINS, DEFAULT_CHAIN, DEFAULT_CURRENCY, FeeConfigSchema, IDENTITY_TTL_SECONDS, IPFS_SAFE_CONTENT_TYPE_PREFIXES, MAX_IMAGE_PROXY_BYTES, MAX_IMAGE_REDIRECTS, MAX_IPFS_GATEWAY_RESPONSE_BYTES, MedialaneApiError, OPEN_LICENSES, PAID_UPSTREAM_MARKERS, POLICY_REFUSAL_CODES, PUBLIC_RPC_FALLBACKS, RESERVED_TRAITS, STARKNET_COLLECTION_1155_CLASS_HASH, STARKNET_COLLECTION_1155_CONTRACT, STARKNET_COLLECTION_1155_FACTORY_CLASS_HASH, STARKNET_COLLECTION_1155_START_BLOCK, STARKNET_COLLECTION_721_CONTRACT, STARKNET_COLLECTION_721_START_BLOCK, STARKNET_CREATOR_COIN_CLASS_HASH, STARKNET_CREATOR_COIN_EKUBO_LAUNCHER, STARKNET_CREATOR_COIN_FACTORY_CLASS_HASH, STARKNET_CREATOR_COIN_FACTORY_CONTRACT, STARKNET_CREATOR_COIN_START_BLOCK, STARKNET_DROP_COLLECTION_CLASS_HASH, STARKNET_DROP_FACTORY_CONTRACT, STARKNET_EKUBO_CORE, STARKNET_GENESIS_MINT_BR_CONTRACT, STARKNET_GENESIS_MINT_GLOBAL_CONTRACT, STARKNET_GENESIS_MINT_LAUNCH_CONTRACT, STARKNET_IPCOLLECTION_CLASS_HASH, STARKNET_IPNFT_CLASS_HASH, STARKNET_IP_CLUB_COLLECTION_CLASS_HASH, STARKNET_IP_CLUB_FACTORY_CONTRACT, STARKNET_IP_SPONSORSHIP_CLASS_HASH, STARKNET_IP_SPONSORSHIP_CONTRACT, STARKNET_IP_TICKETS_FACTORY_CONTRACT, STARKNET_IP_TICKET_COLLECTION_CLASS_HASH, STARKNET_MARKETPLACE_1155_CLASS_HASH, STARKNET_MARKETPLACE_1155_CONTRACT, STARKNET_MARKETPLACE_1155_START_BLOCK, STARKNET_MARKETPLACE_721_CLASS_HASH, STARKNET_MARKETPLACE_721_CONTRACT, STARKNET_MARKETPLACE_721_START_BLOCK, STARKNET_NFTCOMMENTS_CONTRACT, STARKNET_POP_COLLECTION_CLASS_HASH, STARKNET_POP_FACTORY_CONTRACT, SUPPORTED_TOKENS, SUPPORTED_URL_CHAINS, assetHref, buildAssetMetadata, chainFromSlug, chainSlug, coinHref, collectionHref, createBackendProxyHandler, createFailoverFetch, createImageProxyHandler, createRateLimiter, createRpcProxyHandler, encodeU256, formatAmount, getBackendSignedUrl, getCoordinates, getDerivativesTerm, getListableTokens, getService, getServicesByCapability, getStarknetCoordinates, getTokenByAddress, getTokenBySymbol, hasCapability, isPolicyRefusal, isPrivateHost, isPrivateOrInsecureUrl, isSameOrigin, isServiceId, isTransientRpcError, isValidIpfsCidPath, issueAccountSessionToken, issueSiwsToken, listServices, normalizeAddress, normalizeHash, parseAmount, readBodyWithCap, requestIp, resolveAppFeeConfig, resolveConfig, resolveFeeConfig, resolveRemixPolicy, resolveSafeImageContentType, shortenAddress, stringifyBigInts, u256ToBigInt, uploadDirectoryToBackend, uploadFileToBackend, uploadJsonToBackend, validateUrl, verifyAccountSessionToken, verifySiwsToken };
1655
1929
  //# sourceMappingURL=index.js.map
1656
1930
  //# sourceMappingURL=index.js.map