@misofm/sdk 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/dist/catalog.d.ts +4 -17
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +46 -58
- package/dist/catalog.js.map +1 -1
- package/dist/cover.d.ts +10 -0
- package/dist/cover.d.ts.map +1 -1
- package/dist/cover.js +66 -19
- package/dist/cover.js.map +1 -1
- package/dist/credits.d.ts +10 -0
- package/dist/credits.d.ts.map +1 -1
- package/dist/credits.js +149 -39
- package/dist/credits.js.map +1 -1
- package/dist/drop.d.ts +7 -0
- package/dist/drop.d.ts.map +1 -1
- package/dist/drop.js +84 -34
- package/dist/drop.js.map +1 -1
- package/dist/pressing.d.ts.map +1 -1
- package/dist/pressing.js +59 -16
- package/dist/pressing.js.map +1 -1
- package/dist/read/artist.d.ts +1 -6
- package/dist/read/artist.d.ts.map +1 -1
- package/dist/read/artist.js +39 -22
- package/dist/read/artist.js.map +1 -1
- package/dist/read/catalog.d.ts +24 -3
- package/dist/read/catalog.d.ts.map +1 -1
- package/dist/read/catalog.js +190 -89
- package/dist/read/catalog.js.map +1 -1
- package/dist/read/index.d.ts +5 -3
- package/dist/read/index.d.ts.map +1 -1
- package/dist/read/index.js +2 -2
- package/dist/read/index.js.map +1 -1
- package/dist/read/types.d.ts +12 -1
- package/dist/read/types.d.ts.map +1 -1
- package/dist/read/works.d.ts +3 -2
- package/dist/read/works.d.ts.map +1 -1
- package/dist/read/works.js +158 -48
- package/dist/read/works.js.map +1 -1
- package/package.json +1 -1
- package/src/catalog.ts +71 -77
- package/src/cover.ts +107 -26
- package/src/credits.ts +353 -89
- package/src/drop.ts +150 -40
- package/src/pressing.ts +146 -36
- package/src/read/artist.ts +73 -34
- package/src/read/catalog.ts +324 -89
- package/src/read/index.ts +23 -2
- package/src/read/types.ts +16 -2
- package/src/read/works.ts +224 -59
package/src/read/works.ts
CHANGED
|
@@ -3,14 +3,15 @@
|
|
|
3
3
|
|
|
4
4
|
import type { ClientWithCoreApi } from "@mysten/sui/client";
|
|
5
5
|
import type { SuiGraphQLClient } from "@mysten/sui/graphql";
|
|
6
|
+
import { normalizeSuiAddress } from "@mysten/sui/utils";
|
|
6
7
|
import {
|
|
8
|
+
contracts,
|
|
7
9
|
extractTypeParams2,
|
|
8
10
|
getCompositionsByIds,
|
|
9
|
-
getRecordingsByIds,
|
|
10
|
-
getReleasesByIds,
|
|
11
11
|
type Composition,
|
|
12
12
|
type Recording,
|
|
13
13
|
type Release,
|
|
14
|
+
type TrackState,
|
|
14
15
|
} from "@misonetwork/sdk";
|
|
15
16
|
|
|
16
17
|
export interface WorkShareTypes {
|
|
@@ -23,70 +24,114 @@ export interface WorkAddressesByShareType {
|
|
|
23
24
|
recordings: Partial<Record<string, string>>;
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
interface
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
}
|
|
27
|
+
interface WorkAddressConnection {
|
|
28
|
+
nodes: Array<{
|
|
29
|
+
address: string;
|
|
30
|
+
asMoveObject?: {
|
|
31
|
+
contents?: { type?: { repr?: string } | null } | null;
|
|
32
|
+
} | null;
|
|
33
|
+
}>;
|
|
34
|
+
pageInfo?: { hasNextPage: boolean; endCursor: string | null };
|
|
33
35
|
}
|
|
34
36
|
|
|
35
|
-
/** Resolve work share types
|
|
37
|
+
/** Resolve all work share types in one aliased GraphQL request. */
|
|
36
38
|
export async function getWorkAddressesByShareTypes(
|
|
37
39
|
client: SuiGraphQLClient,
|
|
38
40
|
shareTypes: WorkShareTypes,
|
|
39
41
|
misoPackageId: string,
|
|
40
42
|
): Promise<WorkAddressesByShareType> {
|
|
41
43
|
const compositions = [...new Set(shareTypes.compositions)];
|
|
42
|
-
const
|
|
44
|
+
const recordings = new Set(shareTypes.recordings);
|
|
43
45
|
const out: WorkAddressesByShareType = { compositions: {}, recordings: {} };
|
|
46
|
+
if (compositions.length === 0 && recordings.size === 0) return out;
|
|
44
47
|
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
48
|
+
const declarations: string[] = [];
|
|
49
|
+
const selections: string[] = [];
|
|
50
|
+
const variables: Record<string, string> = {};
|
|
51
|
+
compositions.forEach((shareType, index) => {
|
|
52
|
+
const variable = `compositionType${index}`;
|
|
53
|
+
declarations.push(`$${variable}: String!`);
|
|
54
|
+
selections.push(
|
|
55
|
+
`composition${index}: objects(first: 1, filter: { type: $${variable} }) { nodes { address } }`,
|
|
56
|
+
);
|
|
57
|
+
variables[variable] =
|
|
58
|
+
`${misoPackageId}::composition::Composition<${shareType}>`;
|
|
59
|
+
});
|
|
60
|
+
if (recordings.size > 0) {
|
|
61
|
+
declarations.push("$recordingType: String!");
|
|
62
|
+
selections.push(`recordings: objects(first: 50, filter: { type: $recordingType }) {
|
|
63
|
+
pageInfo { hasNextPage endCursor }
|
|
64
|
+
nodes { address asMoveObject { contents { type { repr } } } }
|
|
65
|
+
}`);
|
|
66
|
+
variables.recordingType = `${misoPackageId}::recording::Recording`;
|
|
62
67
|
}
|
|
63
68
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
69
|
+
const result = await client.query<
|
|
70
|
+
Record<string, WorkAddressConnection | null>,
|
|
71
|
+
Record<string, string>
|
|
72
|
+
>({
|
|
73
|
+
query: `query WorkAddressesByShareTypes(${declarations.join(", ")}) {
|
|
74
|
+
${selections.join("\n")}
|
|
75
|
+
}`,
|
|
76
|
+
variables,
|
|
77
|
+
});
|
|
78
|
+
if (result.errors?.length) {
|
|
79
|
+
throw new AggregateError(
|
|
80
|
+
result.errors.map((error) => new Error(error.message)),
|
|
81
|
+
"Work type discovery failed",
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
compositions.forEach((shareType, index) => {
|
|
85
|
+
const address = result.data?.[`composition${index}`]?.nodes[0]?.address;
|
|
86
|
+
if (address) out.compositions[shareType] = address;
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const readRecordingPage = (
|
|
90
|
+
page: WorkAddressConnection | null | undefined,
|
|
91
|
+
) => {
|
|
92
|
+
for (const node of page?.nodes ?? []) {
|
|
93
|
+
const repr = node.asMoveObject?.contents?.type?.repr;
|
|
77
94
|
if (!repr) continue;
|
|
78
|
-
let recordingShareType: string | undefined;
|
|
79
95
|
try {
|
|
80
|
-
[
|
|
96
|
+
const [shareType] = extractTypeParams2(repr);
|
|
97
|
+
if (recordings.has(shareType)) out.recordings[shareType] = node.address;
|
|
81
98
|
} catch {
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
if (recordingShareType && recordingSet.has(recordingShareType)) {
|
|
85
|
-
out.recordings[recordingShareType] = node.address;
|
|
99
|
+
// Ignore objects whose deployed type does not match the Recording ABI.
|
|
86
100
|
}
|
|
87
101
|
}
|
|
88
|
-
}
|
|
102
|
+
};
|
|
89
103
|
|
|
104
|
+
let page = result.data?.recordings;
|
|
105
|
+
readRecordingPage(page);
|
|
106
|
+
while (
|
|
107
|
+
page?.pageInfo?.hasNextPage &&
|
|
108
|
+
page.pageInfo.endCursor &&
|
|
109
|
+
Object.keys(out.recordings).length < recordings.size
|
|
110
|
+
) {
|
|
111
|
+
const next = await client.query<
|
|
112
|
+
{ recordings: WorkAddressConnection | null },
|
|
113
|
+
{ recordingType: string; cursor: string }
|
|
114
|
+
>({
|
|
115
|
+
query: `query RecordingWorkAddresses($recordingType: String!, $cursor: String!) {
|
|
116
|
+
recordings: objects(first: 50, after: $cursor, filter: { type: $recordingType }) {
|
|
117
|
+
pageInfo { hasNextPage endCursor }
|
|
118
|
+
nodes { address asMoveObject { contents { type { repr } } } }
|
|
119
|
+
}
|
|
120
|
+
}`,
|
|
121
|
+
variables: {
|
|
122
|
+
recordingType: variables.recordingType!,
|
|
123
|
+
cursor: page.pageInfo.endCursor,
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
if (next.errors?.length) {
|
|
127
|
+
throw new AggregateError(
|
|
128
|
+
next.errors.map((error) => new Error(error.message)),
|
|
129
|
+
"Recording type discovery failed",
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
page = next.data?.recordings;
|
|
133
|
+
readRecordingPage(page);
|
|
134
|
+
}
|
|
90
135
|
return out;
|
|
91
136
|
}
|
|
92
137
|
|
|
@@ -102,14 +147,127 @@ export interface WorksById {
|
|
|
102
147
|
releases: Partial<Record<string, Release>>;
|
|
103
148
|
}
|
|
104
149
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
150
|
+
type Parsed = Record<string, any>;
|
|
151
|
+
|
|
152
|
+
function workState(value: Parsed): Composition["state"] {
|
|
153
|
+
return value?.$kind === "Published"
|
|
154
|
+
? { type: "Published", timestampMs: Number(value.Published) }
|
|
155
|
+
: { type: "Initialized" };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function parseComposition(id: string, content: Uint8Array): Composition {
|
|
159
|
+
const value = contracts.composition.Composition.parse(content) as Parsed;
|
|
160
|
+
return {
|
|
161
|
+
id,
|
|
162
|
+
state: workState(value.state),
|
|
163
|
+
title: String(value.title),
|
|
164
|
+
royaltyRate: {
|
|
165
|
+
value: Number(
|
|
166
|
+
Array.isArray(value.royalty_rate)
|
|
167
|
+
? value.royalty_rate[0]
|
|
168
|
+
: value.royalty_rate,
|
|
169
|
+
),
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function parseRecording(id: string, content: Uint8Array): Recording {
|
|
175
|
+
const value = contracts.recording.Recording.parse(content) as Parsed;
|
|
176
|
+
return { id, state: workState(value.state) };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function parseReleaseObject(
|
|
180
|
+
id: string,
|
|
181
|
+
content: Uint8Array,
|
|
182
|
+
json: unknown,
|
|
183
|
+
): Release {
|
|
184
|
+
const deployed = json as {
|
|
185
|
+
discs?: Parsed[];
|
|
186
|
+
state?: Parsed;
|
|
187
|
+
title?: unknown;
|
|
188
|
+
} | null;
|
|
189
|
+
if (deployed?.discs) {
|
|
190
|
+
const tracks = deployed.discs.flatMap((disc) => disc.tracks ?? []);
|
|
191
|
+
const publishedAt =
|
|
192
|
+
deployed.state?.["@variant"] === "Published" ? deployed.state.pos0 : null;
|
|
193
|
+
return {
|
|
194
|
+
id,
|
|
195
|
+
state:
|
|
196
|
+
publishedAt == null
|
|
197
|
+
? { type: "Initialized" }
|
|
198
|
+
: { type: "Published", timestampMs: Number(publishedAt) },
|
|
199
|
+
title: String(deployed.title ?? ""),
|
|
200
|
+
tracks: tracks.map((track) => ({
|
|
201
|
+
state: (track.state?.["@variant"] ?? "Unassigned") as TrackState,
|
|
202
|
+
recordingId: String(track.recording_id),
|
|
203
|
+
splitBps: { value: Number(track.split_bps?.pos0 ?? track.split_bps) },
|
|
204
|
+
})),
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
const value = contracts.release.Release.parse(content) as Parsed;
|
|
208
|
+
return {
|
|
209
|
+
id,
|
|
210
|
+
state: workState(value.state),
|
|
211
|
+
title: String(value.title),
|
|
212
|
+
tracks: (value.tracks ?? []).map((track: Parsed) => ({
|
|
213
|
+
state: (track.state?.$kind ?? "Unassigned") as TrackState,
|
|
214
|
+
recordingId: String(track.recording_id),
|
|
215
|
+
splitBps: {
|
|
216
|
+
value: Number(
|
|
217
|
+
Array.isArray(track.split_bps) ? track.split_bps[0] : track.split_bps,
|
|
218
|
+
),
|
|
219
|
+
},
|
|
220
|
+
})),
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/** Fetch and parse heterogeneous works through one Core bulk request. */
|
|
225
|
+
export async function getWorksByIds(
|
|
226
|
+
client: ClientWithCoreApi,
|
|
227
|
+
ids: WorkIds,
|
|
228
|
+
): Promise<WorksById> {
|
|
229
|
+
const kinds = new Map<string, keyof WorksById>();
|
|
230
|
+
for (const [kind, objectIds] of Object.entries(ids) as Array<
|
|
231
|
+
[keyof WorksById, readonly string[]]
|
|
232
|
+
>) {
|
|
233
|
+
for (const objectId of objectIds) {
|
|
234
|
+
const normalized = normalizeSuiAddress(objectId);
|
|
235
|
+
const previous = kinds.get(normalized);
|
|
236
|
+
if (previous && previous !== kind) {
|
|
237
|
+
throw new Error(
|
|
238
|
+
`Work ${normalized} was requested as both ${previous} and ${kind}`,
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
kinds.set(normalized, kind);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
const out: WorksById = { compositions: {}, recordings: {}, releases: {} };
|
|
245
|
+
if (kinds.size === 0) return out;
|
|
246
|
+
const { objects } = await client.core.getObjects({
|
|
247
|
+
objectIds: [...kinds.keys()],
|
|
248
|
+
include: { content: true, json: true },
|
|
249
|
+
});
|
|
250
|
+
for (const object of objects) {
|
|
251
|
+
if (object instanceof Error || !object.content) continue;
|
|
252
|
+
const kind = kinds.get(normalizeSuiAddress(object.objectId));
|
|
253
|
+
if (kind === "compositions")
|
|
254
|
+
out.compositions[object.objectId] = parseComposition(
|
|
255
|
+
object.objectId,
|
|
256
|
+
object.content,
|
|
257
|
+
);
|
|
258
|
+
else if (kind === "recordings")
|
|
259
|
+
out.recordings[object.objectId] = parseRecording(
|
|
260
|
+
object.objectId,
|
|
261
|
+
object.content,
|
|
262
|
+
);
|
|
263
|
+
else if (kind === "releases")
|
|
264
|
+
out.releases[object.objectId] = parseReleaseObject(
|
|
265
|
+
object.objectId,
|
|
266
|
+
object.content,
|
|
267
|
+
object.json,
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
return out;
|
|
113
271
|
}
|
|
114
272
|
|
|
115
273
|
/**
|
|
@@ -125,7 +283,10 @@ export async function getRecordingTitles(
|
|
|
125
283
|
const ids = [...new Set(recordingIds)];
|
|
126
284
|
if (ids.length === 0) return {};
|
|
127
285
|
|
|
128
|
-
const { objects } = await client.core.getObjects({
|
|
286
|
+
const { objects } = await client.core.getObjects({
|
|
287
|
+
objectIds: ids,
|
|
288
|
+
include: { json: true },
|
|
289
|
+
});
|
|
129
290
|
const titles: Record<string, string> = {};
|
|
130
291
|
const compositionShareByRecording: Record<string, string> = {};
|
|
131
292
|
|
|
@@ -155,9 +316,13 @@ export async function getRecordingTitles(
|
|
|
155
316
|
client,
|
|
156
317
|
Object.values(addresses.compositions).filter((id): id is string => !!id),
|
|
157
318
|
);
|
|
158
|
-
for (const [recordingId, shareType] of Object.entries(
|
|
319
|
+
for (const [recordingId, shareType] of Object.entries(
|
|
320
|
+
compositionShareByRecording,
|
|
321
|
+
)) {
|
|
159
322
|
const compositionId = addresses.compositions[shareType];
|
|
160
|
-
const title = compositionId
|
|
323
|
+
const title = compositionId
|
|
324
|
+
? compositions[compositionId]?.title
|
|
325
|
+
: undefined;
|
|
161
326
|
if (title) titles[recordingId] = title;
|
|
162
327
|
}
|
|
163
328
|
return titles;
|