@misofm/musicos 0.1.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 (103) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +75 -0
  3. package/dist/client.d.ts +309 -0
  4. package/dist/client.d.ts.map +1 -0
  5. package/dist/client.js +216 -0
  6. package/dist/client.js.map +1 -0
  7. package/dist/contracts/musicos/composition.d.ts +195 -0
  8. package/dist/contracts/musicos/composition.d.ts.map +1 -0
  9. package/dist/contracts/musicos/composition.js +195 -0
  10. package/dist/contracts/musicos/composition.js.map +1 -0
  11. package/dist/contracts/musicos/deps/bps/bps.d.ts +17 -0
  12. package/dist/contracts/musicos/deps/bps/bps.d.ts.map +1 -0
  13. package/dist/contracts/musicos/deps/bps/bps.js +19 -0
  14. package/dist/contracts/musicos/deps/bps/bps.js.map +1 -0
  15. package/dist/contracts/musicos/recording.d.ts +238 -0
  16. package/dist/contracts/musicos/recording.d.ts.map +1 -0
  17. package/dist/contracts/musicos/recording.js +232 -0
  18. package/dist/contracts/musicos/recording.js.map +1 -0
  19. package/dist/contracts/musicos/release.d.ts +298 -0
  20. package/dist/contracts/musicos/release.d.ts.map +1 -0
  21. package/dist/contracts/musicos/release.js +298 -0
  22. package/dist/contracts/musicos/release.js.map +1 -0
  23. package/dist/contracts/musicos/track.d.ts +193 -0
  24. package/dist/contracts/musicos/track.d.ts.map +1 -0
  25. package/dist/contracts/musicos/track.js +197 -0
  26. package/dist/contracts/musicos/track.js.map +1 -0
  27. package/dist/contracts/utils/index.d.ts +104 -0
  28. package/dist/contracts/utils/index.d.ts.map +1 -0
  29. package/dist/contracts/utils/index.js +272 -0
  30. package/dist/contracts/utils/index.js.map +1 -0
  31. package/dist/contracts.d.ts +9 -0
  32. package/dist/contracts.d.ts.map +1 -0
  33. package/dist/contracts.js +14 -0
  34. package/dist/contracts.js.map +1 -0
  35. package/dist/deployments.d.ts +60 -0
  36. package/dist/deployments.d.ts.map +1 -0
  37. package/dist/deployments.js +109 -0
  38. package/dist/deployments.js.map +1 -0
  39. package/dist/events.d.ts +16 -0
  40. package/dist/events.d.ts.map +1 -0
  41. package/dist/events.js +25 -0
  42. package/dist/events.js.map +1 -0
  43. package/dist/execute.d.ts +49 -0
  44. package/dist/execute.d.ts.map +1 -0
  45. package/dist/execute.js +110 -0
  46. package/dist/execute.js.map +1 -0
  47. package/dist/index.d.ts +13 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/dist/index.js +20 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/internal.d.ts +17 -0
  52. package/dist/internal.d.ts.map +1 -0
  53. package/dist/internal.js +46 -0
  54. package/dist/internal.js.map +1 -0
  55. package/dist/numeric.d.ts +5 -0
  56. package/dist/numeric.d.ts.map +1 -0
  57. package/dist/numeric.js +21 -0
  58. package/dist/numeric.js.map +1 -0
  59. package/dist/packages.d.ts +67 -0
  60. package/dist/packages.d.ts.map +1 -0
  61. package/dist/packages.js +85 -0
  62. package/dist/packages.js.map +1 -0
  63. package/dist/parsers.d.ts +9 -0
  64. package/dist/parsers.d.ts.map +1 -0
  65. package/dist/parsers.js +40 -0
  66. package/dist/parsers.js.map +1 -0
  67. package/dist/queries.d.ts +178 -0
  68. package/dist/queries.d.ts.map +1 -0
  69. package/dist/queries.js +611 -0
  70. package/dist/queries.js.map +1 -0
  71. package/dist/transactions.d.ts +136 -0
  72. package/dist/transactions.d.ts.map +1 -0
  73. package/dist/transactions.js +145 -0
  74. package/dist/transactions.js.map +1 -0
  75. package/dist/types.d.ts +194 -0
  76. package/dist/types.d.ts.map +1 -0
  77. package/dist/types.js +4 -0
  78. package/dist/types.js.map +1 -0
  79. package/dist/view.d.ts +22 -0
  80. package/dist/view.d.ts.map +1 -0
  81. package/dist/view.js +42 -0
  82. package/dist/view.js.map +1 -0
  83. package/package.json +119 -0
  84. package/src/client.ts +341 -0
  85. package/src/contracts/musicos/composition.ts +280 -0
  86. package/src/contracts/musicos/deps/bps/bps.ts +22 -0
  87. package/src/contracts/musicos/recording.ts +308 -0
  88. package/src/contracts/musicos/release.ts +409 -0
  89. package/src/contracts/musicos/track.ts +255 -0
  90. package/src/contracts/utils/index.ts +428 -0
  91. package/src/contracts.ts +29 -0
  92. package/src/deployments.ts +166 -0
  93. package/src/events.ts +30 -0
  94. package/src/execute.ts +137 -0
  95. package/src/index.ts +21 -0
  96. package/src/internal.ts +78 -0
  97. package/src/numeric.ts +25 -0
  98. package/src/packages.ts +117 -0
  99. package/src/parsers.ts +66 -0
  100. package/src/queries.ts +928 -0
  101. package/src/transactions.ts +305 -0
  102. package/src/types.ts +222 -0
  103. package/src/view.ts +65 -0
@@ -0,0 +1,428 @@
1
+
2
+ import {
3
+ bcs,
4
+ type BcsType,
5
+ type TypeTag,
6
+ TypeTagSerializer,
7
+ BcsStruct,
8
+ BcsEnum,
9
+ BcsTuple,
10
+ } from '@mysten/sui/bcs';
11
+ import { normalizeStructTag, normalizeSuiAddress } from '@mysten/sui/utils';
12
+ import {
13
+ type TransactionArgument,
14
+ type TransactionObjectArgument,
15
+ isArgument,
16
+ } from '@mysten/sui/transactions';
17
+ import { type ClientWithCoreApi, type SuiClientTypes } from '@mysten/sui/client';
18
+
19
+ const MOVE_STDLIB_ADDRESS = normalizeSuiAddress('0x1');
20
+ const SUI_FRAMEWORK_ADDRESS = normalizeSuiAddress('0x2');
21
+
22
+ export type RawTransactionArgument<T> = T | TransactionArgument;
23
+
24
+ export type GetOptions<Include extends Omit<SuiClientTypes.ObjectInclude, 'content'> = {}> =
25
+ SuiClientTypes.GetObjectOptions<Include> & { client: ClientWithCoreApi };
26
+
27
+ export type GetManyOptions<Include extends Omit<SuiClientTypes.ObjectInclude, 'content'> = {}> =
28
+ SuiClientTypes.GetObjectsOptions<Include> & { client: ClientWithCoreApi };
29
+
30
+ export function getPureBcsSchema(typeTag: string | TypeTag): BcsType<any> | null {
31
+ const parsedTag = typeof typeTag === 'string' ? TypeTagSerializer.parseFromStr(typeTag) : typeTag;
32
+
33
+ if ('u8' in parsedTag) {
34
+ return bcs.U8;
35
+ } else if ('u16' in parsedTag) {
36
+ return bcs.U16;
37
+ } else if ('u32' in parsedTag) {
38
+ return bcs.U32;
39
+ } else if ('u64' in parsedTag) {
40
+ return bcs.U64;
41
+ } else if ('u128' in parsedTag) {
42
+ return bcs.U128;
43
+ } else if ('u256' in parsedTag) {
44
+ return bcs.U256;
45
+ } else if ('address' in parsedTag) {
46
+ return bcs.Address;
47
+ } else if ('bool' in parsedTag) {
48
+ return bcs.Bool;
49
+ } else if ('vector' in parsedTag) {
50
+ const type = getPureBcsSchema(parsedTag.vector);
51
+ return type ? bcs.vector(type) : null;
52
+ } else if ('struct' in parsedTag) {
53
+ const structTag = parsedTag.struct;
54
+ const pkg = normalizeSuiAddress(structTag.address);
55
+
56
+ if (pkg === MOVE_STDLIB_ADDRESS) {
57
+ if (
58
+ (structTag.module === 'ascii' || structTag.module === 'string') &&
59
+ structTag.name === 'String'
60
+ ) {
61
+ return bcs.String;
62
+ }
63
+
64
+ if (structTag.module === 'option' && structTag.name === 'Option') {
65
+ const inner = structTag.typeParams[0];
66
+ const type = inner ? getPureBcsSchema(inner) : null;
67
+ return type ? bcs.option(type) : null;
68
+ }
69
+ }
70
+
71
+ if (
72
+ pkg === SUI_FRAMEWORK_ADDRESS &&
73
+ structTag.module === 'object' &&
74
+ (structTag.name === 'ID' || structTag.name === 'UID')
75
+ ) {
76
+ return bcs.Address;
77
+ }
78
+ }
79
+
80
+ return null;
81
+ }
82
+
83
+ export function normalizeMoveArguments(
84
+ args: unknown[] | object,
85
+ argTypes: readonly (string | null)[],
86
+ parameterNames?: string[],
87
+ ) {
88
+ const argLen = Array.isArray(args) ? args.length : Object.keys(args).length;
89
+ if (parameterNames && argLen !== parameterNames.length) {
90
+ throw new Error(
91
+ `Invalid number of arguments, expected ${parameterNames.length}, got ${argLen}`,
92
+ );
93
+ }
94
+
95
+ const normalizedArgs: TransactionArgument[] = [];
96
+
97
+ let index = 0;
98
+ for (const argType of argTypes) {
99
+ if (argType === '0x2::clock::Clock') {
100
+ normalizedArgs.push((tx) => tx.object.clock());
101
+ continue;
102
+ }
103
+
104
+ if (argType === '0x2::random::Random') {
105
+ normalizedArgs.push((tx) => tx.object.random());
106
+ continue;
107
+ }
108
+
109
+ if (argType === '0x2::deny_list::DenyList') {
110
+ normalizedArgs.push((tx) => tx.object.denyList());
111
+ continue;
112
+ }
113
+
114
+ if (argType === '0x2::accumulator::AccumulatorRoot') {
115
+ // Chain-wide shared singleton at a fixed address (SUI_ACCUMULATOR_ROOT_OBJECT_ID).
116
+ normalizedArgs.push((tx) => tx.object('0xacc'));
117
+ continue;
118
+ }
119
+
120
+ if (argType === '0x3::sui_system::SuiSystemState') {
121
+ normalizedArgs.push((tx) => tx.object.system());
122
+ continue;
123
+ }
124
+
125
+ let arg;
126
+ if (Array.isArray(args)) {
127
+ if (index >= args.length) {
128
+ throw new Error(
129
+ `Invalid number of arguments, expected at least ${index + 1}, got ${args.length}`,
130
+ );
131
+ }
132
+ arg = args[index];
133
+ } else {
134
+ if (!parameterNames) {
135
+ throw new Error(`Expected arguments to be passed as an array`);
136
+ }
137
+ const name = parameterNames[index];
138
+ arg =
139
+ name !== undefined && Object.prototype.hasOwnProperty.call(args, name)
140
+ ? args[name as keyof typeof args]
141
+ : undefined;
142
+
143
+ if (arg === undefined) {
144
+ throw new Error(`Parameter ${name} is required`);
145
+ }
146
+ }
147
+
148
+ index += 1;
149
+
150
+ if (typeof arg === 'function' || isArgument(arg)) {
151
+ normalizedArgs.push(arg as TransactionArgument);
152
+ continue;
153
+ }
154
+
155
+ const bcsType = argType === null ? null : getPureBcsSchema(argType);
156
+
157
+ if (bcsType) {
158
+ const bytes = bcsType.serialize(arg as never);
159
+ normalizedArgs.push((tx) => tx.pure(bytes));
160
+ continue;
161
+ }
162
+
163
+ if (typeof arg === 'string') {
164
+ normalizedArgs.push((tx) => tx.object(arg));
165
+ continue;
166
+ }
167
+
168
+ throw new Error(`Invalid argument ${stringify(arg)} for type ${argType}`);
169
+ }
170
+
171
+ return normalizedArgs;
172
+ }
173
+
174
+ /* -------------------------- Config-mapped arguments -------------------------- */
175
+
176
+ /** Context passed to config resolver functions. */
177
+ export interface ConfigResolverContext {
178
+ /**
179
+ * The matched parameter's own instantiated type arguments (not the whole function's type
180
+ * argument tuple). Concrete instantiations are canonical type tags; generic positions pass
181
+ * the caller's `typeArguments` strings through as provided.
182
+ */
183
+ typeArguments: string[];
184
+ /** The package address the generated call will be sent to. */
185
+ packageAddress: string;
186
+ /** The Move module of the generated call. */
187
+ moduleName: string;
188
+ /** The Move function of the generated call. */
189
+ functionName: string;
190
+ /** The Move name of the matched parameter, when the summary includes parameter names. */
191
+ parameterName?: string;
192
+ /** The matched parameter's position in the generated function's arguments. */
193
+ parameterIndex: number;
194
+ }
195
+
196
+ /**
197
+ * A non-callback transaction argument in a generated config object, used for keys whose matched
198
+ * parameter can't be supplied as an object id string.
199
+ */
200
+ export type ConfigObjectValue = Exclude<TransactionObjectArgument, (...args: never[]) => unknown>;
201
+
202
+ /**
203
+ * A plain value in a generated config object: an object id or a non-callback transaction
204
+ * argument. Keys that can resolve multiple bindings are typed as resolver functions instead.
205
+ */
206
+ export type ConfigValue = string | ConfigObjectValue;
207
+
208
+ /* -------------------------- Move type tags -------------------------- */
209
+
210
+ /** A type argument: a type tag string, or a BCS type whose name is a Move type. */
211
+ export type TypeArgument = string | BcsType<any>;
212
+
213
+ export interface TypeTagOptions {
214
+ package?: string;
215
+ typeArguments?: readonly TypeArgument[];
216
+ }
217
+
218
+ /**
219
+ * `typeArguments` is required when the type's name contains unfilled
220
+ * `phantom X` parameters (at any depth). Everything else — argument arity,
221
+ * position contents, and tag validity — is validated at runtime.
222
+ */
223
+ type TypeTagParams<Name extends string> = Name extends `${string}phantom ${string}`
224
+ ? [options: TypeTagOptions & { typeArguments: readonly TypeArgument[] }]
225
+ : [options?: TypeTagOptions];
226
+
227
+ type ResolveTypeTagOptions<Name extends string> = { client: ClientWithCoreApi } & (
228
+ Name extends `${string}phantom ${string}`
229
+ ? TypeTagOptions & { typeArguments: readonly TypeArgument[] }
230
+ : TypeTagOptions
231
+ );
232
+
233
+ const HAS_PHANTOM_REGEX = /phantom [A-Za-z_$][A-Za-z0-9_$]*/;
234
+
235
+ function splitTopLevelTypeArgs(inner: string): string[] {
236
+ const parts: string[] = [];
237
+ let depth = 0;
238
+ let current = '';
239
+ for (const char of inner) {
240
+ if (char === ',' && depth === 0) {
241
+ parts.push(current.trim());
242
+ current = '';
243
+ continue;
244
+ }
245
+ if (char === '<') depth++;
246
+ if (char === '>') depth--;
247
+ current += char;
248
+ }
249
+ if (current) parts.push(current.trim());
250
+ return parts;
251
+ }
252
+
253
+ function buildTypeTag(name: string, options: TypeTagOptions | undefined): string {
254
+ const lt = name.indexOf('<');
255
+ const base = lt === -1 ? name : name.slice(0, lt);
256
+
257
+ if (base.split('::').length !== 3) {
258
+ throw new Error(`${name} is not a top-level Move type`);
259
+ }
260
+
261
+ let result = name;
262
+
263
+ if (options?.typeArguments) {
264
+ const baked = lt === -1 ? [] : splitTopLevelTypeArgs(name.slice(lt + 1, -1));
265
+ const supplied = options.typeArguments.map((arg) => {
266
+ if (typeof arg === 'string') {
267
+ return arg;
268
+ }
269
+ if (arg && typeof arg.serialize === 'function' && typeof arg.name === 'string') {
270
+ return arg.name;
271
+ }
272
+ throw new Error(`Invalid type argument ${stringify(arg)}`);
273
+ });
274
+
275
+ if (supplied.length !== baked.length) {
276
+ throw new Error(
277
+ `Expected ${baked.length} type arguments for ${base}, got ${supplied.length}`,
278
+ );
279
+ }
280
+
281
+ result = supplied.length === 0 ? base : `${base}<${supplied.join(', ')}>`;
282
+ }
283
+
284
+ if (HAS_PHANTOM_REGEX.test(result)) {
285
+ throw new Error(
286
+ options?.typeArguments
287
+ ? `A type argument contains an unfilled phantom parameter in ${result}`
288
+ : `Missing type arguments for ${result}`,
289
+ );
290
+ }
291
+
292
+ if (options?.package) {
293
+ const [, ...rest] = result.split('::');
294
+ result = [options.package, ...rest].join('::');
295
+ }
296
+
297
+ // fully validate address-only tags (MVR names can't be parsed as type tags)
298
+ if (!HAS_PHANTOM_REGEX.test(result) && !/[@/]/.test(result)) {
299
+ TypeTagSerializer.parseFromStr(result);
300
+ }
301
+
302
+ return result;
303
+ }
304
+
305
+ async function resolveBuiltTypeTag(
306
+ name: string,
307
+ options: { client: ClientWithCoreApi } & TypeTagOptions,
308
+ ): Promise<string> {
309
+ const { client, ...rest } = options;
310
+ const { type } = await client.core.mvr.resolveType({
311
+ type: buildTypeTag(name, rest),
312
+ });
313
+ return normalizeStructTag(type);
314
+ }
315
+
316
+ export class MoveStruct<
317
+ T extends Record<string, BcsType<any>>,
318
+ const Name extends string = string,
319
+ > extends BcsStruct<T, Name> {
320
+ /**
321
+ * Build the type tag for this struct.
322
+ *
323
+ * `typeArguments` is the full positional list, in Move declaration order, and
324
+ * is required when the struct has unfilled phantom parameters. The result may
325
+ * contain MVR names: those are valid in transaction `typeArguments`, but for
326
+ * queries or comparisons against on-chain data use `resolveTypeTag` instead.
327
+ */
328
+ typeTag(...args: TypeTagParams<Name>): string {
329
+ return buildTypeTag(this.name, args[0] as TypeTagOptions | undefined);
330
+ }
331
+
332
+ /**
333
+ * Build the type tag for this struct, then resolve any MVR names through the
334
+ * client (using its configured overrides and the MVR API) and return the
335
+ * normalized, address-only form suitable for queries and comparisons against
336
+ * on-chain data.
337
+ */
338
+ async resolveTypeTag(options: ResolveTypeTagOptions<Name>): Promise<string> {
339
+ return resolveBuiltTypeTag(this.name, options as { client: ClientWithCoreApi } & TypeTagOptions);
340
+ }
341
+
342
+ async get<Include extends Omit<SuiClientTypes.ObjectInclude, 'content' | 'json'> = {}>({
343
+ objectId,
344
+ ...options
345
+ }: GetOptions<Include>): Promise<
346
+ SuiClientTypes.Object<Include & { content: true, json: true }> & { json: BcsStruct<T>['$inferType'] }
347
+ > {
348
+ const [res] = await this.getMany<Include>({
349
+ ...options,
350
+ objectIds: [objectId],
351
+ });
352
+
353
+ if (!res) {
354
+ throw new Error(`No object found for id ${objectId}`);
355
+ }
356
+
357
+ return res;
358
+ }
359
+
360
+ async getMany<Include extends Omit<SuiClientTypes.ObjectInclude, 'content' | 'json'> = {}>({
361
+ client,
362
+ ...options
363
+ }: GetManyOptions<Include>): Promise<
364
+ Array<SuiClientTypes.Object<Include & { content: true, json: true }> & { json: BcsStruct<T>['$inferType'] }>
365
+ > {
366
+ const response = (await client.core.getObjects({
367
+ ...options,
368
+ include: {
369
+ ...options.include,
370
+ content: true,
371
+ },
372
+ })) as SuiClientTypes.GetObjectsResponse<Include & { content: true }>;
373
+
374
+ return response.objects.map((obj) => {
375
+ if (obj instanceof Error) {
376
+ throw obj;
377
+ }
378
+
379
+ return {
380
+ ...obj,
381
+ json: this.parse(obj.content),
382
+ };
383
+ });
384
+ }
385
+ }
386
+
387
+ export class MoveEnum<
388
+ T extends Record<string, BcsType<any> | null>,
389
+ const Name extends string,
390
+ > extends BcsEnum<T, Name> {
391
+ /** Build the type tag for this enum. See `MoveStruct.typeTag` for semantics. */
392
+ typeTag(...args: TypeTagParams<Name>): string {
393
+ return buildTypeTag(this.name, args[0] as TypeTagOptions | undefined);
394
+ }
395
+
396
+ /** Build and resolve the type tag for this enum. See `MoveStruct.resolveTypeTag`. */
397
+ async resolveTypeTag(options: ResolveTypeTagOptions<Name>): Promise<string> {
398
+ return resolveBuiltTypeTag(this.name, options as { client: ClientWithCoreApi } & TypeTagOptions);
399
+ }
400
+ }
401
+
402
+ export class MoveTuple<
403
+ const T extends readonly BcsType<any>[],
404
+ const Name extends string,
405
+ > extends BcsTuple<T, Name> {
406
+ /** Build the type tag for this struct. See `MoveStruct.typeTag` for semantics. */
407
+ typeTag(...args: TypeTagParams<Name>): string {
408
+ return buildTypeTag(this.name, args[0] as TypeTagOptions | undefined);
409
+ }
410
+
411
+ /** Build and resolve the type tag for this struct. See `MoveStruct.resolveTypeTag`. */
412
+ async resolveTypeTag(options: ResolveTypeTagOptions<Name>): Promise<string> {
413
+ return resolveBuiltTypeTag(this.name, options as { client: ClientWithCoreApi } & TypeTagOptions);
414
+ }
415
+ }
416
+
417
+ function stringify(val: unknown) {
418
+ if (typeof val === 'object') {
419
+ return JSON.stringify(val, (_key, value) =>
420
+ typeof value === 'bigint' ? value.toString() : value,
421
+ );
422
+ }
423
+ if (typeof val === 'bigint') {
424
+ return val.toString();
425
+ }
426
+
427
+ return val;
428
+ }
@@ -0,0 +1,29 @@
1
+ // Copyright (c) Miso Labs, Inc.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // Barrel for the codegen-generated, ABI-bound bindings (BCS structs + type-safe
5
+ // Move calls). Re-exported from the package root as the `contracts` namespace.
6
+
7
+ // Every package here is generated from the current first-party Move source. The
8
+ // generated default `@local-pkg/*` identities are source labels only: callers
9
+ // must inject exact published addresses through `MisoDeployment` before building
10
+ // a transaction. Do not use a historic package ID as a substitute for a fresh
11
+ // deployment.
12
+
13
+ type PublicModule<M extends object, K extends readonly (keyof M)[]> = Omit<M, K[number]>;
14
+ function withoutUnsafeCalls<M extends object, K extends readonly (keyof M)[]>(
15
+ module: M,
16
+ keys: K,
17
+ ): PublicModule<M, K> {
18
+ return Object.fromEntries(Object.entries(module).filter(([key]) => !keys.includes(key as keyof M))) as PublicModule<M, K>;
19
+ }
20
+
21
+ import * as rawComposition from "./contracts/musicos/composition.ts";
22
+ import * as rawRecording from "./contracts/musicos/recording.ts";
23
+ import * as rawRelease from "./contracts/musicos/release.ts";
24
+
25
+ // Object model core (BCS codecs remain available; PTB-inaccessible references do not).
26
+ export const composition = withoutUnsafeCalls(rawComposition, ["title", "uid", "uidMut"] as const);
27
+ export const recording = withoutUnsafeCalls(rawRecording, ["uid", "uidMut"] as const);
28
+ export const release = withoutUnsafeCalls(rawRelease, ["title", "tracks", "uid", "uidMut"] as const);
29
+ export * as track from "./contracts/musicos/track.ts";
@@ -0,0 +1,166 @@
1
+ // Copyright (c) Miso Labs, Inc.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { normalizeSuiAddress } from "@mysten/sui/utils";
5
+
6
+ /** Sui networks for which this SDK may bundle a verified deployment. */
7
+ export type MisoNetwork = "mainnet" | "testnet";
8
+
9
+ /**
10
+ * The single independently published package required by this SDK surface:
11
+ * the `musicos` object-model package (modules composition, recording,
12
+ * release, track).
13
+ *
14
+ * This name is a stable deployment-manifest key, not a Move module name. Its
15
+ * value is never inferred from a source label or a previous publish.
16
+ */
17
+ const CANONICAL_MISO_PACKAGE_NAMES = ["musicos"] as const;
18
+
19
+ /** Public immutable copy; validation always uses the private canonical tuple. */
20
+ export const MISO_PACKAGE_NAMES = Object.freeze([
21
+ ...CANONICAL_MISO_PACKAGE_NAMES,
22
+ ]) as readonly [...typeof CANONICAL_MISO_PACKAGE_NAMES];
23
+
24
+ export type MisoPackageName = (typeof CANONICAL_MISO_PACKAGE_NAMES)[number];
25
+
26
+ /**
27
+ * Exact package address from one compatible publish.
28
+ *
29
+ * A complete manifest is intentional: a caller cannot accidentally combine
30
+ * generated bindings for a fresh ABI with a stale package ID. Fill this only
31
+ * from the verified immutable `admin-cli` publish record after the
32
+ * dependency-ordered release is complete.
33
+ */
34
+ export type MisoDeployment = Readonly<Record<MisoPackageName, string>>;
35
+
36
+ /** A minimal core-only deployment, retained for callers that use only `miso()`. */
37
+ export interface MisoProtocolDeployment {
38
+ /** The published `musicos` package used for calls, types, and derived IDs. */
39
+ readonly packageId: string;
40
+ }
41
+
42
+ /** Validate and freeze a core-only package binding at a client boundary. */
43
+ export function normalizeMisoProtocolDeployment(
44
+ deployment: unknown,
45
+ ): MisoProtocolDeployment {
46
+ if (!deployment || typeof deployment !== "object" || Array.isArray(deployment)) {
47
+ throw new Error("@misofm/musicos: core deployment must be an object with a normalized Miso package ID.");
48
+ }
49
+ const packageId = (deployment as { packageId?: unknown }).packageId;
50
+ if (typeof packageId !== "string") {
51
+ throw new Error("@misofm/musicos: core deployment is missing a Miso package ID.");
52
+ }
53
+ let normalized: string;
54
+ try {
55
+ normalized = normalizeSuiAddress(packageId);
56
+ } catch {
57
+ throw new Error("@misofm/musicos: core deployment package ID is not a valid Sui address.");
58
+ }
59
+ if (packageId !== normalized) {
60
+ throw new Error(
61
+ `@misofm/musicos: core deployment package ID must be normalized (${normalized}).`,
62
+ );
63
+ }
64
+ return Object.freeze({ packageId });
65
+ }
66
+
67
+ /** Verified immutable deployments bundled with this SDK release. */
68
+ export const MISO_DEPLOYMENTS = Object.freeze({
69
+ testnet: Object.freeze({
70
+ musicos: "0x95bb43d650fe582caba27d4ead2c3f9939c38125943f8716c5baf5af528bc671",
71
+ } as const),
72
+ } as const) satisfies Partial<
73
+ Record<MisoNetwork, MisoDeployment>
74
+ >;
75
+
76
+ /**
77
+ * @deprecated Use `MISO_DEPLOYMENTS`. This alias preserves source compatibility
78
+ * for callers that still use the former protocol-only constant name.
79
+ */
80
+ export const MISO_PROTOCOL_DEPLOYMENTS = MISO_DEPLOYMENTS;
81
+
82
+ /** Resolve a verified full manifest, failing closed for unbundled networks. */
83
+ export function getMisoDeployment(network: string): MisoDeployment {
84
+ const deployment = (
85
+ MISO_DEPLOYMENTS as Partial<Record<string, MisoDeployment>>
86
+ )[network];
87
+ if (!deployment) {
88
+ throw new Error(
89
+ `@misofm/musicos: no verified Miso deployment is bundled for network "${network}". ` +
90
+ "Inject the exact post-publish manifest or pass an explicit deployment; historic package IDs are rejected.",
91
+ );
92
+ }
93
+ return normalizeMisoDeployment(deployment);
94
+ }
95
+
96
+ /** Extract the core package identity from a complete deployment manifest. */
97
+ export function protocolDeployment(
98
+ deployment: MisoDeployment,
99
+ ): MisoProtocolDeployment {
100
+ return normalizeMisoProtocolDeployment({ packageId: normalizeMisoDeployment(deployment).musicos });
101
+ }
102
+
103
+ /** Resolve the core package only, preserving the established `miso()` default API. */
104
+ export function getMisoProtocolDeployment(
105
+ network: string,
106
+ ): MisoProtocolDeployment {
107
+ return protocolDeployment(getMisoDeployment(network));
108
+ }
109
+
110
+ /**
111
+ * Validates an explicit manifest before any Move target is constructed. This is
112
+ * useful at configuration boundaries such as environment-file loading.
113
+ */
114
+ export function assertMisoDeployment(
115
+ deployment: unknown,
116
+ ): asserts deployment is MisoDeployment {
117
+ if (!deployment || typeof deployment !== "object" || Array.isArray(deployment)) {
118
+ throw new Error(
119
+ `@misofm/musicos: deployment must be an object with exactly ${CANONICAL_MISO_PACKAGE_NAMES.length} Miso package IDs.`,
120
+ );
121
+ }
122
+ const entries = deployment as Record<string, unknown>;
123
+ const keys = Object.keys(entries);
124
+ const unexpected = keys.filter((key) => !CANONICAL_MISO_PACKAGE_NAMES.includes(key as MisoPackageName));
125
+ if (unexpected.length > 0 || keys.length !== CANONICAL_MISO_PACKAGE_NAMES.length) {
126
+ throw new Error(
127
+ `@misofm/musicos: deployment must contain exactly these package IDs: ${CANONICAL_MISO_PACKAGE_NAMES.join(", ")}.`,
128
+ );
129
+ }
130
+ const seenPackageIds = new Set<string>();
131
+ for (const name of CANONICAL_MISO_PACKAGE_NAMES) {
132
+ const packageId = entries[name];
133
+ if (typeof packageId !== "string") {
134
+ throw new Error(
135
+ `@misofm/musicos: deployment is missing package ID for "${name}". ` +
136
+ "Use a complete manifest from the fresh verified immutable admin-cli publish record.",
137
+ );
138
+ }
139
+ let normalized: string;
140
+ try {
141
+ normalized = normalizeSuiAddress(packageId);
142
+ } catch {
143
+ throw new Error(`@misofm/musicos: deployment package ID for "${name}" is not a valid Sui address.`);
144
+ }
145
+ if (packageId !== normalized) {
146
+ throw new Error(
147
+ `@misofm/musicos: deployment package ID for "${name}" must be normalized (${normalized}).`,
148
+ );
149
+ }
150
+ if (seenPackageIds.has(packageId)) {
151
+ throw new Error(
152
+ `@misofm/musicos: deployment package ID for "${name}" duplicates another package. ` +
153
+ "A publish manifest must bind every package to its own address.",
154
+ );
155
+ }
156
+ seenPackageIds.add(packageId);
157
+ }
158
+ }
159
+
160
+ /** Validate and snapshot an untrusted manifest so later caller mutation is inert. */
161
+ export function normalizeMisoDeployment(deployment: unknown): MisoDeployment {
162
+ assertMisoDeployment(deployment);
163
+ return Object.freeze(
164
+ Object.fromEntries(CANONICAL_MISO_PACKAGE_NAMES.map((name) => [name, deployment[name]])),
165
+ ) as MisoDeployment;
166
+ }
package/src/events.ts ADDED
@@ -0,0 +1,30 @@
1
+ // Copyright (c) Miso Labs, Inc.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ /**
5
+ * BCS decoders for object-model execution events outside the legacy core
6
+ * convenience parsers. Each decoder preserves the generated Move field names
7
+ * and nested layouts exactly, which is the safe indexer boundary.
8
+ */
9
+
10
+ import * as release from "./contracts/musicos/release.ts";
11
+ import type { BcsParser } from "./queries.ts";
12
+
13
+ /** Decode raw event BCS with the corresponding generated codec. */
14
+ export function decodeEvent<T>(codec: BcsParser<T>, bytes: Uint8Array): T {
15
+ return codec.parse(bytes);
16
+ }
17
+
18
+ function decoder<T>(codec: BcsParser<T>) {
19
+ return (bytes: Uint8Array): T => decodeEvent(codec, bytes);
20
+ }
21
+
22
+ /**
23
+ * Event decoder registry grouped by package responsibility. Core publication
24
+ * events retain camel-case wrappers in `parsers.ts`.
25
+ */
26
+ export const eventParsers = {
27
+ core: {
28
+ releaseRegistryCreated: decoder(release.ReleaseRegistryCreatedEvent),
29
+ },
30
+ } as const;