@misofm/api-client 0.2.0 → 0.4.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/cache.ts +3 -6
- package/src/client.test.ts +9 -3
- package/src/client.ts +12 -6
- package/src/schemas.ts +46 -27
- package/src/types.ts +5 -2
package/package.json
CHANGED
package/src/cache.ts
CHANGED
|
@@ -20,12 +20,9 @@
|
|
|
20
20
|
// draft the same work before publication: actively being edited in
|
|
21
21
|
// studio, so the editor must see their own change land.
|
|
22
22
|
// artist a profile page. Owner edits should surface within a minute.
|
|
23
|
-
// sale a
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
// gates nothing. It is a display number, not a stock level, which
|
|
27
|
-
// is why a minute of staleness is cosmetic rather than a
|
|
28
|
-
// correctness problem.
|
|
23
|
+
// sale a Pressing or Listing. Their state and the uncapped run's supply
|
|
24
|
+
// can move, so clients refresh them quickly. The chain remains the
|
|
25
|
+
// purchase gate when an edge response is briefly stale.
|
|
29
26
|
// private address-scoped. Never enters a shared cache — the failure mode is
|
|
30
27
|
// serving one user's library to another.
|
|
31
28
|
//
|
package/src/client.test.ts
CHANGED
|
@@ -71,9 +71,9 @@ describe("URL construction", () => {
|
|
|
71
71
|
test("joins the artist include list into one param", async () => {
|
|
72
72
|
const { fetch, calls } = stubFetch({ status: 404 });
|
|
73
73
|
await createMisoApiClient({ baseUrl: BASE, fetch }).getArtist("0xp", {
|
|
74
|
-
include: ["roles", "tags"
|
|
74
|
+
include: ["roles", "tags"],
|
|
75
75
|
});
|
|
76
|
-
expect(calls[0]).toContain("include=roles%2Ctags
|
|
76
|
+
expect(calls[0]).toContain("include=roles%2Ctags");
|
|
77
77
|
});
|
|
78
78
|
|
|
79
79
|
test("joins catalog relationship expansions into one param", async () => {
|
|
@@ -89,6 +89,12 @@ describe("URL construction", () => {
|
|
|
89
89
|
expect(calls[2]).toContain("include=release%2CtrackCredits");
|
|
90
90
|
});
|
|
91
91
|
|
|
92
|
+
test("uses the permanent pressing preview route", async () => {
|
|
93
|
+
const { fetch, calls } = stubFetch({ status: 404 });
|
|
94
|
+
await createMisoApiClient({ baseUrl: BASE, fetch }).getPressingPreview("0xp");
|
|
95
|
+
expect(calls[0]).toBe(`${BASE}/read/v1/pressings/0xp/preview`);
|
|
96
|
+
});
|
|
97
|
+
|
|
92
98
|
test("a custom prefix is honored (self-hosted / direct-to-service)", async () => {
|
|
93
99
|
const { fetch, calls } = stubFetch({ body: balance });
|
|
94
100
|
await createMisoApiClient({
|
|
@@ -101,7 +107,7 @@ describe("URL construction", () => {
|
|
|
101
107
|
});
|
|
102
108
|
|
|
103
109
|
describe("not-found handling", () => {
|
|
104
|
-
test("
|
|
110
|
+
test("an unknown pressing is null, not an error", async () => {
|
|
105
111
|
const { fetch } = stubFetch({
|
|
106
112
|
status: 404,
|
|
107
113
|
body: { error: { code: "not_found", message: "gone" } },
|
package/src/client.ts
CHANGED
|
@@ -22,8 +22,9 @@ import type {
|
|
|
22
22
|
ArtistProfile,
|
|
23
23
|
Balance,
|
|
24
24
|
DiscoverShelf,
|
|
25
|
-
|
|
25
|
+
PressingPreview,
|
|
26
26
|
OwnedParty,
|
|
27
|
+
PendingMembership,
|
|
27
28
|
OwnedRecord,
|
|
28
29
|
OwnedWork,
|
|
29
30
|
Ownership,
|
|
@@ -203,10 +204,10 @@ export function createMisoApiClient(options: MisoApiClientOptions) {
|
|
|
203
204
|
),
|
|
204
205
|
|
|
205
206
|
/** Confirmation preview for a pasted pressing id. `null` when it isn't a pressing. */
|
|
206
|
-
|
|
207
|
+
getPressingPreview: (pressingId: string): Promise<PressingPreview | null> =>
|
|
207
208
|
request(
|
|
208
|
-
s.
|
|
209
|
-
`/
|
|
209
|
+
s.pressingPreviewSchema,
|
|
210
|
+
`/pressings/${pressingId}/preview`,
|
|
210
211
|
{},
|
|
211
212
|
{ nullOn404: true },
|
|
212
213
|
),
|
|
@@ -215,7 +216,7 @@ export function createMisoApiClient(options: MisoApiClientOptions) {
|
|
|
215
216
|
/** An artist plus optional relationship expansions. */
|
|
216
217
|
getArtist: (
|
|
217
218
|
partyId: string,
|
|
218
|
-
opts: { include?: readonly ("roles" | "tags"
|
|
219
|
+
opts: { include?: readonly ("roles" | "tags")[] } = {},
|
|
219
220
|
): Promise<ArtistProfile | null> =>
|
|
220
221
|
request(
|
|
221
222
|
s.artistProfileSchema,
|
|
@@ -239,6 +240,10 @@ export function createMisoApiClient(options: MisoApiClientOptions) {
|
|
|
239
240
|
getWalletParties: (address: string): Promise<OwnedParty[]> =>
|
|
240
241
|
required(s.ownedPartiesSchema, `/wallets/${address}/parties`),
|
|
241
242
|
|
|
243
|
+
/** Group invitations awaiting acceptance by this wallet's controlled parties. */
|
|
244
|
+
getPendingMemberships: (address: string): Promise<PendingMembership[]> =>
|
|
245
|
+
required(s.pendingMembershipsSchema, `/wallets/${address}/pending-memberships`),
|
|
246
|
+
|
|
242
247
|
/** The works this wallet administers, keyed by admin cap. */
|
|
243
248
|
getWalletWorks: (address: string): Promise<OwnedWork[]> =>
|
|
244
249
|
required(s.ownedWorksSchema, `/wallets/${address}/works`),
|
|
@@ -308,13 +313,14 @@ export function cacheBuster(nowMs: number = Date.now()): string {
|
|
|
308
313
|
export const READ_CACHE_CLASS = {
|
|
309
314
|
getDiscover: "sale",
|
|
310
315
|
getPressing: "sale",
|
|
311
|
-
|
|
316
|
+
getPressingPreview: "sale",
|
|
312
317
|
getRecordAlbum: "immutable",
|
|
313
318
|
getReceipt: "immutable",
|
|
314
319
|
getArtist: "artist",
|
|
315
320
|
getArtists: "artist",
|
|
316
321
|
getWalletRecords: "private",
|
|
317
322
|
getWalletParties: "private",
|
|
323
|
+
getPendingMemberships: "private",
|
|
318
324
|
getWalletWorks: "private",
|
|
319
325
|
getWork: "private",
|
|
320
326
|
getBalance: "private",
|
package/src/schemas.ts
CHANGED
|
@@ -58,6 +58,8 @@ export const trackViewSchema = z.object({
|
|
|
58
58
|
no: z.string(),
|
|
59
59
|
title: z.string(),
|
|
60
60
|
recordingId: suiIdSchema,
|
|
61
|
+
/** The composition underlying this track's recording. */
|
|
62
|
+
compositionId: suiIdSchema,
|
|
61
63
|
/** This track's share of the release's revenue, in basis points. */
|
|
62
64
|
splitBps: z.number().int().min(0).max(10_000),
|
|
63
65
|
disc: z.number().int().min(1),
|
|
@@ -128,19 +130,35 @@ export const currencySchema = z.object({
|
|
|
128
130
|
decimals: z.number().int().min(0).max(18),
|
|
129
131
|
});
|
|
130
132
|
|
|
133
|
+
export const pressingStateSchema = z.union([
|
|
134
|
+
z.object({
|
|
135
|
+
kind: z.literal("scheduled"),
|
|
136
|
+
startTimestampMs: z.number().int(),
|
|
137
|
+
}),
|
|
138
|
+
z.object({ kind: z.literal("active") }),
|
|
139
|
+
z.object({ kind: z.literal("paused") }),
|
|
140
|
+
]);
|
|
141
|
+
|
|
131
142
|
export const pressingViewSchema = z.object({
|
|
132
143
|
id: suiIdSchema,
|
|
133
144
|
releaseId: suiIdSchema,
|
|
134
|
-
|
|
145
|
+
state: pressingStateSchema,
|
|
146
|
+
/** Records pressed so far. The permanent run is intentionally uncapped. */
|
|
147
|
+
supply: u64Schema,
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
export const listingViewSchema = z.object({
|
|
151
|
+
id: suiIdSchema,
|
|
152
|
+
pressingId: suiIdSchema,
|
|
153
|
+
releaseId: suiIdSchema,
|
|
135
154
|
price: priceSchema,
|
|
136
155
|
currency: currencySchema,
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
soldOut: z.boolean(),
|
|
156
|
+
state: z.enum(["enabled", "disabled"]),
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
export const saleViewSchema = z.object({
|
|
160
|
+
pressing: pressingViewSchema,
|
|
161
|
+
listing: listingViewSchema,
|
|
144
162
|
});
|
|
145
163
|
|
|
146
164
|
export const pressingDetailSchema = z.object({
|
|
@@ -154,7 +172,7 @@ export const pressingDetailWireSchema = z.object({
|
|
|
154
172
|
});
|
|
155
173
|
|
|
156
174
|
export const discoverItemSchema = z.object({
|
|
157
|
-
|
|
175
|
+
sale: saleViewSchema,
|
|
158
176
|
releaseId: suiIdSchema,
|
|
159
177
|
title: z.string(),
|
|
160
178
|
/** Primary artists joined for display. Empty when no credits are set. */
|
|
@@ -177,13 +195,13 @@ export const recordAlbumWireSchema = z.object({
|
|
|
177
195
|
release: releaseDetailWireSchema.nullable().optional(),
|
|
178
196
|
});
|
|
179
197
|
|
|
180
|
-
export const
|
|
198
|
+
export const pressingPreviewSchema = z.object({
|
|
181
199
|
pressingId: suiIdSchema,
|
|
182
|
-
currency: currencySchema,
|
|
183
200
|
title: z.string(),
|
|
184
201
|
subtitle: z.string().nullable(),
|
|
185
202
|
coverUrl: z.string().url().nullable(),
|
|
186
|
-
|
|
203
|
+
state: pressingStateSchema,
|
|
204
|
+
supply: u64Schema,
|
|
187
205
|
trackCount: z.number().int().min(0),
|
|
188
206
|
});
|
|
189
207
|
|
|
@@ -243,14 +261,6 @@ export const partyLinkSchema = z.object({
|
|
|
243
261
|
|
|
244
262
|
export const partyCtaSchema = z.object({ label: z.string(), url: z.string() });
|
|
245
263
|
|
|
246
|
-
export const featuredReleaseSchema = z.object({
|
|
247
|
-
pressingId: suiIdSchema,
|
|
248
|
-
releaseId: suiIdSchema,
|
|
249
|
-
title: z.string(),
|
|
250
|
-
artist: z.string(),
|
|
251
|
-
coverUrl: z.string().url().nullable(),
|
|
252
|
-
});
|
|
253
|
-
|
|
254
264
|
export const artistProfileSchema = z.object({
|
|
255
265
|
id: suiIdSchema,
|
|
256
266
|
kind: z.enum(["individual", "group"]),
|
|
@@ -268,8 +278,6 @@ export const artistProfileSchema = z.object({
|
|
|
268
278
|
/** Present only when requested via `include` — the owner-editor fields. */
|
|
269
279
|
roles: z.array(z.string()).optional(),
|
|
270
280
|
tags: z.array(z.string()).optional(),
|
|
271
|
-
/** Present only when requested; null means the artist has no pinned release. */
|
|
272
|
-
featured: featuredReleaseSchema.nullable().optional(),
|
|
273
281
|
avatarUrl: z.string().url(),
|
|
274
282
|
});
|
|
275
283
|
|
|
@@ -302,6 +310,16 @@ export const ownedPartySchema = z.object({
|
|
|
302
310
|
|
|
303
311
|
export const ownedPartiesSchema = z.array(ownedPartySchema);
|
|
304
312
|
|
|
313
|
+
/** A group invitation awaiting action by a party the wallet administers. */
|
|
314
|
+
export const pendingMembershipSchema = z.object({
|
|
315
|
+
memberPartyId: suiIdSchema,
|
|
316
|
+
memberCapId: suiIdSchema,
|
|
317
|
+
groupId: suiIdSchema,
|
|
318
|
+
groupName: z.string(),
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
export const pendingMembershipsSchema = z.array(pendingMembershipSchema);
|
|
322
|
+
|
|
305
323
|
export const workKindSchema = z.enum(["composition", "recording", "release"]);
|
|
306
324
|
|
|
307
325
|
export const ownedWorkSchema = z.object({
|
|
@@ -342,13 +360,14 @@ export const ownershipSchema = z.object({
|
|
|
342
360
|
// ── Receipts ─────────────────────────────────────────────────────────────────
|
|
343
361
|
|
|
344
362
|
export const recordSaleSchema = z.object({
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
363
|
+
listingId: suiIdSchema,
|
|
364
|
+
pressingId: suiIdSchema,
|
|
365
|
+
releaseId: suiIdSchema,
|
|
348
366
|
recordId: suiIdSchema,
|
|
349
367
|
number: u64Schema,
|
|
350
368
|
paid: u64Schema,
|
|
351
|
-
|
|
369
|
+
price: priceSchema,
|
|
370
|
+
buyer: suiIdSchema,
|
|
352
371
|
});
|
|
353
372
|
|
|
354
373
|
export const trackRoyaltySchema = z.object({
|
|
@@ -365,7 +384,7 @@ export const trackRoyaltySchema = z.object({
|
|
|
365
384
|
export const purchaseReceiptSchema = z.object({
|
|
366
385
|
sale: recordSaleSchema,
|
|
367
386
|
detail: pressingDetailSchema,
|
|
368
|
-
/** The
|
|
387
|
+
/** The Listing price — differs from `sale.paid` on a floor-price overpay. */
|
|
369
388
|
price: u64Schema,
|
|
370
389
|
tracks: z.array(trackRoyaltySchema),
|
|
371
390
|
});
|
package/src/types.ts
CHANGED
|
@@ -17,23 +17,26 @@ export type ReleaseDetail = z.infer<typeof s.releaseDetailSchema>;
|
|
|
17
17
|
export type TrackCredits = z.infer<typeof s.trackCreditsSchema>;
|
|
18
18
|
export type Price = z.infer<typeof s.priceSchema>;
|
|
19
19
|
export type Currency = z.infer<typeof s.currencySchema>;
|
|
20
|
+
export type PressingState = z.infer<typeof s.pressingStateSchema>;
|
|
20
21
|
export type PressingView = z.infer<typeof s.pressingViewSchema>;
|
|
22
|
+
export type ListingView = z.infer<typeof s.listingViewSchema>;
|
|
23
|
+
export type SaleView = z.infer<typeof s.saleViewSchema>;
|
|
21
24
|
export type PressingDetail = z.infer<typeof s.pressingDetailSchema>;
|
|
22
25
|
export type DiscoverItem = z.infer<typeof s.discoverItemSchema>;
|
|
23
26
|
export type DiscoverShelf = z.infer<typeof s.discoverShelfSchema>;
|
|
24
27
|
export type RecordAlbum = z.infer<typeof s.recordAlbumSchema>;
|
|
25
|
-
export type
|
|
28
|
+
export type PressingPreview = z.infer<typeof s.pressingPreviewSchema>;
|
|
26
29
|
|
|
27
30
|
export type PartyMember = z.infer<typeof s.partyMemberSchema>;
|
|
28
31
|
export type PlatformKey = z.infer<typeof s.platformKeySchema>;
|
|
29
32
|
export type PartyLink = z.infer<typeof s.partyLinkSchema>;
|
|
30
33
|
export type PartyCta = z.infer<typeof s.partyCtaSchema>;
|
|
31
34
|
export type ArtistProfile = z.infer<typeof s.artistProfileSchema>;
|
|
32
|
-
export type FeaturedRelease = z.infer<typeof s.featuredReleaseSchema>;
|
|
33
35
|
export type PartySummary = z.infer<typeof s.partySummarySchema>;
|
|
34
36
|
|
|
35
37
|
export type OwnedRecord = z.infer<typeof s.ownedRecordSchema>;
|
|
36
38
|
export type OwnedParty = z.infer<typeof s.ownedPartySchema>;
|
|
39
|
+
export type PendingMembership = z.infer<typeof s.pendingMembershipSchema>;
|
|
37
40
|
export type WorkKind = z.infer<typeof s.workKindSchema>;
|
|
38
41
|
export type OwnedWork = z.infer<typeof s.ownedWorkSchema>;
|
|
39
42
|
export type WorkDetail = z.infer<typeof s.workDetailSchema>;
|