@misofm/api-client 0.4.0 → 0.5.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/package.json +1 -1
- package/src/client.test.ts +13 -10
- package/src/client.ts +19 -25
- package/src/schemas.ts +1 -26
- package/src/types.ts +0 -4
package/package.json
CHANGED
package/src/client.test.ts
CHANGED
|
@@ -79,20 +79,23 @@ describe("URL construction", () => {
|
|
|
79
79
|
test("joins catalog relationship expansions into one param", async () => {
|
|
80
80
|
const { fetch, calls } = stubFetch({ status: 404 });
|
|
81
81
|
const client = createMisoApiClient({ baseUrl: BASE, fetch });
|
|
82
|
-
await client.getPressing("0xp", { include: ["trackCredits"] });
|
|
83
82
|
await client.getRelease("0xl", { include: ["trackCredits"] });
|
|
84
83
|
await client.getRecordAlbum("0xr", {
|
|
85
84
|
include: ["release", "trackCredits"],
|
|
86
85
|
});
|
|
87
86
|
expect(calls[0]).toContain("include=trackCredits");
|
|
88
|
-
expect(calls[1]).toContain("include=
|
|
89
|
-
expect(calls[2]).toContain("include=release%2CtrackCredits");
|
|
87
|
+
expect(calls[1]).toContain("include=release%2CtrackCredits");
|
|
90
88
|
});
|
|
91
89
|
|
|
92
|
-
test("
|
|
90
|
+
test("passes the currency to the modular Listing route", async () => {
|
|
93
91
|
const { fetch, calls } = stubFetch({ status: 404 });
|
|
94
|
-
await createMisoApiClient({ baseUrl: BASE, fetch }).
|
|
95
|
-
|
|
92
|
+
await createMisoApiClient({ baseUrl: BASE, fetch }).getListing(
|
|
93
|
+
"0xp",
|
|
94
|
+
"0x2::sui::SUI",
|
|
95
|
+
);
|
|
96
|
+
expect(calls[0]).toBe(
|
|
97
|
+
`${BASE}/read/v1/pressings/0xp/listing?currencyType=0x2%3A%3Asui%3A%3ASUI`,
|
|
98
|
+
);
|
|
96
99
|
});
|
|
97
100
|
|
|
98
101
|
test("a custom prefix is honored (self-hosted / direct-to-service)", async () => {
|
|
@@ -143,9 +146,9 @@ describe("error handling", () => {
|
|
|
143
146
|
body: { error: { code: "rate-limited", message: "Too many requests." } },
|
|
144
147
|
});
|
|
145
148
|
const api = createMisoApiClient({ baseUrl: BASE, fetch });
|
|
146
|
-
await expect(api.
|
|
149
|
+
await expect(api.getPressing("0xp")).rejects.toThrow(MisoApiError);
|
|
147
150
|
try {
|
|
148
|
-
await api.
|
|
151
|
+
await api.getPressing("0xp");
|
|
149
152
|
} catch (e) {
|
|
150
153
|
expect(e).toBeInstanceOf(MisoApiError);
|
|
151
154
|
expect((e as MisoApiError).status).toBe(429);
|
|
@@ -157,7 +160,7 @@ describe("error handling", () => {
|
|
|
157
160
|
test("an error body in an unexpected shape still throws a usable error", async () => {
|
|
158
161
|
const { fetch } = stubFetch({ status: 500, body: { oops: true } });
|
|
159
162
|
try {
|
|
160
|
-
await createMisoApiClient({ baseUrl: BASE, fetch }).
|
|
163
|
+
await createMisoApiClient({ baseUrl: BASE, fetch }).getPressing("0xp");
|
|
161
164
|
throw new Error("should have thrown");
|
|
162
165
|
} catch (e) {
|
|
163
166
|
expect(e).toBeInstanceOf(MisoApiError);
|
|
@@ -237,8 +240,8 @@ describe("READ_CACHE_CLASS", () => {
|
|
|
237
240
|
});
|
|
238
241
|
|
|
239
242
|
test("live sale reads are the sale class", () => {
|
|
240
|
-
expect(READ_CACHE_CLASS.getDiscover).toBe("sale");
|
|
241
243
|
expect(READ_CACHE_CLASS.getPressing).toBe("sale");
|
|
244
|
+
expect(READ_CACHE_CLASS.getListing).toBe("sale");
|
|
242
245
|
});
|
|
243
246
|
});
|
|
244
247
|
|
package/src/client.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// The typed client for the Miso read API. One of these per app.
|
|
5
5
|
//
|
|
6
6
|
// const api = createMisoApiClient({ baseUrl: "https://api.testnet.miso.fm" })
|
|
7
|
-
// const
|
|
7
|
+
// const pressing = await api.getPressing("0x…")
|
|
8
8
|
//
|
|
9
9
|
// Every method returns a PARSED, validated body. Validation on the client is not
|
|
10
10
|
// paranoia about our own server — it is what turns a silent contract break into a
|
|
@@ -21,15 +21,14 @@ import { queryPolicy, type CacheClass } from "./cache.ts";
|
|
|
21
21
|
import type {
|
|
22
22
|
ArtistProfile,
|
|
23
23
|
Balance,
|
|
24
|
-
|
|
25
|
-
PressingPreview,
|
|
24
|
+
ListingView,
|
|
26
25
|
OwnedParty,
|
|
27
26
|
PendingMembership,
|
|
28
27
|
OwnedRecord,
|
|
29
28
|
OwnedWork,
|
|
30
29
|
Ownership,
|
|
31
30
|
PartySummary,
|
|
32
|
-
|
|
31
|
+
PressingView,
|
|
33
32
|
PurchaseReceipt,
|
|
34
33
|
RecordAlbum,
|
|
35
34
|
ReleaseDetail,
|
|
@@ -163,19 +162,24 @@ export function createMisoApiClient(options: MisoApiClientOptions) {
|
|
|
163
162
|
|
|
164
163
|
return {
|
|
165
164
|
// ── Catalog ─────────────────────────────────────────────────────────────
|
|
166
|
-
/**
|
|
167
|
-
|
|
168
|
-
|
|
165
|
+
/** One permanent pressing resource. */
|
|
166
|
+
getPressing: (pressingId: string): Promise<PressingView | null> =>
|
|
167
|
+
request(
|
|
168
|
+
s.pressingViewSchema,
|
|
169
|
+
`/pressings/${pressingId}`,
|
|
170
|
+
{},
|
|
171
|
+
{ nullOn404: true },
|
|
172
|
+
),
|
|
169
173
|
|
|
170
|
-
/**
|
|
171
|
-
|
|
174
|
+
/** One currency-specific Listing derived from a permanent Pressing. */
|
|
175
|
+
getListing: (
|
|
172
176
|
pressingId: string,
|
|
173
|
-
|
|
174
|
-
): Promise<
|
|
177
|
+
currencyType: string,
|
|
178
|
+
): Promise<ListingView | null> =>
|
|
175
179
|
request(
|
|
176
|
-
s.
|
|
177
|
-
`/pressings/${pressingId}`,
|
|
178
|
-
{
|
|
180
|
+
s.listingViewSchema,
|
|
181
|
+
`/pressings/${pressingId}/listing`,
|
|
182
|
+
{ currencyType },
|
|
179
183
|
{ nullOn404: true },
|
|
180
184
|
),
|
|
181
185
|
|
|
@@ -203,15 +207,6 @@ export function createMisoApiClient(options: MisoApiClientOptions) {
|
|
|
203
207
|
{ nullOn404: true },
|
|
204
208
|
),
|
|
205
209
|
|
|
206
|
-
/** Confirmation preview for a pasted pressing id. `null` when it isn't a pressing. */
|
|
207
|
-
getPressingPreview: (pressingId: string): Promise<PressingPreview | null> =>
|
|
208
|
-
request(
|
|
209
|
-
s.pressingPreviewSchema,
|
|
210
|
-
`/pressings/${pressingId}/preview`,
|
|
211
|
-
{},
|
|
212
|
-
{ nullOn404: true },
|
|
213
|
-
),
|
|
214
|
-
|
|
215
210
|
// ── Artist ──────────────────────────────────────────────────────────────
|
|
216
211
|
/** An artist plus optional relationship expansions. */
|
|
217
212
|
getArtist: (
|
|
@@ -311,9 +306,8 @@ export function cacheBuster(nowMs: number = Date.now()): string {
|
|
|
311
306
|
* it with `workCacheClass(release.state)`.
|
|
312
307
|
*/
|
|
313
308
|
export const READ_CACHE_CLASS = {
|
|
314
|
-
getDiscover: "sale",
|
|
315
309
|
getPressing: "sale",
|
|
316
|
-
|
|
310
|
+
getListing: "sale",
|
|
317
311
|
getRecordAlbum: "immutable",
|
|
318
312
|
getReceipt: "immutable",
|
|
319
313
|
getArtist: "artist",
|
package/src/schemas.ts
CHANGED
|
@@ -156,11 +156,6 @@ export const listingViewSchema = z.object({
|
|
|
156
156
|
state: z.enum(["enabled", "disabled"]),
|
|
157
157
|
});
|
|
158
158
|
|
|
159
|
-
export const saleViewSchema = z.object({
|
|
160
|
-
pressing: pressingViewSchema,
|
|
161
|
-
listing: listingViewSchema,
|
|
162
|
-
});
|
|
163
|
-
|
|
164
159
|
export const pressingDetailSchema = z.object({
|
|
165
160
|
pressing: pressingViewSchema,
|
|
166
161
|
release: releaseDetailSchema,
|
|
@@ -171,17 +166,6 @@ export const pressingDetailWireSchema = z.object({
|
|
|
171
166
|
release: releaseDetailWireSchema,
|
|
172
167
|
});
|
|
173
168
|
|
|
174
|
-
export const discoverItemSchema = z.object({
|
|
175
|
-
sale: saleViewSchema,
|
|
176
|
-
releaseId: suiIdSchema,
|
|
177
|
-
title: z.string(),
|
|
178
|
-
/** Primary artists joined for display. Empty when no credits are set. */
|
|
179
|
-
artist: z.string(),
|
|
180
|
-
coverUrl: z.string().url().nullable(),
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
export const discoverShelfSchema = z.array(discoverItemSchema);
|
|
184
|
-
|
|
185
169
|
export const recordAlbumSchema = z.object({
|
|
186
170
|
recordId: suiIdSchema,
|
|
187
171
|
releaseId: suiIdSchema.nullable(),
|
|
@@ -195,16 +179,6 @@ export const recordAlbumWireSchema = z.object({
|
|
|
195
179
|
release: releaseDetailWireSchema.nullable().optional(),
|
|
196
180
|
});
|
|
197
181
|
|
|
198
|
-
export const pressingPreviewSchema = z.object({
|
|
199
|
-
pressingId: suiIdSchema,
|
|
200
|
-
title: z.string(),
|
|
201
|
-
subtitle: z.string().nullable(),
|
|
202
|
-
coverUrl: z.string().url().nullable(),
|
|
203
|
-
state: pressingStateSchema,
|
|
204
|
-
supply: u64Schema,
|
|
205
|
-
trackCount: z.number().int().min(0),
|
|
206
|
-
});
|
|
207
|
-
|
|
208
182
|
// ── Artist ───────────────────────────────────────────────────────────────────
|
|
209
183
|
|
|
210
184
|
export const partyMemberSchema = z.object({
|
|
@@ -367,6 +341,7 @@ export const recordSaleSchema = z.object({
|
|
|
367
341
|
number: u64Schema,
|
|
368
342
|
paid: u64Schema,
|
|
369
343
|
price: priceSchema,
|
|
344
|
+
currencyType: z.string(),
|
|
370
345
|
buyer: suiIdSchema,
|
|
371
346
|
});
|
|
372
347
|
|
package/src/types.ts
CHANGED
|
@@ -20,12 +20,8 @@ export type Currency = z.infer<typeof s.currencySchema>;
|
|
|
20
20
|
export type PressingState = z.infer<typeof s.pressingStateSchema>;
|
|
21
21
|
export type PressingView = z.infer<typeof s.pressingViewSchema>;
|
|
22
22
|
export type ListingView = z.infer<typeof s.listingViewSchema>;
|
|
23
|
-
export type SaleView = z.infer<typeof s.saleViewSchema>;
|
|
24
23
|
export type PressingDetail = z.infer<typeof s.pressingDetailSchema>;
|
|
25
|
-
export type DiscoverItem = z.infer<typeof s.discoverItemSchema>;
|
|
26
|
-
export type DiscoverShelf = z.infer<typeof s.discoverShelfSchema>;
|
|
27
24
|
export type RecordAlbum = z.infer<typeof s.recordAlbumSchema>;
|
|
28
|
-
export type PressingPreview = z.infer<typeof s.pressingPreviewSchema>;
|
|
29
25
|
|
|
30
26
|
export type PartyMember = z.infer<typeof s.partyMemberSchema>;
|
|
31
27
|
export type PlatformKey = z.infer<typeof s.platformKeySchema>;
|