@metatrongg/sdk 0.8.0-dev.09aec63

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/LICENSE ADDED
@@ -0,0 +1 @@
1
+ <LICENSE_TEXT>
package/README.md ADDED
@@ -0,0 +1,229 @@
1
+ # @metatrongg/sdk
2
+
3
+ The universal typed client for the Metatron API. It wraps **every** route the api exposes -- OAuth 2.1, payments (charge
4
+ / status / limits / price / intent / payout / distribute), the realtime sockets, webhook verification, and the full
5
+ first-party surface (profile, wallets, messaging, notifications, social graph, payment dashboard, catalog, developer
6
+ console, admin) -- so any consumer, first-party or third-party, calls it instead of hand-rolling `fetch`/WS plumbing.
7
+
8
+ ## Install
9
+
10
+ ```bash
11
+ npm i @metatrongg/sdk # or: pnpm add @metatrongg/sdk / yarn add @metatrongg/sdk
12
+ ```
13
+
14
+ The `latest` tag is the released build (cut from `main`). For the bleeding edge from the integration branch, pull the
15
+ `dev` prerelease tag (published on `develop`); it is never the default install:
16
+
17
+ ```bash
18
+ npm i @metatrongg/sdk@dev
19
+ ```
20
+
21
+ `zod` is the only runtime dependency. `react` (for `@metatrongg/sdk/react`) and your web framework (for the webhook
22
+ adapters) are optional peers — install them only if you use those entry points.
23
+
24
+ **Type-safe + validated.** Types and runtime zod validators are generated from the Metatron API's OpenAPI document, so
25
+ the client always matches the live API. Every response is parsed against its validator before you receive it — the shape
26
+ you get is the shape the API promises. The SDK depends on no other package; `zod` is its only runtime dependency.
27
+
28
+ ## Docs
29
+
30
+ - Guides — <https://metatron.gg/docs>
31
+ - SDK reference — <https://metatron.gg/sdk>
32
+ - Raw HTTP / OpenAPI — <https://metatron.gg/reference>
33
+ - Building with an agent? — <https://metatron.gg/llms.txt>
34
+
35
+ ## Exports
36
+
37
+ Subpath exports separate the secret-bearing primitives from the browser surface.
38
+
39
+ | Subpath | Use it from | Holds |
40
+ | --------------------------------- | ----------- | -------------------------------------------------------------------------------------- |
41
+ | `@metatrongg/sdk` | any | error model, token store types, logger types, webhook verify (universal) |
42
+ | `@metatrongg/sdk/browser` | browser | PKCE flow, payments user-bearer, reads, `TronBrowserClient`, cookie store |
43
+ | `@metatrongg/sdk/node` | node | everything above + `client_credentials`, payouts, events socket, `TronNodeClient` |
44
+ | `@metatrongg/sdk/react` | react | `createTronReact()` -- provider + `useTronClient` / `useTronQuery` / `useTronMutation` |
45
+ | `@metatrongg/sdk/contracts` | any | spec-generated zod validators + types + `z`/`ZodError` for one-import `safeParse` |
46
+ | `@metatrongg/sdk/webhook` | any | framework-agnostic HMAC verify |
47
+ | `@metatrongg/sdk/webhook/hono` | server | hono middleware adapter |
48
+ | `@metatrongg/sdk/webhook/express` | server | express middleware adapter |
49
+ | `@metatrongg/sdk/webhook/fastify` | server | fastify pre-handler adapter |
50
+
51
+ The browser path excludes every `client_secret`-bearing surface. A bundler that imports the browser entry into a browser
52
+ app cannot accidentally pull `client_credentials` or payouts.
53
+
54
+ ## Client-side safety: rate limiting + request de-duplication
55
+
56
+ The api enforces the real limits; the SDK adds courtesy guards so a well-behaved client paces itself instead of eating
57
+ 429s, and so accidental fan-out doesn't hammer the network:
58
+
59
+ - **Rate limiting** mirrors the server's per-user policy (reads 60 / 60s, mutations 5 / 60s) with separate read/write
60
+ token buckets, keyed by HTTP method. On by default; tune with `rateLimit: { read, write }` or disable with
61
+ `rateLimit: false` on any client config.
62
+ - **Request de-duplication** coalesces concurrent identical GETs onto one round trip (a common React double-render /
63
+ fan-out pattern). On by default; disable with `dedupe: false`. Mutations are never de-duplicated.
64
+
65
+ Both live in the transport, so every method -- and the React hooks below -- get them for free.
66
+
67
+ ## Quickstart: React
68
+
69
+ `@metatrongg/sdk/react` ships a typed factory; `react` is an optional peer dependency.
70
+
71
+ ```tsx
72
+ import { TronBrowserClient } from "@metatrongg/sdk/browser";
73
+ import { createTronReact } from "@metatrongg/sdk/react";
74
+
75
+ const client = new TronBrowserClient({ issuer, clientId, redirectUri, scopes: ["openid", "profile"] });
76
+ export const { TronProvider, useTronClient, useTronQuery, useTronMutation } = createTronReact<TronBrowserClient>();
77
+
78
+ // Wrap once: <TronProvider client={client}>...</TronProvider>
79
+
80
+ function Profile({ handle }: { handle: string }) {
81
+ const { data, loading, error } = useTronQuery((c) => c.users.get({ handle }), [handle]);
82
+ if (loading) return <Spinner />;
83
+ if (error) return <ErrorCard error={error} />;
84
+ return <ProfileCard profile={data} />;
85
+ }
86
+ ```
87
+
88
+ ## Quickstart: browser PKCE flow
89
+
90
+ The user clicks "sign in"; the SDK builds the authorize URL with a fresh PKCE pair + state. Store the verifier + state
91
+ in a session-scoped place (cookie, sessionStorage) before redirecting.
92
+
93
+ ```ts
94
+ import { TronBrowserClient } from "@metatrongg/sdk/browser";
95
+
96
+ const client = new TronBrowserClient({
97
+ issuer: "https://api.metatron.gg",
98
+ clientId: "tg_prod_yourappid",
99
+ redirectUri: "https://yourgame.example/auth/callback",
100
+ scopes: ["openid", "profile", "payments:charge"],
101
+ });
102
+
103
+ // 1) Build the authorize URL + redirect
104
+ const { url, state, codeVerifier } = await client.oauth.buildAuthorizeUrl();
105
+ sessionStorage.setItem("tron_oauth_state", state);
106
+ sessionStorage.setItem("tron_oauth_verifier", codeVerifier);
107
+ window.location.assign(url);
108
+
109
+ // 2) On the redirect-uri callback page
110
+ const params = new URLSearchParams(window.location.search);
111
+ const code = params.get("code")!;
112
+ const returnedState = params.get("state");
113
+ const expectedState = sessionStorage.getItem("tron_oauth_state");
114
+ const codeVerifier = sessionStorage.getItem("tron_oauth_verifier")!;
115
+ if (returnedState !== expectedState) throw new Error("state mismatch");
116
+
117
+ const tokens = await client.oauth.exchangeCode({ code, codeVerifier });
118
+ // tokens.accessToken is now ready for `client.payments.*` and `client.users.*` calls
119
+ ```
120
+
121
+ ## Quickstart: node app-bearer flow
122
+
123
+ The game backend mints a `client_credentials` token to drive payouts. The SDK caches the bearer with preemptive refresh.
124
+
125
+ ```ts
126
+ import { TronNodeClient } from "@metatrongg/sdk/node";
127
+
128
+ const client = new TronNodeClient({
129
+ issuer: process.env.TRON_API_ORIGIN!,
130
+ clientId: process.env.OAUTH_CLIENT_ID!,
131
+ clientSecret: process.env.OAUTH_CLIENT_SECRET!,
132
+ });
133
+
134
+ // One-line payout. The SDK mints + caches the app bearer; no manual /oauth/token round-trip.
135
+ const result = await client.payments.payout({
136
+ body: {
137
+ chain: "base-mainnet",
138
+ token: "0x0000000000000000000000000000000000000000",
139
+ amount: "1000000000000000000", // 1 ETH-equivalent in wei
140
+ recipientUserId: "u_abc123",
141
+ },
142
+ });
143
+
144
+ if (result.kind === "direct") {
145
+ console.log("paid out atomically", result.txHash);
146
+ } else {
147
+ console.log("ledger-credited to recipient vault", result.txHash);
148
+ }
149
+ ```
150
+
151
+ ## Quickstart: events socket
152
+
153
+ Long-lived subscriber to `/oauth/payments/events`. 1s → 30s exponential backoff with jitter; `{kind:"subscribed"}` first
154
+ frame is a no-op; subsequent frames are `OauthPaymentWebhookBody`.
155
+
156
+ ```ts
157
+ import { TronNodeClient } from "@metatrongg/sdk/node";
158
+
159
+ const client = new TronNodeClient({ ... });
160
+
161
+ const subscription = client.subscribeOauthPaymentEvents({
162
+ onSubscribed: (appId) => console.log("tron events subscribed", appId),
163
+ onMessage: async (body) => {
164
+ if (body.event === "intent.completed") {
165
+ await markIntentPaid(body.intent.intentId, body.intent.txHash);
166
+ }
167
+ },
168
+ onClose: (error) => {
169
+ if (error.isPermanent) console.error("tron events: stopping", error.code, error.reason);
170
+ },
171
+ });
172
+
173
+ // On shutdown:
174
+ process.on("SIGTERM", () => subscription.stop());
175
+ ```
176
+
177
+ ## Quickstart: webhook receiver
178
+
179
+ HMAC-SHA256 over `<timestamp>.<body>` with the per-app `payment_status_webhook_secret`. Hono middleware shown; express +
180
+ fastify adapters mirror the shape.
181
+
182
+ ```ts
183
+ import { Hono } from "hono";
184
+ import { createMetatronWebhookMiddleware } from "@metatrongg/sdk/webhook/hono";
185
+
186
+ const app = new Hono();
187
+
188
+ app.post("/webhooks/tron", createMetatronWebhookMiddleware({ secret: process.env.TRON_WEBHOOK_SECRET! }), (c) => {
189
+ const body = c.get("tronWebhook");
190
+ if (body.event === "intent.completed") {
191
+ // ... persist body.intent ...
192
+ }
193
+ return c.json({ ok: true });
194
+ });
195
+ ```
196
+
197
+ Without a framework, use the universal verify:
198
+
199
+ ```ts
200
+ import { verifyWebhook, WEBHOOK_SIGNATURE_HEADER, WEBHOOK_TIMESTAMP_HEADER } from "@metatrongg/sdk/webhook";
201
+
202
+ const rawBody = await req.text();
203
+ const result = await verifyWebhook({
204
+ secret: process.env.TRON_WEBHOOK_SECRET!,
205
+ rawBody,
206
+ signatureHeader: req.headers.get(WEBHOOK_SIGNATURE_HEADER),
207
+ timestampHeader: req.headers.get(WEBHOOK_TIMESTAMP_HEADER),
208
+ });
209
+ if (!result.ok) return new Response(JSON.stringify({ reason: result.reason }), { status: 400 });
210
+ // result.body is the parsed OauthPaymentWebhookBody
211
+ ```
212
+
213
+ ## Error model
214
+
215
+ - `TronError` -- base shape; carries `status`, `code`, `body`.
216
+ - `TronOauthError` -- RFC 6749 §5.2; carries `error` / `error_description` / `error_uri`.
217
+ - `TronWsCloseError` -- WS application close codes (4400 / 4401 / 4403 / 4404 / 4503); the `isPermanent` getter returns
218
+ `true` for the auth-shaped codes so the caller can stop the loop.
219
+ - `charge()` returns `{ status: "monthly_limit_exceeded", ... }` as a value (not an exception) so the caller can drive
220
+ the raise-cap UX without try/catching.
221
+
222
+ ## Token storage
223
+
224
+ The `TokenStore` interface is `get` / `set` / `clear`. Two helpers ship out of the box:
225
+
226
+ - `createInMemoryTokenStore()` -- process-local; default for tests + SPAs that ferry tokens via cookies.
227
+ - `createCookieTokenStore()` -- browser cookie-backed (Secure, SameSite=Lax).
228
+
229
+ Node deployments wire their own DB-backed adapter -- the SDK never persists anything itself.