@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/pressing.ts
CHANGED
|
@@ -4,15 +4,23 @@
|
|
|
4
4
|
// Primary Record sales. `record` owns concrete Records and edition-scoped
|
|
5
5
|
// Pressings; immutable `record_shop` owns per-currency Listings and payment.
|
|
6
6
|
|
|
7
|
-
import type { ClientWithCoreApi } from "@mysten/sui/client";
|
|
8
7
|
import {
|
|
9
8
|
deriveObjectID,
|
|
10
9
|
normalizeStructTag,
|
|
11
10
|
normalizeSuiAddress,
|
|
12
11
|
parseStructTag,
|
|
13
12
|
} from "@mysten/sui/utils";
|
|
13
|
+
import { Effect, Schema } from "effect";
|
|
14
|
+
import {
|
|
15
|
+
assertObjectType,
|
|
16
|
+
decodeBcs,
|
|
17
|
+
getObjectsContent,
|
|
18
|
+
getOptionalObjectContent,
|
|
19
|
+
ObjectTypeMismatchError,
|
|
20
|
+
type SuiClient,
|
|
21
|
+
} from "@misofm/effect";
|
|
22
|
+
import type { BcsDecodeError, SuiRpcError } from "@misofm/effect";
|
|
14
23
|
import type { TxThunk } from "./transactions.ts";
|
|
15
|
-
import { isNotFound } from "./queries.ts";
|
|
16
24
|
import { asU64, type U64Input } from "./vault.ts";
|
|
17
25
|
import * as pressingContract from "./contracts/record/pressing.ts";
|
|
18
26
|
import * as recordContract from "./contracts/record/record.ts";
|
|
@@ -406,54 +414,45 @@ export function purchaseRecord(p: PurchaseRecordParams): TxThunk {
|
|
|
406
414
|
};
|
|
407
415
|
}
|
|
408
416
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
if (!object) return null;
|
|
449
|
-
if (!object.content || !object.type)
|
|
450
|
-
throw new Error(`object ${objectId} has no BCS content or full type`);
|
|
451
|
-
return { content: object.content, type: object.type };
|
|
452
|
-
} catch (error) {
|
|
453
|
-
if (isNotFound(error)) return null;
|
|
454
|
-
throw error;
|
|
455
|
-
}
|
|
456
|
-
}
|
|
417
|
+
/** One permanent record-production run derived from a release. */
|
|
418
|
+
export class Pressing extends Schema.Class<Pressing>("@misofm/platform/Pressing")({
|
|
419
|
+
id: Schema.String,
|
|
420
|
+
releaseId: Schema.String,
|
|
421
|
+
edition: Schema.Number,
|
|
422
|
+
supply: Schema.Number,
|
|
423
|
+
maxSupply: Schema.NullOr(Schema.Number),
|
|
424
|
+
distributors: Schema.Array(Schema.String),
|
|
425
|
+
}) {}
|
|
426
|
+
|
|
427
|
+
/** One permanent currency offer derived from a Pressing. */
|
|
428
|
+
export class Listing extends Schema.Class<Listing>("@misofm/platform/Listing")({
|
|
429
|
+
id: Schema.String,
|
|
430
|
+
releaseId: Schema.String,
|
|
431
|
+
pressingId: Schema.String,
|
|
432
|
+
pricing: Schema.Struct({
|
|
433
|
+
kind: Schema.Literals(["fixed", "floor"]),
|
|
434
|
+
amount: Schema.String,
|
|
435
|
+
}),
|
|
436
|
+
state: Schema.Literals(["enabled", "disabled"]),
|
|
437
|
+
currencyType: Schema.String,
|
|
438
|
+
}) {}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* One purchased copy of a Pressing. Named `PressingRecord`, not `Record` — the
|
|
442
|
+
* Move type is `record::Record`, but `Record` collides with TypeScript's
|
|
443
|
+
* built-in `Record<K, V>` utility type, which this codebase uses constantly.
|
|
444
|
+
*/
|
|
445
|
+
export class PressingRecord extends Schema.Class<PressingRecord>("@misofm/platform/PressingRecord")({
|
|
446
|
+
id: Schema.String,
|
|
447
|
+
releaseId: Schema.String,
|
|
448
|
+
pressingId: Schema.String,
|
|
449
|
+
edition: Schema.Number,
|
|
450
|
+
number: Schema.Number,
|
|
451
|
+
purchaseCurrency: Schema.String,
|
|
452
|
+
purchasePrice: Schema.String,
|
|
453
|
+
purchasedBy: Schema.String,
|
|
454
|
+
purchasedTimestampMs: Schema.String,
|
|
455
|
+
}) {}
|
|
457
456
|
|
|
458
457
|
function sameId(a: string, b: string): boolean {
|
|
459
458
|
return normalizeSuiAddress(a) === normalizeSuiAddress(b);
|
|
@@ -464,15 +463,6 @@ function requireId(label: string, actual: string, expected: string): void {
|
|
|
464
463
|
throw new Error(`${label} ${actual} does not match expected ${expected}`);
|
|
465
464
|
}
|
|
466
465
|
|
|
467
|
-
function requireExactType(type: string, expected: string, label: string): void {
|
|
468
|
-
const actual = normalizeStructTag(type);
|
|
469
|
-
const normalizedExpected = normalizeStructTag(expected);
|
|
470
|
-
if (actual !== normalizedExpected)
|
|
471
|
-
throw new Error(
|
|
472
|
-
`${label} has type ${actual}, expected ${normalizedExpected}`,
|
|
473
|
-
);
|
|
474
|
-
}
|
|
475
|
-
|
|
476
466
|
function requireListingCurrency(
|
|
477
467
|
type: string,
|
|
478
468
|
recordShopPackageId: string,
|
|
@@ -500,7 +490,7 @@ function enumKind(
|
|
|
500
490
|
throw new Error(`unknown enum variant; expected ${names.join(" or ")}`);
|
|
501
491
|
}
|
|
502
492
|
|
|
503
|
-
function parsePricing(value: unknown):
|
|
493
|
+
function parsePricing(value: unknown): { kind: "fixed" | "floor"; amount: string } {
|
|
504
494
|
if (!value || typeof value !== "object")
|
|
505
495
|
throw new Error("Listing has malformed pricing");
|
|
506
496
|
const v = value as { $kind?: string; Fixed?: string; Floor?: string };
|
|
@@ -511,13 +501,8 @@ function parsePricing(value: unknown): ListingView["pricing"] {
|
|
|
511
501
|
return { kind: kind === "Fixed" ? "fixed" : "floor", amount };
|
|
512
502
|
}
|
|
513
503
|
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
content: Uint8Array,
|
|
517
|
-
type: string,
|
|
518
|
-
recordPackageId: string,
|
|
519
|
-
): PressingView {
|
|
520
|
-
requireExactType(type, `${recordPackageId}::pressing::Pressing`, "Pressing");
|
|
504
|
+
/** Raw BCS-parse -> camelCase mapper; `Schema.decodeUnknownEffect` (via `decodeBcs`) validates and constructs `Pressing`. */
|
|
505
|
+
function mapPressing(pressingId: string, recordPackageId: string, content: Uint8Array): typeof Pressing.Encoded {
|
|
521
506
|
const parsed = pressingContract.Pressing.parse(content);
|
|
522
507
|
requireId("Pressing UID", parsed.id, pressingId);
|
|
523
508
|
requireId(
|
|
@@ -531,19 +516,16 @@ function parsePressing(
|
|
|
531
516
|
edition: parsed.edition,
|
|
532
517
|
supply: parsed.supply,
|
|
533
518
|
maxSupply: parsed.max_supply,
|
|
534
|
-
distributors: parsed.distributors.contents.map((item) =>
|
|
535
|
-
normalizeStructTag(item.name),
|
|
536
|
-
),
|
|
519
|
+
distributors: parsed.distributors.contents.map((item) => normalizeStructTag(item.name)),
|
|
537
520
|
};
|
|
538
521
|
}
|
|
539
522
|
|
|
540
|
-
function
|
|
523
|
+
function mapListing(
|
|
541
524
|
listingId: string,
|
|
542
|
-
content: Uint8Array,
|
|
543
|
-
type: string,
|
|
544
525
|
recordShopPackageId: string,
|
|
545
|
-
|
|
546
|
-
|
|
526
|
+
currencyType: string,
|
|
527
|
+
content: Uint8Array,
|
|
528
|
+
): typeof Listing.Encoded {
|
|
547
529
|
const parsed = listingContract.Listing.parse(content);
|
|
548
530
|
requireId("Listing UID", parsed.id, listingId);
|
|
549
531
|
requireId(
|
|
@@ -562,13 +544,7 @@ function parseListing(
|
|
|
562
544
|
};
|
|
563
545
|
}
|
|
564
546
|
|
|
565
|
-
function
|
|
566
|
-
recordId: string,
|
|
567
|
-
content: Uint8Array,
|
|
568
|
-
type: string,
|
|
569
|
-
recordPackageId: string,
|
|
570
|
-
): RecordView {
|
|
571
|
-
requireExactType(type, `${recordPackageId}::record::Record`, "Record");
|
|
547
|
+
function mapRecord(recordId: string, recordPackageId: string, content: Uint8Array): typeof PressingRecord.Encoded {
|
|
572
548
|
const parsed = recordContract.Record.parse(content);
|
|
573
549
|
requireId("Record UID", parsed.id, recordId);
|
|
574
550
|
requireId(
|
|
@@ -589,38 +565,77 @@ function parseRecord(
|
|
|
589
565
|
};
|
|
590
566
|
}
|
|
591
567
|
|
|
592
|
-
|
|
593
|
-
|
|
568
|
+
/** Fails with `ObjectTypeMismatchError` unless `type` is `recordShopPackageId`'s `listing::Listing<Currency>`; returns the currency type argument. */
|
|
569
|
+
function assertListingType(
|
|
570
|
+
objectId: string,
|
|
571
|
+
type: string,
|
|
572
|
+
recordShopPackageId: string,
|
|
573
|
+
): Effect.Effect<string, ObjectTypeMismatchError> {
|
|
574
|
+
return Effect.try({
|
|
575
|
+
try: () => requireListingCurrency(type, recordShopPackageId),
|
|
576
|
+
catch: () =>
|
|
577
|
+
new ObjectTypeMismatchError({
|
|
578
|
+
objectId,
|
|
579
|
+
expected: `${normalizeSuiAddress(recordShopPackageId)}::listing::Listing<Currency>`,
|
|
580
|
+
actual: normalizeStructTag(type),
|
|
581
|
+
}),
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/** One permanent pressing by id, or `null` when no such Pressing exists. */
|
|
586
|
+
export const getPressing = Effect.fn("getPressing")(function* (
|
|
594
587
|
pressingId: string,
|
|
595
588
|
recordPackageId: string,
|
|
596
|
-
):
|
|
597
|
-
const
|
|
598
|
-
return
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
589
|
+
): Effect.fn.Return<Pressing | null, ObjectTypeMismatchError | BcsDecodeError | SuiRpcError, SuiClient> {
|
|
590
|
+
const found = yield* getOptionalObjectContent(pressingId);
|
|
591
|
+
if (found._tag === "None") return null;
|
|
592
|
+
const { content, type } = found.value;
|
|
593
|
+
yield* assertObjectType(
|
|
594
|
+
pressingId,
|
|
595
|
+
normalizeStructTag(type),
|
|
596
|
+
normalizeStructTag(`${recordPackageId}::pressing::Pressing`),
|
|
597
|
+
);
|
|
598
|
+
return yield* decodeBcs(
|
|
599
|
+
{ parse: (bytes) => mapPressing(pressingId, recordPackageId, bytes) },
|
|
600
|
+
Pressing,
|
|
601
|
+
content,
|
|
602
|
+
{ type: "Pressing", objectId: pressingId },
|
|
603
|
+
);
|
|
604
|
+
});
|
|
602
605
|
|
|
603
|
-
|
|
604
|
-
|
|
606
|
+
/** One currency-specific listing by id, or `null` when no such Listing exists. */
|
|
607
|
+
export const getListing = Effect.fn("getListing")(function* (
|
|
605
608
|
listingId: string,
|
|
606
609
|
recordShopPackageId: string,
|
|
607
|
-
):
|
|
608
|
-
const
|
|
609
|
-
return
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
610
|
+
): Effect.fn.Return<Listing | null, ObjectTypeMismatchError | BcsDecodeError | SuiRpcError, SuiClient> {
|
|
611
|
+
const found = yield* getOptionalObjectContent(listingId);
|
|
612
|
+
if (found._tag === "None") return null;
|
|
613
|
+
const { content, type } = found.value;
|
|
614
|
+
const currencyType = yield* assertListingType(listingId, type, recordShopPackageId);
|
|
615
|
+
return yield* decodeBcs(
|
|
616
|
+
{ parse: (bytes) => mapListing(listingId, recordShopPackageId, currencyType, bytes) },
|
|
617
|
+
Listing,
|
|
618
|
+
content,
|
|
619
|
+
{ type: "Listing", objectId: listingId },
|
|
620
|
+
);
|
|
621
|
+
});
|
|
613
622
|
|
|
614
|
-
|
|
615
|
-
|
|
623
|
+
/** One purchased copy by id, or `null` when no such Record exists. */
|
|
624
|
+
export const getRecord = Effect.fn("getRecord")(function* (
|
|
616
625
|
recordId: string,
|
|
617
626
|
recordPackageId: string,
|
|
618
|
-
):
|
|
619
|
-
const
|
|
620
|
-
return
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
627
|
+
): Effect.fn.Return<PressingRecord | null, ObjectTypeMismatchError | BcsDecodeError | SuiRpcError, SuiClient> {
|
|
628
|
+
const found = yield* getOptionalObjectContent(recordId);
|
|
629
|
+
if (found._tag === "None") return null;
|
|
630
|
+
const { content, type } = found.value;
|
|
631
|
+
yield* assertObjectType(recordId, normalizeStructTag(type), normalizeStructTag(`${recordPackageId}::record::Record`));
|
|
632
|
+
return yield* decodeBcs(
|
|
633
|
+
{ parse: (bytes) => mapRecord(recordId, recordPackageId, bytes) },
|
|
634
|
+
PressingRecord,
|
|
635
|
+
content,
|
|
636
|
+
{ type: "Record", objectId: recordId },
|
|
637
|
+
);
|
|
638
|
+
});
|
|
624
639
|
|
|
625
640
|
export interface GetSaleParams {
|
|
626
641
|
releaseId: string;
|
|
@@ -630,10 +645,14 @@ export interface GetSaleParams {
|
|
|
630
645
|
recordShopPackageId: string;
|
|
631
646
|
}
|
|
632
647
|
|
|
633
|
-
|
|
634
|
-
|
|
648
|
+
/** A Pressing and its currency-specific Listing, read in one Core batch. Either half may be `null`. */
|
|
649
|
+
export const getSale = Effect.fn("getSale")(function* (
|
|
635
650
|
p: GetSaleParams,
|
|
636
|
-
):
|
|
651
|
+
): Effect.fn.Return<
|
|
652
|
+
{ pressing: Pressing | null; listing: Listing | null },
|
|
653
|
+
ObjectTypeMismatchError | BcsDecodeError | SuiRpcError,
|
|
654
|
+
SuiClient
|
|
655
|
+
> {
|
|
637
656
|
const { pressingId, listingId } = deriveSaleIds(
|
|
638
657
|
p.releaseId,
|
|
639
658
|
p.edition,
|
|
@@ -641,36 +660,35 @@ export async function getSale(
|
|
|
641
660
|
p.recordPackageId,
|
|
642
661
|
p.recordShopPackageId,
|
|
643
662
|
);
|
|
644
|
-
const
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
p.recordPackageId,
|
|
661
|
-
);
|
|
662
|
-
const listing =
|
|
663
|
-
!listingObject || listingObject instanceof Error
|
|
664
|
-
? null
|
|
665
|
-
: parseBatchListing(
|
|
666
|
-
listingId,
|
|
667
|
-
listingObject.content,
|
|
668
|
-
listingObject.type,
|
|
669
|
-
p.recordShopPackageId,
|
|
670
|
-
);
|
|
671
|
-
if (pressing)
|
|
663
|
+
const contentById = yield* getObjectsContent([pressingId, listingId]);
|
|
664
|
+
|
|
665
|
+
const pressingFound = contentById.get(pressingId);
|
|
666
|
+
let pressing: Pressing | null = null;
|
|
667
|
+
if (pressingFound) {
|
|
668
|
+
yield* assertObjectType(
|
|
669
|
+
pressingId,
|
|
670
|
+
normalizeStructTag(pressingFound.type),
|
|
671
|
+
normalizeStructTag(`${p.recordPackageId}::pressing::Pressing`),
|
|
672
|
+
);
|
|
673
|
+
pressing = yield* decodeBcs(
|
|
674
|
+
{ parse: (bytes) => mapPressing(pressingId, p.recordPackageId, bytes) },
|
|
675
|
+
Pressing,
|
|
676
|
+
pressingFound.content,
|
|
677
|
+
{ type: "Pressing", objectId: pressingId },
|
|
678
|
+
);
|
|
672
679
|
requireId("Sale pressing release", pressing.releaseId, p.releaseId);
|
|
673
|
-
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
const listingFound = contentById.get(listingId);
|
|
683
|
+
let listing: Listing | null = null;
|
|
684
|
+
if (listingFound) {
|
|
685
|
+
const currencyType = yield* assertListingType(listingId, listingFound.type, p.recordShopPackageId);
|
|
686
|
+
listing = yield* decodeBcs(
|
|
687
|
+
{ parse: (bytes) => mapListing(listingId, p.recordShopPackageId, currencyType, bytes) },
|
|
688
|
+
Listing,
|
|
689
|
+
listingFound.content,
|
|
690
|
+
{ type: "Listing", objectId: listingId },
|
|
691
|
+
);
|
|
674
692
|
requireId("Sale listing release", listing.releaseId, p.releaseId);
|
|
675
693
|
requireId("Sale listing pressing", listing.pressingId, pressingId);
|
|
676
694
|
if (listing.currencyType !== normalizeStructTag(p.currencyType)) {
|
|
@@ -679,27 +697,6 @@ export async function getSale(
|
|
|
679
697
|
);
|
|
680
698
|
}
|
|
681
699
|
}
|
|
682
|
-
return { pressing, listing };
|
|
683
|
-
}
|
|
684
700
|
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
content: Uint8Array | undefined,
|
|
688
|
-
type: string | undefined,
|
|
689
|
-
packageId: string,
|
|
690
|
-
): PressingView {
|
|
691
|
-
if (!content || !type)
|
|
692
|
-
throw new Error(`Pressing ${id} has no BCS content or full type`);
|
|
693
|
-
return parsePressing(id, content, type, packageId);
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
function parseBatchListing(
|
|
697
|
-
id: string,
|
|
698
|
-
content: Uint8Array | undefined,
|
|
699
|
-
type: string | undefined,
|
|
700
|
-
packageId: string,
|
|
701
|
-
): ListingView {
|
|
702
|
-
if (!content || !type)
|
|
703
|
-
throw new Error(`Listing ${id} has no BCS content or full type`);
|
|
704
|
-
return parseListing(id, content, type, packageId);
|
|
705
|
-
}
|
|
701
|
+
return { pressing, listing };
|
|
702
|
+
});
|
package/src/queries.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
4
|
// Shared read plumbing for the platform reads.
|
|
5
|
-
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
export {
|
|
5
|
+
//
|
|
6
|
+
// Not-found is a typed `ObjectNotFoundError` (see `@misofm/effect/errors`),
|
|
7
|
+
// matched with `Effect.catchTag("ObjectNotFoundError", ...)` — there is no
|
|
8
|
+
// `isNotFound` predicate to re-export anymore.
|
|
9
|
+
export {};
|
package/src/read/artist.ts
CHANGED
|
@@ -10,8 +10,12 @@
|
|
|
10
10
|
//
|
|
11
11
|
// `roles` and `tags` are opt-in via `include`.
|
|
12
12
|
|
|
13
|
+
import { Effect, Option } from "effect";
|
|
14
|
+
import { getPartiesByIds, getPartyById } from "@misofm/partyos";
|
|
15
|
+
import type { BcsDecodeError, ObjectNotFoundError, ObjectTypeMismatchError, SuiClient, SuiRpcError } from "@misofm/effect";
|
|
16
|
+
import { getCtas, getGenres, getLinks, getProfile, getRoles, getTags } from "../party/queries.ts";
|
|
13
17
|
import { resolveGenreNames } from "./genres.ts";
|
|
14
|
-
import type {
|
|
18
|
+
import type { MisoConfig } from "./config.ts";
|
|
15
19
|
import type {
|
|
16
20
|
ArtistProfile,
|
|
17
21
|
PartyMember,
|
|
@@ -31,39 +35,40 @@ export function partyAvatarUrl(apiBaseUrl: string, partyId: string): string {
|
|
|
31
35
|
}
|
|
32
36
|
|
|
33
37
|
/**
|
|
34
|
-
* An artist page, fully resolved.
|
|
38
|
+
* An artist page, fully resolved. Fails only if the PARTY itself can't be read —
|
|
35
39
|
* every extension is optional by design (a party with no profile set is a new
|
|
36
40
|
* party, not a broken one), so each decoration read falls back to its empty value.
|
|
37
41
|
*/
|
|
38
|
-
export
|
|
39
|
-
client: MisoClient,
|
|
42
|
+
export const getArtistProfile = Effect.fn("getArtistProfile")(function* (
|
|
40
43
|
partyId: string,
|
|
44
|
+
config: MisoConfig,
|
|
41
45
|
options: GetArtistOptions = {},
|
|
42
|
-
):
|
|
46
|
+
): Effect.fn.Return<
|
|
47
|
+
ArtistProfile,
|
|
48
|
+
ObjectNotFoundError | ObjectTypeMismatchError | BcsDecodeError | SuiRpcError,
|
|
49
|
+
SuiClient
|
|
50
|
+
> {
|
|
43
51
|
const include = new Set(options.include ?? []);
|
|
44
|
-
const
|
|
52
|
+
const party = config.party;
|
|
45
53
|
|
|
46
|
-
const [
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
54
|
+
const [profileOpt, ctas, genreIds, links, roles, tags, entity] = yield* Effect.all([
|
|
55
|
+
getProfile(partyId, party.partyProfile).pipe(Effect.catch(() => Effect.succeed(Option.none()))),
|
|
56
|
+
getCtas(partyId, party.partyCta).pipe(Effect.catch(() => Effect.succeed([] as { label: string; url: string }[]))),
|
|
57
|
+
getGenres(partyId, party.partyGenre).pipe(Effect.catch(() => Effect.succeed([] as string[]))),
|
|
58
|
+
getLinks(partyId).pipe(Effect.catch(() => Effect.succeed([] as { platform: string; value: string; url: string }[]))),
|
|
59
|
+
include.has("roles")
|
|
60
|
+
? getRoles(partyId, party.partyRoles).pipe(Effect.catch(() => Effect.succeed([] as string[])))
|
|
61
|
+
: Effect.succeed(undefined),
|
|
62
|
+
include.has("tags")
|
|
63
|
+
? getTags(partyId, party.partyTags).pipe(Effect.catch(() => Effect.succeed([] as string[])))
|
|
64
|
+
: Effect.succeed(undefined),
|
|
65
|
+
getPartyById(partyId, config.partyos.partyos),
|
|
66
|
+
]);
|
|
67
|
+
const profile = Option.getOrNull(profileOpt);
|
|
60
68
|
|
|
61
|
-
const [genres, members] =
|
|
62
|
-
resolveGenreNames(
|
|
63
|
-
resolveMembers(
|
|
64
|
-
client,
|
|
65
|
-
entity.kind === "group" ? (entity.members ?? []) : [],
|
|
66
|
-
),
|
|
69
|
+
const [genres, members] = yield* Effect.all([
|
|
70
|
+
resolveGenreNames(genreIds),
|
|
71
|
+
resolveMembers(entity.kind === "group" ? (entity.members ?? []) : [], config),
|
|
67
72
|
]);
|
|
68
73
|
|
|
69
74
|
return {
|
|
@@ -74,45 +79,45 @@ export async function getArtistProfile(
|
|
|
74
79
|
bioShort: profile?.bioShort ?? null,
|
|
75
80
|
bioLong: profile?.bioLong ?? null,
|
|
76
81
|
country: profile?.country ?? null,
|
|
77
|
-
languages: profile?.languages
|
|
82
|
+
languages: profile?.languages ? [...profile.languages] : [],
|
|
78
83
|
genres,
|
|
79
84
|
links: links.map((l) => ({
|
|
80
85
|
platform: l.platform,
|
|
81
86
|
value: l.value,
|
|
82
87
|
url: l.url,
|
|
83
|
-
})),
|
|
88
|
+
})) as ArtistProfile["links"],
|
|
84
89
|
ctas: ctas.map((c) => ({ label: c.label, url: c.url })),
|
|
85
90
|
members,
|
|
86
91
|
...(roles !== undefined ? { roles } : {}),
|
|
87
92
|
...(tags !== undefined ? { tags } : {}),
|
|
88
|
-
avatarUrl: partyAvatarUrl(
|
|
93
|
+
avatarUrl: partyAvatarUrl(config.apiBaseUrl, entity.id),
|
|
89
94
|
};
|
|
90
|
-
}
|
|
95
|
+
});
|
|
91
96
|
|
|
92
97
|
/** Group members with names resolved. A member that fails to read is dropped. */
|
|
93
|
-
|
|
94
|
-
client: MisoClient,
|
|
98
|
+
const resolveMembers = Effect.fn("resolveMembers")(function* (
|
|
95
99
|
memberIds: readonly string[],
|
|
96
|
-
|
|
100
|
+
config: MisoConfig,
|
|
101
|
+
): Effect.fn.Return<PartyMember[], never, SuiClient> {
|
|
97
102
|
if (memberIds.length === 0) return [];
|
|
98
|
-
const parties =
|
|
99
|
-
.
|
|
100
|
-
|
|
103
|
+
const parties = yield* getPartiesByIds([...memberIds], config.partyos.partyos).pipe(
|
|
104
|
+
Effect.catch(() => Effect.succeed({} as Record<string, undefined>)),
|
|
105
|
+
);
|
|
101
106
|
return memberIds.flatMap((id) => {
|
|
102
107
|
const p = parties[id];
|
|
103
108
|
return p ? [{ id, name: p.name }] : [];
|
|
104
109
|
});
|
|
105
|
-
}
|
|
110
|
+
});
|
|
106
111
|
|
|
107
112
|
/** Name + kind for many parties at once. Ids that don't resolve are omitted. */
|
|
108
|
-
export
|
|
109
|
-
client: MisoClient,
|
|
113
|
+
export const getPartySummaries = Effect.fn("getPartySummaries")(function* (
|
|
110
114
|
ids: readonly string[],
|
|
111
|
-
|
|
115
|
+
config: MisoConfig,
|
|
116
|
+
): Effect.fn.Return<PartySummary[], ObjectTypeMismatchError | BcsDecodeError | SuiRpcError, SuiClient> {
|
|
112
117
|
if (ids.length === 0) return [];
|
|
113
|
-
const parties =
|
|
118
|
+
const parties = yield* getPartiesByIds([...ids], config.partyos.partyos);
|
|
114
119
|
return ids.flatMap((id) => {
|
|
115
120
|
const p = parties[id];
|
|
116
121
|
return p ? [{ id, name: p.name, kind: p.kind }] : [];
|
|
117
122
|
});
|
|
118
|
-
}
|
|
123
|
+
});
|