@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@misofm/api-client",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Typed client and response contract for the Miso API read layer. The one definition of what a Miso read returns.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
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 live pressing. The only field that moves between reads is the
24
- // copies-sold counter and a pressing is UNCAPPED (one open run,
25
- // no editions, no supply cap, no sold-out state), so that counter
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
  //
@@ -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", "featured"],
74
+ include: ["roles", "tags"],
75
75
  });
76
- expect(calls[0]).toContain("include=roles%2Ctags%2Cfeatured");
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("a superseded pressing is null, not an error", async () => {
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
- DropPreview,
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
- getDropPreview: (dropId: string): Promise<DropPreview | null> =>
207
+ getPressingPreview: (pressingId: string): Promise<PressingPreview | null> =>
207
208
  request(
208
- s.dropPreviewSchema,
209
- `/drops/${dropId}/preview`,
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" | "featured")[] } = {},
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
- getDropPreview: "sale",
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
- edition: z.number().int().min(0),
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
- quantitySold: u64Schema,
138
- /** Null = uncapped (an open edition). */
139
- maxSupply: u64Schema.nullable(),
140
- startTimestampMs: z.number().int(),
141
- /** Null = evergreen. */
142
- endTimestampMs: z.number().int().nullable(),
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
- pressing: pressingViewSchema,
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 dropPreviewSchema = z.object({
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
- price: priceSchema,
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
- dropId: z.string(),
346
- releaseId: z.string(),
347
- edition: z.number().int().min(0),
363
+ listingId: suiIdSchema,
364
+ pressingId: suiIdSchema,
365
+ releaseId: suiIdSchema,
348
366
  recordId: suiIdSchema,
349
367
  number: u64Schema,
350
368
  paid: u64Schema,
351
- buyer: z.string(),
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 drop's list price — differs from `sale.paid` on a floor-price overpay. */
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 DropPreview = z.infer<typeof s.dropPreviewSchema>;
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>;