@misofm/api-client 0.1.1 → 0.1.2
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.
- package/README.md +2 -2
- package/package.json +1 -1
- package/src/index.ts +1 -5
- package/src/schemas.ts +22 -13
- package/src/checkout.ts +0 -146
package/README.md
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
Typed client, schemas, and response contracts for the Miso API read layer.
|
|
4
4
|
|
|
5
5
|
```ts
|
|
6
|
-
import {
|
|
6
|
+
import { createMisoApiClient } from "@misofm/api-client";
|
|
7
7
|
|
|
8
|
-
const client =
|
|
8
|
+
const client = createMisoApiClient({ baseUrl: "https://api.testnet.miso.fm" });
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
The package is maintained in the [`misofm/api`](https://github.com/misofm/api)
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// Miso read API.
|
|
6
6
|
//
|
|
7
7
|
// This package is what every Miso frontend reads through: the PWA, the CLI,
|
|
8
|
-
//
|
|
8
|
+
// and third-party clients. It carries no chain code and no Sui dependency — a browser importing it
|
|
9
9
|
// gets zod and a fetch wrapper, not a blockchain SDK.
|
|
10
10
|
//
|
|
11
11
|
// import { createMisoApiClient } from "@misofm/api-client";
|
|
@@ -19,7 +19,3 @@ export type { CacheClass, CachePolicy } from "./cache.ts";
|
|
|
19
19
|
|
|
20
20
|
export * as schemas from "./schemas.ts";
|
|
21
21
|
export type * from "./types.ts";
|
|
22
|
-
|
|
23
|
-
// Card checkout lives in miso-platform-service, not the read service, but it is
|
|
24
|
-
// the same API to a caller — one package, one base URL, one error type.
|
|
25
|
-
export * from "./checkout.ts";
|
package/src/schemas.ts
CHANGED
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
// · @misofm/api-client infers its types from it
|
|
7
7
|
// · miso-read-service generates its OpenAPI document from it
|
|
8
8
|
// · a contract test in that service parses every handler's real output through
|
|
9
|
-
// it, so a change in @
|
|
9
|
+
// it, so a change in @misofm/sdk/read that these schemas don't describe fails CI
|
|
10
10
|
// rather than reaching a client
|
|
11
11
|
//
|
|
12
12
|
// Schemas, not hand-written interfaces, precisely so that third clause is
|
|
13
|
-
// possible.
|
|
13
|
+
// possible. The CLI and any future client read the same OpenAPI.
|
|
14
14
|
//
|
|
15
15
|
// SCALARS: every u64/u128 is a DECIMAL STRING (`u64Schema`), never a number.
|
|
16
16
|
// Prices, supply counts, and balances routinely exceed 2^53, and JSON has no
|
|
@@ -146,19 +146,28 @@ export const recordingCreditsSchema = z.object({
|
|
|
146
146
|
|
|
147
147
|
/** Per-track credits for a release. Composition writing and recording
|
|
148
148
|
* performance/production credits remain separate. */
|
|
149
|
-
|
|
150
|
-
z.
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
recordingCredits,
|
|
159
|
-
})),
|
|
149
|
+
const completeTrackCreditsSchema = z.object({
|
|
150
|
+
compositionCredits: z.array(creditSchema),
|
|
151
|
+
recordingCredits: recordingCreditsSchema,
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
/** The HTTP wire accepts the complete shape and the deployed recording-only shape. */
|
|
155
|
+
export const trackCreditsWireSchema = z.union([
|
|
156
|
+
completeTrackCreditsSchema,
|
|
157
|
+
recordingCreditsSchema,
|
|
160
158
|
]);
|
|
161
159
|
|
|
160
|
+
/** Clients always receive the complete shape, including during the rollout. */
|
|
161
|
+
export const trackCreditsSchema = trackCreditsWireSchema.transform((credits) =>
|
|
162
|
+
"recordingCredits" in credits
|
|
163
|
+
? credits
|
|
164
|
+
: { compositionCredits: [], recordingCredits: credits },
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
/** Transform-free wire schema used when generating OpenAPI. */
|
|
168
|
+
export const releaseTrackCreditsWireSchema = z.object({
|
|
169
|
+
tracks: z.record(suiIdSchema, trackCreditsWireSchema),
|
|
170
|
+
});
|
|
162
171
|
/** Per-track work credits for a release, keyed by recording id. */
|
|
163
172
|
export const releaseTrackCreditsSchema = z.object({
|
|
164
173
|
tracks: z.record(suiIdSchema, trackCreditsSchema),
|
package/src/checkout.ts
DELETED
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
// Copyright (c) Miso Labs, Inc.
|
|
2
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
//
|
|
4
|
-
// The card-checkout contract (Stripe on-ramp for records). Served by
|
|
5
|
-
// miso-platform-service under `/platform/checkout`, not by the read service — it
|
|
6
|
-
// is a WRITE surface with its own Durable Object payment gate — but it lives in
|
|
7
|
-
// this package so an app has one API dependency rather than two.
|
|
8
|
-
//
|
|
9
|
-
// Lifted from miso-app's `lib/checkout-client.ts`, which was hand-written against
|
|
10
|
-
// the backend and had no way to notice the backend changing.
|
|
11
|
-
//
|
|
12
|
-
// quote → sign → session → redirect to Stripe → poll the receipt
|
|
13
|
-
|
|
14
|
-
import { z } from "zod";
|
|
15
|
-
import { MisoApiError } from "./client.ts";
|
|
16
|
-
|
|
17
|
-
export const checkoutQuoteSchema = z.object({
|
|
18
|
-
/** The signed payload the buyer signs to prove intent. */
|
|
19
|
-
payload: z.string(),
|
|
20
|
-
nonce: z.string(),
|
|
21
|
-
/** What the card will actually be charged, in USD cents (price + card fee). */
|
|
22
|
-
cardAmountCents: z.number().int(),
|
|
23
|
-
expiresAt: z.string(),
|
|
24
|
-
priceDisplay: z.string(),
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
export const checkoutSessionSchema = z.object({
|
|
28
|
-
/** Stripe Checkout URL to redirect to. */
|
|
29
|
-
url: z.string().url(),
|
|
30
|
-
orderId: z.string(),
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
export const orderStateSchema = z.enum([
|
|
34
|
-
"awaiting_payment",
|
|
35
|
-
"paid",
|
|
36
|
-
"fulfilling",
|
|
37
|
-
"under_review",
|
|
38
|
-
"fulfilled",
|
|
39
|
-
"fulfill_failed",
|
|
40
|
-
"refund_pending",
|
|
41
|
-
"refunded",
|
|
42
|
-
"expired",
|
|
43
|
-
]);
|
|
44
|
-
|
|
45
|
-
export const receiptLineItemSchema = z.object({
|
|
46
|
-
description: z.string(),
|
|
47
|
-
amountCents: z.number().int(),
|
|
48
|
-
/** ISO-4217, lowercase (always "usd"). */
|
|
49
|
-
currency: z.string(),
|
|
50
|
-
quantity: z.number().int(),
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
export const orderReceiptSchema = z.object({
|
|
54
|
-
state: orderStateSchema,
|
|
55
|
-
/** The Sui address the record is/was delivered to. */
|
|
56
|
-
buyerAddress: z.string(),
|
|
57
|
-
lineItems: z.array(receiptLineItemSchema),
|
|
58
|
-
/** The minted record's object id once fulfilled — the target of "Mix it now". */
|
|
59
|
-
recordObjectId: z.string().optional(),
|
|
60
|
-
txDigest: z.string().optional(),
|
|
61
|
-
network: z.enum(["testnet", "mainnet"]),
|
|
62
|
-
failureReason: z.string().optional(),
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
export type CheckoutQuote = z.infer<typeof checkoutQuoteSchema>;
|
|
66
|
-
export type CheckoutSession = z.infer<typeof checkoutSessionSchema>;
|
|
67
|
-
export type OrderState = z.infer<typeof orderStateSchema>;
|
|
68
|
-
export type ReceiptLineItem = z.infer<typeof receiptLineItemSchema>;
|
|
69
|
-
export type OrderReceipt = z.infer<typeof orderReceiptSchema>;
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Terminal "you can't see this": the order uuid + session_id capability pair did
|
|
73
|
-
* not resolve — unknown order, wrong or expired session, or a malformed link. The
|
|
74
|
-
* API returns an identical 404 for all of these so nothing leaks about whether
|
|
75
|
-
* the order exists, which means the client can't tell them apart either.
|
|
76
|
-
*
|
|
77
|
-
* **Stop polling on this.** Any other non-2xx is transient and safe to retry.
|
|
78
|
-
*/
|
|
79
|
-
export class CheckoutNotFoundError extends MisoApiError {
|
|
80
|
-
constructor(message: string) {
|
|
81
|
-
super(404, "not_found", message);
|
|
82
|
-
this.name = "CheckoutNotFoundError";
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export interface CheckoutClientOptions {
|
|
87
|
-
baseUrl: string;
|
|
88
|
-
fetch?: typeof globalThis.fetch;
|
|
89
|
-
/** Where the platform service is mounted on `baseUrl`. */
|
|
90
|
-
prefix?: string;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export function createCheckoutClient(options: CheckoutClientOptions) {
|
|
94
|
-
const base = options.baseUrl.replace(/\/$/, "");
|
|
95
|
-
const prefix = options.prefix ?? "/platform/checkout";
|
|
96
|
-
const doFetch = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
97
|
-
|
|
98
|
-
async function parseError(res: Response): Promise<MisoApiError> {
|
|
99
|
-
const body = (await res.json().catch(() => null)) as { error?: string | { message?: string } } | null;
|
|
100
|
-
const raw = body?.error;
|
|
101
|
-
const message = typeof raw === "string" ? raw : (raw?.message ?? `Request failed (${res.status})`);
|
|
102
|
-
return new MisoApiError(res.status, "checkout_error", message);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
async function post<T>(schema: z.ZodType<T>, path: string, body: unknown): Promise<T> {
|
|
106
|
-
const res = await doFetch(`${base}${prefix}${path}`, {
|
|
107
|
-
method: "POST",
|
|
108
|
-
headers: { "Content-Type": "application/json" },
|
|
109
|
-
body: JSON.stringify(body),
|
|
110
|
-
});
|
|
111
|
-
if (!res.ok) throw await parseError(res);
|
|
112
|
-
return schema.parse(await res.json());
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
return {
|
|
116
|
-
/** Price + signed-payload quote for a drop, for a given recipient. */
|
|
117
|
-
getQuote: (dropId: string, recipient: string): Promise<CheckoutQuote> =>
|
|
118
|
-
post(checkoutQuoteSchema, "/quote", { dropId, recipient }),
|
|
119
|
-
|
|
120
|
-
/** Verify the signature + Enoki identity, then open a Stripe session. */
|
|
121
|
-
createSession: (params: {
|
|
122
|
-
payload: string;
|
|
123
|
-
signature: string;
|
|
124
|
-
address: string;
|
|
125
|
-
nonce: string;
|
|
126
|
-
jwt: string;
|
|
127
|
-
recordName?: string;
|
|
128
|
-
}): Promise<CheckoutSession> => post(checkoutSessionSchema, "/session", params),
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* The buyer-scoped receipt for the post-purchase page. Requires the order
|
|
132
|
-
* uuid AND the Stripe session id from the success redirect — a capability
|
|
133
|
-
* pair, not an id lookup. A 404/400 throws {@link CheckoutNotFoundError}.
|
|
134
|
-
*/
|
|
135
|
-
getReceipt: async (orderId: string, sessionId: string): Promise<OrderReceipt> => {
|
|
136
|
-
const res = await doFetch(
|
|
137
|
-
`${base}${prefix}/orders/${orderId}/receipt?session_id=${encodeURIComponent(sessionId)}`,
|
|
138
|
-
);
|
|
139
|
-
if (res.status === 404 || res.status === 400) throw new CheckoutNotFoundError((await parseError(res)).message);
|
|
140
|
-
if (!res.ok) throw await parseError(res);
|
|
141
|
-
return orderReceiptSchema.parse(await res.json());
|
|
142
|
-
},
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
export type CheckoutClient = ReturnType<typeof createCheckoutClient>;
|