@misofm/sdk 0.5.0 → 0.7.1
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 +13 -9
- package/dist/client.d.ts +17 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +22 -3
- package/dist/client.js.map +1 -1
- package/dist/contracts/release_registry/release_registry.d.ts +114 -0
- package/dist/contracts/release_registry/release_registry.d.ts.map +1 -0
- package/dist/contracts/release_registry/release_registry.js +119 -0
- package/dist/contracts/release_registry/release_registry.js.map +1 -0
- package/dist/contracts/royalty_pool/pool.d.ts +28 -2
- package/dist/contracts/royalty_pool/pool.d.ts.map +1 -1
- package/dist/contracts/royalty_pool/pool.js +28 -2
- package/dist/contracts/royalty_pool/pool.js.map +1 -1
- package/dist/contracts/utils/index.d.ts +44 -0
- package/dist/contracts/utils/index.d.ts.map +1 -1
- package/dist/contracts/utils/index.js +104 -2
- package/dist/contracts/utils/index.js.map +1 -1
- package/dist/contracts.d.ts +1 -0
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js +4 -1
- package/dist/contracts.js.map +1 -1
- package/dist/cover.d.ts +3 -6
- package/dist/cover.d.ts.map +1 -1
- package/dist/cover.js +7 -16
- package/dist/cover.js.map +1 -1
- package/dist/credits.d.ts.map +1 -1
- package/dist/credits.js +17 -43
- package/dist/credits.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/read/catalog.d.ts +2 -12
- package/dist/read/catalog.d.ts.map +1 -1
- package/dist/read/catalog.js +9 -24
- package/dist/read/catalog.js.map +1 -1
- package/dist/read/config.d.ts +3 -8
- package/dist/read/config.d.ts.map +1 -1
- package/dist/read/config.js +15 -50
- package/dist/read/config.js.map +1 -1
- package/dist/read/index.d.ts +1 -1
- package/dist/read/index.d.ts.map +1 -1
- package/dist/read/index.js +1 -1
- package/dist/read/index.js.map +1 -1
- package/dist/read/types.d.ts +10 -1
- package/dist/read/types.d.ts.map +1 -1
- package/dist/read/wallet.d.ts +13 -8
- package/dist/read/wallet.d.ts.map +1 -1
- package/dist/read/wallet.js +39 -38
- package/dist/read/wallet.js.map +1 -1
- package/dist/read/works.d.ts +2 -5
- package/dist/read/works.d.ts.map +1 -1
- package/dist/read/works.js +7 -37
- package/dist/read/works.js.map +1 -1
- package/dist/release-graph.d.ts +6 -19
- package/dist/release-graph.d.ts.map +1 -1
- package/dist/release-graph.js +29 -43
- package/dist/release-graph.js.map +1 -1
- package/dist/transactions.d.ts +12 -28
- package/dist/transactions.d.ts.map +1 -1
- package/dist/transactions.js +23 -28
- package/dist/transactions.js.map +1 -1
- package/package.json +4 -4
- package/src/client.ts +33 -2
- package/src/contracts/release_registry/release_registry.ts +160 -0
- package/src/contracts/royalty_pool/pool.ts +28 -2
- package/src/contracts/utils/index.ts +156 -4
- package/src/contracts.ts +5 -1
- package/src/cover.ts +8 -19
- package/src/credits.ts +23 -63
- package/src/index.ts +3 -1
- package/src/read/catalog.ts +9 -29
- package/src/read/config.ts +18 -58
- package/src/read/index.ts +1 -1
- package/src/read/types.ts +11 -1
- package/src/read/wallet.ts +56 -38
- package/src/read/works.ts +5 -42
- package/src/release-graph.ts +40 -75
- package/src/transactions.ts +33 -62
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
BcsEnum,
|
|
9
9
|
BcsTuple,
|
|
10
10
|
} from '@mysten/sui/bcs';
|
|
11
|
-
import { normalizeSuiAddress } from '@mysten/sui/utils';
|
|
11
|
+
import { normalizeStructTag, normalizeSuiAddress } from '@mysten/sui/utils';
|
|
12
12
|
import { type TransactionArgument, isArgument } from '@mysten/sui/transactions';
|
|
13
13
|
import { type ClientWithCoreApi, type SuiClientTypes } from '@mysten/sui/client';
|
|
14
14
|
|
|
@@ -158,10 +158,140 @@ export function normalizeMoveArguments(
|
|
|
158
158
|
return normalizedArgs;
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
+
/* -------------------------- Move type tags -------------------------- */
|
|
162
|
+
|
|
163
|
+
/** A type argument: a type tag string, or a BCS type whose name is a Move type. */
|
|
164
|
+
export type TypeArgument = string | BcsType<any>;
|
|
165
|
+
|
|
166
|
+
export interface TypeTagOptions {
|
|
167
|
+
package?: string;
|
|
168
|
+
typeArguments?: readonly TypeArgument[];
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* `typeArguments` is required when the type's name contains unfilled
|
|
173
|
+
* `phantom X` parameters (at any depth). Everything else — argument arity,
|
|
174
|
+
* position contents, and tag validity — is validated at runtime.
|
|
175
|
+
*/
|
|
176
|
+
type TypeTagParams<Name extends string> = Name extends `${string}phantom ${string}`
|
|
177
|
+
? [options: TypeTagOptions & { typeArguments: readonly TypeArgument[] }]
|
|
178
|
+
: [options?: TypeTagOptions];
|
|
179
|
+
|
|
180
|
+
type ResolveTypeTagOptions<Name extends string> = { client: ClientWithCoreApi } & (
|
|
181
|
+
Name extends `${string}phantom ${string}`
|
|
182
|
+
? TypeTagOptions & { typeArguments: readonly TypeArgument[] }
|
|
183
|
+
: TypeTagOptions
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
const HAS_PHANTOM_REGEX = /phantom [A-Za-z_$][A-Za-z0-9_$]*/;
|
|
187
|
+
|
|
188
|
+
function splitTopLevelTypeArgs(inner: string): string[] {
|
|
189
|
+
const parts: string[] = [];
|
|
190
|
+
let depth = 0;
|
|
191
|
+
let current = '';
|
|
192
|
+
for (const char of inner) {
|
|
193
|
+
if (char === ',' && depth === 0) {
|
|
194
|
+
parts.push(current.trim());
|
|
195
|
+
current = '';
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
if (char === '<') depth++;
|
|
199
|
+
if (char === '>') depth--;
|
|
200
|
+
current += char;
|
|
201
|
+
}
|
|
202
|
+
if (current) parts.push(current.trim());
|
|
203
|
+
return parts;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function buildTypeTag(name: string, options: TypeTagOptions | undefined): string {
|
|
207
|
+
const lt = name.indexOf('<');
|
|
208
|
+
const base = lt === -1 ? name : name.slice(0, lt);
|
|
209
|
+
|
|
210
|
+
if (base.split('::').length !== 3) {
|
|
211
|
+
throw new Error(`${name} is not a top-level Move type`);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
let result = name;
|
|
215
|
+
|
|
216
|
+
if (options?.typeArguments) {
|
|
217
|
+
const baked = lt === -1 ? [] : splitTopLevelTypeArgs(name.slice(lt + 1, -1));
|
|
218
|
+
const supplied = options.typeArguments.map((arg) => {
|
|
219
|
+
if (typeof arg === 'string') {
|
|
220
|
+
return arg;
|
|
221
|
+
}
|
|
222
|
+
if (arg && typeof arg.serialize === 'function' && typeof arg.name === 'string') {
|
|
223
|
+
return arg.name;
|
|
224
|
+
}
|
|
225
|
+
throw new Error(`Invalid type argument ${stringify(arg)}`);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
if (supplied.length !== baked.length) {
|
|
229
|
+
throw new Error(
|
|
230
|
+
`Expected ${baked.length} type arguments for ${base}, got ${supplied.length}`,
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
result = supplied.length === 0 ? base : `${base}<${supplied.join(', ')}>`;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (HAS_PHANTOM_REGEX.test(result)) {
|
|
238
|
+
throw new Error(
|
|
239
|
+
options?.typeArguments
|
|
240
|
+
? `A type argument contains an unfilled phantom parameter in ${result}`
|
|
241
|
+
: `Missing type arguments for ${result}`,
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (options?.package) {
|
|
246
|
+
const [, ...rest] = result.split('::');
|
|
247
|
+
result = [options.package, ...rest].join('::');
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// fully validate address-only tags (MVR names can't be parsed as type tags)
|
|
251
|
+
if (!HAS_PHANTOM_REGEX.test(result) && !/[@/]/.test(result)) {
|
|
252
|
+
TypeTagSerializer.parseFromStr(result);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return result;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
async function resolveBuiltTypeTag(
|
|
259
|
+
name: string,
|
|
260
|
+
options: { client: ClientWithCoreApi } & TypeTagOptions,
|
|
261
|
+
): Promise<string> {
|
|
262
|
+
const { client, ...rest } = options;
|
|
263
|
+
const { type } = await client.core.mvr.resolveType({
|
|
264
|
+
type: buildTypeTag(name, rest),
|
|
265
|
+
});
|
|
266
|
+
return normalizeStructTag(type);
|
|
267
|
+
}
|
|
268
|
+
|
|
161
269
|
export class MoveStruct<
|
|
162
270
|
T extends Record<string, BcsType<any>>,
|
|
163
271
|
const Name extends string = string,
|
|
164
272
|
> extends BcsStruct<T, Name> {
|
|
273
|
+
/**
|
|
274
|
+
* Build the type tag for this struct.
|
|
275
|
+
*
|
|
276
|
+
* `typeArguments` is the full positional list, in Move declaration order, and
|
|
277
|
+
* is required when the struct has unfilled phantom parameters. The result may
|
|
278
|
+
* contain MVR names: those are valid in transaction `typeArguments`, but for
|
|
279
|
+
* queries or comparisons against on-chain data use `resolveTypeTag` instead.
|
|
280
|
+
*/
|
|
281
|
+
typeTag(...args: TypeTagParams<Name>): string {
|
|
282
|
+
return buildTypeTag(this.name, args[0] as TypeTagOptions | undefined);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Build the type tag for this struct, then resolve any MVR names through the
|
|
287
|
+
* client (using its configured overrides and the MVR API) and return the
|
|
288
|
+
* normalized, address-only form suitable for queries and comparisons against
|
|
289
|
+
* on-chain data.
|
|
290
|
+
*/
|
|
291
|
+
async resolveTypeTag(options: ResolveTypeTagOptions<Name>): Promise<string> {
|
|
292
|
+
return resolveBuiltTypeTag(this.name, options as { client: ClientWithCoreApi } & TypeTagOptions);
|
|
293
|
+
}
|
|
294
|
+
|
|
165
295
|
async get<Include extends Omit<SuiClientTypes.ObjectInclude, 'content' | 'json'> = {}>({
|
|
166
296
|
objectId,
|
|
167
297
|
...options
|
|
@@ -210,16 +340,38 @@ export class MoveStruct<
|
|
|
210
340
|
export class MoveEnum<
|
|
211
341
|
T extends Record<string, BcsType<any> | null>,
|
|
212
342
|
const Name extends string,
|
|
213
|
-
> extends BcsEnum<T, Name> {
|
|
343
|
+
> extends BcsEnum<T, Name> {
|
|
344
|
+
/** Build the type tag for this enum. See `MoveStruct.typeTag` for semantics. */
|
|
345
|
+
typeTag(...args: TypeTagParams<Name>): string {
|
|
346
|
+
return buildTypeTag(this.name, args[0] as TypeTagOptions | undefined);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/** Build and resolve the type tag for this enum. See `MoveStruct.resolveTypeTag`. */
|
|
350
|
+
async resolveTypeTag(options: ResolveTypeTagOptions<Name>): Promise<string> {
|
|
351
|
+
return resolveBuiltTypeTag(this.name, options as { client: ClientWithCoreApi } & TypeTagOptions);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
214
354
|
|
|
215
355
|
export class MoveTuple<
|
|
216
356
|
const T extends readonly BcsType<any>[],
|
|
217
357
|
const Name extends string,
|
|
218
|
-
> extends BcsTuple<T, Name> {
|
|
358
|
+
> extends BcsTuple<T, Name> {
|
|
359
|
+
/** Build the type tag for this struct. See `MoveStruct.typeTag` for semantics. */
|
|
360
|
+
typeTag(...args: TypeTagParams<Name>): string {
|
|
361
|
+
return buildTypeTag(this.name, args[0] as TypeTagOptions | undefined);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/** Build and resolve the type tag for this struct. See `MoveStruct.resolveTypeTag`. */
|
|
365
|
+
async resolveTypeTag(options: ResolveTypeTagOptions<Name>): Promise<string> {
|
|
366
|
+
return resolveBuiltTypeTag(this.name, options as { client: ClientWithCoreApi } & TypeTagOptions);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
219
369
|
|
|
220
370
|
function stringify(val: unknown) {
|
|
221
371
|
if (typeof val === 'object') {
|
|
222
|
-
return JSON.stringify(val, (
|
|
372
|
+
return JSON.stringify(val, (_key, value) =>
|
|
373
|
+
typeof value === 'bigint' ? value.toString() : value,
|
|
374
|
+
);
|
|
223
375
|
}
|
|
224
376
|
if (typeof val === 'bigint') {
|
|
225
377
|
return val.toString();
|
package/src/contracts.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// Move calls). Re-exported from the package root as the `contracts` namespace.
|
|
6
6
|
//
|
|
7
7
|
// PLATFORM + EXTENSION packages. The protocol core (`miso`: Composition,
|
|
8
|
-
// Recording, Release,
|
|
8
|
+
// Recording, Release, Track) generates into `@misonetwork/sdk` and is
|
|
9
9
|
// re-exported from ITS `contracts` namespace — import it from there rather than
|
|
10
10
|
// mirroring it here.
|
|
11
11
|
|
|
@@ -14,6 +14,10 @@ export * as pressing from "./contracts/miso_pressing/pressing.ts";
|
|
|
14
14
|
export * as listing from "./contracts/miso_pressing/listing.ts";
|
|
15
15
|
export * as certificate from "./contracts/miso_pressing/certificate.ts";
|
|
16
16
|
|
|
17
|
+
// The canonical release-id derivation parent — the only PTB-callable path to
|
|
18
|
+
// minting a `Release` now that core's `release::new` takes `&mut UID`.
|
|
19
|
+
export * as releaseRegistry from "./contracts/release_registry/release_registry.ts";
|
|
20
|
+
|
|
17
21
|
// Existing launch drops remain readable and purchasable while releases move to
|
|
18
22
|
// the pressing/listing contracts.
|
|
19
23
|
export * as drop from "./contracts/miso_drop/drop.ts";
|
package/src/cover.ts
CHANGED
|
@@ -145,7 +145,7 @@ export async function getReleaseCover(
|
|
|
145
145
|
await getReleaseCoversByIds(
|
|
146
146
|
client,
|
|
147
147
|
[releaseId],
|
|
148
|
-
|
|
148
|
+
releaseCoverArtPackageId,
|
|
149
149
|
)
|
|
150
150
|
)[releaseId] ?? null
|
|
151
151
|
);
|
|
@@ -166,7 +166,7 @@ export function parseReleaseCoverContent(
|
|
|
166
166
|
};
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
-
/** Deterministic dynamic-field id for
|
|
169
|
+
/** Deterministic dynamic-field id for the configured release-cover package. */
|
|
170
170
|
export function releaseCoverFieldId(
|
|
171
171
|
releaseId: string,
|
|
172
172
|
releaseCoverArtPackageId: string,
|
|
@@ -179,24 +179,18 @@ export function releaseCoverFieldId(
|
|
|
179
179
|
}
|
|
180
180
|
|
|
181
181
|
/**
|
|
182
|
-
* Read covers for many releases
|
|
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.
|
|
182
|
+
* Read covers for many releases from the configured package in one Core request.
|
|
186
183
|
*/
|
|
187
184
|
export async function getReleaseCoversByIds(
|
|
188
185
|
client: ClientWithCoreApi,
|
|
189
186
|
releaseIdsInput: readonly string[],
|
|
190
|
-
|
|
187
|
+
releaseCoverArtPackageId: string,
|
|
191
188
|
): Promise<Partial<Record<string, ReleaseCoverView>>> {
|
|
192
189
|
const releaseIds = [...new Set(releaseIdsInput)];
|
|
193
|
-
const targets = releaseIds.
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
fieldId: releaseCoverFieldId(releaseId, packageId),
|
|
198
|
-
})),
|
|
199
|
-
);
|
|
190
|
+
const targets = releaseIds.map((releaseId) => ({
|
|
191
|
+
releaseId,
|
|
192
|
+
fieldId: releaseCoverFieldId(releaseId, releaseCoverArtPackageId),
|
|
193
|
+
}));
|
|
200
194
|
if (targets.length === 0) return {};
|
|
201
195
|
|
|
202
196
|
const { objects } = await client.core.getObjects({
|
|
@@ -204,17 +198,12 @@ export async function getReleaseCoversByIds(
|
|
|
204
198
|
include: { content: true },
|
|
205
199
|
});
|
|
206
200
|
const out: Partial<Record<string, ReleaseCoverView>> = {};
|
|
207
|
-
const priorities = new Map<string, number>();
|
|
208
201
|
objects.forEach((object, index) => {
|
|
209
202
|
const target = targets[index];
|
|
210
203
|
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
204
|
const cover = parseReleaseCoverContent(object.content);
|
|
215
205
|
if (cover) {
|
|
216
206
|
out[target.releaseId] = cover;
|
|
217
|
-
priorities.set(target.releaseId, target.priority);
|
|
218
207
|
}
|
|
219
208
|
});
|
|
220
209
|
return out;
|
package/src/credits.ts
CHANGED
|
@@ -605,9 +605,7 @@ const RELEASE_CREDITS_KEY_BYTES = releaseCredits.ExtensionKey.serialize([
|
|
|
605
605
|
|
|
606
606
|
type CreditFieldKind =
|
|
607
607
|
| "composition"
|
|
608
|
-
| "compositionLegacy"
|
|
609
608
|
| "recording"
|
|
610
|
-
| "recordingLegacy"
|
|
611
609
|
| "release";
|
|
612
610
|
|
|
613
611
|
interface CreditFieldTarget {
|
|
@@ -616,13 +614,7 @@ interface CreditFieldTarget {
|
|
|
616
614
|
fieldId: string;
|
|
617
615
|
}
|
|
618
616
|
|
|
619
|
-
/**
|
|
620
|
-
* Fetch many derived credit fields through one Core bulk request.
|
|
621
|
-
*
|
|
622
|
-
* Composition and recording extensions have one legacy key spelling. Both IDs
|
|
623
|
-
* ride in the same request, so fallback compatibility does not add a sequential
|
|
624
|
-
* round trip when the current key is absent.
|
|
625
|
-
*/
|
|
617
|
+
/** Fetch many derived credit fields through one Core bulk request. */
|
|
626
618
|
async function fetchCreditFields(
|
|
627
619
|
client: ClientWithCoreApi,
|
|
628
620
|
targets: readonly CreditFieldTarget[],
|
|
@@ -645,52 +637,30 @@ function compositionCreditTargets(
|
|
|
645
637
|
compositionIds: readonly string[],
|
|
646
638
|
packageId: string,
|
|
647
639
|
): CreditFieldTarget[] {
|
|
648
|
-
return compositionIds.
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
fieldId: deriveDynamicFieldID(
|
|
653
|
-
workId,
|
|
654
|
-
`${packageId}::composition_credits::ExtensionKey`,
|
|
655
|
-
COMPOSITION_CREDITS_KEY_BYTES,
|
|
656
|
-
),
|
|
657
|
-
},
|
|
658
|
-
{
|
|
640
|
+
return compositionIds.map((workId) => ({
|
|
641
|
+
workId,
|
|
642
|
+
kind: "composition" as const,
|
|
643
|
+
fieldId: deriveDynamicFieldID(
|
|
659
644
|
workId,
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
COMPOSITION_CREDITS_KEY_BYTES,
|
|
665
|
-
),
|
|
666
|
-
},
|
|
667
|
-
]);
|
|
645
|
+
`${packageId}::composition_credits::ExtensionKey`,
|
|
646
|
+
COMPOSITION_CREDITS_KEY_BYTES,
|
|
647
|
+
),
|
|
648
|
+
}));
|
|
668
649
|
}
|
|
669
650
|
|
|
670
651
|
function recordingCreditTargets(
|
|
671
652
|
recordingIds: readonly string[],
|
|
672
653
|
packageId: string,
|
|
673
654
|
): CreditFieldTarget[] {
|
|
674
|
-
return recordingIds.
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
fieldId: deriveDynamicFieldID(
|
|
679
|
-
workId,
|
|
680
|
-
`${packageId}::recording_credits::ExtensionKey`,
|
|
681
|
-
RECORDING_CREDITS_KEY_BYTES,
|
|
682
|
-
),
|
|
683
|
-
},
|
|
684
|
-
{
|
|
655
|
+
return recordingIds.map((workId) => ({
|
|
656
|
+
workId,
|
|
657
|
+
kind: "recording" as const,
|
|
658
|
+
fieldId: deriveDynamicFieldID(
|
|
685
659
|
workId,
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
RECORDING_CREDITS_KEY_BYTES,
|
|
691
|
-
),
|
|
692
|
-
},
|
|
693
|
-
]);
|
|
660
|
+
`${packageId}::recording_credits::ExtensionKey`,
|
|
661
|
+
RECORDING_CREDITS_KEY_BYTES,
|
|
662
|
+
),
|
|
663
|
+
}));
|
|
694
664
|
}
|
|
695
665
|
|
|
696
666
|
function releaseCreditTargets(
|
|
@@ -825,15 +795,10 @@ export async function getCompositionCreditsByIds(
|
|
|
825
795
|
);
|
|
826
796
|
const contents = await fetchCreditFields(client, targets);
|
|
827
797
|
const out: Partial<Record<string, CreditView[]>> = {};
|
|
828
|
-
for (const
|
|
829
|
-
const
|
|
830
|
-
(target) => target.workId === compositionId,
|
|
831
|
-
);
|
|
832
|
-
const content = candidates
|
|
833
|
-
.map((target) => contents.get(target.fieldId))
|
|
834
|
-
.find((value): value is Uint8Array => value !== undefined);
|
|
798
|
+
for (const target of targets) {
|
|
799
|
+
const content = contents.get(target.fieldId);
|
|
835
800
|
if (content) {
|
|
836
|
-
out[
|
|
801
|
+
out[target.workId] = creditViews(
|
|
837
802
|
CompositionCreditsField.parse(content).value.credits,
|
|
838
803
|
compositionRoleLabel,
|
|
839
804
|
);
|
|
@@ -875,16 +840,11 @@ export async function getRecordingCreditsByIds(
|
|
|
875
840
|
);
|
|
876
841
|
const contents = await fetchCreditFields(client, targets);
|
|
877
842
|
const out: Partial<Record<string, RecordingCreditsView>> = {};
|
|
878
|
-
for (const
|
|
879
|
-
const
|
|
880
|
-
(target) => target.workId === recordingId,
|
|
881
|
-
);
|
|
882
|
-
const content = candidates
|
|
883
|
-
.map((target) => contents.get(target.fieldId))
|
|
884
|
-
.find((value): value is Uint8Array => value !== undefined);
|
|
843
|
+
for (const target of targets) {
|
|
844
|
+
const content = contents.get(target.fieldId);
|
|
885
845
|
if (!content) continue;
|
|
886
846
|
const value = RecordingCreditsField.parse(content).value;
|
|
887
|
-
out[
|
|
847
|
+
out[target.workId] = {
|
|
888
848
|
credits: creditViews(value.credits, recordingRoleLabel),
|
|
889
849
|
primaryArtistIds: value.primary_artist_ids.contents,
|
|
890
850
|
featuredArtistIds: value.featured_artist_ids.contents,
|
package/src/index.ts
CHANGED
|
@@ -14,7 +14,9 @@
|
|
|
14
14
|
//
|
|
15
15
|
// A release is protocol. Pressing a record off it and selling that record is
|
|
16
16
|
// platform. Keeping the split at the package boundary is what stops the protocol
|
|
17
|
-
// from quietly growing a storefront.
|
|
17
|
+
// from quietly growing a storefront. Core's `release::new` is the sole practical
|
|
18
|
+
// exception: it needs `&mut UID`, which cannot cross a PTB command boundary, so
|
|
19
|
+
// the platform's `release_registry` extension owns the PTB-callable minting path.
|
|
18
20
|
//
|
|
19
21
|
// Extensions live on this side of the line for the same reason. An extension is
|
|
20
22
|
// not part of what a Composition or Recording IS — it is a choice Miso makes
|
package/src/read/catalog.ts
CHANGED
|
@@ -161,24 +161,16 @@ function toTracks(
|
|
|
161
161
|
|
|
162
162
|
// ── Cover ────────────────────────────────────────────────────────────────────
|
|
163
163
|
|
|
164
|
-
/**
|
|
165
|
-
* A release's cover, trying the current `release_cover_art` package and then each
|
|
166
|
-
* legacy one in turn.
|
|
167
|
-
*
|
|
168
|
-
* Covers set before the cover_art package split live under a different
|
|
169
|
-
* `CoverArtKey` TYPE (different package address), so a current-package read
|
|
170
|
-
* simply misses them rather than failing. Ported verbatim from miso-app's
|
|
171
|
-
* `lib/pressing.ts:readReleaseCover`.
|
|
172
|
-
*/
|
|
164
|
+
/** A release's cover from the configured `release_cover_art` package. */
|
|
173
165
|
export async function readReleaseCover(
|
|
174
166
|
client: MisoClient,
|
|
175
167
|
releaseId: string,
|
|
176
168
|
): Promise<Cover | null> {
|
|
177
|
-
const { releaseCoverArt
|
|
169
|
+
const { releaseCoverArt } = client.config.protocol;
|
|
178
170
|
const covers = await getReleaseCoversByIds(
|
|
179
171
|
client.protocol,
|
|
180
172
|
[releaseId],
|
|
181
|
-
|
|
173
|
+
releaseCoverArt,
|
|
182
174
|
).catch(() => ({}) as Partial<Record<string, ReleaseCoverView>>);
|
|
183
175
|
return toCover(client.config.walrusAggregatorUrl, covers[releaseId] ?? null);
|
|
184
176
|
}
|
|
@@ -203,15 +195,9 @@ export async function getReleaseResources(
|
|
|
203
195
|
): Promise<ReleaseResources> {
|
|
204
196
|
const wantsCover = include.includes("cover");
|
|
205
197
|
const wantsCredits = include.includes("credits");
|
|
206
|
-
const
|
|
207
|
-
? [
|
|
208
|
-
client.config.protocol.releaseCoverArt,
|
|
209
|
-
...client.config.protocol.legacyReleaseCoverArt,
|
|
210
|
-
]
|
|
198
|
+
const coverFieldIds = wantsCover
|
|
199
|
+
? [releaseCoverFieldId(releaseId, client.config.protocol.releaseCoverArt)]
|
|
211
200
|
: [];
|
|
212
|
-
const coverFieldIds = coverPackages.map((packageId) =>
|
|
213
|
-
releaseCoverFieldId(releaseId, packageId),
|
|
214
|
-
);
|
|
215
201
|
const creditsFieldId = wantsCredits
|
|
216
202
|
? releaseCreditsFieldId(releaseId, client.config.protocol.releaseCredits)
|
|
217
203
|
: null;
|
|
@@ -222,7 +208,7 @@ export async function getReleaseResources(
|
|
|
222
208
|
];
|
|
223
209
|
const { objects } = await client.protocol.core.getObjects({
|
|
224
210
|
objectIds,
|
|
225
|
-
include: { content: true
|
|
211
|
+
include: { content: true },
|
|
226
212
|
});
|
|
227
213
|
const releaseObject = objects[0];
|
|
228
214
|
if (!releaseObject) throw new Error(`Release not found: ${releaseId}`);
|
|
@@ -232,7 +218,6 @@ export async function getReleaseResources(
|
|
|
232
218
|
const release = parseReleaseObject(
|
|
233
219
|
releaseObject.objectId,
|
|
234
220
|
releaseObject.content,
|
|
235
|
-
releaseObject.json,
|
|
236
221
|
);
|
|
237
222
|
|
|
238
223
|
let cover: Cover | null | undefined;
|
|
@@ -440,10 +425,7 @@ export async function getDiscoverShelf(
|
|
|
440
425
|
|
|
441
426
|
const [releases, coverViews, credits] = await Promise.all([
|
|
442
427
|
getReleasesByIds(client.protocol, releaseIds),
|
|
443
|
-
getReleaseCoversByIds(client.protocol, releaseIds,
|
|
444
|
-
client.config.protocol.releaseCoverArt,
|
|
445
|
-
...client.config.protocol.legacyReleaseCoverArt,
|
|
446
|
-
]).catch(() => ({}) as Partial<Record<string, ReleaseCoverView>>),
|
|
428
|
+
getReleaseCoversByIds(client.protocol, releaseIds, client.config.protocol.releaseCoverArt).catch(() => ({}) as Partial<Record<string, ReleaseCoverView>>),
|
|
447
429
|
getReleaseCreditsByIds(
|
|
448
430
|
client.protocol,
|
|
449
431
|
releaseIds,
|
|
@@ -476,9 +458,7 @@ export async function getDiscoverShelf(
|
|
|
476
458
|
// ── Record → release ─────────────────────────────────────────────────────────
|
|
477
459
|
|
|
478
460
|
/**
|
|
479
|
-
* A record's parent release.
|
|
480
|
-
* checkout's `{ release_id, number }` and the SDK view's
|
|
481
|
-
* `{ release_id, edition, variant }` — so we probe both spellings.
|
|
461
|
+
* A record's parent release. Fresh `Record` objects expose `release_id`.
|
|
482
462
|
*
|
|
483
463
|
* This answer is IMMUTABLE for the life of the record, which is what lets the
|
|
484
464
|
* endpoint in front of it cache for a year.
|
|
@@ -500,7 +480,7 @@ export async function getRecordAlbum(
|
|
|
500
480
|
include: { json: true },
|
|
501
481
|
});
|
|
502
482
|
const json = (object?.json ?? {}) as Record<string, unknown>;
|
|
503
|
-
const raw = json.release_id
|
|
483
|
+
const raw = json.release_id;
|
|
504
484
|
const releaseId = typeof raw === "string" && raw ? raw : null;
|
|
505
485
|
const includeRelease =
|
|
506
486
|
options.include?.some(
|
package/src/read/config.ts
CHANGED
|
@@ -7,9 +7,8 @@
|
|
|
7
7
|
// `lib/money.ts`, `lib/drops.ts`) lives here instead, so a redeploy is a change
|
|
8
8
|
// in ONE file that every surface picks up.
|
|
9
9
|
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
// silently reading testnet ids on a mainnet deployment.
|
|
10
|
+
// Both networks stay unavailable until the fresh protocol publish is configured;
|
|
11
|
+
// pre-launch has no compatibility lane for the old deployment.
|
|
13
12
|
|
|
14
13
|
export type Network = "testnet" | "mainnet";
|
|
15
14
|
|
|
@@ -24,17 +23,12 @@ export interface ProtocolIds {
|
|
|
24
23
|
miso: string;
|
|
25
24
|
/** `miso_drop` — `drop::buy` and the `Drop` objects the pressing pages read. */
|
|
26
25
|
drop: string;
|
|
26
|
+
/** `miso_record` package — the exact owner/type namespace for `record::Record`. */
|
|
27
|
+
record: string;
|
|
27
28
|
/** `miso_record` shared `Settings` object — the mint witness authorizer. */
|
|
28
29
|
recordSettings: string;
|
|
29
|
-
/** `release_cover_art` — the release cover extension
|
|
30
|
+
/** `release_cover_art` — the release cover extension. */
|
|
30
31
|
releaseCoverArt: string;
|
|
31
|
-
/**
|
|
32
|
-
* Cover-art packages from before the `cover_art` package split. A cover set
|
|
33
|
-
* under one of these is invisible to a current-package read, so cover reads
|
|
34
|
-
* fall back through this list in order. Drop an entry once every cover it
|
|
35
|
-
* holds has been re-set under `releaseCoverArt`.
|
|
36
|
-
*/
|
|
37
|
-
legacyReleaseCoverArt: readonly string[];
|
|
38
32
|
/** `composition_credits` / `recording_credits` / `release_credits` extensions. */
|
|
39
33
|
compositionCredits: string;
|
|
40
34
|
recordingCredits: string;
|
|
@@ -94,51 +88,17 @@ export interface MisoConfig {
|
|
|
94
88
|
discoverReleaseIds: readonly string[];
|
|
95
89
|
}
|
|
96
90
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
credit: "0x96c82da135bbd534850ca0a2445fdd2531dd4e0947ffa14645a2eb6636d83646",
|
|
109
|
-
},
|
|
110
|
-
party: {
|
|
111
|
-
partyPackageId: "0xd1c0253f837c57bd0a572673a28374e32c118059356e0c9db26784897175197e",
|
|
112
|
-
partyProfilePackageId: "0x6f8841bb252d3ba0f59f9027546be7a0e0a59d84757214df216eb14ef8a9719e",
|
|
113
|
-
countryCodePackageId: "0x6c3a53f228ccd089825d0fb5ee1f0465a4b7a438bc1b1735b4e2f01df8d056e9",
|
|
114
|
-
languageCodePackageId: "0xa18b786807cfc45488691f93aa647800b77cf9993e420d110afd7024c5b15948",
|
|
115
|
-
partyMediaPackageId: "0xc04dbdc05076f642fa8dc79257de9d45ee71fc8fee4a4fe1c05c6bb27e8982ee",
|
|
116
|
-
partyRolesPackageId: "0x18e6d59fe3a6496bd42273fd97e6e4b0ae7d3a8a876a6bbe4f6ae1b6057a6f38",
|
|
117
|
-
partyTagsPackageId: "0x5a5ed50aac1146b34490949ab33fd29dc2ba13ddf1b8f90b4797e67e1301609d",
|
|
118
|
-
partyGenrePackageId: "0x2d489291ce41392ae887e2e048f752a9bd6bea9f540b2c3d2356e36f0e624e29",
|
|
119
|
-
partyCtaPackageId: "0xbf4b04afa5b9c87b7385c6a1f116165a6e3f9bed3e16ea064fed7be4da14db7f",
|
|
120
|
-
partyPlatformLinkPackageId: "0x2bf95053493e640e3641abef2a9d6080ac563da62a5ca8e878e6d3459fdf27c8",
|
|
121
|
-
partySocialPackageId: "0x24bdc5f4cdf68d5cbd6f2b4b791f8b0311060a7fac7e54c10d3a101aa7d6b56d",
|
|
122
|
-
partyMusicPackageId: "0x40d8c48aa7cd8ade4ccfc7eb68de958403b08a897589934c191993b25961c453",
|
|
123
|
-
partyProLinkPackageId: "0xe2f0160eeac627378538b82209c2cd58b6a73827dae46f5f92368a6c7e84ee8d",
|
|
124
|
-
partyFeaturedDropPackageId: "0x5205a9f88702ed2dadd95e4d0096c691a40c96d8bb05d77127f1a62a6eb72cf7",
|
|
125
|
-
genrePackageId: "0x7ee69a2f1440152ecdacad997364fb28fe1fc140fd720e4c6d37ad78e29a376d",
|
|
126
|
-
},
|
|
127
|
-
money: {
|
|
128
|
-
usdCoinType: "0x77774cb7b8cb5622b4ef2658101bf5f1e965418297fe874b683df8f760b6e749::fakeusd::FakeUsd",
|
|
129
|
-
usdDecimals: 6,
|
|
130
|
-
},
|
|
131
|
-
grpcUrl: "https://fullnode.testnet.sui.io",
|
|
132
|
-
graphqlUrl: "https://graphql.testnet.sui.io/graphql",
|
|
133
|
-
walrusAggregatorUrl: "https://aggregator.walrus-testnet.walrus.space",
|
|
134
|
-
apiBaseUrl: "https://api.testnet.miso.fm",
|
|
135
|
-
discoverReleaseIds: [
|
|
136
|
-
// AMIANGELIKA — BLCK SUN
|
|
137
|
-
"0xa16e7ef7af5104690117321c749995db1164db668a5559cf6a0706b26141c327",
|
|
138
|
-
// Gateway Girl — Between the Doors
|
|
139
|
-
"0x7144600de3bb037f23b9371a2bcd5c0d84a80abc381a9d03b00ff7e03f412623",
|
|
140
|
-
],
|
|
141
|
-
};
|
|
91
|
+
// TODO(publish): replace this guard with a fresh `TESTNET: MisoConfig` after
|
|
92
|
+
// republishing every protocol package. Set every `protocol` package/object id
|
|
93
|
+
// from that publish; do not reuse any id from the retired deployment.
|
|
94
|
+
// TODO(publish): set `protocol.record` to the fresh `miso_record` package id;
|
|
95
|
+
// it is distinct from the `protocol.recordSettings` shared object id.
|
|
96
|
+
// TODO(publish): at the `ProtocolIds` declaration above, add `releaseRegistry`
|
|
97
|
+
// and `releaseRegistryId`; set the latter from
|
|
98
|
+
// `ReleaseRegistryCreatedEvent.registry_id` in the release_registry publish tx.
|
|
99
|
+
// TODO(publish): recreate Discover content under the fresh packages, then add
|
|
100
|
+
// only those release ids to `discoverReleaseIds`.
|
|
101
|
+
const TESTNET: MisoConfig | null = null;
|
|
142
102
|
|
|
143
103
|
/** Fields a deployment may override without forking the whole config (endpoints, shelf). */
|
|
144
104
|
export type MisoConfigOverrides = Partial<
|
|
@@ -150,9 +110,9 @@ export type MisoConfigOverrides = Partial<
|
|
|
150
110
|
* filled in here — a mainnet worker must fail loudly, never read testnet ids.
|
|
151
111
|
*/
|
|
152
112
|
export function misoConfig(network: Network, overrides: MisoConfigOverrides = {}): MisoConfig {
|
|
153
|
-
if (network === "mainnet") {
|
|
113
|
+
if (network === "mainnet" || !TESTNET) {
|
|
154
114
|
throw new Error(
|
|
155
|
-
"@misofm/sdk/read: no
|
|
115
|
+
"@misofm/sdk/read: no fresh deployment configuration yet. Publish the protocol packages, then fill in src/read/config.ts.",
|
|
156
116
|
);
|
|
157
117
|
}
|
|
158
118
|
return { ...TESTNET, ...stripUndefined(overrides) };
|
package/src/read/index.ts
CHANGED
|
@@ -59,9 +59,9 @@ export type { ArtistInclude, GetArtistOptions } from "./artist.ts";
|
|
|
59
59
|
export { resolveGenreNames } from "./genres.ts";
|
|
60
60
|
|
|
61
61
|
export {
|
|
62
|
-
RECORD_TYPE_SUFFIX,
|
|
63
62
|
getBalance,
|
|
64
63
|
getOwnedParties,
|
|
64
|
+
getPendingMemberships,
|
|
65
65
|
getOwnedRecords,
|
|
66
66
|
getOwnedWorks,
|
|
67
67
|
getWorkByCap,
|
package/src/read/types.ts
CHANGED
|
@@ -270,7 +270,7 @@ export interface PartySummary {
|
|
|
270
270
|
export interface OwnedRecord {
|
|
271
271
|
/** Canonical object id — the `recordId` route param. */
|
|
272
272
|
id: string;
|
|
273
|
-
/** Full on-chain type, e.g.
|
|
273
|
+
/** Full on-chain type, e.g. `<miso_record>::record::Record`. */
|
|
274
274
|
type: string;
|
|
275
275
|
releaseId: string | null;
|
|
276
276
|
/** Copy / edition number, when the struct exposes one. */
|
|
@@ -285,6 +285,16 @@ export interface OwnedParty {
|
|
|
285
285
|
kind: "individual" | "group";
|
|
286
286
|
}
|
|
287
287
|
|
|
288
|
+
/** A group invitation awaiting acceptance by a party this wallet administers. */
|
|
289
|
+
export interface PendingMembership {
|
|
290
|
+
/** The invited individual party and the cap that authorizes accept/decline. */
|
|
291
|
+
memberPartyId: string;
|
|
292
|
+
memberCapId: string;
|
|
293
|
+
/** The inviting group party. */
|
|
294
|
+
groupId: string;
|
|
295
|
+
groupName: string;
|
|
296
|
+
}
|
|
297
|
+
|
|
288
298
|
export type WorkKind = "composition" | "recording" | "release";
|
|
289
299
|
|
|
290
300
|
/** A work the wallet administers, as the studio catalog lists it. */
|