@misofm/sdk 0.3.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/README.md +31 -0
- package/dist/auth.d.ts +73 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +193 -0
- package/dist/auth.js.map +1 -0
- 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 +5 -1
- package/src/auth.ts +275 -0
- 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/catalog.ts
CHANGED
|
@@ -14,16 +14,16 @@
|
|
|
14
14
|
import type { ClientWithCoreApi } from "@mysten/sui/client";
|
|
15
15
|
import type { SuiGraphQLClient } from "@mysten/sui/graphql";
|
|
16
16
|
import {
|
|
17
|
-
|
|
17
|
+
extractTypeParams2,
|
|
18
18
|
getOwnedRecordingAdminCaps,
|
|
19
|
-
|
|
20
|
-
getRecordingShareTypes,
|
|
19
|
+
getRecordingsByIds,
|
|
21
20
|
getReleaseById,
|
|
22
21
|
type Recording,
|
|
23
22
|
} from "@misonetwork/sdk";
|
|
23
|
+
import { getWorkAddressesByShareTypes } from "./read/works.ts";
|
|
24
24
|
import {
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
getCompositionCreditsByIds,
|
|
26
|
+
getRecordingCreditsByIds,
|
|
27
27
|
type CreditView,
|
|
28
28
|
type RecordingCreditsView,
|
|
29
29
|
} from "./credits.ts";
|
|
@@ -85,51 +85,60 @@ export async function getTrackCreditsByRecordingIds(
|
|
|
85
85
|
options: GetReleaseTrackCreditsOptions,
|
|
86
86
|
): Promise<Record<string, ReleaseTrackCredits>> {
|
|
87
87
|
const recordingIds = [...new Set(recordingIdsInput)];
|
|
88
|
+
if (recordingIds.length === 0) return {};
|
|
88
89
|
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
)
|
|
90
|
+
const [recordingCreditsById, recordingObjects] = await Promise.all([
|
|
91
|
+
getRecordingCreditsByIds(
|
|
92
|
+
client,
|
|
93
|
+
recordingIds,
|
|
94
|
+
options.recordingCreditsPackageId,
|
|
95
|
+
),
|
|
96
|
+
client.core.getObjects({ objectIds: recordingIds }),
|
|
97
|
+
]);
|
|
98
|
+
const recordingReads = recordingObjects.objects.map((object, index) => {
|
|
99
|
+
const recordingId = recordingIds[index]!;
|
|
100
|
+
if (object instanceof Error) throw object;
|
|
101
|
+
const [, compositionShareType] = extractTypeParams2(object.type);
|
|
102
|
+
return {
|
|
103
|
+
recordingId,
|
|
104
|
+
recordingCredits: recordingCreditsById[recordingId] ?? null,
|
|
105
|
+
compositionShareType,
|
|
106
|
+
};
|
|
107
|
+
});
|
|
98
108
|
|
|
99
|
-
const compositionShareTypes = [
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
);
|
|
107
|
-
if (!compositionId) throw new Error(`Composition not found for share type: ${shareType}`);
|
|
108
|
-
return [shareType, compositionId] as const;
|
|
109
|
-
}),
|
|
109
|
+
const compositionShareTypes = [
|
|
110
|
+
...new Set(recordingReads.map((read) => read.compositionShareType)),
|
|
111
|
+
];
|
|
112
|
+
const addresses = await getWorkAddressesByShareTypes(
|
|
113
|
+
graphqlClient,
|
|
114
|
+
{ compositions: compositionShareTypes, recordings: [] },
|
|
115
|
+
options.misoPackageId,
|
|
110
116
|
);
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
)
|
|
121
|
-
|
|
122
|
-
|
|
117
|
+
for (const shareType of compositionShareTypes) {
|
|
118
|
+
if (!addresses.compositions[shareType]) {
|
|
119
|
+
throw new Error(`Composition not found for share type: ${shareType}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
const compositionIds = [
|
|
123
|
+
...new Set(
|
|
124
|
+
Object.values(addresses.compositions).filter(
|
|
125
|
+
(id): id is string => id !== undefined,
|
|
126
|
+
),
|
|
127
|
+
),
|
|
128
|
+
];
|
|
129
|
+
const compositionCreditsById = await getCompositionCreditsByIds(
|
|
130
|
+
client,
|
|
131
|
+
compositionIds,
|
|
132
|
+
options.compositionCreditsPackageId,
|
|
123
133
|
);
|
|
124
|
-
const compositionCreditsById = new Map(compositionCreditEntries);
|
|
125
134
|
|
|
126
135
|
return Object.fromEntries(
|
|
127
136
|
recordingReads.map((read) => {
|
|
128
|
-
const compositionId =
|
|
137
|
+
const compositionId = addresses.compositions[read.compositionShareType]!;
|
|
129
138
|
return [
|
|
130
139
|
read.recordingId,
|
|
131
140
|
{
|
|
132
|
-
compositionCredits: compositionCreditsById
|
|
141
|
+
compositionCredits: compositionCreditsById[compositionId] ?? [],
|
|
133
142
|
recordingCredits: read.recordingCredits ?? EMPTY_RECORDING_CREDITS,
|
|
134
143
|
},
|
|
135
144
|
];
|
|
@@ -139,9 +148,8 @@ export async function getTrackCreditsByRecordingIds(
|
|
|
139
148
|
|
|
140
149
|
export interface GetAdministeredRecordingsOptions {
|
|
141
150
|
/**
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
* concurrent requests as they have recordings. Defaults to 10.
|
|
151
|
+
* @deprecated Resolution is batched; this option is retained for source
|
|
152
|
+
* compatibility and no longer affects request concurrency.
|
|
145
153
|
*/
|
|
146
154
|
concurrency?: number;
|
|
147
155
|
}
|
|
@@ -149,20 +157,8 @@ export interface GetAdministeredRecordingsOptions {
|
|
|
149
157
|
/**
|
|
150
158
|
* Every `Recording` administered by `owner`.
|
|
151
159
|
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
* The second stage needs GraphQL and is inherently one lookup per cap, because
|
|
156
|
-
* `RecordingAdminCap` carries no back-pointer to its recording — unlike
|
|
157
|
-
* `ReleaseAdminCap`, which stores `release_id` and can therefore be resolved
|
|
158
|
-
* entirely over the Core API. The caps are derived objects (recording → cap via
|
|
159
|
-
* `deriveObjectID`), and that derivation cannot be inverted, so the share type
|
|
160
|
-
* is the only link back. Adding a `recording_id: ID` field to
|
|
161
|
-
* `RecordingAdminCap` on the protocol side would make this a pure Core-API
|
|
162
|
-
* batch read and remove both the GraphQL dependency and the fan-out.
|
|
163
|
-
*
|
|
164
|
-
* Until then the lookups run concurrently in bounded batches rather than
|
|
165
|
-
* serially.
|
|
160
|
+
* Three bounded stages: list the owner's `RecordingAdminCap`s, resolve every
|
|
161
|
+
* share type in one aliased GraphQL query, then batch-fetch the recordings.
|
|
166
162
|
*/
|
|
167
163
|
export async function getAdministeredRecordings(
|
|
168
164
|
client: ClientWithCoreApi,
|
|
@@ -171,25 +167,23 @@ export async function getAdministeredRecordings(
|
|
|
171
167
|
misoPackageId: string,
|
|
172
168
|
options: GetAdministeredRecordingsOptions = {},
|
|
173
169
|
): Promise<Recording[]> {
|
|
174
|
-
|
|
170
|
+
void options;
|
|
175
171
|
const caps = await getOwnedRecordingAdminCaps(client, owner, misoPackageId);
|
|
176
|
-
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
}
|
|
194
|
-
return recordings;
|
|
172
|
+
if (caps.length === 0) return [];
|
|
173
|
+
const addresses = await getWorkAddressesByShareTypes(
|
|
174
|
+
graphqlClient,
|
|
175
|
+
{ compositions: [], recordings: caps.map((cap) => cap.shareType) },
|
|
176
|
+
misoPackageId,
|
|
177
|
+
);
|
|
178
|
+
const byId = await getRecordingsByIds(
|
|
179
|
+
client,
|
|
180
|
+
Object.values(addresses.recordings).filter(
|
|
181
|
+
(id): id is string => id !== undefined,
|
|
182
|
+
),
|
|
183
|
+
);
|
|
184
|
+
return caps.flatMap((cap) => {
|
|
185
|
+
const id = addresses.recordings[cap.shareType];
|
|
186
|
+
const recording = id ? byId[id] : undefined;
|
|
187
|
+
return recording ? [recording] : [];
|
|
188
|
+
});
|
|
195
189
|
}
|
package/src/cover.ts
CHANGED
|
@@ -19,7 +19,6 @@ import { bcs } from "@mysten/sui/bcs";
|
|
|
19
19
|
import { deriveDynamicFieldID } from "@mysten/sui/utils";
|
|
20
20
|
import type { TxThunk } from "./transactions.ts";
|
|
21
21
|
import { OPTION_NONE, OPTION_SOME } from "./internal.ts";
|
|
22
|
-
import { isNotFound } from "./queries.ts";
|
|
23
22
|
import * as coverArt from "./contracts/cover_art/cover_art.ts";
|
|
24
23
|
import * as releaseCoverArt from "./contracts/release_cover_art/release_cover_art.ts";
|
|
25
24
|
|
|
@@ -45,15 +44,27 @@ export function setReleaseCover(p: SetReleaseCoverParams): TxThunk {
|
|
|
45
44
|
return (tx) => {
|
|
46
45
|
const walrusType = `${p.oriPackageId}::walrus_data::WalrusData`;
|
|
47
46
|
const blob = (id: bigint | string) =>
|
|
48
|
-
tx.moveCall({
|
|
47
|
+
tx.moveCall({
|
|
48
|
+
target: `${p.oriPackageId}::walrus_data::new_blob`,
|
|
49
|
+
arguments: [tx.pure.u256(id)],
|
|
50
|
+
});
|
|
49
51
|
|
|
50
52
|
const still = blob(p.stillBlobId);
|
|
51
53
|
const animated =
|
|
52
54
|
p.animatedBlobId == null
|
|
53
55
|
? tx.moveCall({ target: OPTION_NONE, typeArguments: [walrusType] })
|
|
54
|
-
: tx.moveCall({
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
: tx.moveCall({
|
|
57
|
+
target: OPTION_SOME,
|
|
58
|
+
typeArguments: [walrusType],
|
|
59
|
+
arguments: [blob(p.animatedBlobId)],
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const cover = tx.add(
|
|
63
|
+
coverArt._new({
|
|
64
|
+
package: p.coverArtPackageId,
|
|
65
|
+
arguments: [still, animated],
|
|
66
|
+
}),
|
|
67
|
+
);
|
|
57
68
|
tx.add(
|
|
58
69
|
releaseCoverArt.setCover({
|
|
59
70
|
package: p.releaseCoverArtPackageId,
|
|
@@ -72,7 +83,13 @@ export function setReleaseCover(p: SetReleaseCoverParams): TxThunk {
|
|
|
72
83
|
*/
|
|
73
84
|
export type CoverImageRef =
|
|
74
85
|
| { kind: "blob"; blobId: string }
|
|
75
|
-
| {
|
|
86
|
+
| {
|
|
87
|
+
kind: "quiltPatch";
|
|
88
|
+
quiltId: string;
|
|
89
|
+
version: number;
|
|
90
|
+
startIndex: number;
|
|
91
|
+
endIndex: number;
|
|
92
|
+
};
|
|
76
93
|
|
|
77
94
|
/** A release's album-level cover: a still image and an optional animation. */
|
|
78
95
|
export interface ReleaseCoverView {
|
|
@@ -88,17 +105,28 @@ const CoverArtField = bcs.struct("Field", {
|
|
|
88
105
|
name: releaseCoverArt.ExtensionKey,
|
|
89
106
|
value: releaseCoverArt.ReleaseCoverArt,
|
|
90
107
|
});
|
|
91
|
-
const COVER_ART_KEY_BYTES = releaseCoverArt.ExtensionKey.serialize([
|
|
108
|
+
const COVER_ART_KEY_BYTES = releaseCoverArt.ExtensionKey.serialize([
|
|
109
|
+
false,
|
|
110
|
+
]).toBytes();
|
|
92
111
|
|
|
93
112
|
/** A parsed `ori::WalrusData` value (a MoveEnum: `Blob` or `QuiltPatch`). */
|
|
94
113
|
type ParsedWalrusData =
|
|
95
114
|
| { $kind: "Blob"; Blob: [string | number | bigint, unknown] }
|
|
96
|
-
| {
|
|
115
|
+
| {
|
|
116
|
+
$kind: "QuiltPatch";
|
|
117
|
+
QuiltPatch: [string | number | bigint, number, number, number];
|
|
118
|
+
};
|
|
97
119
|
|
|
98
120
|
function toCoverImageRef(wd: ParsedWalrusData): CoverImageRef {
|
|
99
121
|
if (wd.$kind === "Blob") return { kind: "blob", blobId: String(wd.Blob[0]) };
|
|
100
122
|
const [quiltId, version, startIndex, endIndex] = wd.QuiltPatch;
|
|
101
|
-
return {
|
|
123
|
+
return {
|
|
124
|
+
kind: "quiltPatch",
|
|
125
|
+
quiltId: String(quiltId),
|
|
126
|
+
version,
|
|
127
|
+
startIndex,
|
|
128
|
+
endIndex,
|
|
129
|
+
};
|
|
102
130
|
}
|
|
103
131
|
|
|
104
132
|
/**
|
|
@@ -112,25 +140,24 @@ export async function getReleaseCover(
|
|
|
112
140
|
releaseId: string,
|
|
113
141
|
releaseCoverArtPackageId: string,
|
|
114
142
|
): Promise<ReleaseCoverView | null> {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
143
|
+
return (
|
|
144
|
+
(
|
|
145
|
+
await getReleaseCoversByIds(
|
|
146
|
+
client,
|
|
147
|
+
[releaseId],
|
|
148
|
+
[releaseCoverArtPackageId],
|
|
149
|
+
)
|
|
150
|
+
)[releaseId] ?? null
|
|
119
151
|
);
|
|
152
|
+
}
|
|
120
153
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
if (!content) return null;
|
|
130
|
-
|
|
131
|
-
const cover = CoverArtField.parse(content).value.cover as
|
|
132
|
-
| { still: ParsedWalrusData; animated: ParsedWalrusData | null }
|
|
133
|
-
| null;
|
|
154
|
+
export function parseReleaseCoverContent(
|
|
155
|
+
content: Uint8Array,
|
|
156
|
+
): ReleaseCoverView | null {
|
|
157
|
+
const cover = CoverArtField.parse(content).value.cover as {
|
|
158
|
+
still: ParsedWalrusData;
|
|
159
|
+
animated: ParsedWalrusData | null;
|
|
160
|
+
} | null;
|
|
134
161
|
if (!cover) return null;
|
|
135
162
|
|
|
136
163
|
return {
|
|
@@ -138,3 +165,57 @@ export async function getReleaseCover(
|
|
|
138
165
|
animated: cover.animated ? toCoverImageRef(cover.animated) : null,
|
|
139
166
|
};
|
|
140
167
|
}
|
|
168
|
+
|
|
169
|
+
/** Deterministic dynamic-field id for one release-cover package generation. */
|
|
170
|
+
export function releaseCoverFieldId(
|
|
171
|
+
releaseId: string,
|
|
172
|
+
releaseCoverArtPackageId: string,
|
|
173
|
+
): string {
|
|
174
|
+
return deriveDynamicFieldID(
|
|
175
|
+
releaseId,
|
|
176
|
+
`${releaseCoverArtPackageId}::release_cover_art::ExtensionKey`,
|
|
177
|
+
COVER_ART_KEY_BYTES,
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Read covers for many releases and package generations in one Core request.
|
|
183
|
+
*
|
|
184
|
+
* Package IDs are ordered newest to oldest. If both fields exist, the newest
|
|
185
|
+
* wins; legacy fallbacks add bytes to the same bulk request, not serial probes.
|
|
186
|
+
*/
|
|
187
|
+
export async function getReleaseCoversByIds(
|
|
188
|
+
client: ClientWithCoreApi,
|
|
189
|
+
releaseIdsInput: readonly string[],
|
|
190
|
+
releaseCoverArtPackageIds: readonly string[],
|
|
191
|
+
): Promise<Partial<Record<string, ReleaseCoverView>>> {
|
|
192
|
+
const releaseIds = [...new Set(releaseIdsInput)];
|
|
193
|
+
const targets = releaseIds.flatMap((releaseId) =>
|
|
194
|
+
releaseCoverArtPackageIds.map((packageId, priority) => ({
|
|
195
|
+
releaseId,
|
|
196
|
+
priority,
|
|
197
|
+
fieldId: releaseCoverFieldId(releaseId, packageId),
|
|
198
|
+
})),
|
|
199
|
+
);
|
|
200
|
+
if (targets.length === 0) return {};
|
|
201
|
+
|
|
202
|
+
const { objects } = await client.core.getObjects({
|
|
203
|
+
objectIds: targets.map((target) => target.fieldId),
|
|
204
|
+
include: { content: true },
|
|
205
|
+
});
|
|
206
|
+
const out: Partial<Record<string, ReleaseCoverView>> = {};
|
|
207
|
+
const priorities = new Map<string, number>();
|
|
208
|
+
objects.forEach((object, index) => {
|
|
209
|
+
const target = targets[index];
|
|
210
|
+
if (!target || object instanceof Error || !object.content) return;
|
|
211
|
+
const currentPriority = priorities.get(target.releaseId);
|
|
212
|
+
if (currentPriority !== undefined && currentPriority <= target.priority)
|
|
213
|
+
return;
|
|
214
|
+
const cover = parseReleaseCoverContent(object.content);
|
|
215
|
+
if (cover) {
|
|
216
|
+
out[target.releaseId] = cover;
|
|
217
|
+
priorities.set(target.releaseId, target.priority);
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
return out;
|
|
221
|
+
}
|