@misofm/platform 0.25.0 → 0.26.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.
Files changed (140) hide show
  1. package/README.md +144 -45
  2. package/dist/auth.d.ts +8 -13
  3. package/dist/auth.d.ts.map +1 -1
  4. package/dist/auth.js +78 -59
  5. package/dist/auth.js.map +1 -1
  6. package/dist/catalog.d.ts +5 -5
  7. package/dist/catalog.d.ts.map +1 -1
  8. package/dist/catalog.js +33 -21
  9. package/dist/catalog.js.map +1 -1
  10. package/dist/client.d.ts +17 -30
  11. package/dist/client.d.ts.map +1 -1
  12. package/dist/client.js +53 -75
  13. package/dist/client.js.map +1 -1
  14. package/dist/cover.d.ts +4 -3
  15. package/dist/cover.d.ts.map +1 -1
  16. package/dist/cover.js +28 -17
  17. package/dist/cover.js.map +1 -1
  18. package/dist/credits.d.ts +12 -10
  19. package/dist/credits.d.ts.map +1 -1
  20. package/dist/credits.js +60 -33
  21. package/dist/credits.js.map +1 -1
  22. package/dist/deployments.d.ts +21 -13
  23. package/dist/deployments.d.ts.map +1 -1
  24. package/dist/deployments.js +84 -37
  25. package/dist/deployments.js.map +1 -1
  26. package/dist/errors.d.ts +91 -0
  27. package/dist/errors.d.ts.map +1 -0
  28. package/dist/errors.js +111 -0
  29. package/dist/errors.js.map +1 -0
  30. package/dist/execute.d.ts +3 -1
  31. package/dist/execute.d.ts.map +1 -1
  32. package/dist/execute.js +9 -4
  33. package/dist/execute.js.map +1 -1
  34. package/dist/genre.d.ts +25 -41
  35. package/dist/genre.d.ts.map +1 -1
  36. package/dist/genre.js +71 -20
  37. package/dist/genre.js.map +1 -1
  38. package/dist/party/client.d.ts +16 -13
  39. package/dist/party/client.d.ts.map +1 -1
  40. package/dist/party/client.js +33 -22
  41. package/dist/party/client.js.map +1 -1
  42. package/dist/party/extensions/links.d.ts +2 -1
  43. package/dist/party/extensions/links.d.ts.map +1 -1
  44. package/dist/party/extensions/links.js +2 -1
  45. package/dist/party/extensions/links.js.map +1 -1
  46. package/dist/party/queries.d.ts +12 -11
  47. package/dist/party/queries.d.ts.map +1 -1
  48. package/dist/party/queries.js +63 -57
  49. package/dist/party/queries.js.map +1 -1
  50. package/dist/party/types.d.ts +31 -20
  51. package/dist/party/types.d.ts.map +1 -1
  52. package/dist/party/types.js +82 -1
  53. package/dist/party/types.js.map +1 -1
  54. package/dist/pressing.d.ts +55 -35
  55. package/dist/pressing.d.ts.map +1 -1
  56. package/dist/pressing.js +99 -76
  57. package/dist/pressing.js.map +1 -1
  58. package/dist/queries.d.ts +1 -1
  59. package/dist/queries.d.ts.map +1 -1
  60. package/dist/queries.js +5 -4
  61. package/dist/queries.js.map +1 -1
  62. package/dist/read/artist.d.ts +6 -4
  63. package/dist/read/artist.d.ts.map +1 -1
  64. package/dist/read/artist.js +29 -27
  65. package/dist/read/artist.js.map +1 -1
  66. package/dist/read/catalog.d.ts +20 -17
  67. package/dist/read/catalog.d.ts.map +1 -1
  68. package/dist/read/catalog.js +120 -141
  69. package/dist/read/catalog.js.map +1 -1
  70. package/dist/read/client.d.ts +10 -9
  71. package/dist/read/client.d.ts.map +1 -1
  72. package/dist/read/client.js.map +1 -1
  73. package/dist/read/genres.d.ts +5 -3
  74. package/dist/read/genres.d.ts.map +1 -1
  75. package/dist/read/genres.js +18 -15
  76. package/dist/read/genres.js.map +1 -1
  77. package/dist/read/index.d.ts.map +1 -1
  78. package/dist/read/index.js +7 -1
  79. package/dist/read/index.js.map +1 -1
  80. package/dist/read/receipts.d.ts +15 -6
  81. package/dist/read/receipts.d.ts.map +1 -1
  82. package/dist/read/receipts.js +73 -60
  83. package/dist/read/receipts.js.map +1 -1
  84. package/dist/read/wallet.d.ts +11 -9
  85. package/dist/read/wallet.d.ts.map +1 -1
  86. package/dist/read/wallet.js +117 -132
  87. package/dist/read/wallet.js.map +1 -1
  88. package/dist/read/works.d.ts +9 -16
  89. package/dist/read/works.d.ts.map +1 -1
  90. package/dist/read/works.js +48 -117
  91. package/dist/read/works.js.map +1 -1
  92. package/dist/recording-extensions.d.ts +8 -7
  93. package/dist/recording-extensions.d.ts.map +1 -1
  94. package/dist/recording-extensions.js +30 -63
  95. package/dist/recording-extensions.js.map +1 -1
  96. package/dist/release-extensions.d.ts +3 -2
  97. package/dist/release-extensions.d.ts.map +1 -1
  98. package/dist/release-extensions.js +9 -10
  99. package/dist/release-extensions.js.map +1 -1
  100. package/dist/royalty.d.ts +51 -51
  101. package/dist/royalty.d.ts.map +1 -1
  102. package/dist/royalty.js +93 -11
  103. package/dist/royalty.js.map +1 -1
  104. package/dist/share.d.ts +7 -6
  105. package/dist/share.d.ts.map +1 -1
  106. package/dist/share.js +37 -30
  107. package/dist/share.js.map +1 -1
  108. package/dist/vault.d.ts +20 -8
  109. package/dist/vault.d.ts.map +1 -1
  110. package/dist/vault.js +31 -15
  111. package/dist/vault.js.map +1 -1
  112. package/package.json +11 -4
  113. package/src/auth.ts +89 -70
  114. package/src/catalog.ts +33 -49
  115. package/src/client.ts +117 -97
  116. package/src/cover.ts +17 -26
  117. package/src/credits.ts +43 -75
  118. package/src/deployments.ts +103 -69
  119. package/src/errors.ts +156 -0
  120. package/src/execute.ts +10 -6
  121. package/src/genre.ts +38 -23
  122. package/src/party/client.ts +35 -23
  123. package/src/party/extensions/links.ts +3 -2
  124. package/src/party/queries.ts +77 -65
  125. package/src/party/types.ts +52 -48
  126. package/src/pressing.ts +158 -161
  127. package/src/queries.ts +5 -5
  128. package/src/read/artist.ts +47 -42
  129. package/src/read/catalog.ts +200 -223
  130. package/src/read/client.ts +12 -11
  131. package/src/read/genres.ts +24 -16
  132. package/src/read/index.ts +7 -1
  133. package/src/read/receipts.ts +128 -67
  134. package/src/read/wallet.ts +194 -162
  135. package/src/read/works.ts +75 -194
  136. package/src/recording-extensions.ts +38 -92
  137. package/src/release-extensions.ts +7 -12
  138. package/src/royalty.ts +117 -20
  139. package/src/share.ts +44 -29
  140. 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
- export interface PressingView {
410
- id: string;
411
- releaseId: string;
412
- edition: number;
413
- supply: number;
414
- maxSupply: number | null;
415
- distributors: string[];
416
- }
417
-
418
- export interface ListingView {
419
- id: string;
420
- releaseId: string;
421
- pressingId: string;
422
- pricing: { kind: "fixed" | "floor"; amount: string };
423
- state: ListingSwitch;
424
- currencyType: string;
425
- }
426
-
427
- export interface RecordView {
428
- id: string;
429
- releaseId: string;
430
- pressingId: string;
431
- edition: number;
432
- number: number;
433
- purchaseCurrency: string;
434
- purchasePrice: string;
435
- purchasedBy: string;
436
- purchasedTimestampMs: string;
437
- }
438
-
439
- async function fetch(
440
- client: ClientWithCoreApi,
441
- objectId: string,
442
- ): Promise<{ content: Uint8Array; type: string } | null> {
443
- try {
444
- const { object } = await client.core.getObject({
445
- objectId,
446
- include: { content: true },
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): ListingView["pricing"] {
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
- function parsePressing(
515
- pressingId: string,
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 parseListing(
523
+ function mapListing(
541
524
  listingId: string,
542
- content: Uint8Array,
543
- type: string,
544
525
  recordShopPackageId: string,
545
- ): ListingView {
546
- const currencyType = requireListingCurrency(type, recordShopPackageId);
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 parseRecord(
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
- export async function getPressing(
593
- client: ClientWithCoreApi,
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
- ): Promise<PressingView | null> {
597
- const got = await fetch(client, pressingId);
598
- return got
599
- ? parsePressing(pressingId, got.content, got.type, recordPackageId)
600
- : null;
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
- export async function getListing(
604
- client: ClientWithCoreApi,
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
- ): Promise<ListingView | null> {
608
- const got = await fetch(client, listingId);
609
- return got
610
- ? parseListing(listingId, got.content, got.type, recordShopPackageId)
611
- : null;
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
- export async function getRecord(
615
- client: ClientWithCoreApi,
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
- ): Promise<RecordView | null> {
619
- const got = await fetch(client, recordId);
620
- return got
621
- ? parseRecord(recordId, got.content, got.type, recordPackageId)
622
- : null;
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
- export async function getSale(
634
- client: ClientWithCoreApi,
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
- ): Promise<{ pressing: PressingView | null; listing: ListingView | null }> {
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 { objects } = await client.core.getObjects({
645
- objectIds: [pressingId, listingId],
646
- include: { content: true },
647
- });
648
- const [pressingObject, listingObject] = objects;
649
- if (pressingObject instanceof Error && !isNotFound(pressingObject))
650
- throw pressingObject;
651
- if (listingObject instanceof Error && !isNotFound(listingObject))
652
- throw listingObject;
653
- const pressing =
654
- !pressingObject || pressingObject instanceof Error
655
- ? null
656
- : parseBatchPressing(
657
- pressingId,
658
- pressingObject.content,
659
- pressingObject.type,
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
- if (listing) {
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
- function parseBatchPressing(
686
- id: string,
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
- // `isNotFound` is the protocol SDK's — the not-found taxonomy is a property of
7
- // the Sui transports, not of any one package's objects, so it is defined once in
8
- // `@misofm/musicos` and re-exported here for the platform readers.
9
- export { isNotFound } from "@misofm/musicos";
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 {};
@@ -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 { MisoClient } from "./client.ts";
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. Throws only if the PARTY itself can't be read —
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 async function getArtistProfile(
39
- client: MisoClient,
42
+ export const getArtistProfile = Effect.fn("getArtistProfile")(function* (
40
43
  partyId: string,
44
+ config: MisoConfig,
41
45
  options: GetArtistOptions = {},
42
- ): Promise<ArtistProfile> {
46
+ ): Effect.fn.Return<
47
+ ArtistProfile,
48
+ ObjectNotFoundError | ObjectTypeMismatchError | BcsDecodeError | SuiRpcError,
49
+ SuiClient
50
+ > {
43
51
  const include = new Set(options.include ?? []);
44
- const { party } = client;
52
+ const party = config.party;
45
53
 
46
- const [profile, ctas, genreIds, links, roles, tags, entity] =
47
- await Promise.all([
48
- party.getProfile(partyId).catch(() => null),
49
- party.getCtas(partyId).catch(() => []),
50
- party.getGenres(partyId).catch(() => [] as string[]),
51
- party.getLinks(partyId).catch(() => []),
52
- include.has("roles")
53
- ? party.getRoles(partyId).catch(() => [] as string[])
54
- : Promise.resolve(undefined),
55
- include.has("tags")
56
- ? party.getTags(partyId).catch(() => [] as string[])
57
- : Promise.resolve(undefined),
58
- party.getPartyById(partyId),
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] = await Promise.all([
62
- resolveGenreNames(client, genreIds),
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(client.config.apiBaseUrl, entity.id),
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
- async function resolveMembers(
94
- client: MisoClient,
98
+ const resolveMembers = Effect.fn("resolveMembers")(function* (
95
99
  memberIds: readonly string[],
96
- ): Promise<PartyMember[]> {
100
+ config: MisoConfig,
101
+ ): Effect.fn.Return<PartyMember[], never, SuiClient> {
97
102
  if (memberIds.length === 0) return [];
98
- const parties = await client.party
99
- .getPartiesByIds([...memberIds])
100
- .catch(() => ({}) as Record<string, undefined>);
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 async function getPartySummaries(
109
- client: MisoClient,
113
+ export const getPartySummaries = Effect.fn("getPartySummaries")(function* (
110
114
  ids: readonly string[],
111
- ): Promise<PartySummary[]> {
115
+ config: MisoConfig,
116
+ ): Effect.fn.Return<PartySummary[], ObjectTypeMismatchError | BcsDecodeError | SuiRpcError, SuiClient> {
112
117
  if (ids.length === 0) return [];
113
- const parties = await client.party.getPartiesByIds([...ids]);
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
+ });