@medialane/sdk 0.70.0 → 0.70.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.
@@ -982,12 +982,20 @@ declare class ApiClient {
982
982
  constructor(baseUrl: string, apiKey?: string, retryOptions?: RetryOptions, chain?: Chain);
983
983
  /** Normalize an address for this client's chain (chain-scoped — Decision B). */
984
984
  private addr;
985
+ /**
986
+ * The one HTTP path for the whole client: base headers (incl. x-api-key),
987
+ * JSON error unwrapping, and `withRetry` (5xx/network only — 4xx never
988
+ * retried). `allow404`/`allow403` turn those statuses into a `null` result
989
+ * instead of a throw, for "profile may not exist" / "not a holder" reads —
990
+ * so no method needs to hand-roll `fetch` to get that behavior.
991
+ */
985
992
  private request;
986
993
  private get;
987
994
  private post;
988
995
  private patch;
989
996
  private del;
990
- private checkResponse;
997
+ /** Bearer header for Clerk-JWT-authenticated routes. */
998
+ private bearer;
991
999
  getOrders(query?: ApiOrdersQuery): Promise<ApiResponse<ApiOrder[]>>;
992
1000
  getOrder(orderHash: string): Promise<ApiResponse<ApiOrder>>;
993
1001
  getActiveOrdersForToken(contract: string, tokenId: string): Promise<ApiResponse<ApiOrder[]>>;
@@ -982,12 +982,20 @@ declare class ApiClient {
982
982
  constructor(baseUrl: string, apiKey?: string, retryOptions?: RetryOptions, chain?: Chain);
983
983
  /** Normalize an address for this client's chain (chain-scoped — Decision B). */
984
984
  private addr;
985
+ /**
986
+ * The one HTTP path for the whole client: base headers (incl. x-api-key),
987
+ * JSON error unwrapping, and `withRetry` (5xx/network only — 4xx never
988
+ * retried). `allow404`/`allow403` turn those statuses into a `null` result
989
+ * instead of a throw, for "profile may not exist" / "not a holder" reads —
990
+ * so no method needs to hand-roll `fetch` to get that behavior.
991
+ */
985
992
  private request;
986
993
  private get;
987
994
  private post;
988
995
  private patch;
989
996
  private del;
990
- private checkResponse;
997
+ /** Bearer header for Clerk-JWT-authenticated routes. */
998
+ private bearer;
991
999
  getOrders(query?: ApiOrdersQuery): Promise<ApiResponse<ApiOrder[]>>;
992
1000
  getOrder(orderHash: string): Promise<ApiResponse<ApiOrder>>;
993
1001
  getActiveOrdersForToken(contract: string, tokenId: string): Promise<ApiResponse<ApiOrder[]>>;
package/dist/index.cjs CHANGED
@@ -275,18 +275,26 @@ var ApiClient = class {
275
275
  addr(a) {
276
276
  return normalizeAddress(this.chain, a);
277
277
  }
278
- async request(path, init) {
278
+ /**
279
+ * The one HTTP path for the whole client: base headers (incl. x-api-key),
280
+ * JSON error unwrapping, and `withRetry` (5xx/network only — 4xx never
281
+ * retried). `allow404`/`allow403` turn those statuses into a `null` result
282
+ * instead of a throw, for "profile may not exist" / "not a holder" reads —
283
+ * so no method needs to hand-roll `fetch` to get that behavior.
284
+ */
285
+ async request(path, init, opts) {
279
286
  const url = `${this.baseUrl.replace(/\/$/, "")}${path}`;
280
287
  const headers = { ...this.baseHeaders };
281
288
  if (!(init?.body instanceof FormData)) {
282
289
  headers["Content-Type"] = "application/json";
283
290
  }
291
+ const allowed = (status) => opts?.allow404 === true && status === 404 || opts?.allow403 === true && status === 403;
284
292
  const res = await withRetry(async () => {
285
293
  const response = await fetch(url, {
286
294
  ...init,
287
295
  headers: { ...headers, ...init?.headers }
288
296
  });
289
- if (!response.ok) {
297
+ if (!response.ok && !allowed(response.status)) {
290
298
  const text = await response.text().catch(() => response.statusText);
291
299
  let message = text;
292
300
  try {
@@ -298,6 +306,7 @@ var ApiClient = class {
298
306
  }
299
307
  return response;
300
308
  }, this.retryOptions);
309
+ if (allowed(res.status)) return null;
301
310
  return res.json();
302
311
  }
303
312
  get(path) {
@@ -312,20 +321,9 @@ var ApiClient = class {
312
321
  del(path) {
313
322
  return this.request(path, { method: "DELETE" });
314
323
  }
315
- async checkResponse(res, options) {
316
- if (options?.allow404 && res.status === 404) return null;
317
- if (options?.allow403 && res.status === 403) return null;
318
- if (!res.ok) {
319
- const text = await res.text().catch(() => res.statusText);
320
- let message = text;
321
- try {
322
- const body = JSON.parse(text);
323
- if (body.error) message = body.error;
324
- } catch {
325
- }
326
- throw new MedialaneApiError(res.status, message);
327
- }
328
- return res.json();
324
+ /** Bearer header for Clerk-JWT-authenticated routes. */
325
+ bearer(clerkToken) {
326
+ return { Authorization: `Bearer ${clerkToken}` };
329
327
  }
330
328
  // ─── Orders ────────────────────────────────────────────────────────────────
331
329
  getOrders(query = {}) {
@@ -529,17 +527,11 @@ var ApiClient = class {
529
527
  * Authorization: Bearer (Clerk JWT) simultaneously.
530
528
  */
531
529
  async claimCollection(contractAddress, walletAddress, clerkToken) {
532
- const url = `${this.baseUrl.replace(/\/$/, "")}/v1/collections/claim`;
533
- const res = await fetch(url, {
530
+ return this.request("/v1/collections/claim", {
534
531
  method: "POST",
535
- headers: {
536
- "x-api-key": this.baseHeaders["x-api-key"] ?? "",
537
- "Content-Type": "application/json",
538
- "Authorization": `Bearer ${clerkToken}`
539
- },
540
- body: JSON.stringify({ contractAddress, walletAddress })
532
+ body: JSON.stringify({ contractAddress, walletAddress }),
533
+ headers: this.bearer(clerkToken)
541
534
  });
542
- return this.checkResponse(res);
543
535
  }
544
536
  /**
545
537
  * Path 3: Manual off-chain claim request (email-based).
@@ -551,106 +543,92 @@ var ApiClient = class {
551
543
  });
552
544
  }
553
545
  // ─── Collection Profiles ────────────────────────────────────────────────────
554
- async getCollectionProfile(contractAddress) {
555
- const url = `${this.baseUrl.replace(/\/$/, "")}/v1/collections/${this.addr(contractAddress)}/profile`;
556
- const res = await fetch(url, { headers: this.baseHeaders });
557
- return this.checkResponse(res, { allow404: true });
546
+ getCollectionProfile(contractAddress) {
547
+ return this.request(
548
+ `/v1/collections/${this.addr(contractAddress)}/profile`,
549
+ { method: "GET" },
550
+ { allow404: true }
551
+ );
558
552
  }
559
553
  /**
560
554
  * Update collection profile. Requires Clerk JWT for ownership check.
561
555
  */
562
- async updateCollectionProfile(contractAddress, data, clerkToken) {
563
- const url = `${this.baseUrl.replace(/\/$/, "")}/v1/collections/${this.addr(contractAddress)}/profile`;
564
- const res = await fetch(url, {
565
- method: "PATCH",
566
- headers: {
567
- "x-api-key": this.baseHeaders["x-api-key"] ?? "",
568
- "Content-Type": "application/json",
569
- "Authorization": `Bearer ${clerkToken}`
570
- },
571
- body: JSON.stringify(data)
572
- });
573
- return this.checkResponse(res);
556
+ updateCollectionProfile(contractAddress, data, clerkToken) {
557
+ return this.request(
558
+ `/v1/collections/${this.addr(contractAddress)}/profile`,
559
+ { method: "PATCH", body: JSON.stringify(data), headers: this.bearer(clerkToken) }
560
+ );
574
561
  }
575
- async getGatedContent(contractAddress, clerkToken) {
576
- const url = `${this.baseUrl.replace(/\/$/, "")}/v1/collections/${this.addr(contractAddress)}/gated-content`;
577
- const res = await fetch(url, {
578
- headers: { ...this.baseHeaders, "Authorization": `Bearer ${clerkToken}` }
579
- });
580
- return this.checkResponse(res, { allow404: true, allow403: true });
562
+ getGatedContent(contractAddress, clerkToken) {
563
+ return this.request(
564
+ `/v1/collections/${this.addr(contractAddress)}/gated-content`,
565
+ { method: "GET", headers: this.bearer(clerkToken) },
566
+ { allow404: true, allow403: true }
567
+ );
581
568
  }
582
569
  // ─── Creator Profiles ───────────────────────────────────────────────────────
583
570
  /** List all creators with an approved username. */
584
- async getCreators(opts = {}) {
571
+ getCreators(opts = {}) {
585
572
  const params = new URLSearchParams();
586
573
  if (opts.search) params.set("search", opts.search);
587
574
  if (opts.page) params.set("page", String(opts.page));
588
575
  if (opts.limit) params.set("limit", String(opts.limit));
589
- const url = `${this.baseUrl.replace(/\/$/, "")}/v1/creators?${params}`;
590
- const res = await fetch(url, { headers: this.baseHeaders });
591
- return this.checkResponse(res);
576
+ const qs = params.toString();
577
+ return this.get(`/v1/creators${qs ? `?${qs}` : ""}`);
592
578
  }
593
- async getCreatorProfile(walletAddress) {
594
- const url = `${this.baseUrl.replace(/\/$/, "")}/v1/creators/${this.addr(walletAddress)}/profile`;
595
- const res = await fetch(url, { headers: this.baseHeaders });
596
- return this.checkResponse(res, { allow404: true });
579
+ getCreatorProfile(walletAddress) {
580
+ return this.request(
581
+ `/v1/creators/${this.addr(walletAddress)}/profile`,
582
+ { method: "GET" },
583
+ { allow404: true }
584
+ );
597
585
  }
598
586
  /** Resolve a username slug to a creator profile (public). */
599
- async getCreatorByUsername(username) {
600
- const url = `${this.baseUrl.replace(/\/$/, "")}/v1/creators/by-username/${encodeURIComponent(username.toLowerCase().trim())}`;
601
- const res = await fetch(url, { headers: this.baseHeaders });
602
- return this.checkResponse(res, { allow404: true });
587
+ getCreatorByUsername(username) {
588
+ return this.request(
589
+ `/v1/creators/by-username/${encodeURIComponent(username.toLowerCase().trim())}`,
590
+ { method: "GET" },
591
+ { allow404: true }
592
+ );
603
593
  }
604
594
  /**
605
595
  * Update creator profile. Requires Clerk JWT; wallet must match authenticated user.
606
596
  */
607
- async updateCreatorProfile(walletAddress, data, clerkToken) {
608
- const url = `${this.baseUrl.replace(/\/$/, "")}/v1/creators/${this.addr(walletAddress)}/profile`;
609
- const res = await fetch(url, {
610
- method: "PATCH",
611
- headers: {
612
- "x-api-key": this.baseHeaders["x-api-key"] ?? "",
613
- "Content-Type": "application/json",
614
- "Authorization": `Bearer ${clerkToken}`
615
- },
616
- body: JSON.stringify(data)
617
- });
618
- return this.checkResponse(res);
597
+ updateCreatorProfile(walletAddress, data, clerkToken) {
598
+ return this.request(
599
+ `/v1/creators/${this.addr(walletAddress)}/profile`,
600
+ { method: "PATCH", body: JSON.stringify(data), headers: this.bearer(clerkToken) }
601
+ );
619
602
  }
620
603
  // ─── Collection Slug Claims ───────────────────────────────────────────────────
621
604
  /** Check if a collection slug is available (public, no auth). */
622
- async checkCollectionSlugAvailability(slug) {
623
- const url = `${this.baseUrl.replace(/\/$/, "")}/v1/collection-slug-claims/check/${encodeURIComponent(slug.toLowerCase().trim())}`;
624
- const res = await fetch(url, { headers: this.baseHeaders });
625
- return this.checkResponse(res);
605
+ checkCollectionSlugAvailability(slug) {
606
+ return this.get(
607
+ `/v1/collection-slug-claims/check/${encodeURIComponent(slug.toLowerCase().trim())}`
608
+ );
626
609
  }
627
610
  /** Submit a slug claim for a collection. Requires Clerk JWT — caller must be the collection owner. */
628
- async submitCollectionSlugClaim(contractAddress, slug, clerkToken, notifyEmail) {
629
- const url = `${this.baseUrl.replace(/\/$/, "")}/v1/collection-slug-claims`;
630
- const res = await fetch(url, {
611
+ submitCollectionSlugClaim(contractAddress, slug, clerkToken, notifyEmail) {
612
+ return this.request("/v1/collection-slug-claims", {
631
613
  method: "POST",
632
- headers: {
633
- "x-api-key": this.baseHeaders["x-api-key"] ?? "",
634
- "Content-Type": "application/json",
635
- Authorization: `Bearer ${clerkToken}`
636
- },
637
- body: JSON.stringify({ contractAddress, slug, notifyEmail })
614
+ body: JSON.stringify({ contractAddress, slug, notifyEmail }),
615
+ headers: this.bearer(clerkToken)
638
616
  });
639
- return this.checkResponse(res);
640
617
  }
641
618
  /** Returns all slug claims submitted by the authenticated wallet. Requires Clerk JWT. */
642
- async getMyCollectionSlugClaims(clerkToken) {
643
- const url = `${this.baseUrl.replace(/\/$/, "")}/v1/collection-slug-claims/me`;
644
- const res = await fetch(url, {
645
- headers: { ...this.baseHeaders, Authorization: `Bearer ${clerkToken}` }
619
+ getMyCollectionSlugClaims(clerkToken) {
620
+ return this.request("/v1/collection-slug-claims/me", {
621
+ method: "GET",
622
+ headers: this.bearer(clerkToken)
646
623
  });
647
- return this.checkResponse(res);
648
624
  }
649
625
  /** Resolve a collection slug to a full collection. Returns null if not found. */
650
- async getCollectionBySlug(slug) {
651
- const url = `${this.baseUrl.replace(/\/$/, "")}/v1/collections/by-slug/${encodeURIComponent(slug.toLowerCase().trim())}`;
652
- const res = await fetch(url, { headers: this.baseHeaders });
653
- return this.checkResponse(res, { allow404: true });
626
+ getCollectionBySlug(slug) {
627
+ return this.request(
628
+ `/v1/collections/by-slug/${encodeURIComponent(slug.toLowerCase().trim())}`,
629
+ { method: "GET" },
630
+ { allow404: true }
631
+ );
654
632
  }
655
633
  // ─── User Wallet ─────────────────────────────────────────────────────────────
656
634
  /**
@@ -667,33 +645,28 @@ var ApiClient = class {
667
645
  return this.post("/v1/users/register", params);
668
646
  }
669
647
  async upsertMyWallet(clerkToken, options = {}) {
670
- const url = `${this.baseUrl.replace(/\/$/, "")}/v1/users/me`;
671
648
  const body = {
672
649
  walletType: options.walletType ?? "UNKNOWN",
673
650
  appSource: options.appSource ?? "MEDIALANE_SDK"
674
651
  };
675
652
  if (options.chain) body.chain = options.chain;
676
- const res = await fetch(url, {
653
+ return this.request("/v1/users/me", {
677
654
  method: "POST",
678
- headers: {
679
- "Content-Type": "application/json",
680
- "Authorization": `Bearer ${clerkToken}`
681
- },
682
- body: JSON.stringify(body)
655
+ body: JSON.stringify(body),
656
+ headers: this.bearer(clerkToken)
683
657
  });
684
- return this.checkResponse(res);
685
658
  }
686
659
  /**
687
660
  * Get the authenticated user's stored wallet address from the backend DB.
688
661
  * Returns null if the user has not completed onboarding yet.
689
662
  * Requires Clerk JWT; no tenant API key needed.
690
663
  */
691
- async getMyWallet(clerkToken) {
692
- const url = `${this.baseUrl.replace(/\/$/, "")}/v1/users/me`;
693
- const res = await fetch(url, {
694
- headers: { "Authorization": `Bearer ${clerkToken}` }
695
- });
696
- return this.checkResponse(res, { allow404: true });
664
+ getMyWallet(clerkToken) {
665
+ return this.request(
666
+ "/v1/users/me",
667
+ { method: "GET", headers: this.bearer(clerkToken) },
668
+ { allow404: true }
669
+ );
697
670
  }
698
671
  // ─── Remix Licensing ─────────────────────────────────────────────────────────
699
672
  /**
@@ -743,25 +716,23 @@ var ApiClient = class {
743
716
  * role="creator" — offers where you are the original creator.
744
717
  * role="requester" — offers you made.
745
718
  */
746
- async getRemixOffers(query, clerkToken) {
719
+ getRemixOffers(query, clerkToken) {
747
720
  const params = new URLSearchParams({ role: query.role });
748
721
  if (query.page !== void 0) params.set("page", String(query.page));
749
722
  if (query.limit !== void 0) params.set("limit", String(query.limit));
750
- const url = `${this.baseUrl.replace(/\/$/, "")}/v1/remix-offers?${params}`;
751
- const res = await fetch(url, {
752
- headers: { ...this.baseHeaders, "Authorization": `Bearer ${clerkToken}` }
723
+ return this.request(`/v1/remix-offers?${params}`, {
724
+ method: "GET",
725
+ headers: this.bearer(clerkToken)
753
726
  });
754
- return this.checkResponse(res);
755
727
  }
756
728
  /**
757
729
  * Get a single remix offer. Clerk JWT optional (price/currency hidden for non-participants).
758
730
  */
759
- async getRemixOffer(id, clerkToken) {
760
- const url = `${this.baseUrl.replace(/\/$/, "")}/v1/remix-offers/${id}`;
761
- const headers = { ...this.baseHeaders };
762
- if (clerkToken) headers["Authorization"] = `Bearer ${clerkToken}`;
763
- const res = await fetch(url, { headers });
764
- return this.checkResponse(res);
731
+ getRemixOffer(id, clerkToken) {
732
+ return this.request(`/v1/remix-offers/${id}`, {
733
+ method: "GET",
734
+ headers: clerkToken ? this.bearer(clerkToken) : void 0
735
+ });
765
736
  }
766
737
  /**
767
738
  * Creator approves a remix offer (authorises the requester to mint). Requires Clerk JWT.
@@ -1226,15 +1197,28 @@ function parseAmount(human, decimals) {
1226
1197
  }
1227
1198
  function formatAmount(raw, decimals) {
1228
1199
  const value = BigInt(raw);
1229
- const factor = BigInt(Math.pow(10, decimals));
1200
+ const factor = 10n ** BigInt(decimals);
1230
1201
  const whole = value / factor;
1231
1202
  const remainder = value % factor;
1232
1203
  const fractional = remainder.toString().padStart(decimals, "0");
1233
1204
  return `${whole}.${fractional}`;
1234
1205
  }
1235
1206
  function getTokenByAddress(address) {
1236
- const lower = address.toLowerCase();
1237
- return SUPPORTED_TOKENS.find((t) => t.address.toLowerCase() === lower);
1207
+ let target = null;
1208
+ try {
1209
+ target = BigInt(address);
1210
+ } catch {
1211
+ target = null;
1212
+ }
1213
+ return SUPPORTED_TOKENS.find((t) => {
1214
+ if (target !== null) {
1215
+ try {
1216
+ return BigInt(t.address) === target;
1217
+ } catch {
1218
+ }
1219
+ }
1220
+ return t.address.toLowerCase() === address.toLowerCase();
1221
+ });
1238
1222
  }
1239
1223
  function getTokenBySymbol(symbol) {
1240
1224
  const upper = symbol.toUpperCase();