@misofm/platform 0.20.0 → 0.21.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.
Files changed (47) hide show
  1. package/README.md +15 -17
  2. package/dist/deployments.d.ts +6 -2
  3. package/dist/deployments.d.ts.map +1 -1
  4. package/dist/deployments.js +3 -1
  5. package/dist/deployments.js.map +1 -1
  6. package/dist/index.d.ts +1 -2
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +1 -2
  9. package/dist/index.js.map +1 -1
  10. package/dist/publication.d.ts +14 -2
  11. package/dist/publication.d.ts.map +1 -1
  12. package/dist/publication.js +4 -3
  13. package/dist/publication.js.map +1 -1
  14. package/dist/read/catalog.d.ts +1 -1
  15. package/dist/read/catalog.d.ts.map +1 -1
  16. package/dist/read/catalog.js +3 -16
  17. package/dist/read/catalog.js.map +1 -1
  18. package/dist/read/config.d.ts +0 -2
  19. package/dist/read/config.d.ts.map +1 -1
  20. package/dist/read/config.js.map +1 -1
  21. package/dist/read/internal/walrus.js +1 -1
  22. package/dist/read/internal/walrus.js.map +1 -1
  23. package/dist/read/types.d.ts +0 -2
  24. package/dist/read/types.d.ts.map +1 -1
  25. package/dist/recording-extensions.d.ts +32 -7
  26. package/dist/recording-extensions.d.ts.map +1 -1
  27. package/dist/recording-extensions.js +90 -8
  28. package/dist/recording-extensions.js.map +1 -1
  29. package/dist/walrus-ids.d.ts +11 -0
  30. package/dist/walrus-ids.d.ts.map +1 -0
  31. package/dist/walrus-ids.js +96 -0
  32. package/dist/walrus-ids.js.map +1 -0
  33. package/package.json +2 -8
  34. package/src/deployments.ts +8 -3
  35. package/src/index.ts +1 -2
  36. package/src/publication.ts +20 -5
  37. package/src/read/catalog.ts +2 -18
  38. package/src/read/config.ts +0 -2
  39. package/src/read/internal/walrus.ts +1 -1
  40. package/src/read/types.ts +0 -2
  41. package/src/recording-extensions.ts +127 -11
  42. package/src/walrus-ids.ts +102 -0
  43. package/dist/mix.d.ts +0 -130
  44. package/dist/mix.d.ts.map +0 -1
  45. package/dist/mix.js +0 -634
  46. package/dist/mix.js.map +0 -1
  47. package/src/mix.ts +0 -863
package/src/index.ts CHANGED
@@ -49,7 +49,7 @@ export * from "./share-template.ts";
49
49
  export * from "./release-graph.ts";
50
50
  export * from "./publication.ts";
51
51
  export * from "./catalog.ts";
52
- export * from "./mix.ts";
52
+ export * from "./walrus-ids.ts";
53
53
 
54
54
  // High-level, JSON-safe platform projections used directly by clients and by
55
55
  // the thin HTTP read service. Namespaced because these composed view types are
@@ -62,7 +62,6 @@ export * from "./credits.ts";
62
62
  export * from "./cover.ts";
63
63
  export * from "./release-extensions.ts";
64
64
  export * from "./recording-extensions.ts";
65
- export * from "./mix.ts";
66
65
  export * from "./vault.ts";
67
66
 
68
67
  // Generated, ABI-bound bindings (BCS structs + type-safe Move calls).
@@ -150,6 +150,20 @@ type PublicationRecordingParent =
150
150
  | { readonly parentCompositionIndex: number; readonly parentCompositionId?: never }
151
151
  | { readonly parentCompositionIndex?: never; readonly parentCompositionId: string };
152
152
 
153
+ /** One stem a Session V1 document references, paired with its Walrus blob. */
154
+ export interface PublicationEngineSessionStem {
155
+ /** 32-byte SHA-256 of the stem's canonical PCM (engine STEM_IDENTITY_V1); bytes or 64 hex chars. */
156
+ readonly digest: Uint8Array | string;
157
+ /** Unencrypted Walrus blob holding the stem's FLAC, as its on-chain `u256`. */
158
+ readonly blobId: bigint | string;
159
+ }
160
+
161
+ export interface PublicationEngineSession {
162
+ /** Unencrypted Walrus blob holding the canonical Session V1 JSON, as its on-chain `u256`. */
163
+ readonly sessionBlobId: bigint | string;
164
+ readonly stems: readonly PublicationEngineSessionStem[];
165
+ }
166
+
153
167
  export type PublicationRecordingLanguages =
154
168
  | { readonly kind: "instrumental" }
155
169
  | { readonly kind: "languages"; readonly codes: string[] };
@@ -173,8 +187,8 @@ export type PublicationRecording = PublicationRecordingParent & {
173
187
  readonly masterReferenceBlobId?: bigint | string;
174
188
  /** Complete Walrus Quilt ID containing this Recording's streaming transcodes. */
175
189
  readonly streamingTranscodeQuiltId?: bigint | string;
176
- /** Unencrypted Walrus blob ID of this Recording's Miso Engine session file. */
177
- readonly engineSessionBlobId?: bigint | string;
190
+ /** This Recording's Miso Engine session: the Session V1 blob and every stem it plays. */
191
+ readonly engineSession?: PublicationEngineSession;
178
192
  };
179
193
 
180
194
  export interface PublicationFreshTrack {
@@ -654,7 +668,7 @@ export function publishAtomicCatalog(p: AtomicPublicationParams): TxThunk {
654
668
  );
655
669
  }
656
670
  if (
657
- p.recordings.some((node) => node.engineSessionBlobId !== undefined) &&
671
+ p.recordings.some((node) => node.engineSession !== undefined) &&
658
672
  !p.deployment.packages.recordingEngineSession
659
673
  ) {
660
674
  throw new Error(
@@ -878,13 +892,14 @@ export function publishAtomicCatalog(p: AtomicPublicationParams): TxThunk {
878
892
  oriPackageId: p.deployment.packages.ori,
879
893
  quiltId: node.streamingTranscodeQuiltId,
880
894
  })(tx);
881
- if (node.engineSessionBlobId !== undefined) setRecordingEngineSession({
895
+ if (node.engineSession !== undefined) setRecordingEngineSession({
882
896
  recordingId: parts.work, authority,
883
897
  recordingShareType: node.shareType,
884
898
  compositionShareType: node.compositionShareType,
885
899
  recordingEngineSessionPackageId: p.deployment.packages.recordingEngineSession!,
886
900
  oriPackageId: p.deployment.packages.ori,
887
- sessionBlobId: node.engineSessionBlobId,
901
+ sessionBlobId: node.engineSession.sessionBlobId,
902
+ stems: node.engineSession.stems,
888
903
  })(tx);
889
904
  });
890
905
 
@@ -38,10 +38,6 @@ import {
38
38
  releaseKindFieldId,
39
39
  } from "../release-extensions.ts";
40
40
  import { getRecordingMasterReferencesByIds } from "../recording-extensions.ts";
41
- import {
42
- getReleaseMixReferences,
43
- walrusBlobIdFromU256,
44
- } from "../mix.ts";
45
41
  import { getTrackCreditsByRecordingIds } from "../catalog.ts";
46
42
  import { getReleaseById, getReleasesByIds, isNotFound } from "@misofm/protocol";
47
43
  import type { Release } from "@misofm/protocol";
@@ -203,11 +199,9 @@ export function toTracks(
203
199
  release: Release,
204
200
  titles: Record<string, string>,
205
201
  masterBlobIds: Partial<Record<string, string>>,
206
- mixBlobIds: Partial<Record<number, string>> = {},
207
202
  ): TrackView[] {
208
203
  return release.tracks.map((track, index) => {
209
204
  const masterBlobId = masterBlobIds[track.recordingId];
210
- const mixBlobId = mixBlobIds[index];
211
205
  return {
212
206
  no: `${index + 1}`,
213
207
  title: titles[track.recordingId] ?? "Untitled",
@@ -216,7 +210,6 @@ export function toTracks(
216
210
  splitBps: int(track.splitBps.value),
217
211
  disc: 1,
218
212
  ...(masterBlobId ? { masterBlobId } : {}),
219
- ...(mixBlobId ? { mixBlobId } : {}),
220
213
  };
221
214
  });
222
215
  }
@@ -364,8 +357,7 @@ export async function getReleaseDetail(
364
357
  );
365
358
 
366
359
  const recordingIds = release.tracks.map((track) => track.recordingId);
367
- const mixPackageId = client.config.protocol.releaseMixReference;
368
- const [titles, masterReferences, mixReferences, trackCredits] = await Promise.all([
360
+ const [titles, masterReferences, trackCredits] = await Promise.all([
369
361
  getRecordingTitles(
370
362
  client.protocol,
371
363
  client.graphql,
@@ -377,9 +369,6 @@ export async function getReleaseDetail(
377
369
  recordingIds,
378
370
  client.config.protocol.recordingMasterReference,
379
371
  ).catch(() => ({})),
380
- mixPackageId
381
- ? getReleaseMixReferences(client.protocol, releaseId, mixPackageId).catch(() => null)
382
- : Promise.resolve(null),
383
372
  options.include?.includes("trackCredits")
384
373
  ? getTrackCreditsForRecordingIds(client, recordingIds)
385
374
  : Promise.resolve(undefined),
@@ -388,11 +377,6 @@ export async function getReleaseDetail(
388
377
  for (const [recordingId, blobId] of Object.entries(masterReferences)) {
389
378
  if (blobId) masterBlobIds[recordingId] = u256ToB64Url(blobId);
390
379
  }
391
- const mixBlobIds: Record<number, string> = {};
392
- for (let index = 0; index < (mixReferences?.length ?? 0); index += 1) {
393
- const reference = mixReferences?.[index];
394
- if (reference) mixBlobIds[index] = walrusBlobIdFromU256(reference.blobId);
395
- }
396
380
 
397
381
  const creditViews = credits ?? [];
398
382
  return {
@@ -407,7 +391,7 @@ export async function getReleaseDetail(
407
391
  credits: creditViews,
408
392
  primaryArtists: primaryArtistNames(creditViews),
409
393
  discCount: release.tracks.length > 0 ? 1 : 0,
410
- tracks: toTracks(release, titles, masterBlobIds, mixBlobIds),
394
+ tracks: toTracks(release, titles, masterBlobIds),
411
395
  ...(trackCredits !== undefined ? { trackCredits } : {}),
412
396
  };
413
397
  }
@@ -33,8 +33,6 @@ export interface ProtocolIds {
33
33
  releaseKind: string;
34
34
  /** `recording_master_reference` — optional Walrus master pointers. */
35
35
  recordingMasterReference: string;
36
- /** `release_mix_reference` — optional per-track delivery descriptor pointers. */
37
- releaseMixReference?: string;
38
36
  /** `composition_credits` / `recording_credits` / `release_credits` extensions. */
39
37
  compositionCredits: string;
40
38
  recordingCredits: string;
@@ -3,7 +3,7 @@
3
3
 
4
4
  import { bcs } from "@mysten/sui/bcs";
5
5
  import { toBase64 } from "@mysten/sui/utils";
6
- import { walrusBlobIdFromU256 } from "../../mix.ts";
6
+ import { walrusBlobIdFromU256 } from "../../walrus-ids.ts";
7
7
 
8
8
  const u256 = bcs.u256();
9
9
 
package/src/read/types.ts CHANGED
@@ -76,8 +76,6 @@ export interface TrackView {
76
76
  disc: number;
77
77
  /** Base64url Walrus blob id for the master stream, when attached on-chain. */
78
78
  masterBlobId?: string;
79
- /** Base64url Walrus blob id for the public mix-delivery descriptor. */
80
- mixBlobId?: string;
81
79
  }
82
80
 
83
81
  /** A release with everything a page renders: metadata, cover, credits, tracklist. */
@@ -6,7 +6,8 @@
6
6
  import { bcs } from "@mysten/sui/bcs";
7
7
  import type { ClientWithCoreApi } from "@mysten/sui/client";
8
8
  import type { Transaction, TransactionArgument, TransactionObjectArgument } from "@mysten/sui/transactions";
9
- import { deriveDynamicFieldID } from "@mysten/sui/utils";
9
+ import { deriveDynamicFieldID, fromHex, toHex } from "@mysten/sui/utils";
10
+ import { isNotFound } from "@misofm/protocol";
10
11
  import type { TxThunk } from "./transactions.ts";
11
12
  import { invokeWithAdminCap, type AdminCapAuthority, type ObjectInput } from "./vault.ts";
12
13
  import * as advisory from "@misofm/protocol/contracts/recording_advisory/recording_advisory";
@@ -132,28 +133,76 @@ export type UnsetRecordingStreamingTranscodeParams = Omit<
132
133
  "oriPackageId" | "quiltId"
133
134
  >;
134
135
 
136
+ /** One stem a Session V1 document references, paired with its Walrus blob. */
137
+ export interface RecordingEngineSessionStemInput {
138
+ /** 32-byte SHA-256 of the stem's canonical PCM (engine STEM_IDENTITY_V1): raw bytes or 64 hex chars. */
139
+ readonly digest: Uint8Array | string;
140
+ /** Unencrypted Walrus blob holding the stem's FLAC, as its on-chain `u256`. */
141
+ readonly blobId: bigint | string;
142
+ }
143
+
135
144
  export interface SetRecordingEngineSessionParams extends RecordingExtensionTarget {
136
145
  readonly recordingEngineSessionPackageId: string;
137
- /** External `ori` package used to construct the plaintext Walrus blob reference. */
146
+ /** External `ori` package used to construct the plaintext Walrus blob references. */
138
147
  readonly oriPackageId: string;
139
- /** Unencrypted Walrus blob ID of the Miso Engine session file, as its on-chain `u256`. */
148
+ /** Unencrypted Walrus blob holding the canonical Session V1 JSON, as its on-chain `u256`. */
140
149
  readonly sessionBlobId: bigint | string;
150
+ /** Every stem the document's sources reference. Order does not matter; the builder sorts. */
151
+ readonly stems: readonly RecordingEngineSessionStemInput[];
152
+ }
153
+
154
+ const STEM_DIGEST_BYTES = 32;
155
+
156
+ function stemDigestBytes(digest: Uint8Array | string): Uint8Array {
157
+ const bytes = typeof digest === "string" ? fromHex(digest) : digest;
158
+ if (bytes.length !== STEM_DIGEST_BYTES) {
159
+ throw new Error(`stem digest must be ${STEM_DIGEST_BYTES} bytes`);
160
+ }
161
+ return bytes;
162
+ }
163
+
164
+ function compareBytes(a: Uint8Array, b: Uint8Array): number {
165
+ for (let index = 0; index < a.length; index += 1) {
166
+ if (a[index] !== b[index]) return a[index]! - b[index]!;
167
+ }
168
+ return 0;
141
169
  }
142
170
 
143
171
  /**
144
- * Sets or replaces the Miso Engine session file attached to a Recording.
145
- *
146
- * `recording_engine_session::new` rejects encrypted blobs on chain, so the
147
- * reference is always built as a plaintext `ori::data::WalrusBlob`.
172
+ * Sets or replaces the Miso Engine session attached to a Recording: the
173
+ * Session V1 blob plus one `Stem` per source. Stems are sorted by digest and
174
+ * checked for duplicates here, mirroring `recording_engine_session::new`, so a
175
+ * bad input fails before signing rather than on chain.
148
176
  */
149
177
  export function setRecordingEngineSession(p: SetRecordingEngineSessionParams): TxThunk {
178
+ const stems = p.stems
179
+ .map((stem) => ({ digest: stemDigestBytes(stem.digest), blobId: stem.blobId }))
180
+ .sort((a, b) => compareBytes(a.digest, b.digest));
181
+ for (let index = 1; index < stems.length; index += 1) {
182
+ if (compareBytes(stems[index - 1]!.digest, stems[index]!.digest) === 0) {
183
+ throw new Error(`duplicate stem digest ${toHex(stems[index]!.digest)}`);
184
+ }
185
+ }
150
186
  return (tx) => {
187
+ const pkg = p.recordingEngineSessionPackageId;
188
+ const sessionBlob = unencryptedWalrusBlob(tx, p.oriPackageId, p.sessionBlobId);
189
+ const stemValues = stems.map((stem) => tx.add(engineSession.newStem({
190
+ package: pkg,
191
+ arguments: [
192
+ tx.pure.vector("u8", Array.from(stem.digest)),
193
+ unencryptedWalrusBlob(tx, p.oriPackageId, stem.blobId),
194
+ ],
195
+ })));
196
+ const stemVector = tx.makeMoveVec({
197
+ type: `${pkg}::recording_engine_session::Stem`,
198
+ elements: stemValues,
199
+ });
151
200
  const session = tx.add(engineSession._new({
152
- package: p.recordingEngineSessionPackageId,
153
- arguments: [unencryptedWalrusBlob(tx, p.oriPackageId, p.sessionBlobId)],
201
+ package: pkg,
202
+ arguments: [sessionBlob, stemVector],
154
203
  }));
155
204
  invokeWithAdminCap(tx, p.authority, {
156
- target: `${p.recordingEngineSessionPackageId}::recording_engine_session::set_engine_session`,
205
+ target: `${pkg}::recording_engine_session::set_engine_session`,
157
206
  typeArguments: [p.recordingShareType, p.compositionShareType],
158
207
  arguments: [object(tx, p.recordingId), session],
159
208
  adminCapIndex: 1,
@@ -163,7 +212,7 @@ export function setRecordingEngineSession(p: SetRecordingEngineSessionParams): T
163
212
 
164
213
  export type UnsetRecordingEngineSessionParams = Omit<
165
214
  SetRecordingEngineSessionParams,
166
- "oriPackageId" | "sessionBlobId"
215
+ "oriPackageId" | "sessionBlobId" | "stems"
167
216
  >;
168
217
 
169
218
  /** Removes the Recording's Miso Engine session reference, if present. */
@@ -192,6 +241,73 @@ export function unsetRecordingStreamingTranscode(
192
241
  };
193
242
  }
194
243
 
244
+ // ── Engine-session reads ─────────────────────────────────────────────────────
245
+
246
+ /** A Recording's attached engine session, ids as decimal `u256` strings. */
247
+ export interface RecordingEngineSessionView {
248
+ /** Unencrypted Walrus blob holding the canonical Session V1 JSON. */
249
+ readonly sessionBlobId: string;
250
+ /** Sorted by digest, as stored. `digest` is 64 lowercase hex chars. */
251
+ readonly stems: readonly { readonly digest: string; readonly blobId: string }[];
252
+ }
253
+
254
+ const EngineSessionField = bcs.struct("Field", {
255
+ id: bcs.Address,
256
+ name: engineSession.ExtensionKey,
257
+ value: engineSession.EngineSession,
258
+ });
259
+ const ENGINE_SESSION_KEY_BYTES = engineSession.ExtensionKey.serialize([false]).toBytes();
260
+
261
+ /** Parse an engine-session dynamic field's BCS content. */
262
+ export function parseRecordingEngineSessionContent(content: Uint8Array): RecordingEngineSessionView {
263
+ const { value } = EngineSessionField.parse(content);
264
+ if (value.data.confidentiality.$kind !== "Unencrypted") {
265
+ throw new Error("Recording engine session is unexpectedly encrypted");
266
+ }
267
+ return {
268
+ sessionBlobId: String(value.data.blob_id),
269
+ stems: value.stems.map((stem) => {
270
+ if (stem.data.confidentiality.$kind !== "Unencrypted") {
271
+ throw new Error("Recording engine session stem is unexpectedly encrypted");
272
+ }
273
+ return { digest: toHex(Uint8Array.from(stem.digest)), blobId: String(stem.data.blob_id) };
274
+ }),
275
+ };
276
+ }
277
+
278
+ /** Deterministic dynamic-field id for a Recording's engine session. */
279
+ export function recordingEngineSessionFieldId(
280
+ recordingId: string,
281
+ recordingEngineSessionPackageId: string,
282
+ ): string {
283
+ return deriveDynamicFieldID(
284
+ recordingId,
285
+ `${recordingEngineSessionPackageId}::recording_engine_session::ExtensionKey`,
286
+ ENGINE_SESSION_KEY_BYTES,
287
+ );
288
+ }
289
+
290
+ /** Read one Recording's engine session, or null when none is attached. */
291
+ export async function getRecordingEngineSession(
292
+ client: ClientWithCoreApi,
293
+ recordingId: string,
294
+ recordingEngineSessionPackageId: string,
295
+ ): Promise<RecordingEngineSessionView | null> {
296
+ const fieldId = recordingEngineSessionFieldId(recordingId, recordingEngineSessionPackageId);
297
+ const { objects } = await client.core.getObjects({
298
+ objectIds: [fieldId],
299
+ include: { content: true },
300
+ });
301
+ const field = objects[0];
302
+ if (!field) throw new Error("Recording engine-session read returned no result");
303
+ if (field instanceof Error) {
304
+ if (isNotFound(field)) return null;
305
+ throw field;
306
+ }
307
+ if (!field.content) throw new Error("Recording engine-session field is missing BCS content");
308
+ return parseRecordingEngineSessionContent(field.content);
309
+ }
310
+
195
311
  // ── Master-reference reads ───────────────────────────────────────────────────
196
312
 
197
313
  // recording_master_reference stores the ori::data::WalrusBlob value inline in a
@@ -0,0 +1,102 @@
1
+ // Copyright (c) Miso Labs, Inc.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ /**
5
+ * Walrus blob id conversions.
6
+ *
7
+ * On chain a blob id is a `u256` (BCS little-endian). Off chain it is the
8
+ * unpadded base64url encoding of those same 32 little-endian bytes, which is
9
+ * also its key in a `miso-sources` staging bucket and its path on an
10
+ * aggregator. Both directions here are canonical: a value that does not
11
+ * round-trip byte for byte is rejected.
12
+ */
13
+
14
+ const ALPHABET =
15
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
16
+
17
+ /** Parse a `bigint` or decimal string as an exact `u256`. */
18
+ export function walrusU256(label: string, value: bigint | string): bigint {
19
+ let parsed: bigint;
20
+ try {
21
+ parsed = typeof value === "bigint" ? value : BigInt(value);
22
+ } catch {
23
+ throw new Error(`${label} must be an exact u256 integer`);
24
+ }
25
+ if (parsed < 0n || parsed >= 1n << 256n) {
26
+ throw new Error(`${label} is outside u256 range`);
27
+ }
28
+ return parsed;
29
+ }
30
+
31
+ /** The base64url Walrus blob id for an on-chain `u256` blob id. */
32
+ export function walrusBlobIdFromU256(value: bigint | string): string {
33
+ let remaining = walrusU256("Walrus blob id", value);
34
+ const bytes = new Uint8Array(32);
35
+ for (let index = 0; index < 32; index += 1) {
36
+ bytes[index] = Number(remaining & 0xffn);
37
+ remaining >>= 8n;
38
+ }
39
+ return bytesToBase64Url(bytes);
40
+ }
41
+
42
+ /** The on-chain `u256` for a base64url Walrus blob id (43 characters). */
43
+ export function walrusBlobIdToU256(blobId: string): bigint {
44
+ const bytes = base64UrlToBytes(blobId);
45
+ if (bytes.length !== 32) {
46
+ throw new Error("Walrus blob id must decode to exactly 32 bytes");
47
+ }
48
+ let value = 0n;
49
+ for (let index = 31; index >= 0; index -= 1) {
50
+ value = (value << 8n) | BigInt(bytes[index]!);
51
+ }
52
+ return value;
53
+ }
54
+
55
+ /** Unpadded base64url. */
56
+ export function bytesToBase64Url(bytes: Uint8Array): string {
57
+ let output = "";
58
+ for (let index = 0; index < bytes.length; index += 3) {
59
+ const a = bytes[index] ?? 0;
60
+ const b = bytes[index + 1] ?? 0;
61
+ const c = bytes[index + 2] ?? 0;
62
+ output += ALPHABET[a >>> 2];
63
+ output += ALPHABET[((a & 3) << 4) | (b >>> 4)];
64
+ if (index + 1 < bytes.length) {
65
+ output += ALPHABET[((b & 15) << 2) | (c >>> 6)];
66
+ }
67
+ if (index + 2 < bytes.length) output += ALPHABET[c & 63];
68
+ }
69
+ return output;
70
+ }
71
+
72
+ /** Strict inverse of {@link bytesToBase64Url}: no padding, no stray bits. */
73
+ export function base64UrlToBytes(value: string): Uint8Array {
74
+ if (
75
+ typeof value !== "string" ||
76
+ value.length % 4 === 1 ||
77
+ value.includes("=") ||
78
+ !/^[A-Za-z0-9_-]*$/.test(value)
79
+ ) {
80
+ throw new Error("invalid canonical base64url");
81
+ }
82
+ const bytes = new Uint8Array(Math.floor((value.length * 6) / 8));
83
+ let accumulator = 0;
84
+ let bits = 0;
85
+ let outputIndex = 0;
86
+ for (const character of value) {
87
+ const digit = ALPHABET.indexOf(character);
88
+ if (digit < 0) throw new Error("invalid canonical base64url");
89
+ accumulator = (accumulator << 6) | digit;
90
+ bits += 6;
91
+ if (bits >= 8) {
92
+ bits -= 8;
93
+ bytes[outputIndex] = (accumulator >>> bits) & 0xff;
94
+ outputIndex += 1;
95
+ accumulator &= (1 << bits) - 1;
96
+ }
97
+ }
98
+ if (bytesToBase64Url(bytes) !== value) {
99
+ throw new Error("invalid canonical base64url padding bits");
100
+ }
101
+ return bytes;
102
+ }
package/dist/mix.d.ts DELETED
@@ -1,130 +0,0 @@
1
- import type { ClientWithCoreApi } from "@mysten/sui/client";
2
- export declare const RECORDING_SESSION_ID_BYTES = 98;
3
- export declare const RECORDING_SESSION_ID_SCHEMA: 1;
4
- export declare const RECORDING_SESSION_ID_KIND: 1;
5
- export declare const ENGINE_SESSION_SCHEMA_V1: "miso.engine-session/1";
6
- export declare const ENGINE_SESSION_NETWORK_V1: "testnet";
7
- export declare const STEM_CIPHER_FRAMING_V1: "aes-256-gcm-hkdf-stream/2";
8
- export declare const STEM_CIPHER_CHUNK_BYTES: 1048576;
9
- export declare const STEM_CIPHER_TAG_BYTES: 16;
10
- export declare const STEM_CIPHER_HEADER_BYTES: 25;
11
- export declare const MAX_ENGINE_SESSION_BYTES: number;
12
- export declare const MAX_ENGINE_SESSION_STEMS = 32;
13
- export declare const MAX_STEM_FLAC_BYTES: number;
14
- export declare const MAX_SESSION_FLAC_BYTES: number;
15
- export declare const MAX_STEM_CANONICAL_PCM_BYTES: number;
16
- export declare const ENGINE_SESSION_SAMPLE_RATES: readonly [44100, 48000, 88200, 96000];
17
- export type EngineSessionSampleRate = (typeof ENGINE_SESSION_SAMPLE_RATES)[number];
18
- export interface RecordingSessionIdentity {
19
- readonly schema: typeof RECORDING_SESSION_ID_SCHEMA;
20
- readonly kind: typeof RECORDING_SESSION_ID_KIND;
21
- readonly recordGateId: string;
22
- readonly recordingId: string;
23
- readonly nonce: Uint8Array;
24
- }
25
- /** Encode `[schema | kind | gate | recording | 32-byte nonce]`. */
26
- export declare function encodeRecordingSessionIdentity(identity: Omit<RecordingSessionIdentity, "schema" | "kind"> & Partial<Pick<RecordingSessionIdentity, "schema" | "kind">>): Uint8Array;
27
- /** Strictly parse one exact identity; short and trailing bytes are rejected. */
28
- export declare function parseRecordingSessionIdentity(bytes: Uint8Array): RecordingSessionIdentity;
29
- export declare function recordingSessionIdentityHex(identity: Parameters<typeof encodeRecordingSessionIdentity>[0]): string;
30
- /** Stable unique HKDF/AAD domain for one encrypted stem. */
31
- export declare function stemCipherDomain(stemId: string): Uint8Array;
32
- export interface EngineSessionEffectPreset {
33
- readonly slot: number;
34
- readonly bypassed: boolean;
35
- readonly params: Readonly<Record<string, number>>;
36
- }
37
- export interface EngineSessionStemPreset {
38
- readonly faderDb: number;
39
- readonly pan: number;
40
- readonly muted: boolean;
41
- readonly soloed: boolean;
42
- readonly effects: readonly EngineSessionEffectPreset[];
43
- }
44
- export interface EngineSessionStemV1 {
45
- /** Portable encryption-domain id, independent of PCM/container identity. */
46
- readonly id: string;
47
- /** Canonical decoded PCM identity consumed by the stem store. */
48
- readonly identity: `sha256:${string}`;
49
- readonly name: string;
50
- readonly role: string;
51
- readonly channels: 1 | 2;
52
- readonly bitDepth: 16 | 24;
53
- readonly frames: number;
54
- readonly preset: EngineSessionStemPreset;
55
- /** Standalone Walrus blob containing framed AES-GCM ciphertext. */
56
- readonly blobId: string;
57
- readonly ciphertextSha256: string;
58
- readonly plainBytes: number;
59
- readonly cipherBytes: number;
60
- }
61
- export interface EngineSessionV1 {
62
- readonly schema: typeof ENGINE_SESSION_SCHEMA_V1;
63
- readonly network: typeof ENGINE_SESSION_NETWORK_V1;
64
- readonly recordingId: string;
65
- readonly title: string;
66
- readonly artist: string;
67
- readonly sampleRateHz: EngineSessionSampleRate;
68
- readonly seal: {
69
- /** Canonical base64url Seal EncryptedObject wrapping exactly 32 AES bytes. */
70
- readonly encryptedKey: string;
71
- };
72
- readonly cipher: {
73
- readonly framing: typeof STEM_CIPHER_FRAMING_V1;
74
- readonly chunkSize: typeof STEM_CIPHER_CHUNK_BYTES;
75
- readonly tagSize: typeof STEM_CIPHER_TAG_BYTES;
76
- };
77
- readonly stems: readonly EngineSessionStemV1[];
78
- }
79
- /** Ciphertext length implied by the fixed authenticated-record framing. */
80
- export declare function stemCipherBytes(plainBytes: number): number;
81
- /** Validate exact shape, bounds, identity, and the embedded Seal object. */
82
- export declare function assertEngineSessionV1(value: unknown): asserts value is EngineSessionV1;
83
- /** Canonical plaintext bytes uploaded as the Recording's session blob. */
84
- export declare function encodeEngineSessionV1(session: EngineSessionV1): Uint8Array;
85
- /** Parse only canonical JSON: unknown keys, whitespace, and trailing data fail. */
86
- export declare function parseEngineSessionV1(input: Uint8Array | string): EngineSessionV1;
87
- export interface InspectedEngineSessionKey {
88
- readonly packageId: string;
89
- readonly innerId: Uint8Array;
90
- readonly services: readonly (readonly [string, number])[];
91
- readonly threshold: number;
92
- }
93
- /** Parse and constrain the official Seal object carrying the session AES key. */
94
- export declare function inspectEngineSessionKey(bytes: Uint8Array): InspectedEngineSessionKey;
95
- export interface RecordingEngineSessionReference {
96
- readonly blobId: bigint;
97
- }
98
- export interface ReleaseMixReference {
99
- readonly blobId: bigint;
100
- }
101
- /** Read the release-aligned optional descriptor references in track order. */
102
- export declare function getReleaseMixReferences(client: ClientWithCoreApi, releaseId: string, packageId: string): Promise<Array<ReleaseMixReference | null> | null>;
103
- export declare function recordingEngineSessionFieldId(recordingId: string, packageId: string): string;
104
- export declare function parseRecordingEngineSessionContent(content: Uint8Array): RecordingEngineSessionReference;
105
- export declare function getRecordingEngineSession(client: ClientWithCoreApi, recordingId: string, packageId: string): Promise<RecordingEngineSessionReference | null>;
106
- /** Exact concrete Record read using BCS, never transport-dependent JSON. */
107
- export declare function getRecordReleaseId(client: ClientWithCoreApi, recordId: string, recordPackageId: string): Promise<string>;
108
- export interface ResolvedRecordEngineSession {
109
- readonly releaseId: string;
110
- readonly recordingId: string;
111
- readonly sessionBlobId: string;
112
- }
113
- /**
114
- * Resolve Record → immutable Release track → Recording dynamic field.
115
- *
116
- * Every edge is a direct Sui object read. No event scan, dynamic-field list,
117
- * API hint, or indexer-maintained relation participates in the answer.
118
- */
119
- export declare function resolveRecordEngineSession(client: ClientWithCoreApi, input: {
120
- readonly recordId: string;
121
- readonly trackIndex: number;
122
- readonly recordPackageId: string;
123
- readonly recordingEngineSessionPackageId: string;
124
- }): Promise<ResolvedRecordEngineSession>;
125
- export declare function walrusBlobIdFromU256(value: bigint | string): string;
126
- export declare function bytesToBase64Url(bytes: Uint8Array): string;
127
- export declare function base64UrlToBytes(value: string): Uint8Array;
128
- export declare function bytesToHex(bytes: Uint8Array): string;
129
- export declare function hexToBytes(value: string, expectedBytes?: number, label?: string): Uint8Array;
130
- //# sourceMappingURL=mix.d.ts.map
package/dist/mix.d.ts.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"mix.d.ts","sourceRoot":"","sources":["../src/mix.ts"],"names":[],"mappings":"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"}