@misofm/sdk 0.6.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.
Files changed (78) hide show
  1. package/README.md +13 -9
  2. package/dist/client.d.ts +17 -1
  3. package/dist/client.d.ts.map +1 -1
  4. package/dist/client.js +22 -3
  5. package/dist/client.js.map +1 -1
  6. package/dist/contracts/release_registry/release_registry.d.ts +114 -0
  7. package/dist/contracts/release_registry/release_registry.d.ts.map +1 -0
  8. package/dist/contracts/release_registry/release_registry.js +119 -0
  9. package/dist/contracts/release_registry/release_registry.js.map +1 -0
  10. package/dist/contracts/royalty_pool/pool.d.ts +28 -2
  11. package/dist/contracts/royalty_pool/pool.d.ts.map +1 -1
  12. package/dist/contracts/royalty_pool/pool.js +28 -2
  13. package/dist/contracts/royalty_pool/pool.js.map +1 -1
  14. package/dist/contracts/utils/index.d.ts +44 -0
  15. package/dist/contracts/utils/index.d.ts.map +1 -1
  16. package/dist/contracts/utils/index.js +104 -2
  17. package/dist/contracts/utils/index.js.map +1 -1
  18. package/dist/contracts.d.ts +1 -0
  19. package/dist/contracts.d.ts.map +1 -1
  20. package/dist/contracts.js +4 -1
  21. package/dist/contracts.js.map +1 -1
  22. package/dist/cover.d.ts +3 -6
  23. package/dist/cover.d.ts.map +1 -1
  24. package/dist/cover.js +7 -16
  25. package/dist/cover.js.map +1 -1
  26. package/dist/credits.d.ts.map +1 -1
  27. package/dist/credits.js +17 -43
  28. package/dist/credits.js.map +1 -1
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +3 -1
  31. package/dist/index.js.map +1 -1
  32. package/dist/read/catalog.d.ts +2 -12
  33. package/dist/read/catalog.d.ts.map +1 -1
  34. package/dist/read/catalog.js +9 -24
  35. package/dist/read/catalog.js.map +1 -1
  36. package/dist/read/config.d.ts +3 -8
  37. package/dist/read/config.d.ts.map +1 -1
  38. package/dist/read/config.js +15 -50
  39. package/dist/read/config.js.map +1 -1
  40. package/dist/read/index.d.ts +1 -1
  41. package/dist/read/index.d.ts.map +1 -1
  42. package/dist/read/index.js +1 -1
  43. package/dist/read/index.js.map +1 -1
  44. package/dist/read/types.d.ts +1 -1
  45. package/dist/read/types.d.ts.map +1 -1
  46. package/dist/read/wallet.d.ts +3 -7
  47. package/dist/read/wallet.d.ts.map +1 -1
  48. package/dist/read/wallet.js +11 -38
  49. package/dist/read/wallet.js.map +1 -1
  50. package/dist/read/works.d.ts +2 -5
  51. package/dist/read/works.d.ts.map +1 -1
  52. package/dist/read/works.js +7 -37
  53. package/dist/read/works.js.map +1 -1
  54. package/dist/release-graph.d.ts +6 -19
  55. package/dist/release-graph.d.ts.map +1 -1
  56. package/dist/release-graph.js +29 -43
  57. package/dist/release-graph.js.map +1 -1
  58. package/dist/transactions.d.ts +12 -28
  59. package/dist/transactions.d.ts.map +1 -1
  60. package/dist/transactions.js +23 -28
  61. package/dist/transactions.js.map +1 -1
  62. package/package.json +3 -3
  63. package/src/client.ts +33 -2
  64. package/src/contracts/release_registry/release_registry.ts +160 -0
  65. package/src/contracts/royalty_pool/pool.ts +28 -2
  66. package/src/contracts/utils/index.ts +156 -4
  67. package/src/contracts.ts +5 -1
  68. package/src/cover.ts +8 -19
  69. package/src/credits.ts +23 -63
  70. package/src/index.ts +3 -1
  71. package/src/read/catalog.ts +9 -29
  72. package/src/read/config.ts +18 -58
  73. package/src/read/index.ts +0 -1
  74. package/src/read/types.ts +1 -1
  75. package/src/read/wallet.ts +11 -37
  76. package/src/read/works.ts +5 -42
  77. package/src/release-graph.ts +40 -75
  78. package/src/transactions.ts +33 -62
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.flatMap((workId) => [
649
- {
650
- workId,
651
- kind: "composition" as const,
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
- kind: "compositionLegacy" as const,
661
- fieldId: deriveDynamicFieldID(
662
- workId,
663
- `${packageId}::composition_credits::CompositionCreditsKey`,
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.flatMap((workId) => [
675
- {
676
- workId,
677
- kind: "recording" as const,
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
- kind: "recordingLegacy" as const,
687
- fieldId: deriveDynamicFieldID(
688
- workId,
689
- `${packageId}::recording_credits::RecordingCreditsKey`,
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 compositionId of compositionIds) {
829
- const candidates = targets.filter(
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[compositionId] = creditViews(
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 recordingId of recordingIds) {
879
- const candidates = targets.filter(
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[recordingId] = {
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
@@ -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, legacyReleaseCoverArt } = client.config.protocol;
169
+ const { releaseCoverArt } = client.config.protocol;
178
170
  const covers = await getReleaseCoversByIds(
179
171
  client.protocol,
180
172
  [releaseId],
181
- [releaseCoverArt, ...legacyReleaseCoverArt],
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 coverPackages = wantsCover
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, json: 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. Both live `Record` struct layouts expose it — card
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 ?? json.releaseId;
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(
@@ -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
- // Testnet values are the deployed ones (verified against chain 2026-08-09).
11
- // Mainnet is deliberately EMPTY: `misoConfig("mainnet")` throws rather than
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 currently written to. */
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
- const TESTNET: MisoConfig = {
98
- network: "testnet",
99
- protocol: {
100
- miso: "0x648c9dfba8c800ebd5e608599551f51e1a157942e29d59a1c35e14c44367acf0",
101
- drop: "0xfc2b51f068dee9d5482d39fa017164f6a8c3601cabb59084a518de5f609ef1c7",
102
- recordSettings: "0xadf53b5be0d9eea43712b0c73d0ce55d1fb990ba06e35e12a9a371b2dfe3fd35",
103
- releaseCoverArt: "0x1ee2cae0da7595d7973c310c912434ef531589b468cc57839296810c1385f7f6",
104
- legacyReleaseCoverArt: ["0x7d4a205a68f8e768a408c9f4c45a4d4076722c5edeba9068c0ac058f554e964e"],
105
- compositionCredits: "0x25ba72b5737cac577ca8c92d0f1962efbe5d688a1088cfdc580740605086b366",
106
- recordingCredits: "0xd272b1960e5fae15ae2a5425b910a96619727c85acd0c0095b6089531fdb32a9",
107
- releaseCredits: "0xed764634b9c9a6af04e3c7f60f657c8e2129baf9b43fb9aee6f3523a423228e4",
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 mainnet configuration yet. Deploy the Move packages, then fill in the mainnet block in src/read/config.ts.",
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,7 +59,6 @@ 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,
65
64
  getPendingMemberships,
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. `0xfc2b51…::record::Record`. */
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. */
@@ -32,9 +32,6 @@ import type {
32
32
  } from "./types.ts";
33
33
  import { getRecordingTitles, getWorkAddressesByShareTypes, getWorksByIds } from "./works.ts";
34
34
 
35
- /** The on-chain type suffix every record object shares, across both live packages. */
36
- export const RECORD_TYPE_SUFFIX = "::record::Record";
37
-
38
35
  /**
39
36
  * Page cap on the owned-objects scan, so a wallet holding a huge unrelated object
40
37
  * set can't spin forever. 50/page × 20 = 1000 objects — far beyond any realistic
@@ -52,53 +49,29 @@ function coerceNumber(v: unknown): number | null {
52
49
  }
53
50
 
54
51
  /**
55
- * This copy's number in its run, across both record struct layouts: card
56
- * checkout's `{ release_id, number }` and the SDK view's
57
- * `{ release_id, edition, variant: { Prototype|Production: { number } } }`.
58
- * The gRPC JSON enum shape varies by transport, so this probes defensively.
59
- *
60
- * Precedence is MOST SPECIFIC FIRST — a top-level `number`, then the variant's
61
- * `number`, and only then `edition`. miso-app's original
62
- * (`lib/records.ts:readRecordNumber`) checked `edition` before descending into
63
- * the variant, so an SDK-view record reported which EDITION it came from (2)
64
- * where the field means which COPY it is (12). `edition` survives as the last
65
- * resort for a layout that carries nothing better.
52
+ * This copy's number in its run. Fresh records expose the canonical
53
+ * `{ release_id, number }` shape; pre-publish layouts are intentionally not
54
+ * accepted by the pre-launch SDK.
66
55
  */
67
56
  function readRecordNumber(json: Record<string, unknown>): number | null {
68
- const direct = coerceNumber(json.number);
69
- if (direct != null) return direct;
70
-
71
- const variant = json.variant;
72
- if (variant && typeof variant === "object") {
73
- const v = variant as Record<string, unknown>;
74
- const top = coerceNumber(v.number);
75
- if (top != null) return top;
76
- for (const inner of Object.values(v)) {
77
- if (inner && typeof inner === "object") {
78
- const n = coerceNumber((inner as Record<string, unknown>).number);
79
- if (n != null) return n;
80
- }
81
- }
82
- }
83
- return coerceNumber(json.edition);
57
+ return coerceNumber(json.number);
84
58
  }
85
59
 
86
60
  function readReleaseId(json: Record<string, unknown>): string | null {
87
- const r = json.release_id ?? json.releaseId;
61
+ const r = json.release_id;
88
62
  return typeof r === "string" && r ? r : null;
89
63
  }
90
64
 
91
65
  /**
92
66
  * The records `owner` holds. Ownership is DIRECT — a record is an address-owned
93
- * `<pkg>::record::Record` with no pressing/license/receipt intermediary.
94
- *
95
- * There is deliberately no `type` filter on the listing: two record packages are
96
- * live, so we match the `::record::Record` SUFFIX client-side and catch both with
97
- * no package-id config. Objects that fail to parse are skipped, never thrown.
67
+ * `<miso_record>::record::Record` with no pressing/license/receipt intermediary.
68
+ * The server-side type filter and exact local check deliberately exclude records
69
+ * from any retired package namespace.
98
70
  */
99
71
  export async function getOwnedRecords(client: MisoClient, owner: string): Promise<OwnedRecord[]> {
100
72
  const out: OwnedRecord[] = [];
101
73
  let cursor: string | null = null;
74
+ const recordType = `${client.config.protocol.record}::record::Record`;
102
75
 
103
76
  for (let page = 0; page < MAX_PAGES; page++) {
104
77
  // Annotated: `cursor` is both an input and assigned from the result, which
@@ -106,12 +79,13 @@ export async function getOwnedRecords(client: MisoClient, owner: string): Promis
106
79
  const res: Awaited<ReturnType<typeof client.protocol.core.listOwnedObjects>> =
107
80
  await client.protocol.core.listOwnedObjects({
108
81
  owner,
82
+ type: recordType,
109
83
  cursor,
110
84
  limit: 50,
111
85
  include: { json: true },
112
86
  });
113
87
  for (const obj of res.objects) {
114
- if (!obj.type || !obj.type.endsWith(RECORD_TYPE_SUFFIX)) continue;
88
+ if (obj.type !== recordType) continue;
115
89
  const json = (obj.json ?? {}) as Record<string, unknown>;
116
90
  out.push({
117
91
  id: obj.objectId,
package/src/read/works.ts CHANGED
@@ -179,31 +179,7 @@ function parseRecording(id: string, content: Uint8Array): Recording {
179
179
  export function parseReleaseObject(
180
180
  id: string,
181
181
  content: Uint8Array,
182
- json: unknown,
183
182
  ): Release {
184
- const deployed = json as {
185
- discs?: Parsed[];
186
- state?: Parsed;
187
- title?: unknown;
188
- } | null;
189
- if (deployed?.discs) {
190
- const tracks = deployed.discs.flatMap((disc) => disc.tracks ?? []);
191
- const publishedAt =
192
- deployed.state?.["@variant"] === "Published" ? deployed.state.pos0 : null;
193
- return {
194
- id,
195
- state:
196
- publishedAt == null
197
- ? { type: "Initialized" }
198
- : { type: "Published", timestampMs: Number(publishedAt) },
199
- title: String(deployed.title ?? ""),
200
- tracks: tracks.map((track) => ({
201
- state: (track.state?.["@variant"] ?? "Unassigned") as TrackState,
202
- recordingId: String(track.recording_id),
203
- splitBps: { value: Number(track.split_bps?.pos0 ?? track.split_bps) },
204
- })),
205
- };
206
- }
207
183
  const value = contracts.release.Release.parse(content) as Parsed;
208
184
  return {
209
185
  id,
@@ -245,7 +221,7 @@ export async function getWorksByIds(
245
221
  if (kinds.size === 0) return out;
246
222
  const { objects } = await client.core.getObjects({
247
223
  objectIds: [...kinds.keys()],
248
- include: { content: true, json: true },
224
+ include: { content: true },
249
225
  });
250
226
  for (const object of objects) {
251
227
  if (object instanceof Error || !object.content) continue;
@@ -264,16 +240,12 @@ export async function getWorksByIds(
264
240
  out.releases[object.objectId] = parseReleaseObject(
265
241
  object.objectId,
266
242
  object.content,
267
- object.json,
268
243
  );
269
244
  }
270
245
  return out;
271
246
  }
272
247
 
273
- /**
274
- * Resolve display titles for both deployed recording layouts. Older recordings
275
- * carry `title` in JSON; current recordings inherit it from their composition.
276
- */
248
+ /** Resolve recording titles through each recording's canonical composition type. */
277
249
  export async function getRecordingTitles(
278
250
  client: ClientWithCoreApi,
279
251
  graphql: SuiGraphQLClient,
@@ -285,24 +257,15 @@ export async function getRecordingTitles(
285
257
 
286
258
  const { objects } = await client.core.getObjects({
287
259
  objectIds: ids,
288
- include: { json: true },
260
+ include: {},
289
261
  });
290
262
  const titles: Record<string, string> = {};
291
263
  const compositionShareByRecording: Record<string, string> = {};
292
264
 
293
265
  for (const object of objects) {
294
266
  if (object instanceof Error) continue;
295
- const json = object.json as { title?: unknown } | null;
296
- if (typeof json?.title === "string" && json.title) {
297
- titles[object.objectId] = json.title;
298
- continue;
299
- }
300
- try {
301
- const [, compositionShareType] = extractTypeParams2(object.type);
302
- compositionShareByRecording[object.objectId] = compositionShareType;
303
- } catch {
304
- // A deployed layout with no composition type and no JSON title remains Untitled.
305
- }
267
+ const [, compositionShareType] = extractTypeParams2(object.type);
268
+ compositionShareByRecording[object.objectId] = compositionShareType;
306
269
  }
307
270
 
308
271
  const compositionShareTypes = Object.values(compositionShareByRecording);