@misofm/sdk 0.3.0 → 0.5.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 (54) hide show
  1. package/README.md +31 -0
  2. package/dist/auth.d.ts +73 -0
  3. package/dist/auth.d.ts.map +1 -0
  4. package/dist/auth.js +193 -0
  5. package/dist/auth.js.map +1 -0
  6. package/dist/catalog.d.ts +4 -17
  7. package/dist/catalog.d.ts.map +1 -1
  8. package/dist/catalog.js +46 -58
  9. package/dist/catalog.js.map +1 -1
  10. package/dist/cover.d.ts +10 -0
  11. package/dist/cover.d.ts.map +1 -1
  12. package/dist/cover.js +66 -19
  13. package/dist/cover.js.map +1 -1
  14. package/dist/credits.d.ts +10 -0
  15. package/dist/credits.d.ts.map +1 -1
  16. package/dist/credits.js +149 -39
  17. package/dist/credits.js.map +1 -1
  18. package/dist/drop.d.ts +7 -0
  19. package/dist/drop.d.ts.map +1 -1
  20. package/dist/drop.js +84 -34
  21. package/dist/drop.js.map +1 -1
  22. package/dist/pressing.d.ts.map +1 -1
  23. package/dist/pressing.js +59 -16
  24. package/dist/pressing.js.map +1 -1
  25. package/dist/read/artist.d.ts +1 -6
  26. package/dist/read/artist.d.ts.map +1 -1
  27. package/dist/read/artist.js +39 -22
  28. package/dist/read/artist.js.map +1 -1
  29. package/dist/read/catalog.d.ts +24 -3
  30. package/dist/read/catalog.d.ts.map +1 -1
  31. package/dist/read/catalog.js +190 -89
  32. package/dist/read/catalog.js.map +1 -1
  33. package/dist/read/index.d.ts +5 -3
  34. package/dist/read/index.d.ts.map +1 -1
  35. package/dist/read/index.js +2 -2
  36. package/dist/read/index.js.map +1 -1
  37. package/dist/read/types.d.ts +12 -1
  38. package/dist/read/types.d.ts.map +1 -1
  39. package/dist/read/works.d.ts +3 -2
  40. package/dist/read/works.d.ts.map +1 -1
  41. package/dist/read/works.js +158 -48
  42. package/dist/read/works.js.map +1 -1
  43. package/package.json +5 -1
  44. package/src/auth.ts +275 -0
  45. package/src/catalog.ts +71 -77
  46. package/src/cover.ts +107 -26
  47. package/src/credits.ts +353 -89
  48. package/src/drop.ts +150 -40
  49. package/src/pressing.ts +146 -36
  50. package/src/read/artist.ts +73 -34
  51. package/src/read/catalog.ts +324 -89
  52. package/src/read/index.ts +23 -2
  53. package/src/read/types.ts +16 -2
  54. package/src/read/works.ts +224 -59
@@ -1 +1 @@
1
- {"version":3,"file":"works.d.ts","sourceRoot":"","sources":["../../src/read/works.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAKL,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,OAAO,EACb,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,wBAAwB;IACvC,YAAY,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC9C,UAAU,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAC7C;AAWD,gFAAgF;AAChF,wBAAsB,4BAA4B,CAChD,MAAM,EAAE,gBAAgB,EACxB,UAAU,EAAE,cAAc,EAC1B,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,wBAAwB,CAAC,CAmDnC;AAED,MAAM,WAAW,OAAO;IACtB,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,SAAS;IACxB,YAAY,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;IACnD,UAAU,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;IAC/C,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAC5C;AAED,mEAAmE;AACnE,wBAAsB,aAAa,CAAC,MAAM,EAAE,iBAAiB,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,CAO/F;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,gBAAgB,EACzB,YAAY,EAAE,SAAS,MAAM,EAAE,EAC/B,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAwCjC"}
1
+ {"version":3,"file":"works.d.ts","sourceRoot":"","sources":["../../src/read/works.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,OAAO,EAIL,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,OAAO,EAEb,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,wBAAwB;IACvC,YAAY,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC9C,UAAU,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAC7C;AAYD,mEAAmE;AACnE,wBAAsB,4BAA4B,CAChD,MAAM,EAAE,gBAAgB,EACxB,UAAU,EAAE,cAAc,EAC1B,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,wBAAwB,CAAC,CA8FnC;AAED,MAAM,WAAW,OAAO;IACtB,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,SAAS;IACxB,YAAY,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;IACnD,UAAU,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;IAC/C,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAC5C;AA+BD,wBAAgB,kBAAkB,CAChC,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,UAAU,EACnB,IAAI,EAAE,OAAO,GACZ,OAAO,CAuCT;AAED,yEAAyE;AACzE,wBAAsB,aAAa,CACjC,MAAM,EAAE,iBAAiB,EACzB,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,SAAS,CAAC,CA2CpB;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,gBAAgB,EACzB,YAAY,EAAE,SAAS,MAAM,EAAE,EAC/B,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CA+CjC"}
@@ -1,67 +1,172 @@
1
1
  // Copyright (c) Miso Labs, Inc.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
- import { extractTypeParams2, getCompositionsByIds, getRecordingsByIds, getReleasesByIds, } from "@misonetwork/sdk";
4
- /** Resolve work share types without relying on unpublished protocol helpers. */
3
+ import { normalizeSuiAddress } from "@mysten/sui/utils";
4
+ import { contracts, extractTypeParams2, getCompositionsByIds, } from "@misonetwork/sdk";
5
+ /** Resolve all work share types in one aliased GraphQL request. */
5
6
  export async function getWorkAddressesByShareTypes(client, shareTypes, misoPackageId) {
6
7
  const compositions = [...new Set(shareTypes.compositions)];
7
- const recordingSet = new Set(shareTypes.recordings);
8
+ const recordings = new Set(shareTypes.recordings);
8
9
  const out = { compositions: {}, recordings: {} };
9
- const compositionEntries = await Promise.all(compositions.map(async (shareType) => {
10
- const type = `${misoPackageId}::composition::Composition<${shareType}>`;
11
- const result = await client.query({
12
- query: `query WorkByType($type: String!) {
13
- objects(filter: { type: $type }) {
14
- nodes { address asMoveObject { contents { type { repr } } } }
15
- }
16
- }`,
17
- variables: { type },
18
- });
19
- if (result.errors?.length)
20
- throw new Error(result.errors[0].message);
21
- return [shareType, result.data?.objects?.nodes[0]?.address];
22
- }));
23
- for (const [shareType, address] of compositionEntries) {
10
+ if (compositions.length === 0 && recordings.size === 0)
11
+ return out;
12
+ const declarations = [];
13
+ const selections = [];
14
+ const variables = {};
15
+ compositions.forEach((shareType, index) => {
16
+ const variable = `compositionType${index}`;
17
+ declarations.push(`$${variable}: String!`);
18
+ selections.push(`composition${index}: objects(first: 1, filter: { type: $${variable} }) { nodes { address } }`);
19
+ variables[variable] =
20
+ `${misoPackageId}::composition::Composition<${shareType}>`;
21
+ });
22
+ if (recordings.size > 0) {
23
+ declarations.push("$recordingType: String!");
24
+ selections.push(`recordings: objects(first: 50, filter: { type: $recordingType }) {
25
+ pageInfo { hasNextPage endCursor }
26
+ nodes { address asMoveObject { contents { type { repr } } } }
27
+ }`);
28
+ variables.recordingType = `${misoPackageId}::recording::Recording`;
29
+ }
30
+ const result = await client.query({
31
+ query: `query WorkAddressesByShareTypes(${declarations.join(", ")}) {
32
+ ${selections.join("\n")}
33
+ }`,
34
+ variables,
35
+ });
36
+ if (result.errors?.length) {
37
+ throw new AggregateError(result.errors.map((error) => new Error(error.message)), "Work type discovery failed");
38
+ }
39
+ compositions.forEach((shareType, index) => {
40
+ const address = result.data?.[`composition${index}`]?.nodes[0]?.address;
24
41
  if (address)
25
42
  out.compositions[shareType] = address;
26
- }
27
- if (recordingSet.size > 0) {
28
- const type = `${misoPackageId}::recording::Recording`;
29
- const result = await client.query({
30
- query: `query RecordingsByType($type: String!) {
31
- objects(filter: { type: $type }) {
32
- nodes { address asMoveObject { contents { type { repr } } } }
33
- }
34
- }`,
35
- variables: { type },
36
- });
37
- if (result.errors?.length)
38
- throw new Error(result.errors[0].message);
39
- for (const node of result.data?.objects?.nodes ?? []) {
40
- const repr = node.asMoveObject?.contents?.type.repr;
43
+ });
44
+ const readRecordingPage = (page) => {
45
+ for (const node of page?.nodes ?? []) {
46
+ const repr = node.asMoveObject?.contents?.type?.repr;
41
47
  if (!repr)
42
48
  continue;
43
- let recordingShareType;
44
49
  try {
45
- [recordingShareType] = extractTypeParams2(repr);
50
+ const [shareType] = extractTypeParams2(repr);
51
+ if (recordings.has(shareType))
52
+ out.recordings[shareType] = node.address;
46
53
  }
47
54
  catch {
48
- recordingShareType = /<(.+)>$/.exec(repr)?.[1]?.trim();
49
- }
50
- if (recordingShareType && recordingSet.has(recordingShareType)) {
51
- out.recordings[recordingShareType] = node.address;
55
+ // Ignore objects whose deployed type does not match the Recording ABI.
52
56
  }
53
57
  }
58
+ };
59
+ let page = result.data?.recordings;
60
+ readRecordingPage(page);
61
+ while (page?.pageInfo?.hasNextPage &&
62
+ page.pageInfo.endCursor &&
63
+ Object.keys(out.recordings).length < recordings.size) {
64
+ const next = await client.query({
65
+ query: `query RecordingWorkAddresses($recordingType: String!, $cursor: String!) {
66
+ recordings: objects(first: 50, after: $cursor, filter: { type: $recordingType }) {
67
+ pageInfo { hasNextPage endCursor }
68
+ nodes { address asMoveObject { contents { type { repr } } } }
69
+ }
70
+ }`,
71
+ variables: {
72
+ recordingType: variables.recordingType,
73
+ cursor: page.pageInfo.endCursor,
74
+ },
75
+ });
76
+ if (next.errors?.length) {
77
+ throw new AggregateError(next.errors.map((error) => new Error(error.message)), "Recording type discovery failed");
78
+ }
79
+ page = next.data?.recordings;
80
+ readRecordingPage(page);
54
81
  }
55
82
  return out;
56
83
  }
57
- /** Fetch each work kind through the authoritative protocol SDK. */
84
+ function workState(value) {
85
+ return value?.$kind === "Published"
86
+ ? { type: "Published", timestampMs: Number(value.Published) }
87
+ : { type: "Initialized" };
88
+ }
89
+ function parseComposition(id, content) {
90
+ const value = contracts.composition.Composition.parse(content);
91
+ return {
92
+ id,
93
+ state: workState(value.state),
94
+ title: String(value.title),
95
+ royaltyRate: {
96
+ value: Number(Array.isArray(value.royalty_rate)
97
+ ? value.royalty_rate[0]
98
+ : value.royalty_rate),
99
+ },
100
+ };
101
+ }
102
+ function parseRecording(id, content) {
103
+ const value = contracts.recording.Recording.parse(content);
104
+ return { id, state: workState(value.state) };
105
+ }
106
+ export function parseReleaseObject(id, content, json) {
107
+ const deployed = json;
108
+ if (deployed?.discs) {
109
+ const tracks = deployed.discs.flatMap((disc) => disc.tracks ?? []);
110
+ const publishedAt = deployed.state?.["@variant"] === "Published" ? deployed.state.pos0 : null;
111
+ return {
112
+ id,
113
+ state: publishedAt == null
114
+ ? { type: "Initialized" }
115
+ : { type: "Published", timestampMs: Number(publishedAt) },
116
+ title: String(deployed.title ?? ""),
117
+ tracks: tracks.map((track) => ({
118
+ state: (track.state?.["@variant"] ?? "Unassigned"),
119
+ recordingId: String(track.recording_id),
120
+ splitBps: { value: Number(track.split_bps?.pos0 ?? track.split_bps) },
121
+ })),
122
+ };
123
+ }
124
+ const value = contracts.release.Release.parse(content);
125
+ return {
126
+ id,
127
+ state: workState(value.state),
128
+ title: String(value.title),
129
+ tracks: (value.tracks ?? []).map((track) => ({
130
+ state: (track.state?.$kind ?? "Unassigned"),
131
+ recordingId: String(track.recording_id),
132
+ splitBps: {
133
+ value: Number(Array.isArray(track.split_bps) ? track.split_bps[0] : track.split_bps),
134
+ },
135
+ })),
136
+ };
137
+ }
138
+ /** Fetch and parse heterogeneous works through one Core bulk request. */
58
139
  export async function getWorksByIds(client, ids) {
59
- const [compositions, recordings, releases] = await Promise.all([
60
- getCompositionsByIds(client, [...ids.compositions]),
61
- getRecordingsByIds(client, [...ids.recordings]),
62
- getReleasesByIds(client, [...ids.releases]),
63
- ]);
64
- return { compositions, recordings, releases };
140
+ const kinds = new Map();
141
+ for (const [kind, objectIds] of Object.entries(ids)) {
142
+ for (const objectId of objectIds) {
143
+ const normalized = normalizeSuiAddress(objectId);
144
+ const previous = kinds.get(normalized);
145
+ if (previous && previous !== kind) {
146
+ throw new Error(`Work ${normalized} was requested as both ${previous} and ${kind}`);
147
+ }
148
+ kinds.set(normalized, kind);
149
+ }
150
+ }
151
+ const out = { compositions: {}, recordings: {}, releases: {} };
152
+ if (kinds.size === 0)
153
+ return out;
154
+ const { objects } = await client.core.getObjects({
155
+ objectIds: [...kinds.keys()],
156
+ include: { content: true, json: true },
157
+ });
158
+ for (const object of objects) {
159
+ if (object instanceof Error || !object.content)
160
+ continue;
161
+ const kind = kinds.get(normalizeSuiAddress(object.objectId));
162
+ if (kind === "compositions")
163
+ out.compositions[object.objectId] = parseComposition(object.objectId, object.content);
164
+ else if (kind === "recordings")
165
+ out.recordings[object.objectId] = parseRecording(object.objectId, object.content);
166
+ else if (kind === "releases")
167
+ out.releases[object.objectId] = parseReleaseObject(object.objectId, object.content, object.json);
168
+ }
169
+ return out;
65
170
  }
66
171
  /**
67
172
  * Resolve display titles for both deployed recording layouts. Older recordings
@@ -71,7 +176,10 @@ export async function getRecordingTitles(client, graphql, recordingIds, misoPack
71
176
  const ids = [...new Set(recordingIds)];
72
177
  if (ids.length === 0)
73
178
  return {};
74
- const { objects } = await client.core.getObjects({ objectIds: ids, include: { json: true } });
179
+ const { objects } = await client.core.getObjects({
180
+ objectIds: ids,
181
+ include: { json: true },
182
+ });
75
183
  const titles = {};
76
184
  const compositionShareByRecording = {};
77
185
  for (const object of objects) {
@@ -97,7 +205,9 @@ export async function getRecordingTitles(client, graphql, recordingIds, misoPack
97
205
  const compositions = await getCompositionsByIds(client, Object.values(addresses.compositions).filter((id) => !!id));
98
206
  for (const [recordingId, shareType] of Object.entries(compositionShareByRecording)) {
99
207
  const compositionId = addresses.compositions[shareType];
100
- const title = compositionId ? compositions[compositionId]?.title : undefined;
208
+ const title = compositionId
209
+ ? compositions[compositionId]?.title
210
+ : undefined;
101
211
  if (title)
102
212
  titles[recordingId] = title;
103
213
  }
@@ -1 +1 @@
1
- {"version":3,"file":"works.js","sourceRoot":"","sources":["../../src/read/works.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"works.js","sourceRoot":"","sources":["../../src/read/works.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@misofm/sdk",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "Complete client SDK for the Miso platform on Sui \u2014 high-level reads, pressings, listings, records, and work publishing.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -80,6 +80,10 @@
80
80
  "types": "./dist/read/types.d.ts",
81
81
  "default": "./dist/read/types.js"
82
82
  },
83
+ "./auth": {
84
+ "types": "./dist/auth.d.ts",
85
+ "default": "./dist/auth.js"
86
+ },
83
87
  "./drop": {
84
88
  "types": "./dist/drop.d.ts",
85
89
  "default": "./dist/drop.js"
package/src/auth.ts ADDED
@@ -0,0 +1,275 @@
1
+ // Copyright (c) Miso Labs, Inc.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { isValidSuiAddress, normalizeSuiAddress } from "@mysten/sui/utils";
5
+
6
+ export type AuthNetwork = "testnet" | "mainnet";
7
+
8
+ export interface ApiAuthorizationFields {
9
+ method: string;
10
+ path: string;
11
+ address: string;
12
+ network: AuthNetwork;
13
+ issuedAt: string;
14
+ }
15
+
16
+ export interface AuthorizationChallenge extends ApiAuthorizationFields {
17
+ payload: string;
18
+ expiresAt: string;
19
+ }
20
+
21
+ export interface PersonalMessageSigner {
22
+ signPersonalMessage(message: Uint8Array): Promise<{ signature: string; bytes?: string }>;
23
+ }
24
+
25
+ export const AUTHORIZATION_MAX_AGE_MS = 5 * 60 * 1_000;
26
+ export const AUTHORIZATION_FUTURE_SKEW_MS = 60 * 1_000;
27
+
28
+ export const MISO_AUTH_HEADERS = {
29
+ address: "X-Sui-Address",
30
+ signature: "X-Sui-Signature",
31
+ issuedAt: "X-Sui-Issued-At",
32
+ } as const;
33
+
34
+ export type MisoAuthErrorCode =
35
+ | "invalid_target"
36
+ | "challenge_rejected"
37
+ | "invalid_challenge"
38
+ | "signing_failed";
39
+
40
+ export class MisoAuthError extends Error {
41
+ readonly code: MisoAuthErrorCode;
42
+ readonly status?: number;
43
+
44
+ constructor(code: MisoAuthErrorCode, message: string, options: { status?: number; cause?: unknown } = {}) {
45
+ super(message, options.cause === undefined ? undefined : { cause: options.cause });
46
+ this.name = "MisoAuthError";
47
+ this.code = code;
48
+ this.status = options.status;
49
+ }
50
+ }
51
+
52
+ /** The byte-exact Sui personal message required for an authenticated API mutation. */
53
+ export function buildApiAuthorizationPayload(fields: ApiAuthorizationFields): string {
54
+ return [
55
+ "miso.fm API authorization v1",
56
+ "",
57
+ `Method: ${fields.method.toUpperCase()}`,
58
+ `Path: ${fields.path}`,
59
+ `Address: ${fields.address}`,
60
+ `Network: ${fields.network}`,
61
+ `Issued At: ${fields.issuedAt}`,
62
+ ].join("\n");
63
+ }
64
+
65
+ export function isValidAuthorizationTarget(method: string, path: string): boolean {
66
+ return (
67
+ ["POST", "PUT", "PATCH", "DELETE"].includes(method.toUpperCase()) &&
68
+ path.startsWith("/platform/") &&
69
+ path.length <= 2_048 &&
70
+ !/[\r\n?#]/.test(path)
71
+ );
72
+ }
73
+
74
+ /** Accept only fresh timestamps in one canonical spelling. */
75
+ export function parseFreshAuthorizationIssuedAt(input: unknown, nowMs: number = Date.now()): string | null {
76
+ if (typeof input !== "string") return null;
77
+ const issuedAtMs = Date.parse(input);
78
+ if (!Number.isFinite(issuedAtMs)) return null;
79
+ if (
80
+ issuedAtMs > nowMs + AUTHORIZATION_FUTURE_SKEW_MS ||
81
+ nowMs - issuedAtMs > AUTHORIZATION_MAX_AGE_MS
82
+ ) {
83
+ return null;
84
+ }
85
+ const canonical = new Date(issuedAtMs).toISOString();
86
+ return input === canonical ? canonical : null;
87
+ }
88
+
89
+ function target(method: string, path: string): { method: string; path: string } {
90
+ const normalized = { method: method.toUpperCase(), path };
91
+ if (!isValidAuthorizationTarget(normalized.method, normalized.path)) {
92
+ throw new MisoAuthError("invalid_target", "Authenticated requests require a mutation under /platform/.");
93
+ }
94
+ return normalized;
95
+ }
96
+
97
+ function parseChallenge(
98
+ input: unknown,
99
+ expected: { method: string; path: string; address: string; network?: AuthNetwork; nowMs?: number },
100
+ ): AuthorizationChallenge {
101
+ if (input === null || typeof input !== "object" || Array.isArray(input)) {
102
+ throw new MisoAuthError("invalid_challenge", "The API returned an invalid authorization challenge.");
103
+ }
104
+ const body = input as Partial<Record<keyof AuthorizationChallenge, unknown>>;
105
+ if (
106
+ typeof body.payload !== "string" ||
107
+ typeof body.method !== "string" ||
108
+ typeof body.path !== "string" ||
109
+ typeof body.address !== "string" ||
110
+ typeof body.network !== "string" ||
111
+ typeof body.issuedAt !== "string" ||
112
+ typeof body.expiresAt !== "string" ||
113
+ !isValidSuiAddress(body.address) ||
114
+ (body.network !== "testnet" && body.network !== "mainnet")
115
+ ) {
116
+ throw new MisoAuthError("invalid_challenge", "The API returned an invalid authorization challenge.");
117
+ }
118
+
119
+ const nowMs = expected.nowMs ?? Date.now();
120
+ const issuedAt = parseFreshAuthorizationIssuedAt(body.issuedAt, nowMs);
121
+ const expiresAtMs = Date.parse(body.expiresAt);
122
+ const canonicalAddress = normalizeSuiAddress(body.address);
123
+ const expectedAddress = normalizeSuiAddress(expected.address);
124
+ const challenge: AuthorizationChallenge = {
125
+ payload: body.payload,
126
+ method: body.method,
127
+ path: body.path,
128
+ address: canonicalAddress,
129
+ network: body.network,
130
+ issuedAt: body.issuedAt,
131
+ expiresAt: body.expiresAt,
132
+ };
133
+ const canonicalExpiresAt = Number.isFinite(expiresAtMs) ? new Date(expiresAtMs).toISOString() : "";
134
+
135
+ if (
136
+ !issuedAt ||
137
+ body.method !== expected.method ||
138
+ body.path !== expected.path ||
139
+ canonicalAddress !== expectedAddress ||
140
+ (expected.network !== undefined && body.network !== expected.network) ||
141
+ body.expiresAt !== canonicalExpiresAt ||
142
+ expiresAtMs <= nowMs ||
143
+ expiresAtMs > Date.parse(issuedAt) + AUTHORIZATION_MAX_AGE_MS ||
144
+ body.payload !== buildApiAuthorizationPayload(challenge)
145
+ ) {
146
+ throw new MisoAuthError("invalid_challenge", "The authorization challenge did not match this request.");
147
+ }
148
+ return challenge;
149
+ }
150
+
151
+ async function responseMessage(response: Response): Promise<string> {
152
+ const body = await response.clone().json().catch(() => null) as {
153
+ error?: { message?: unknown } | string;
154
+ message?: unknown;
155
+ } | null;
156
+ if (typeof body?.error === "object" && typeof body.error.message === "string") return body.error.message;
157
+ if (typeof body?.error === "string") return body.error;
158
+ if (typeof body?.message === "string") return body.message;
159
+ return `Authorization challenge failed (${response.status}).`;
160
+ }
161
+
162
+ export interface RequestAuthorizationChallengeOptions {
163
+ apiUrl: string | URL;
164
+ token: string;
165
+ address: string;
166
+ method: string;
167
+ path: string;
168
+ network?: AuthNetwork;
169
+ challengeUrl?: string | URL;
170
+ fetch?: typeof globalThis.fetch;
171
+ nowMs?: number;
172
+ }
173
+
174
+ /** Ask Miso to verify the Enoki account and issue the exact message to sign. */
175
+ export async function requestAuthorizationChallenge(
176
+ options: RequestAuthorizationChallengeOptions,
177
+ ): Promise<AuthorizationChallenge> {
178
+ const expected = target(options.method, options.path);
179
+ if (!options.token || !isValidSuiAddress(options.address)) {
180
+ throw new MisoAuthError("challenge_rejected", "A valid Enoki token and Sui address are required.");
181
+ }
182
+ const apiUrl = new URL(options.apiUrl);
183
+ const challengeUrl = options.challengeUrl === undefined
184
+ ? new URL("/platform/auth/challenge", apiUrl.origin)
185
+ : new URL(options.challengeUrl, apiUrl);
186
+ const fetcher = options.fetch ?? globalThis.fetch;
187
+ const response = await fetcher(challengeUrl, {
188
+ method: "POST",
189
+ headers: {
190
+ Authorization: `Bearer ${options.token}`,
191
+ [MISO_AUTH_HEADERS.address]: normalizeSuiAddress(options.address),
192
+ "Content-Type": "application/json",
193
+ },
194
+ body: JSON.stringify(expected),
195
+ });
196
+ if (!response.ok) {
197
+ throw new MisoAuthError("challenge_rejected", await responseMessage(response), { status: response.status });
198
+ }
199
+ const body = await response.json().catch(() => null);
200
+ return parseChallenge(body, {
201
+ ...expected,
202
+ address: options.address,
203
+ network: options.network,
204
+ nowMs: options.nowMs,
205
+ });
206
+ }
207
+
208
+ export interface CreateAuthorizationHeadersOptions extends RequestAuthorizationChallengeOptions {
209
+ signer: PersonalMessageSigner;
210
+ }
211
+
212
+ /** Verify the challenge locally, sign it as a Sui personal message, and produce API headers. */
213
+ export async function createAuthorizationHeaders(
214
+ options: CreateAuthorizationHeadersOptions,
215
+ ): Promise<{ challenge: AuthorizationChallenge; headers: Headers }> {
216
+ const challenge = await requestAuthorizationChallenge(options);
217
+ let signature: string;
218
+ try {
219
+ const signed = await options.signer.signPersonalMessage(new TextEncoder().encode(challenge.payload));
220
+ signature = signed.signature;
221
+ } catch (cause) {
222
+ throw new MisoAuthError("signing_failed", "The authorization request was not signed.", { cause });
223
+ }
224
+ if (!signature) {
225
+ throw new MisoAuthError("signing_failed", "The signer returned an empty authorization signature.");
226
+ }
227
+ return {
228
+ challenge,
229
+ headers: new Headers({
230
+ Authorization: `Bearer ${options.token}`,
231
+ [MISO_AUTH_HEADERS.address]: challenge.address,
232
+ [MISO_AUTH_HEADERS.signature]: signature,
233
+ [MISO_AUTH_HEADERS.issuedAt]: challenge.issuedAt,
234
+ }),
235
+ };
236
+ }
237
+
238
+ export interface AuthenticatedFetchOptions extends RequestInit {
239
+ auth: {
240
+ token: string;
241
+ address: string;
242
+ signer: PersonalMessageSigner;
243
+ network?: AuthNetwork;
244
+ };
245
+ challengeUrl?: string | URL;
246
+ fetch?: typeof globalThis.fetch;
247
+ }
248
+
249
+ /** Perform a protected Miso API mutation with a fresh Enoki-verified Sui signature. */
250
+ export async function authenticatedFetch(
251
+ input: string | URL,
252
+ options: AuthenticatedFetchOptions,
253
+ ): Promise<Response> {
254
+ const { auth, challengeUrl, fetch: fetchOption, ...init } = options;
255
+ const url = new URL(input);
256
+ const method = (init.method ?? "").toUpperCase();
257
+ if (url.search || url.hash) {
258
+ throw new MisoAuthError("invalid_target", "Authenticated request URLs cannot include a query or fragment.");
259
+ }
260
+ const fetcher = fetchOption ?? globalThis.fetch;
261
+ const authorization = await createAuthorizationHeaders({
262
+ apiUrl: url,
263
+ challengeUrl,
264
+ fetch: fetcher,
265
+ token: auth.token,
266
+ address: auth.address,
267
+ signer: auth.signer,
268
+ network: auth.network,
269
+ method,
270
+ path: url.pathname,
271
+ });
272
+ const headers = new Headers(init.headers);
273
+ authorization.headers.forEach((value, name) => headers.set(name, value));
274
+ return fetcher(url, { ...init, method, headers });
275
+ }