@misofm/platform 0.25.0 → 0.26.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 +144 -45
- package/dist/auth.d.ts +8 -13
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +78 -59
- package/dist/auth.js.map +1 -1
- package/dist/catalog.d.ts +5 -5
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +33 -21
- package/dist/catalog.js.map +1 -1
- package/dist/client.d.ts +17 -30
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +53 -75
- package/dist/client.js.map +1 -1
- package/dist/cover.d.ts +4 -3
- package/dist/cover.d.ts.map +1 -1
- package/dist/cover.js +28 -17
- package/dist/cover.js.map +1 -1
- package/dist/credits.d.ts +12 -10
- package/dist/credits.d.ts.map +1 -1
- package/dist/credits.js +60 -33
- package/dist/credits.js.map +1 -1
- package/dist/deployments.d.ts +21 -13
- package/dist/deployments.d.ts.map +1 -1
- package/dist/deployments.js +84 -37
- package/dist/deployments.js.map +1 -1
- package/dist/errors.d.ts +91 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +111 -0
- package/dist/errors.js.map +1 -0
- package/dist/execute.d.ts +3 -1
- package/dist/execute.d.ts.map +1 -1
- package/dist/execute.js +9 -4
- package/dist/execute.js.map +1 -1
- package/dist/genre.d.ts +25 -41
- package/dist/genre.d.ts.map +1 -1
- package/dist/genre.js +71 -20
- package/dist/genre.js.map +1 -1
- package/dist/party/client.d.ts +16 -13
- package/dist/party/client.d.ts.map +1 -1
- package/dist/party/client.js +33 -22
- package/dist/party/client.js.map +1 -1
- package/dist/party/extensions/links.d.ts +2 -1
- package/dist/party/extensions/links.d.ts.map +1 -1
- package/dist/party/extensions/links.js +2 -1
- package/dist/party/extensions/links.js.map +1 -1
- package/dist/party/queries.d.ts +12 -11
- package/dist/party/queries.d.ts.map +1 -1
- package/dist/party/queries.js +63 -57
- package/dist/party/queries.js.map +1 -1
- package/dist/party/types.d.ts +31 -20
- package/dist/party/types.d.ts.map +1 -1
- package/dist/party/types.js +82 -1
- package/dist/party/types.js.map +1 -1
- package/dist/pressing.d.ts +55 -35
- package/dist/pressing.d.ts.map +1 -1
- package/dist/pressing.js +99 -76
- package/dist/pressing.js.map +1 -1
- package/dist/queries.d.ts +1 -1
- package/dist/queries.d.ts.map +1 -1
- package/dist/queries.js +5 -4
- package/dist/queries.js.map +1 -1
- package/dist/read/artist.d.ts +6 -4
- package/dist/read/artist.d.ts.map +1 -1
- package/dist/read/artist.js +29 -27
- package/dist/read/artist.js.map +1 -1
- package/dist/read/catalog.d.ts +20 -17
- package/dist/read/catalog.d.ts.map +1 -1
- package/dist/read/catalog.js +120 -141
- package/dist/read/catalog.js.map +1 -1
- package/dist/read/client.d.ts +10 -9
- package/dist/read/client.d.ts.map +1 -1
- package/dist/read/client.js.map +1 -1
- package/dist/read/genres.d.ts +5 -3
- package/dist/read/genres.d.ts.map +1 -1
- package/dist/read/genres.js +18 -15
- package/dist/read/genres.js.map +1 -1
- package/dist/read/index.d.ts.map +1 -1
- package/dist/read/index.js +7 -1
- package/dist/read/index.js.map +1 -1
- package/dist/read/receipts.d.ts +15 -6
- package/dist/read/receipts.d.ts.map +1 -1
- package/dist/read/receipts.js +73 -60
- package/dist/read/receipts.js.map +1 -1
- package/dist/read/wallet.d.ts +11 -9
- package/dist/read/wallet.d.ts.map +1 -1
- package/dist/read/wallet.js +117 -132
- package/dist/read/wallet.js.map +1 -1
- package/dist/read/works.d.ts +9 -16
- package/dist/read/works.d.ts.map +1 -1
- package/dist/read/works.js +48 -117
- package/dist/read/works.js.map +1 -1
- package/dist/recording-extensions.d.ts +8 -7
- package/dist/recording-extensions.d.ts.map +1 -1
- package/dist/recording-extensions.js +30 -63
- package/dist/recording-extensions.js.map +1 -1
- package/dist/release-extensions.d.ts +3 -2
- package/dist/release-extensions.d.ts.map +1 -1
- package/dist/release-extensions.js +9 -10
- package/dist/release-extensions.js.map +1 -1
- package/dist/royalty.d.ts +51 -51
- package/dist/royalty.d.ts.map +1 -1
- package/dist/royalty.js +93 -11
- package/dist/royalty.js.map +1 -1
- package/dist/share.d.ts +7 -6
- package/dist/share.d.ts.map +1 -1
- package/dist/share.js +37 -30
- package/dist/share.js.map +1 -1
- package/dist/vault.d.ts +20 -8
- package/dist/vault.d.ts.map +1 -1
- package/dist/vault.js +31 -15
- package/dist/vault.js.map +1 -1
- package/package.json +11 -4
- package/src/auth.ts +89 -70
- package/src/catalog.ts +33 -49
- package/src/client.ts +117 -97
- package/src/cover.ts +17 -26
- package/src/credits.ts +43 -75
- package/src/deployments.ts +103 -69
- package/src/errors.ts +156 -0
- package/src/execute.ts +10 -6
- package/src/genre.ts +38 -23
- package/src/party/client.ts +35 -23
- package/src/party/extensions/links.ts +3 -2
- package/src/party/queries.ts +77 -65
- package/src/party/types.ts +52 -48
- package/src/pressing.ts +158 -161
- package/src/queries.ts +5 -5
- package/src/read/artist.ts +47 -42
- package/src/read/catalog.ts +200 -223
- package/src/read/client.ts +12 -11
- package/src/read/genres.ts +24 -16
- package/src/read/index.ts +7 -1
- package/src/read/receipts.ts +128 -67
- package/src/read/wallet.ts +194 -162
- package/src/read/works.ts +75 -194
- package/src/recording-extensions.ts +38 -92
- package/src/release-extensions.ts +7 -12
- package/src/royalty.ts +117 -20
- package/src/share.ts +44 -29
- package/src/vault.ts +47 -24
package/src/read/works.ts
CHANGED
|
@@ -1,139 +1,27 @@
|
|
|
1
1
|
// Copyright (c) Miso Labs, Inc.
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
|
-
import
|
|
5
|
-
import type { SuiGraphQLClient } from "@mysten/sui/graphql";
|
|
4
|
+
import { Effect } from "effect";
|
|
6
5
|
import { normalizeSuiAddress } from "@mysten/sui/utils";
|
|
7
6
|
import {
|
|
8
7
|
contracts,
|
|
9
8
|
extractTypeParams2,
|
|
10
9
|
getCompositionsByIds,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
getWorkAddressesByShareTypes,
|
|
11
|
+
Composition,
|
|
12
|
+
Recording,
|
|
13
|
+
Release,
|
|
14
14
|
type TrackState,
|
|
15
|
+
type WorkAddressesByShareType,
|
|
16
|
+
type WorkShareTypes,
|
|
15
17
|
} from "@misofm/musicos";
|
|
18
|
+
import { decodeBcs, SuiClient, SuiGraphQL, SuiRpcError, type BcsDecodeError } from "@misofm/effect";
|
|
16
19
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface WorkAddressesByShareType {
|
|
23
|
-
compositions: Partial<Record<string, string>>;
|
|
24
|
-
recordings: Partial<Record<string, string>>;
|
|
25
|
-
}
|
|
26
|
-
|
|
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 };
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/** Resolve all work share types in one aliased GraphQL request. */
|
|
38
|
-
export async function getWorkAddressesByShareTypes(
|
|
39
|
-
client: SuiGraphQLClient,
|
|
40
|
-
shareTypes: WorkShareTypes,
|
|
41
|
-
misoPackageId: string,
|
|
42
|
-
): Promise<WorkAddressesByShareType> {
|
|
43
|
-
const compositions = [...new Set(shareTypes.compositions)];
|
|
44
|
-
const recordings = new Set(shareTypes.recordings);
|
|
45
|
-
const out: WorkAddressesByShareType = { compositions: {}, recordings: {} };
|
|
46
|
-
if (compositions.length === 0 && recordings.size === 0) return out;
|
|
47
|
-
|
|
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`;
|
|
67
|
-
}
|
|
68
|
-
|
|
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;
|
|
94
|
-
if (!repr) continue;
|
|
95
|
-
try {
|
|
96
|
-
const [shareType] = extractTypeParams2(repr);
|
|
97
|
-
if (recordings.has(shareType)) out.recordings[shareType] = node.address;
|
|
98
|
-
} catch {
|
|
99
|
-
// Ignore objects whose deployed type does not match the Recording ABI.
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
};
|
|
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
|
-
}
|
|
135
|
-
return out;
|
|
136
|
-
}
|
|
20
|
+
// `getWorkAddressesByShareTypes` (GraphQL type discovery) is owned by
|
|
21
|
+
// `@misofm/musicos` now — re-exported here so existing platform imports keep
|
|
22
|
+
// working.
|
|
23
|
+
export { getWorkAddressesByShareTypes };
|
|
24
|
+
export type { WorkAddressesByShareType, WorkShareTypes };
|
|
137
25
|
|
|
138
26
|
export interface WorkIds {
|
|
139
27
|
compositions: readonly string[];
|
|
@@ -147,122 +35,121 @@ export interface WorksById {
|
|
|
147
35
|
releases: Partial<Record<string, Release>>;
|
|
148
36
|
}
|
|
149
37
|
|
|
38
|
+
// deno-lint-ignore no-explicit-any -- generated parse output is loosely typed
|
|
150
39
|
type Parsed = Record<string, any>;
|
|
151
40
|
|
|
152
|
-
function workState(value: Parsed): Composition["state"] {
|
|
41
|
+
function workState(value: Parsed): (typeof Composition.Type)["state"] {
|
|
153
42
|
return value?.$kind === "Published"
|
|
154
43
|
? { type: "Published", timestampMs: Number(value.Published) }
|
|
155
44
|
: { type: "Initialized" };
|
|
156
45
|
}
|
|
157
46
|
|
|
158
|
-
function
|
|
159
|
-
const value = contracts.composition.Composition.parse(content) as Parsed;
|
|
47
|
+
function mapComposition(id: string, d: Parsed) {
|
|
160
48
|
return {
|
|
161
49
|
id,
|
|
162
|
-
state: workState(
|
|
163
|
-
title: String(
|
|
50
|
+
state: workState(d.state),
|
|
51
|
+
title: String(d.title),
|
|
164
52
|
royaltyRate: {
|
|
165
|
-
value: Number(
|
|
166
|
-
Array.isArray(value.royalty_rate)
|
|
167
|
-
? value.royalty_rate[0]
|
|
168
|
-
: value.royalty_rate,
|
|
169
|
-
),
|
|
53
|
+
value: Number(Array.isArray(d.royalty_rate) ? d.royalty_rate[0] : d.royalty_rate),
|
|
170
54
|
},
|
|
171
55
|
};
|
|
172
56
|
}
|
|
173
57
|
|
|
174
|
-
function
|
|
175
|
-
const value = contracts.recording.Recording.parse(content) as Parsed;
|
|
58
|
+
function mapRecording(id: string, d: Parsed) {
|
|
176
59
|
return {
|
|
177
60
|
id,
|
|
178
|
-
state: workState(
|
|
179
|
-
compositionId: String(
|
|
61
|
+
state: workState(d.state),
|
|
62
|
+
compositionId: String(d.composition_id),
|
|
180
63
|
};
|
|
181
64
|
}
|
|
182
65
|
|
|
183
|
-
|
|
184
|
-
id: string,
|
|
185
|
-
content: Uint8Array,
|
|
186
|
-
): Release {
|
|
187
|
-
const value = contracts.release.Release.parse(content) as Parsed;
|
|
66
|
+
function mapReleaseObject(id: string, d: Parsed) {
|
|
188
67
|
return {
|
|
189
68
|
id,
|
|
190
|
-
state: workState(
|
|
191
|
-
title: String(
|
|
192
|
-
tracks: (
|
|
69
|
+
state: workState(d.state),
|
|
70
|
+
title: String(d.title),
|
|
71
|
+
tracks: (d.tracks ?? []).map((track: Parsed) => ({
|
|
193
72
|
state: (track.state?.$kind ?? "Unassigned") as TrackState,
|
|
194
73
|
compositionId: String(track.composition_id),
|
|
195
74
|
recordingId: String(track.recording_id),
|
|
196
75
|
splitBps: {
|
|
197
|
-
value: Number(
|
|
198
|
-
Array.isArray(track.split_bps) ? track.split_bps[0] : track.split_bps,
|
|
199
|
-
),
|
|
76
|
+
value: Number(Array.isArray(track.split_bps) ? track.split_bps[0] : track.split_bps),
|
|
200
77
|
},
|
|
201
78
|
})),
|
|
202
79
|
};
|
|
203
80
|
}
|
|
204
81
|
|
|
82
|
+
/** Decode a Release object's raw BCS content into the public `Release` type. */
|
|
83
|
+
export function parseReleaseObject(id: string, content: Uint8Array): Effect.Effect<Release, BcsDecodeError> {
|
|
84
|
+
return decodeBcs(
|
|
85
|
+
{ parse: (bytes: Uint8Array) => mapReleaseObject(id, contracts.release.Release.parse(bytes) as Parsed) },
|
|
86
|
+
Release,
|
|
87
|
+
content,
|
|
88
|
+
{ type: "Release", objectId: id },
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
205
92
|
/** Fetch and parse heterogeneous works through one Core bulk request. */
|
|
206
|
-
export
|
|
207
|
-
client: ClientWithCoreApi,
|
|
93
|
+
export const getWorksByIds = Effect.fn("getWorksByIds")(function* (
|
|
208
94
|
ids: WorkIds,
|
|
209
|
-
):
|
|
95
|
+
): Effect.fn.Return<WorksById, SuiRpcError | BcsDecodeError, SuiClient> {
|
|
210
96
|
const kinds = new Map<string, keyof WorksById>();
|
|
211
|
-
for (const [kind, objectIds] of Object.entries(ids) as Array<
|
|
212
|
-
[keyof WorksById, readonly string[]]
|
|
213
|
-
>) {
|
|
97
|
+
for (const [kind, objectIds] of Object.entries(ids) as Array<[keyof WorksById, readonly string[]]>) {
|
|
214
98
|
for (const objectId of objectIds) {
|
|
215
99
|
const normalized = normalizeSuiAddress(objectId);
|
|
216
100
|
const previous = kinds.get(normalized);
|
|
217
101
|
if (previous && previous !== kind) {
|
|
218
|
-
throw new Error(
|
|
219
|
-
`Work ${normalized} was requested as both ${previous} and ${kind}`,
|
|
220
|
-
);
|
|
102
|
+
throw new Error(`Work ${normalized} was requested as both ${previous} and ${kind}`);
|
|
221
103
|
}
|
|
222
104
|
kinds.set(normalized, kind);
|
|
223
105
|
}
|
|
224
106
|
}
|
|
225
107
|
const out: WorksById = { compositions: {}, recordings: {}, releases: {} };
|
|
226
108
|
if (kinds.size === 0) return out;
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
109
|
+
|
|
110
|
+
const client = yield* SuiClient;
|
|
111
|
+
const { objects } = yield* Effect.tryPromise({
|
|
112
|
+
try: (signal) => client.core.getObjects({ objectIds: [...kinds.keys()], include: { content: true }, signal }),
|
|
113
|
+
catch: (cause) => new SuiRpcError({ operation: "getObjects", cause }),
|
|
230
114
|
});
|
|
231
115
|
for (const object of objects) {
|
|
232
116
|
if (object instanceof Error || !object.content) continue;
|
|
233
117
|
const kind = kinds.get(normalizeSuiAddress(object.objectId));
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
118
|
+
const objectId = object.objectId;
|
|
119
|
+
const content = object.content;
|
|
120
|
+
if (kind === "compositions") {
|
|
121
|
+
out.compositions[objectId] = yield* decodeBcs(
|
|
122
|
+
{ parse: (bytes: Uint8Array) => mapComposition(objectId, contracts.composition.Composition.parse(bytes) as Parsed) },
|
|
123
|
+
Composition,
|
|
124
|
+
content,
|
|
125
|
+
{ type: "Composition", objectId },
|
|
238
126
|
);
|
|
239
|
-
else if (kind === "recordings")
|
|
240
|
-
out.recordings[
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
out.releases[object.objectId] = parseReleaseObject(
|
|
246
|
-
object.objectId,
|
|
247
|
-
object.content,
|
|
127
|
+
} else if (kind === "recordings") {
|
|
128
|
+
out.recordings[objectId] = yield* decodeBcs(
|
|
129
|
+
{ parse: (bytes: Uint8Array) => mapRecording(objectId, contracts.recording.Recording.parse(bytes) as Parsed) },
|
|
130
|
+
Recording,
|
|
131
|
+
content,
|
|
132
|
+
{ type: "Recording", objectId },
|
|
248
133
|
);
|
|
134
|
+
} else if (kind === "releases") {
|
|
135
|
+
out.releases[objectId] = yield* parseReleaseObject(objectId, content);
|
|
136
|
+
}
|
|
249
137
|
}
|
|
250
138
|
return out;
|
|
251
|
-
}
|
|
139
|
+
});
|
|
252
140
|
|
|
253
141
|
/** Resolve recording titles through each recording's canonical composition type. */
|
|
254
|
-
export
|
|
255
|
-
client: ClientWithCoreApi,
|
|
256
|
-
graphql: SuiGraphQLClient,
|
|
142
|
+
export const getRecordingTitles = Effect.fn("getRecordingTitles")(function* (
|
|
257
143
|
recordingIds: readonly string[],
|
|
258
144
|
misoPackageId: string,
|
|
259
|
-
):
|
|
145
|
+
): Effect.fn.Return<Record<string, string>, SuiRpcError | BcsDecodeError, SuiClient | SuiGraphQL> {
|
|
260
146
|
const ids = [...new Set(recordingIds)];
|
|
261
147
|
if (ids.length === 0) return {};
|
|
262
148
|
|
|
263
|
-
const
|
|
264
|
-
|
|
265
|
-
include: {},
|
|
149
|
+
const client = yield* SuiClient;
|
|
150
|
+
const { objects } = yield* Effect.tryPromise({
|
|
151
|
+
try: (signal) => client.core.getObjects({ objectIds: ids, include: {}, signal }),
|
|
152
|
+
catch: (cause) => new SuiRpcError({ operation: "getObjects", cause }),
|
|
266
153
|
});
|
|
267
154
|
const titles: Record<string, string> = {};
|
|
268
155
|
const compositionShareByRecording: Record<string, string> = {};
|
|
@@ -275,23 +162,17 @@ export async function getRecordingTitles(
|
|
|
275
162
|
|
|
276
163
|
const compositionShareTypes = Object.values(compositionShareByRecording);
|
|
277
164
|
if (compositionShareTypes.length === 0) return titles;
|
|
278
|
-
const addresses =
|
|
279
|
-
graphql,
|
|
165
|
+
const addresses = yield* getWorkAddressesByShareTypes(
|
|
280
166
|
{ compositions: compositionShareTypes, recordings: [] },
|
|
281
167
|
misoPackageId,
|
|
282
168
|
);
|
|
283
|
-
const compositions =
|
|
284
|
-
client,
|
|
169
|
+
const compositions = yield* getCompositionsByIds(
|
|
285
170
|
Object.values(addresses.compositions).filter((id): id is string => !!id),
|
|
286
171
|
);
|
|
287
|
-
for (const [recordingId, shareType] of Object.entries(
|
|
288
|
-
compositionShareByRecording,
|
|
289
|
-
)) {
|
|
172
|
+
for (const [recordingId, shareType] of Object.entries(compositionShareByRecording)) {
|
|
290
173
|
const compositionId = addresses.compositions[shareType];
|
|
291
|
-
const title = compositionId
|
|
292
|
-
? compositions[compositionId]?.title
|
|
293
|
-
: undefined;
|
|
174
|
+
const title = compositionId ? compositions[compositionId]?.title : undefined;
|
|
294
175
|
if (title) titles[recordingId] = title;
|
|
295
176
|
}
|
|
296
177
|
return titles;
|
|
297
|
-
}
|
|
178
|
+
});
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
/** Cap-authorized builders and reads for data-only Recording metadata extensions. */
|
|
5
5
|
|
|
6
6
|
import { bcs } from "@mysten/sui/bcs";
|
|
7
|
-
import type { ClientWithCoreApi } from "@mysten/sui/client";
|
|
8
7
|
import type { Transaction, TransactionArgument, TransactionObjectArgument } from "@mysten/sui/transactions";
|
|
9
8
|
import { deriveDynamicFieldID, fromHex, toHex } from "@mysten/sui/utils";
|
|
10
|
-
import {
|
|
9
|
+
import { Effect, Option } from "effect";
|
|
10
|
+
import { getObjectsContent, getOptionalObjectContent, type SuiClient, type SuiRpcError } from "@misofm/effect";
|
|
11
11
|
import type { TxThunk } from "./transactions.ts";
|
|
12
12
|
import { invokeWithAdminCap, type AdminCapAuthority, type ObjectInput } from "./vault.ts";
|
|
13
13
|
import * as advisory from "./contracts/recording_advisory/recording_advisory.ts";
|
|
@@ -271,33 +271,23 @@ export function recordingStreamingTranscodeFieldId(
|
|
|
271
271
|
}
|
|
272
272
|
|
|
273
273
|
/** Read one Recording's streaming-transcode Quilt id, or null when absent. */
|
|
274
|
-
export
|
|
275
|
-
client: ClientWithCoreApi,
|
|
274
|
+
export const getRecordingStreamingTranscode = Effect.fn("getRecordingStreamingTranscode")(function* (
|
|
276
275
|
recordingId: string,
|
|
277
276
|
recordingStreamingTranscodePackageId: string,
|
|
278
|
-
):
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
client,
|
|
283
|
-
[recordingId],
|
|
284
|
-
recordingStreamingTranscodePackageId,
|
|
285
|
-
)
|
|
286
|
-
)[recordingId] ?? null
|
|
287
|
-
);
|
|
288
|
-
}
|
|
277
|
+
): Effect.fn.Return<string | null, SuiRpcError, SuiClient> {
|
|
278
|
+
const found = yield* getRecordingStreamingTranscodesByIds([recordingId], recordingStreamingTranscodePackageId);
|
|
279
|
+
return found[recordingId] ?? null;
|
|
280
|
+
});
|
|
289
281
|
|
|
290
282
|
/**
|
|
291
283
|
* Read streaming-transcode Quilt ids for many Recordings in one Core request.
|
|
292
284
|
* Missing and malformed fields are omitted, like master references.
|
|
293
285
|
*/
|
|
294
|
-
export
|
|
295
|
-
client: ClientWithCoreApi,
|
|
286
|
+
export function getRecordingStreamingTranscodesByIds(
|
|
296
287
|
recordingIdsInput: readonly string[],
|
|
297
288
|
recordingStreamingTranscodePackageId: string,
|
|
298
|
-
):
|
|
289
|
+
): Effect.Effect<Partial<Record<string, string>>, SuiRpcError, SuiClient> {
|
|
299
290
|
return readSoftFields(
|
|
300
|
-
client,
|
|
301
291
|
recordingIdsInput,
|
|
302
292
|
(recordingId) => recordingStreamingTranscodeFieldId(recordingId, recordingStreamingTranscodePackageId),
|
|
303
293
|
parseRecordingStreamingTranscodeContent,
|
|
@@ -309,30 +299,27 @@ export async function getRecordingStreamingTranscodesByIds(
|
|
|
309
299
|
* parse each, dropping Recordings whose field is missing or malformed so one
|
|
310
300
|
* stale extension cannot take a whole tracklist down.
|
|
311
301
|
*/
|
|
312
|
-
|
|
313
|
-
client: ClientWithCoreApi,
|
|
302
|
+
const readSoftFields = Effect.fn("readSoftFields")(function* <T>(
|
|
314
303
|
recordingIdsInput: readonly string[],
|
|
315
304
|
fieldIdOf: (recordingId: string) => string,
|
|
316
305
|
parse: (content: Uint8Array) => T,
|
|
317
|
-
):
|
|
306
|
+
): Effect.fn.Return<Partial<Record<string, T>>, SuiRpcError, SuiClient> {
|
|
318
307
|
const recordingIds = [...new Set(recordingIdsInput)];
|
|
319
308
|
if (recordingIds.length === 0) return {};
|
|
320
|
-
const
|
|
321
|
-
|
|
322
|
-
include: { content: true },
|
|
323
|
-
});
|
|
309
|
+
const fieldIds = recordingIds.map(fieldIdOf);
|
|
310
|
+
const contentById = yield* getObjectsContent(fieldIds);
|
|
324
311
|
const out: Partial<Record<string, T>> = {};
|
|
325
|
-
|
|
326
|
-
const
|
|
327
|
-
if (!
|
|
312
|
+
recordingIds.forEach((recordingId, index) => {
|
|
313
|
+
const found = contentById.get(fieldIds[index]!);
|
|
314
|
+
if (!found) return;
|
|
328
315
|
try {
|
|
329
|
-
out[recordingId] = parse(
|
|
316
|
+
out[recordingId] = parse(found.content);
|
|
330
317
|
} catch {
|
|
331
318
|
// Extension metadata is soft: retain valid tracks when one field is stale.
|
|
332
319
|
}
|
|
333
320
|
});
|
|
334
321
|
return out;
|
|
335
|
-
}
|
|
322
|
+
});
|
|
336
323
|
|
|
337
324
|
// ── Engine-session reads ─────────────────────────────────────────────────────
|
|
338
325
|
|
|
@@ -381,37 +368,25 @@ export function recordingEngineSessionFieldId(
|
|
|
381
368
|
}
|
|
382
369
|
|
|
383
370
|
/** Read one Recording's engine session, or null when none is attached. */
|
|
384
|
-
export
|
|
385
|
-
client: ClientWithCoreApi,
|
|
371
|
+
export const getRecordingEngineSession = Effect.fn("getRecordingEngineSession")(function* (
|
|
386
372
|
recordingId: string,
|
|
387
373
|
recordingEngineSessionPackageId: string,
|
|
388
|
-
):
|
|
374
|
+
): Effect.fn.Return<RecordingEngineSessionView | null, SuiRpcError, SuiClient> {
|
|
389
375
|
const fieldId = recordingEngineSessionFieldId(recordingId, recordingEngineSessionPackageId);
|
|
390
|
-
const
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
const field = objects[0];
|
|
395
|
-
if (!field) throw new Error("Recording engine-session read returned no result");
|
|
396
|
-
if (field instanceof Error) {
|
|
397
|
-
if (isNotFound(field)) return null;
|
|
398
|
-
throw field;
|
|
399
|
-
}
|
|
400
|
-
if (!field.content) throw new Error("Recording engine-session field is missing BCS content");
|
|
401
|
-
return parseRecordingEngineSessionContent(field.content);
|
|
402
|
-
}
|
|
376
|
+
const found = yield* getOptionalObjectContent(fieldId);
|
|
377
|
+
if (Option.isNone(found)) return null;
|
|
378
|
+
return parseRecordingEngineSessionContent(found.value.content);
|
|
379
|
+
});
|
|
403
380
|
|
|
404
381
|
/**
|
|
405
382
|
* Read engine sessions for many Recordings in one Core request. Missing and
|
|
406
383
|
* malformed fields are omitted.
|
|
407
384
|
*/
|
|
408
|
-
export
|
|
409
|
-
client: ClientWithCoreApi,
|
|
385
|
+
export function getRecordingEngineSessionsByIds(
|
|
410
386
|
recordingIdsInput: readonly string[],
|
|
411
387
|
recordingEngineSessionPackageId: string,
|
|
412
|
-
):
|
|
388
|
+
): Effect.Effect<Partial<Record<string, RecordingEngineSessionView>>, SuiRpcError, SuiClient> {
|
|
413
389
|
return readSoftFields(
|
|
414
|
-
client,
|
|
415
390
|
recordingIdsInput,
|
|
416
391
|
(recordingId) => recordingEngineSessionFieldId(recordingId, recordingEngineSessionPackageId),
|
|
417
392
|
parseRecordingEngineSessionContent,
|
|
@@ -455,21 +430,13 @@ export function recordingMasterReferenceFieldId(
|
|
|
455
430
|
}
|
|
456
431
|
|
|
457
432
|
/** Read one Recording's master-reference blob id, or null when absent. */
|
|
458
|
-
export
|
|
459
|
-
client: ClientWithCoreApi,
|
|
433
|
+
export const getRecordingMasterReference = Effect.fn("getRecordingMasterReference")(function* (
|
|
460
434
|
recordingId: string,
|
|
461
435
|
recordingMasterReferencePackageId: string,
|
|
462
|
-
):
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
client,
|
|
467
|
-
[recordingId],
|
|
468
|
-
recordingMasterReferencePackageId,
|
|
469
|
-
)
|
|
470
|
-
)[recordingId] ?? null
|
|
471
|
-
);
|
|
472
|
-
}
|
|
436
|
+
): Effect.fn.Return<string | null, SuiRpcError, SuiClient> {
|
|
437
|
+
const found = yield* getRecordingMasterReferencesByIds([recordingId], recordingMasterReferencePackageId);
|
|
438
|
+
return found[recordingId] ?? null;
|
|
439
|
+
});
|
|
473
440
|
|
|
474
441
|
/**
|
|
475
442
|
* Read master-reference blob ids for many Recordings in one Core request.
|
|
@@ -477,34 +444,13 @@ export async function getRecordingMasterReference(
|
|
|
477
444
|
* Missing fields and malformed individual objects are omitted so one Recording
|
|
478
445
|
* without a playable master cannot make the rest of an album unplayable.
|
|
479
446
|
*/
|
|
480
|
-
export
|
|
481
|
-
client: ClientWithCoreApi,
|
|
447
|
+
export function getRecordingMasterReferencesByIds(
|
|
482
448
|
recordingIdsInput: readonly string[],
|
|
483
449
|
recordingMasterReferencePackageId: string,
|
|
484
|
-
):
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
recordingId,
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
recordingMasterReferencePackageId,
|
|
491
|
-
),
|
|
492
|
-
}));
|
|
493
|
-
if (targets.length === 0) return {};
|
|
494
|
-
|
|
495
|
-
const { objects } = await client.core.getObjects({
|
|
496
|
-
objectIds: targets.map((target) => target.fieldId),
|
|
497
|
-
include: { content: true },
|
|
498
|
-
});
|
|
499
|
-
const out: Partial<Record<string, string>> = {};
|
|
500
|
-
objects.forEach((object, index) => {
|
|
501
|
-
const target = targets[index];
|
|
502
|
-
if (!target || object instanceof Error || !object.content) return;
|
|
503
|
-
try {
|
|
504
|
-
out[target.recordingId] = parseRecordingMasterReferenceContent(object.content);
|
|
505
|
-
} catch {
|
|
506
|
-
// Extension metadata is soft: retain valid tracks when one field is stale.
|
|
507
|
-
}
|
|
508
|
-
});
|
|
509
|
-
return out;
|
|
450
|
+
): Effect.Effect<Partial<Record<string, string>>, SuiRpcError, SuiClient> {
|
|
451
|
+
return readSoftFields(
|
|
452
|
+
recordingIdsInput,
|
|
453
|
+
(recordingId) => recordingMasterReferenceFieldId(recordingId, recordingMasterReferencePackageId),
|
|
454
|
+
parseRecordingMasterReferenceContent,
|
|
455
|
+
);
|
|
510
456
|
}
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
// intent aggregates these concerns, but every value is written by its own
|
|
6
6
|
// independently deployed extension package.
|
|
7
7
|
|
|
8
|
-
import type { ClientWithCoreApi } from "@mysten/sui/client";
|
|
9
8
|
import { bcs } from "@mysten/sui/bcs";
|
|
10
9
|
import { deriveDynamicFieldID } from "@mysten/sui/utils";
|
|
11
10
|
import type {
|
|
@@ -13,6 +12,8 @@ import type {
|
|
|
13
12
|
TransactionArgument,
|
|
14
13
|
TransactionObjectArgument,
|
|
15
14
|
} from "@mysten/sui/transactions";
|
|
15
|
+
import { Effect, Option } from "effect";
|
|
16
|
+
import { getOptionalObjectContent, type SuiClient, type SuiRpcError } from "@misofm/effect";
|
|
16
17
|
import type { TxThunk } from "./transactions.ts";
|
|
17
18
|
import * as releaseDescription from "./contracts/release_description/release_description.ts";
|
|
18
19
|
import * as releaseDspLink from "./contracts/release_dsp_link/release_dsp_link.ts";
|
|
@@ -86,19 +87,13 @@ export function parseReleaseKindContent(content: Uint8Array): string {
|
|
|
86
87
|
}
|
|
87
88
|
|
|
88
89
|
/** Read a Release's self-declared kind, or `null` when the extension is absent. */
|
|
89
|
-
export
|
|
90
|
-
client: ClientWithCoreApi,
|
|
90
|
+
export const getReleaseKind = Effect.fn("getReleaseKind")(function* (
|
|
91
91
|
releaseId: string,
|
|
92
92
|
releaseKindPackageId: string,
|
|
93
|
-
):
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
});
|
|
98
|
-
const object = objects[0];
|
|
99
|
-
if (!object || object instanceof Error || !object.content) return null;
|
|
100
|
-
return parseReleaseKindContent(object.content);
|
|
101
|
-
}
|
|
93
|
+
): Effect.fn.Return<string | null, SuiRpcError, SuiClient> {
|
|
94
|
+
const found = yield* getOptionalObjectContent(releaseKindFieldId(releaseId, releaseKindPackageId));
|
|
95
|
+
return Option.isNone(found) ? null : parseReleaseKindContent(found.value.content);
|
|
96
|
+
});
|
|
102
97
|
|
|
103
98
|
export type SetReleaseDescriptionParams = ReleaseExtensionTarget & {
|
|
104
99
|
description: string;
|