@misofm/api-client 0.1.0 → 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 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 { MisoApiClient } from "@misofm/api-client";
6
+ import { createMisoApiClient } from "@misofm/api-client";
7
7
 
8
- const client = new MisoApiClient({ baseUrl: "https://api.testnet.miso.fm" });
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@misofm/api-client",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Typed client and response contract for the Miso API read layer. The one definition of what a Miso read returns.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
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
- // agents. It carries no chain code and no Sui dependency — a browser importing it
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 @misonetwork/sdk that these schemas don't describe fails CI
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. Agents, the CLI, and any future surface read the same OpenAPI.
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
- export const trackCreditsSchema = z.union([
150
- z.object({
151
- compositionCredits: z.array(creditSchema),
152
- recordingCredits: recordingCreditsSchema,
153
- }),
154
- // Keep already-deployed recording-only responses usable while the read service
155
- // rolls out the composition-credit projection.
156
- recordingCreditsSchema.transform((recordingCredits) => ({
157
- compositionCredits: [],
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>;