@medialane/sdk 0.3.2 → 0.4.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
@@ -181,7 +181,14 @@ const history = await client.api.getTokenHistory(contract, tokenId);
181
181
  ### Query Collections
182
182
 
183
183
  ```typescript
184
+ // All collections — newest first by default
184
185
  const collections = await client.api.getCollections();
186
+
187
+ // With sort and pagination
188
+ const byVolume = await client.api.getCollections(1, 20, undefined, "volume");
189
+ const verified = await client.api.getCollections(1, 18, true, "recent");
190
+
191
+ // Sort options: "recent" | "supply" | "floor" | "volume" | "name"
185
192
  const collection = await client.api.getCollection(contract);
186
193
  const tokens = await client.api.getCollectionTokens(contract);
187
194
  ```
@@ -406,6 +413,10 @@ Built with:
406
413
 
407
414
  ## Changelog
408
415
 
416
+ ### v0.3.3
417
+ - `getCollections(page?, limit?, isKnown?, sort?)` — added `sort` parameter: `"recent"` (default) | `"supply"` | `"floor"` | `"volume"` | `"name"`
418
+ - Default sort changed from `totalSupply DESC` to `createdAt DESC` (newest first) — matches backend default
419
+
409
420
  ### v0.3.1
410
421
  - `ApiCollection.collectionId: string | null` — on-chain registry numeric ID (decimal string). Required for `createMintIntent`. Populated for collections indexed after 2026-03-09.
411
422
 
package/dist/index.cjs CHANGED
@@ -8,6 +8,8 @@ var starknet = require('starknet');
8
8
  // src/constants.ts
9
9
  var MARKETPLACE_CONTRACT_MAINNET = "0x04299b51289aa700de4ce19cc77bcea8430bfd1aef04193efab09d60a3a7ee0f";
10
10
  var COLLECTION_CONTRACT_MAINNET = "0x05e73b7be06d82beeb390a0e0d655f2c9e7cf519658e04f05d9c690ccc41da03";
11
+ var MARKETPLACE_CONTRACT_SEPOLIA = "";
12
+ var COLLECTION_CONTRACT_SEPOLIA = "";
11
13
  var SUPPORTED_TOKENS = [
12
14
  {
13
15
  // Circle-native USDC on Starknet (canonical — preferred)
@@ -44,122 +46,6 @@ var DEFAULT_RPC_URLS = {
44
46
  sepolia: "https://rpc.starknet-sepolia.lava.build"
45
47
  };
46
48
 
47
- // src/config.ts
48
- var MedialaneConfigSchema = zod.z.object({
49
- network: zod.z.enum(SUPPORTED_NETWORKS).default("mainnet"),
50
- rpcUrl: zod.z.string().url().optional(),
51
- backendUrl: zod.z.string().url().optional(),
52
- /** API key for authenticated /v1/* backend endpoints */
53
- apiKey: zod.z.string().optional(),
54
- marketplaceContract: zod.z.string().optional(),
55
- collectionContract: zod.z.string().optional()
56
- });
57
- function resolveConfig(raw) {
58
- const parsed = MedialaneConfigSchema.parse(raw);
59
- return {
60
- network: parsed.network,
61
- rpcUrl: parsed.rpcUrl ?? DEFAULT_RPC_URLS[parsed.network],
62
- backendUrl: parsed.backendUrl,
63
- apiKey: parsed.apiKey,
64
- marketplaceContract: parsed.marketplaceContract ?? MARKETPLACE_CONTRACT_MAINNET,
65
- collectionContract: parsed.collectionContract ?? COLLECTION_CONTRACT_MAINNET
66
- };
67
- }
68
- function buildOrderTypedData(message, chainId) {
69
- return {
70
- domain: {
71
- name: "Medialane",
72
- version: "1",
73
- chainId,
74
- revision: starknet.TypedDataRevision.ACTIVE
75
- },
76
- primaryType: "OrderParameters",
77
- types: {
78
- StarknetDomain: [
79
- { name: "name", type: "shortstring" },
80
- { name: "version", type: "shortstring" },
81
- { name: "chainId", type: "shortstring" },
82
- { name: "revision", type: "shortstring" }
83
- ],
84
- OrderParameters: [
85
- { name: "offerer", type: "ContractAddress" },
86
- { name: "offer", type: "OfferItem" },
87
- { name: "consideration", type: "ConsiderationItem" },
88
- { name: "start_time", type: "felt" },
89
- { name: "end_time", type: "felt" },
90
- { name: "salt", type: "felt" },
91
- { name: "nonce", type: "felt" }
92
- ],
93
- OfferItem: [
94
- { name: "item_type", type: "shortstring" },
95
- { name: "token", type: "ContractAddress" },
96
- { name: "identifier_or_criteria", type: "felt" },
97
- { name: "start_amount", type: "felt" },
98
- { name: "end_amount", type: "felt" }
99
- ],
100
- ConsiderationItem: [
101
- { name: "item_type", type: "shortstring" },
102
- { name: "token", type: "ContractAddress" },
103
- { name: "identifier_or_criteria", type: "felt" },
104
- { name: "start_amount", type: "felt" },
105
- { name: "end_amount", type: "felt" },
106
- { name: "recipient", type: "ContractAddress" }
107
- ]
108
- },
109
- message
110
- };
111
- }
112
- function buildFulfillmentTypedData(message, chainId) {
113
- return {
114
- domain: {
115
- name: "Medialane",
116
- version: "1",
117
- chainId,
118
- revision: starknet.TypedDataRevision.ACTIVE
119
- },
120
- primaryType: "OrderFulfillment",
121
- types: {
122
- StarknetDomain: [
123
- { name: "name", type: "shortstring" },
124
- { name: "version", type: "shortstring" },
125
- { name: "chainId", type: "shortstring" },
126
- { name: "revision", type: "shortstring" }
127
- ],
128
- OrderFulfillment: [
129
- { name: "order_hash", type: "felt" },
130
- { name: "fulfiller", type: "ContractAddress" },
131
- { name: "nonce", type: "felt" }
132
- ]
133
- },
134
- message
135
- };
136
- }
137
- function buildCancellationTypedData(message, chainId) {
138
- return {
139
- domain: {
140
- name: "Medialane",
141
- version: "1",
142
- chainId,
143
- revision: starknet.TypedDataRevision.ACTIVE
144
- },
145
- primaryType: "OrderCancellation",
146
- types: {
147
- StarknetDomain: [
148
- { name: "name", type: "shortstring" },
149
- { name: "version", type: "shortstring" },
150
- { name: "chainId", type: "shortstring" },
151
- { name: "revision", type: "shortstring" }
152
- ],
153
- OrderCancellation: [
154
- { name: "order_hash", type: "felt" },
155
- { name: "offerer", type: "ContractAddress" },
156
- { name: "nonce", type: "felt" }
157
- ]
158
- },
159
- message
160
- };
161
- }
162
-
163
49
  // src/abis.ts
164
50
  var IPMarketplaceABI = [
165
51
  {
@@ -512,11 +398,106 @@ function getTokenBySymbol(symbol) {
512
398
  const upper = symbol.toUpperCase();
513
399
  return SUPPORTED_TOKENS.find((t) => t.symbol === upper);
514
400
  }
401
+ function buildOrderTypedData(message, chainId) {
402
+ return {
403
+ domain: {
404
+ name: "Medialane",
405
+ version: "1",
406
+ chainId,
407
+ revision: starknet.TypedDataRevision.ACTIVE
408
+ },
409
+ primaryType: "OrderParameters",
410
+ types: {
411
+ StarknetDomain: [
412
+ { name: "name", type: "shortstring" },
413
+ { name: "version", type: "shortstring" },
414
+ { name: "chainId", type: "shortstring" },
415
+ { name: "revision", type: "shortstring" }
416
+ ],
417
+ OrderParameters: [
418
+ { name: "offerer", type: "ContractAddress" },
419
+ { name: "offer", type: "OfferItem" },
420
+ { name: "consideration", type: "ConsiderationItem" },
421
+ { name: "start_time", type: "felt" },
422
+ { name: "end_time", type: "felt" },
423
+ { name: "salt", type: "felt" },
424
+ { name: "nonce", type: "felt" }
425
+ ],
426
+ OfferItem: [
427
+ { name: "item_type", type: "shortstring" },
428
+ { name: "token", type: "ContractAddress" },
429
+ { name: "identifier_or_criteria", type: "felt" },
430
+ { name: "start_amount", type: "felt" },
431
+ { name: "end_amount", type: "felt" }
432
+ ],
433
+ ConsiderationItem: [
434
+ { name: "item_type", type: "shortstring" },
435
+ { name: "token", type: "ContractAddress" },
436
+ { name: "identifier_or_criteria", type: "felt" },
437
+ { name: "start_amount", type: "felt" },
438
+ { name: "end_amount", type: "felt" },
439
+ { name: "recipient", type: "ContractAddress" }
440
+ ]
441
+ },
442
+ message
443
+ };
444
+ }
445
+ function buildFulfillmentTypedData(message, chainId) {
446
+ return {
447
+ domain: {
448
+ name: "Medialane",
449
+ version: "1",
450
+ chainId,
451
+ revision: starknet.TypedDataRevision.ACTIVE
452
+ },
453
+ primaryType: "OrderFulfillment",
454
+ types: {
455
+ StarknetDomain: [
456
+ { name: "name", type: "shortstring" },
457
+ { name: "version", type: "shortstring" },
458
+ { name: "chainId", type: "shortstring" },
459
+ { name: "revision", type: "shortstring" }
460
+ ],
461
+ OrderFulfillment: [
462
+ { name: "order_hash", type: "felt" },
463
+ { name: "fulfiller", type: "ContractAddress" },
464
+ { name: "nonce", type: "felt" }
465
+ ]
466
+ },
467
+ message
468
+ };
469
+ }
470
+ function buildCancellationTypedData(message, chainId) {
471
+ return {
472
+ domain: {
473
+ name: "Medialane",
474
+ version: "1",
475
+ chainId,
476
+ revision: starknet.TypedDataRevision.ACTIVE
477
+ },
478
+ primaryType: "OrderCancellation",
479
+ types: {
480
+ StarknetDomain: [
481
+ { name: "name", type: "shortstring" },
482
+ { name: "version", type: "shortstring" },
483
+ { name: "chainId", type: "shortstring" },
484
+ { name: "revision", type: "shortstring" }
485
+ ],
486
+ OrderCancellation: [
487
+ { name: "order_hash", type: "felt" },
488
+ { name: "offerer", type: "ContractAddress" },
489
+ { name: "nonce", type: "felt" }
490
+ ]
491
+ },
492
+ message
493
+ };
494
+ }
515
495
 
516
496
  // src/marketplace/orders.ts
517
497
  var MedialaneError = class extends Error {
518
- constructor(message, cause) {
498
+ constructor(message, code = "UNKNOWN", cause) {
519
499
  super(message);
500
+ this.code = code;
520
501
  this.cause = cause;
521
502
  this.name = "MedialaneError";
522
503
  }
@@ -556,7 +537,7 @@ function resolveToken(currency) {
556
537
  const token = SUPPORTED_TOKENS.find(
557
538
  (t) => t.symbol === currency.toUpperCase() || t.address.toLowerCase() === currency.toLowerCase()
558
539
  );
559
- if (!token) throw new MedialaneError(`Unsupported currency: ${currency}`);
540
+ if (!token) throw new MedialaneError(`Unsupported currency: ${currency}`, "INVALID_PARAMS");
560
541
  return token;
561
542
  }
562
543
  async function createListing(account, params, config) {
@@ -640,7 +621,7 @@ async function createListing(account, params, config) {
640
621
  await provider.waitForTransaction(tx.transaction_hash);
641
622
  return { txHash: tx.transaction_hash };
642
623
  } catch (err) {
643
- throw new MedialaneError("Failed to create listing", err);
624
+ throw new MedialaneError("Failed to create listing", "TRANSACTION_FAILED", err);
644
625
  }
645
626
  }
646
627
  async function makeOffer(account, params, config) {
@@ -711,7 +692,7 @@ async function makeOffer(account, params, config) {
711
692
  await provider.waitForTransaction(tx.transaction_hash);
712
693
  return { txHash: tx.transaction_hash };
713
694
  } catch (err) {
714
- throw new MedialaneError("Failed to make offer", err);
695
+ throw new MedialaneError("Failed to make offer", "TRANSACTION_FAILED", err);
715
696
  }
716
697
  }
717
698
  async function fulfillOrder(account, params, config) {
@@ -739,7 +720,7 @@ async function fulfillOrder(account, params, config) {
739
720
  await provider.waitForTransaction(tx.transaction_hash);
740
721
  return { txHash: tx.transaction_hash };
741
722
  } catch (err) {
742
- throw new MedialaneError("Failed to fulfill order", err);
723
+ throw new MedialaneError("Failed to fulfill order", "TRANSACTION_FAILED", err);
743
724
  }
744
725
  }
745
726
  async function cancelOrder(account, params, config) {
@@ -767,7 +748,7 @@ async function cancelOrder(account, params, config) {
767
748
  await provider.waitForTransaction(tx.transaction_hash);
768
749
  return { txHash: tx.transaction_hash };
769
750
  } catch (err) {
770
- throw new MedialaneError("Failed to cancel order", err);
751
+ throw new MedialaneError("Failed to cancel order", "TRANSACTION_FAILED", err);
771
752
  }
772
753
  }
773
754
  function encodeByteArray(str) {
@@ -790,7 +771,7 @@ async function mint(account, params, config) {
790
771
  await provider.waitForTransaction(tx.transaction_hash);
791
772
  return { txHash: tx.transaction_hash };
792
773
  } catch (err) {
793
- throw new MedialaneError("Failed to mint NFT", err);
774
+ throw new MedialaneError("Failed to mint NFT", "TRANSACTION_FAILED", err);
794
775
  }
795
776
  }
796
777
  async function createCollection(account, params, config) {
@@ -807,11 +788,11 @@ async function createCollection(account, params, config) {
807
788
  await provider.waitForTransaction(tx.transaction_hash);
808
789
  return { txHash: tx.transaction_hash };
809
790
  } catch (err) {
810
- throw new MedialaneError("Failed to create collection", err);
791
+ throw new MedialaneError("Failed to create collection", "TRANSACTION_FAILED", err);
811
792
  }
812
793
  }
813
794
  async function checkoutCart(account, items, config) {
814
- if (items.length === 0) throw new MedialaneError("Cart is empty");
795
+ if (items.length === 0) throw new MedialaneError("Cart is empty", "INVALID_PARAMS");
815
796
  const { contract, provider } = makeContract(config);
816
797
  const tokenTotals = /* @__PURE__ */ new Map();
817
798
  for (const item of items) {
@@ -858,10 +839,49 @@ async function checkoutCart(account, items, config) {
858
839
  await provider.waitForTransaction(tx.transaction_hash);
859
840
  return { txHash: tx.transaction_hash };
860
841
  } catch (err) {
861
- throw new MedialaneError("Cart checkout failed", err);
842
+ throw new MedialaneError("Cart checkout failed", "TRANSACTION_FAILED", err);
862
843
  }
863
844
  }
864
845
 
846
+ // src/config.ts
847
+ var MedialaneConfigSchema = zod.z.object({
848
+ network: zod.z.enum(SUPPORTED_NETWORKS).default("mainnet"),
849
+ rpcUrl: zod.z.string().url().optional(),
850
+ backendUrl: zod.z.string().url().optional(),
851
+ /** API key for authenticated /v1/* backend endpoints */
852
+ apiKey: zod.z.string().optional(),
853
+ marketplaceContract: zod.z.string().optional(),
854
+ collectionContract: zod.z.string().optional(),
855
+ retryOptions: zod.z.object({
856
+ maxAttempts: zod.z.number().int().min(1).max(10).optional(),
857
+ baseDelayMs: zod.z.number().int().min(0).optional(),
858
+ maxDelayMs: zod.z.number().int().min(0).optional()
859
+ }).optional()
860
+ });
861
+ function resolveConfig(raw) {
862
+ const parsed = MedialaneConfigSchema.parse(raw);
863
+ const isMainnet = parsed.network === "mainnet";
864
+ const defaultMarketplace = isMainnet ? MARKETPLACE_CONTRACT_MAINNET : MARKETPLACE_CONTRACT_SEPOLIA;
865
+ const defaultCollection = isMainnet ? COLLECTION_CONTRACT_MAINNET : COLLECTION_CONTRACT_SEPOLIA;
866
+ const marketplaceContract = parsed.marketplaceContract ?? defaultMarketplace;
867
+ const collectionContract = parsed.collectionContract ?? defaultCollection;
868
+ if (!marketplaceContract || !collectionContract) {
869
+ throw new MedialaneError(
870
+ `Sepolia network is not yet supported: marketplace and collection contract addresses are not configured. Pass 'marketplaceContract' and 'collectionContract' explicitly in your MedialaneClient config.`,
871
+ "NETWORK_NOT_SUPPORTED"
872
+ );
873
+ }
874
+ return {
875
+ network: parsed.network,
876
+ rpcUrl: parsed.rpcUrl ?? DEFAULT_RPC_URLS[parsed.network],
877
+ backendUrl: parsed.backendUrl,
878
+ apiKey: parsed.apiKey,
879
+ marketplaceContract,
880
+ collectionContract,
881
+ retryOptions: parsed.retryOptions
882
+ };
883
+ }
884
+
865
885
  // src/marketplace/index.ts
866
886
  var MarketplaceModule = class {
867
887
  constructor(config) {
@@ -911,18 +931,60 @@ function shortenAddress(address, chars = 4) {
911
931
  return `${norm.slice(0, chars + 2)}...${norm.slice(-chars)}`;
912
932
  }
913
933
 
934
+ // src/utils/retry.ts
935
+ var DEFAULT_MAX_ATTEMPTS = 3;
936
+ var DEFAULT_BASE_DELAY_MS = 300;
937
+ var DEFAULT_MAX_DELAY_MS = 5e3;
938
+ function sleep(ms) {
939
+ return new Promise((resolve) => setTimeout(resolve, ms));
940
+ }
941
+ async function withRetry(fn, opts) {
942
+ const maxAttempts = opts?.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
943
+ const baseDelayMs = opts?.baseDelayMs ?? DEFAULT_BASE_DELAY_MS;
944
+ const maxDelayMs = opts?.maxDelayMs ?? DEFAULT_MAX_DELAY_MS;
945
+ let lastError;
946
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
947
+ try {
948
+ return await fn();
949
+ } catch (err) {
950
+ lastError = err;
951
+ if (err instanceof MedialaneApiError && err.status < 500) {
952
+ throw err;
953
+ }
954
+ const isRetryable = err instanceof MedialaneApiError && err.status >= 500 || err instanceof TypeError;
955
+ if (!isRetryable || attempt === maxAttempts - 1) {
956
+ throw err;
957
+ }
958
+ const jitter = Math.random() * baseDelayMs;
959
+ const delay = Math.min(baseDelayMs * Math.pow(2, attempt) + jitter, maxDelayMs);
960
+ await sleep(delay);
961
+ }
962
+ }
963
+ throw lastError;
964
+ }
965
+
914
966
  // src/api/client.ts
967
+ function deriveErrorCode(status) {
968
+ if (status === 404) return "TOKEN_NOT_FOUND";
969
+ if (status === 429) return "RATE_LIMITED";
970
+ if (status === 410) return "INTENT_EXPIRED";
971
+ if (status === 401 || status === 403) return "UNAUTHORIZED";
972
+ if (status === 400) return "INVALID_PARAMS";
973
+ return "UNKNOWN";
974
+ }
915
975
  var MedialaneApiError = class extends Error {
916
976
  constructor(status, message) {
917
977
  super(message);
918
978
  this.status = status;
919
979
  this.name = "MedialaneApiError";
980
+ this.code = deriveErrorCode(status);
920
981
  }
921
982
  };
922
983
  var ApiClient = class {
923
- constructor(baseUrl, apiKey) {
984
+ constructor(baseUrl, apiKey, retryOptions) {
924
985
  this.baseUrl = baseUrl;
925
986
  this.baseHeaders = apiKey ? { "x-api-key": apiKey } : {};
987
+ this.retryOptions = retryOptions;
926
988
  }
927
989
  async request(path, init) {
928
990
  const url = `${this.baseUrl.replace(/\/$/, "")}${path}`;
@@ -930,19 +992,23 @@ var ApiClient = class {
930
992
  if (!(init?.body instanceof FormData)) {
931
993
  headers["Content-Type"] = "application/json";
932
994
  }
933
- const res = await fetch(url, {
934
- ...init,
935
- headers: { ...headers, ...init?.headers }
936
- });
937
- if (!res.ok) {
938
- let message = res.statusText;
939
- try {
940
- const body = await res.json();
941
- if (body.error) message = body.error;
942
- } catch {
995
+ const res = await withRetry(async () => {
996
+ const response = await fetch(url, {
997
+ ...init,
998
+ headers: { ...headers, ...init?.headers }
999
+ });
1000
+ if (!response.ok) {
1001
+ const text = await response.text().catch(() => response.statusText);
1002
+ let message = text;
1003
+ try {
1004
+ const body = JSON.parse(text);
1005
+ if (body.error) message = body.error;
1006
+ } catch {
1007
+ }
1008
+ throw new MedialaneApiError(response.status, message);
943
1009
  }
944
- throw new MedialaneApiError(res.status, message);
945
- }
1010
+ return response;
1011
+ }, this.retryOptions);
946
1012
  return res.json();
947
1013
  }
948
1014
  get(path) {
@@ -1000,9 +1066,10 @@ var ApiClient = class {
1000
1066
  );
1001
1067
  }
1002
1068
  // ─── Collections ───────────────────────────────────────────────────────────
1003
- getCollections(page = 1, limit = 20, isKnown) {
1069
+ getCollections(page = 1, limit = 20, isKnown, sort) {
1004
1070
  const params = new URLSearchParams({ page: String(page), limit: String(limit) });
1005
1071
  if (isKnown !== void 0) params.set("isKnown", String(isKnown));
1072
+ if (sort) params.set("sort", sort);
1006
1073
  return this.get(`/v1/collections?${params}`);
1007
1074
  }
1008
1075
  getCollectionsByOwner(owner, page = 1, limit = 50) {
@@ -1109,6 +1176,79 @@ var ApiClient = class {
1109
1176
  `/v1/portal/webhooks/${id}`
1110
1177
  );
1111
1178
  }
1179
+ // ─── Collection Claims ──────────────────────────────────────────────────────
1180
+ /**
1181
+ * Path 1: On-chain auto claim. Sends both x-api-key (tenant auth) and
1182
+ * Authorization: Bearer (Clerk JWT) simultaneously.
1183
+ */
1184
+ async claimCollection(contractAddress, walletAddress, clerkToken) {
1185
+ const url = `${this.baseUrl.replace(/\/$/, "")}/v1/collections/claim`;
1186
+ const res = await fetch(url, {
1187
+ method: "POST",
1188
+ headers: {
1189
+ "x-api-key": this.baseHeaders["x-api-key"] ?? "",
1190
+ "Content-Type": "application/json",
1191
+ "Authorization": `Bearer ${clerkToken}`
1192
+ },
1193
+ body: JSON.stringify({ contractAddress, walletAddress })
1194
+ });
1195
+ return res.json();
1196
+ }
1197
+ /**
1198
+ * Path 3: Manual off-chain claim request (email-based).
1199
+ */
1200
+ requestCollectionClaim(params) {
1201
+ return this.request("/v1/collections/claim/request", {
1202
+ method: "POST",
1203
+ body: JSON.stringify(params)
1204
+ });
1205
+ }
1206
+ // ─── Collection Profiles ────────────────────────────────────────────────────
1207
+ async getCollectionProfile(contractAddress) {
1208
+ const url = `${this.baseUrl.replace(/\/$/, "")}/v1/collections/${normalizeAddress(contractAddress)}/profile`;
1209
+ const res = await fetch(url, { headers: this.baseHeaders });
1210
+ if (res.status === 404) return null;
1211
+ return res.json();
1212
+ }
1213
+ /**
1214
+ * Update collection profile. Requires Clerk JWT for ownership check.
1215
+ */
1216
+ async updateCollectionProfile(contractAddress, data, clerkToken) {
1217
+ const url = `${this.baseUrl.replace(/\/$/, "")}/v1/collections/${normalizeAddress(contractAddress)}/profile`;
1218
+ const res = await fetch(url, {
1219
+ method: "PATCH",
1220
+ headers: {
1221
+ "x-api-key": this.baseHeaders["x-api-key"] ?? "",
1222
+ "Content-Type": "application/json",
1223
+ "Authorization": `Bearer ${clerkToken}`
1224
+ },
1225
+ body: JSON.stringify(data)
1226
+ });
1227
+ return res.json();
1228
+ }
1229
+ // ─── Creator Profiles ───────────────────────────────────────────────────────
1230
+ async getCreatorProfile(walletAddress) {
1231
+ const url = `${this.baseUrl.replace(/\/$/, "")}/v1/creators/${normalizeAddress(walletAddress)}/profile`;
1232
+ const res = await fetch(url, { headers: this.baseHeaders });
1233
+ if (res.status === 404) return null;
1234
+ return res.json();
1235
+ }
1236
+ /**
1237
+ * Update creator profile. Requires Clerk JWT; wallet must match authenticated user.
1238
+ */
1239
+ async updateCreatorProfile(walletAddress, data, clerkToken) {
1240
+ const url = `${this.baseUrl.replace(/\/$/, "")}/v1/creators/${normalizeAddress(walletAddress)}/profile`;
1241
+ const res = await fetch(url, {
1242
+ method: "PATCH",
1243
+ headers: {
1244
+ "x-api-key": this.baseHeaders["x-api-key"] ?? "",
1245
+ "Content-Type": "application/json",
1246
+ "Authorization": `Bearer ${clerkToken}`
1247
+ },
1248
+ body: JSON.stringify(data)
1249
+ });
1250
+ return res.json();
1251
+ }
1112
1252
  };
1113
1253
 
1114
1254
  // src/client.ts
@@ -1127,7 +1267,7 @@ var MedialaneClient = class {
1127
1267
  }
1128
1268
  });
1129
1269
  } else {
1130
- this.api = new ApiClient(this.config.backendUrl, this.config.apiKey);
1270
+ this.api = new ApiClient(this.config.backendUrl, this.config.apiKey, this.config.retryOptions);
1131
1271
  }
1132
1272
  }
1133
1273
  get network() {