@misofm/api-client 0.4.1 → 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 +8 -16
- package/src/client.ts +14 -35
- package/src/schemas.ts +0 -36
- package/src/types.ts +0 -5
package/package.json
CHANGED
package/src/client.test.ts
CHANGED
|
@@ -79,30 +79,22 @@ 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
|
-
expect(calls[0]).toBe(`${BASE}/read/v1/pressings/0xp/preview`);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
test("passes the Listing currency to the pressing sale route", async () => {
|
|
99
|
-
const { fetch, calls } = stubFetch({ status: 404 });
|
|
100
|
-
await createMisoApiClient({ baseUrl: BASE, fetch }).getPressingSale(
|
|
92
|
+
await createMisoApiClient({ baseUrl: BASE, fetch }).getListing(
|
|
101
93
|
"0xp",
|
|
102
94
|
"0x2::sui::SUI",
|
|
103
95
|
);
|
|
104
96
|
expect(calls[0]).toBe(
|
|
105
|
-
`${BASE}/read/v1/pressings/0xp/
|
|
97
|
+
`${BASE}/read/v1/pressings/0xp/listing?currencyType=0x2%3A%3Asui%3A%3ASUI`,
|
|
106
98
|
);
|
|
107
99
|
});
|
|
108
100
|
|
|
@@ -154,9 +146,9 @@ describe("error handling", () => {
|
|
|
154
146
|
body: { error: { code: "rate-limited", message: "Too many requests." } },
|
|
155
147
|
});
|
|
156
148
|
const api = createMisoApiClient({ baseUrl: BASE, fetch });
|
|
157
|
-
await expect(api.
|
|
149
|
+
await expect(api.getPressing("0xp")).rejects.toThrow(MisoApiError);
|
|
158
150
|
try {
|
|
159
|
-
await api.
|
|
151
|
+
await api.getPressing("0xp");
|
|
160
152
|
} catch (e) {
|
|
161
153
|
expect(e).toBeInstanceOf(MisoApiError);
|
|
162
154
|
expect((e as MisoApiError).status).toBe(429);
|
|
@@ -168,7 +160,7 @@ describe("error handling", () => {
|
|
|
168
160
|
test("an error body in an unexpected shape still throws a usable error", async () => {
|
|
169
161
|
const { fetch } = stubFetch({ status: 500, body: { oops: true } });
|
|
170
162
|
try {
|
|
171
|
-
await createMisoApiClient({ baseUrl: BASE, fetch }).
|
|
163
|
+
await createMisoApiClient({ baseUrl: BASE, fetch }).getPressing("0xp");
|
|
172
164
|
throw new Error("should have thrown");
|
|
173
165
|
} catch (e) {
|
|
174
166
|
expect(e).toBeInstanceOf(MisoApiError);
|
|
@@ -248,8 +240,8 @@ describe("READ_CACHE_CLASS", () => {
|
|
|
248
240
|
});
|
|
249
241
|
|
|
250
242
|
test("live sale reads are the sale class", () => {
|
|
251
|
-
expect(READ_CACHE_CLASS.getDiscover).toBe("sale");
|
|
252
243
|
expect(READ_CACHE_CLASS.getPressing).toBe("sale");
|
|
244
|
+
expect(READ_CACHE_CLASS.getListing).toBe("sale");
|
|
253
245
|
});
|
|
254
246
|
});
|
|
255
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,16 +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
|
-
|
|
33
|
-
SaleDetail,
|
|
31
|
+
PressingView,
|
|
34
32
|
PurchaseReceipt,
|
|
35
33
|
RecordAlbum,
|
|
36
34
|
ReleaseDetail,
|
|
@@ -164,32 +162,24 @@ export function createMisoApiClient(options: MisoApiClientOptions) {
|
|
|
164
162
|
|
|
165
163
|
return {
|
|
166
164
|
// ── Catalog ─────────────────────────────────────────────────────────────
|
|
167
|
-
/**
|
|
168
|
-
|
|
169
|
-
required(s.discoverShelfSchema, "/discover"),
|
|
170
|
-
|
|
171
|
-
/** A pressing and everything its buy page renders. `null` when there is no such pressing. */
|
|
172
|
-
getPressing: (
|
|
173
|
-
pressingId: string,
|
|
174
|
-
opts: { include?: readonly "trackCredits"[] } = {},
|
|
175
|
-
): Promise<PressingDetail | null> =>
|
|
165
|
+
/** One permanent pressing resource. */
|
|
166
|
+
getPressing: (pressingId: string): Promise<PressingView | null> =>
|
|
176
167
|
request(
|
|
177
|
-
s.
|
|
168
|
+
s.pressingViewSchema,
|
|
178
169
|
`/pressings/${pressingId}`,
|
|
179
|
-
{
|
|
170
|
+
{},
|
|
180
171
|
{ nullOn404: true },
|
|
181
172
|
),
|
|
182
173
|
|
|
183
|
-
/** One
|
|
184
|
-
|
|
174
|
+
/** One currency-specific Listing derived from a permanent Pressing. */
|
|
175
|
+
getListing: (
|
|
185
176
|
pressingId: string,
|
|
186
177
|
currencyType: string,
|
|
187
|
-
|
|
188
|
-
): Promise<SaleDetail | null> =>
|
|
178
|
+
): Promise<ListingView | null> =>
|
|
189
179
|
request(
|
|
190
|
-
s.
|
|
191
|
-
`/pressings/${pressingId}/
|
|
192
|
-
{ currencyType
|
|
180
|
+
s.listingViewSchema,
|
|
181
|
+
`/pressings/${pressingId}/listing`,
|
|
182
|
+
{ currencyType },
|
|
193
183
|
{ nullOn404: true },
|
|
194
184
|
),
|
|
195
185
|
|
|
@@ -217,15 +207,6 @@ export function createMisoApiClient(options: MisoApiClientOptions) {
|
|
|
217
207
|
{ nullOn404: true },
|
|
218
208
|
),
|
|
219
209
|
|
|
220
|
-
/** Confirmation preview for a pasted pressing id. `null` when it isn't a pressing. */
|
|
221
|
-
getPressingPreview: (pressingId: string): Promise<PressingPreview | null> =>
|
|
222
|
-
request(
|
|
223
|
-
s.pressingPreviewSchema,
|
|
224
|
-
`/pressings/${pressingId}/preview`,
|
|
225
|
-
{},
|
|
226
|
-
{ nullOn404: true },
|
|
227
|
-
),
|
|
228
|
-
|
|
229
210
|
// ── Artist ──────────────────────────────────────────────────────────────
|
|
230
211
|
/** An artist plus optional relationship expansions. */
|
|
231
212
|
getArtist: (
|
|
@@ -325,10 +306,8 @@ export function cacheBuster(nowMs: number = Date.now()): string {
|
|
|
325
306
|
* it with `workCacheClass(release.state)`.
|
|
326
307
|
*/
|
|
327
308
|
export const READ_CACHE_CLASS = {
|
|
328
|
-
getDiscover: "sale",
|
|
329
309
|
getPressing: "sale",
|
|
330
|
-
|
|
331
|
-
getPressingPreview: "sale",
|
|
310
|
+
getListing: "sale",
|
|
332
311
|
getRecordAlbum: "immutable",
|
|
333
312
|
getReceipt: "immutable",
|
|
334
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,27 +166,6 @@ export const pressingDetailWireSchema = z.object({
|
|
|
171
166
|
release: releaseDetailWireSchema,
|
|
172
167
|
});
|
|
173
168
|
|
|
174
|
-
export const saleDetailSchema = z.object({
|
|
175
|
-
sale: saleViewSchema,
|
|
176
|
-
release: releaseDetailSchema,
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
export const saleDetailWireSchema = z.object({
|
|
180
|
-
sale: saleViewSchema,
|
|
181
|
-
release: releaseDetailWireSchema,
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
export const discoverItemSchema = z.object({
|
|
185
|
-
sale: saleViewSchema,
|
|
186
|
-
releaseId: suiIdSchema,
|
|
187
|
-
title: z.string(),
|
|
188
|
-
/** Primary artists joined for display. Empty when no credits are set. */
|
|
189
|
-
artist: z.string(),
|
|
190
|
-
coverUrl: z.string().url().nullable(),
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
export const discoverShelfSchema = z.array(discoverItemSchema);
|
|
194
|
-
|
|
195
169
|
export const recordAlbumSchema = z.object({
|
|
196
170
|
recordId: suiIdSchema,
|
|
197
171
|
releaseId: suiIdSchema.nullable(),
|
|
@@ -205,16 +179,6 @@ export const recordAlbumWireSchema = z.object({
|
|
|
205
179
|
release: releaseDetailWireSchema.nullable().optional(),
|
|
206
180
|
});
|
|
207
181
|
|
|
208
|
-
export const pressingPreviewSchema = z.object({
|
|
209
|
-
pressingId: suiIdSchema,
|
|
210
|
-
title: z.string(),
|
|
211
|
-
subtitle: z.string().nullable(),
|
|
212
|
-
coverUrl: z.string().url().nullable(),
|
|
213
|
-
state: pressingStateSchema,
|
|
214
|
-
supply: u64Schema,
|
|
215
|
-
trackCount: z.number().int().min(0),
|
|
216
|
-
});
|
|
217
|
-
|
|
218
182
|
// ── Artist ───────────────────────────────────────────────────────────────────
|
|
219
183
|
|
|
220
184
|
export const partyMemberSchema = z.object({
|
package/src/types.ts
CHANGED
|
@@ -20,13 +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 SaleDetail = z.infer<typeof s.saleDetailSchema>;
|
|
26
|
-
export type DiscoverItem = z.infer<typeof s.discoverItemSchema>;
|
|
27
|
-
export type DiscoverShelf = z.infer<typeof s.discoverShelfSchema>;
|
|
28
24
|
export type RecordAlbum = z.infer<typeof s.recordAlbumSchema>;
|
|
29
|
-
export type PressingPreview = z.infer<typeof s.pressingPreviewSchema>;
|
|
30
25
|
|
|
31
26
|
export type PartyMember = z.infer<typeof s.partyMemberSchema>;
|
|
32
27
|
export type PlatformKey = z.infer<typeof s.platformKeySchema>;
|