@terminal3/t3n-sdk 3.3.0 → 3.4.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 (42) hide show
  1. package/README.md +33 -796
  2. package/dist/index.d.ts +281 -115
  3. package/dist/index.esm.js +1 -1
  4. package/dist/index.js +1 -1
  5. package/package.json +10 -60
  6. package/README.OIDC.md +0 -216
  7. package/dist/demo.d.ts +0 -25
  8. package/dist/src/client/actions.d.ts +0 -31
  9. package/dist/src/client/config.d.ts +0 -33
  10. package/dist/src/client/contract-response.d.ts +0 -59
  11. package/dist/src/client/delegation.d.ts +0 -388
  12. package/dist/src/client/encryption.d.ts +0 -30
  13. package/dist/src/client/handlers.d.ts +0 -73
  14. package/dist/src/client/index.d.ts +0 -13
  15. package/dist/src/client/org-data.d.ts +0 -276
  16. package/dist/src/client/request-parser.d.ts +0 -48
  17. package/dist/src/client/t3n-client.d.ts +0 -544
  18. package/dist/src/client/transport.d.ts +0 -131
  19. package/dist/src/config/index.d.ts +0 -82
  20. package/dist/src/config/loader.d.ts +0 -8
  21. package/dist/src/config/types.d.ts +0 -25
  22. package/dist/src/index.d.ts +0 -39
  23. package/dist/src/types/auth.d.ts +0 -66
  24. package/dist/src/types/index.d.ts +0 -45
  25. package/dist/src/types/kyc.d.ts +0 -135
  26. package/dist/src/types/org-data.d.ts +0 -180
  27. package/dist/src/types/session.d.ts +0 -24
  28. package/dist/src/types/token.d.ts +0 -102
  29. package/dist/src/types/user.d.ts +0 -236
  30. package/dist/src/utils/contract-version.d.ts +0 -5
  31. package/dist/src/utils/crypto.d.ts +0 -52
  32. package/dist/src/utils/errors.d.ts +0 -144
  33. package/dist/src/utils/index.d.ts +0 -10
  34. package/dist/src/utils/logger.d.ts +0 -102
  35. package/dist/src/utils/redaction.d.ts +0 -13
  36. package/dist/src/utils/session.d.ts +0 -37
  37. package/dist/src/utils/shape.d.ts +0 -30
  38. package/dist/src/wasm/index.d.ts +0 -5
  39. package/dist/src/wasm/interface.d.ts +0 -110
  40. package/dist/src/wasm/loader.d.ts +0 -43
  41. package/dist/src/wasm/quote-verifier/quote_verifier_bytes.d.ts +0 -1
  42. package/dist/src/wasm/quote-verifier-loader.d.ts +0 -58
@@ -1,544 +0,0 @@
1
- /**
2
- * T3n Client - Main SDK class
3
- *
4
- * Provides a simple interface for establishing secure sessions with T3n nodes.
5
- * All cryptographic complexity is handled in WASM components.
6
- */
7
- import { T3nClientConfig } from "./config";
8
- import { type ContractResponseSchema } from "./contract-response";
9
- import { SessionId, Did, SessionStatus, AuthInput, EthAuthInput, HandshakeResult } from "../types";
10
- import { KycPollOptions, KycStatus } from "../types/kyc";
11
- import { OtpChannel, OtpRequestInput, OtpRequestResult, OtpVerifyInput, OtpVerifyResult, SubmitUserInputArgs, SubmitUserInputResult } from "../types/user";
12
- import { GetUsageOptions, UsagePage } from "../types/token";
13
- /**
14
- * Main T3n SDK Client
15
- */
16
- export declare class T3nClient {
17
- private readonly config;
18
- private readonly transport;
19
- /**
20
- * Resolved node base URL. Snapshotted in the constructor so the
21
- * typed contract wrappers (`kycStatus`, `getSelfEthAddress`, …)
22
- * can call `getScriptVersion()` against the same host the
23
- * transport talks to. Used only by the `script_version: "latest"`
24
- * resolution path in {@link executeUserContract}.
25
- */
26
- private readonly effectiveBaseUrl;
27
- /**
28
- * Server-minted session ID, set by {@link handshake} from the
29
- * `Session-Id` response header (pentest M-1 / MAT-983). `null`
30
- * until the handshake completes. Client code cannot set it — the
31
- * former `config.sessionId` hook was the session-fixation vector
32
- * this fix closes.
33
- */
34
- private sessionId;
35
- /**
36
- * Set by {@link sendRpcRequest} when an `auth.handshake` RPC is
37
- * actually issued. Decouples the "flow completed without talking
38
- * to a server" case (unit-test mocks that only exercise handler
39
- * delegation) from the real "server must mint the id" invariant:
40
- * we only enforce the mint requirement when a round-trip happened.
41
- */
42
- private handshakeSentRpc;
43
- private readonly logger;
44
- private readonly encryption;
45
- private status;
46
- /**
47
- * In-flight WASM state-machine bytes. Holds the opaque state
48
- * returned by `flow[method].next()` between iterations of
49
- * `runFlow`. Always cleared at the top of `runFlow` and again
50
- * once `tryFinalize` has extracted the terminal payload — so
51
- * outside of an active loop these slots are always `null`.
52
- */
53
- private wasmState;
54
- /**
55
- * Terminal payloads produced by `flow[method].finish()`:
56
- * - `handshake` → serialized session blob, used by
57
- * `getSessionState()` for subsequent `session.encrypt` calls.
58
- * - `auth` → serialized DID; the public `authenticate()` decodes
59
- * it into `this.did` and the slot is otherwise unused.
60
- * - `execute` → unused (executes return immediately to the caller).
61
- *
62
- * Stored in a dedicated field instead of reusing `wasmState`
63
- * because the two meanings — "in-flight state machine" vs
64
- * "finalized payload" — are semantically different and merging
65
- * them invites the bug-class Devin flagged in PR #1140.
66
- */
67
- private finalizedPayload;
68
- private did;
69
- private handshakeResult;
70
- constructor(config: T3nClientConfig);
71
- /**
72
- * Start the handshake process with the T3n node
73
- */
74
- handshake(): Promise<HandshakeResult>;
75
- /**
76
- * Authenticate with the T3n node.
77
- *
78
- * For OIDC, this runs a two-step nonce-bound flow:
79
- * 1. Sends `InitOidcAuth` to server → receives session-binding nonce.
80
- * 2. Calls `getIdToken(nonce)` callback so the app can include the
81
- * nonce in the Google authorization URL.
82
- * 3. Sends `SubmitIdToken` with the nonce-bearing token → receives DID.
83
- */
84
- authenticate(authInput: AuthInput): Promise<Did>;
85
- /**
86
- * Add an Ethereum wallet as an additional authenticator on the
87
- * already-authenticated account.
88
- *
89
- * Proves control of the wallet via SIWE and links it to the session's
90
- * EXISTING DID (resolved server-side from the session) — it does NOT
91
- * mint a new DID and does NOT change the session: `this.did` and
92
- * `this.status` are left untouched, and no new cookie is issued.
93
- *
94
- * Requires a completed {@link authenticate} first (e.g. an OIDC
95
- * login). Only Ethereum is supported — OIDC/email identities are
96
- * established at login, not added afterwards.
97
- *
98
- * Reuses the same client-side eth state machine as login, but posts
99
- * to the authenticated `auth.add-authenticator` route. If the wallet
100
- * is already linked to a different account, the server rejects with a
101
- * typed `eth_auth_map_conflict` error (resolve via account merge).
102
- *
103
- * @returns the existing DID the wallet was linked to.
104
- */
105
- addAuthMethod(authInput: EthAuthInput): Promise<Did>;
106
- /**
107
- * OIDC two-step authentication with session-binding nonce.
108
- *
109
- * Bypasses the WASM client state machine and makes two encrypted
110
- * RPC calls directly:
111
- * 1. `InitOidcAuth { provider }` → server generates nonce → returns
112
- * `ProvideNonce { nonce }`.
113
- * 2. App calls `getIdToken(nonce)` to obtain a nonce-bound `id_token`.
114
- * 3. `SubmitIdToken { id_token }` → server verifies token + nonce →
115
- * returns `Finish { did }`.
116
- */
117
- private authenticateOidc;
118
- /**
119
- * Execute an action on the T3n node.
120
- *
121
- * Returns the JSON-stringified contract response. Most callers should
122
- * prefer {@link executeAndDecode}, which JSON-parses the response and
123
- * optionally validates it with a schema.
124
- */
125
- execute(payload: unknown): Promise<string>;
126
- /**
127
- * Fetch the caller's usage feed — balance plus a bounded slice of
128
- * recent token-ledger entries (charges, mints, transfers), newest
129
- * first. T3-TS-030 Phase 1D step A (`token.get-usage`).
130
- *
131
- * `limit` defaults to 50 server-side and clamps silently to
132
- * `1..=200`. `afterSeq` is the inclusive upper-bound `seq_no`
133
- * passed back from a previous page's `next_cursor` to walk older
134
- * entries. `kinds` filters by `TokenTxKind`; an empty array is
135
- * treated as "no filter".
136
- *
137
- * Unlike {@link execute}, the body of this RPC is plaintext —
138
- * `token.get-usage` is a session-authed read endpoint and the
139
- * server-side handler does not run the encryption layer.
140
- */
141
- getUsage(opts?: GetUsageOptions): Promise<UsagePage>;
142
- /**
143
- * Execute an action with an attached binary blob using multipart RPC.
144
- */
145
- executeWithBlob(payload: unknown, blob: Blob): Promise<string>;
146
- /**
147
- * Execute an action and JSON-decode the response.
148
- *
149
- * The server strips its internal `ContractResponse` wrapper at
150
- * `node/app/src/services/wasm.rs` before sending — the string returned
151
- * by {@link execute} is the contract's decoded return value directly
152
- * (no `{response, otp_state}` envelope). This helper is a typed
153
- * `JSON.parse` with optional schema validation so callers stop
154
- * reimplementing it at every call site.
155
- *
156
- * @param payload - action payload, identical to {@link execute}
157
- * @param schema - optional validator applied to the parsed value
158
- * (e.g. a zod schema); anything exposing `.parse(value)` is accepted
159
- * @returns the decoded response, typed as `T`
160
- * @throws {ContractResponseError} when the response is not valid JSON
161
- */
162
- executeAndDecode<T = unknown>(payload: unknown, schema?: ContractResponseSchema<T>): Promise<T>;
163
- /**
164
- * Build the canonical `ExecuteActionRequest` shape the server
165
- * expects in `node/primitives/src/action.rs::ExecuteActionRequest`
166
- * (`script_name`, `script_version`, `function_name`, `input`,
167
- * optional `pii_did`) and dispatch it through {@link execute}.
168
- *
169
- * Two pieces of glue live here that every typed user-contract
170
- * wrapper would otherwise duplicate:
171
- *
172
- * 1. **Field naming.** The server deserialises strictly into
173
- * `script_name` / `script_version` / `function_name` —
174
- * sending `contract` / `version` / `function` produces
175
- * `Invalid action request: missing field …` 400s. Centralising
176
- * the names here means every wrapper agrees with the server.
177
- * 2. **`"latest"` resolution.** `script_version` is `SemVer` on
178
- * the server, so a literal `"latest"` cannot be parsed. We
179
- * fetch the registered current version via
180
- * `GET /api/contracts/current?name=…` (cached per script name
181
- * in `getScriptVersion`) and forward the resolved
182
- * `MAJOR.MINOR.PATCH` string.
183
- *
184
- * Wrappers that need PII delegation can extend this helper
185
- * later — current call sites are all SelfOnly so `pii_did` stays
186
- * implicit.
187
- */
188
- private executeUserContract;
189
- /**
190
- * Return the authenticated user's Ethereum address from their
191
- * T3N-hosted per-user wallet, as a 0x-prefixed lowercase hex string.
192
- * Returns `null` if the user has no wallet yet (pre-backfill edge
193
- * case — `tee:user` has not yet been migrated for this DID).
194
- *
195
- * Backed by the `tee:user/get-self-eth-address` contract function,
196
- * which delegates to the signing host's `get-user-eth-address`
197
- * primitive (T3-TS-027 §7.1). The request carries no body; the
198
- * authenticated user's DID is read from session context by the host.
199
- *
200
- * Requires the session to be Authenticated (same precondition as
201
- * {@link execute}).
202
- *
203
- * @throws if unauthenticated, if the node rejects the action, or if
204
- * the response is not a JSON string / `null`.
205
- */
206
- getSelfEthAddress(): Promise<string | null>;
207
- /**
208
- * Enumerate every wallet the authenticated user currently controls
209
- * under T3-TS-028 multi-wallet custody.
210
- *
211
- * `primary` is the identity-bearing wallet — same address
212
- * {@link getSelfEthAddress} returns, same address the host signs
213
- * with under `sign-as-user`. `secondary` is an insertion-ordered
214
- * list of archival wallets absorbed through prior
215
- * {@link mergeProfiles} calls (most recent last); these are signable
216
- * only via an explicit sign-with-wallet flow — never ambient.
217
- *
218
- * Backed by `tee:user/list-user-wallets` which delegates to the
219
- * signing host's `list-user-wallets` primitive. See T3-TS-028 §7.1.
220
- *
221
- * @throws if unauthenticated, if the node rejects the action, or if
222
- * the response shape is unexpected.
223
- */
224
- listUserWallets(): Promise<{
225
- primary: string;
226
- secondary: string[];
227
- }>;
228
- /**
229
- * Return the ownership audit trail for a specific wallet address.
230
- *
231
- * The response is an array of `AuditEntry` objects (newest last)
232
- * describing every DID that has owned the wallet: `Created` at DID
233
- * creation, `MergedFrom { source_did }` for every merge that pulled
234
- * this wallet onto a new owner, `Abandoned` if
235
- * {@link removeUserWithWalletAbandonment} was called on the last
236
- * owner. Public-safe data: DIDs + timestamps + reason codes only,
237
- * no key material or PII. See T3-TS-028 §4.1.
238
- *
239
- * @param walletAddress 0x-prefixed 40-char hex Ethereum address.
240
- * @throws if the node rejects the action or if the response is not
241
- * a JSON array.
242
- */
243
- getWalletHistory(walletAddress: string): Promise<unknown[]>;
244
- /**
245
- * Remove the authenticated user's account AND explicitly abandon
246
- * any wallets. Writes `Abandoned` audit rows to wallet history,
247
- * clears the DID's wallet index, then runs the usual user-removal
248
- * cleanup (profile, authenticators, VCs, attribution).
249
- *
250
- * `wallet_secrets[*]` is NOT deleted — key material stays preserved
251
- * in the TEE but becomes unreachable from any DID. This path
252
- * exists as an opt-in alternative to {@link removeUser}, which
253
- * refuses when the DID owns wallets. Callers must sweep funds
254
- * off-chain BEFORE calling this if they want to retain access —
255
- * the host does not reconcile balances. See T3-TS-028 §6.2.
256
- *
257
- * Requires the session to be Authenticated (SelfOnly delegation).
258
- *
259
- * @throws if unauthenticated, if the node rejects the action, or if
260
- * the response cannot be decoded.
261
- */
262
- removeUserWithWalletAbandonment(): Promise<unknown>;
263
- /**
264
- * One-shot poll of `tee:user/contracts::kyc-status`.
265
- *
266
- * Returns the current snapshot of the authenticated user's Level 2
267
- * KYC state — see [[KycStatus]] for the four possible terminal /
268
- * non-terminal values. The caller usually wants
269
- * [[kycStatusPoll]] (which loops with §8.4 cadence until a
270
- * terminal status arrives), but the bare snapshot is useful for
271
- * pages that need to render the user's standing without waiting
272
- * (e.g. account-status views, "did the user finish KYC last time
273
- * they were here?" gates).
274
- *
275
- * Phase one default — `providerId` defaults to `"veriff"` (the
276
- * contract applies the same default when the field is absent),
277
- * so the most common call site is `await t3n.kycStatus()`.
278
- *
279
- * @param providerId optional provider id, mirrored on the wire as
280
- * `input.provider_id`. Omit for phase-one MetaMask flows.
281
- * @throws if unauthenticated, if the node rejects the action
282
- * (e.g. `precondition_failed:` when no `create-kyc-provider-session`
283
- * row exists yet), or if the response shape is unexpected.
284
- */
285
- kycStatus(providerId?: string): Promise<KycStatus>;
286
- /**
287
- * Dispatch a one-time code to the supplied contact via the host's
288
- * OTP provider. Backed by `tee:user/contracts::otp-request`.
289
- *
290
- * The contract persists the unverified contact in the channel's
291
- * pending slot (`pending_email` / `pending_phone`) and returns
292
- * `OtpRequestResult` with `status = "otp_pending"` (or `undefined`
293
- * when the node is configured with `skip_otp = true`). The next
294
- * step is {@link otpVerify} with the code the user typed.
295
- *
296
- * Behaviour notes:
297
- *
298
- * - Contact is a discriminated object: `emailChannel` or
299
- * `smsChannel` (mirrors Rust `OtpRequest`). The legacy
300
- * `keys.generic_api.otp_channel` body shadow is rejected by the
301
- * contract.
302
- * - SMS channel is gated on a verified email. Calling with
303
- * `channel = "sms"` against a DID that hasn't completed an
304
- * email roundtrip first throws.
305
- * - On a fresh DID's first call, `result.isNewProfile === true`.
306
- * Use this signal — not [[otpVerify]]'s response — for "did the
307
- * user just register" gating.
308
- *
309
- * @throws {RpcError} if the node rejects the action; the message
310
- * carries the contract-side detail (e.g. sequential-flow guard
311
- * when the email is missing).
312
- */
313
- otpRequest(input: OtpRequestInput): Promise<OtpRequestResult>;
314
- /**
315
- * Redeem a one-time code and bind the contact to the
316
- * authenticated DID. Backed by
317
- * `tee:user/contracts::otp-verify`.
318
- *
319
- * On success the contract promotes the pending contact back to
320
- * the canonical slot, writes the AUTH_MAP / DIDS_MAP /
321
- * USER_AUTHS_MAP authenticator entries, stamps
322
- * `verified_contacts.{email,phone}`, and mints the ambient
323
- * `t3n.personal.contact.1` ownership VC when both contacts are
324
- * now verified.
325
- *
326
- * If the contact is already owned by a different DID the
327
- * contract refuses to silently reparent the authenticator and
328
- * surfaces a `mergeSuggestion` instead — the caller resolves the
329
- * merge (typically via {@link mergeProfiles}) and re-attempts.
330
- *
331
- * On a wrong / expired code the contract returns the result with
332
- * `status = "otp_failed"` or `"otp_expired"` — no exception is
333
- * thrown, the error is surfaced as data so the UI can stay on
334
- * the verify screen and let the user retry.
335
- *
336
- * @throws {RpcError} if the node rejects the action outright
337
- * (network / decode / bad input shape). Branch on
338
- * `result.status` for retryable OTP failures.
339
- */
340
- otpVerify(input: OtpVerifyInput): Promise<OtpVerifyResult>;
341
- /**
342
- * Submit Level-1 user-input fields to the slim
343
- * `tee:user/contracts::user-upsert`. The contract merges the
344
- * supplied profile fields, validates them, mints
345
- * `t3n.user-input.kyc.1` once every Level-1 field is present, and
346
- * commits the write.
347
- *
348
- * **Pre-condition** (MAT-1374): the DID must already have a
349
- * verified email — either because {@link otpVerify} bound one or
350
- * because the session carries a proving authenticator (OIDC /
351
- * Email auth). Calls without proof are rejected with
352
- * {@link UserUpsertError} `kind = "EmailNotVerified"`. The
353
- * recommended UX is "request OTP -> verify OTP -> submit user
354
- * input" (or use {@link runOtpThenUserInput} which chains all
355
- * three).
356
- *
357
- * The KYC webhook orphan-attestation flow stays here: when
358
- * `requireExistingUser` is set, the contract identifies the user
359
- * by DID (vendorData) instead of email and the gate is bypassed.
360
- *
361
- * @throws {UserUpsertError} when the contract returns a typed
362
- * error (`email_not_verified`, `legacy_field`, `user_not_found`).
363
- * Branch on `err.kind`.
364
- * @throws {RpcError} for non-typed transport / decode failures.
365
- */
366
- submitUserInput(input: SubmitUserInputArgs): Promise<SubmitUserInputResult>;
367
- /**
368
- * Convenience helper: run the explicit OTP roundtrip and submit
369
- * the slim user-upsert in one call.
370
- *
371
- * The caller supplies a `getOtpCode(contact, channel)` callback
372
- * the SDK invokes between request and verify — this is where
373
- * a UI prompts the user to type the code that arrived on email
374
- * / SMS. Throw from the callback to abort the flow; the helper
375
- * does not retry on its own.
376
- *
377
- * Returns the slim `submitUserInput` result on success. Throws
378
- * {@link UserUpsertError} or `RpcError` on the same conditions
379
- * as the underlying wrappers.
380
- *
381
- * Optional, opt-in path — the recommended default is to call
382
- * {@link otpRequest}, {@link otpVerify}, and
383
- * {@link submitUserInput} explicitly so the application owns the
384
- * flow.
385
- */
386
- runOtpThenUserInput(args: {
387
- channel: OtpChannel;
388
- emailAddress?: string;
389
- phoneNumber?: string;
390
- profile: SubmitUserInputArgs["profile"];
391
- organisationDid?: string;
392
- attestations?: unknown;
393
- keys?: Record<string, unknown>;
394
- /**
395
- * MAT-1618 testnet self-admit, propagated to the inner
396
- * {@link submitUserInput} call. Inspect `result.tenantAdmit` for
397
- * the outcome.
398
- */
399
- becomeDevTenant?: boolean;
400
- getOtpCode: (contact: string, channel: OtpChannel) => Promise<string> | string;
401
- }): Promise<SubmitUserInputResult>;
402
- /**
403
- * Poll `kyc-status` until a terminal status arrives or the
404
- * configured timeout elapses.
405
- *
406
- * Cadence defaults to T3-TS-026 §8.4: 2-second interval for the
407
- * first 30 seconds, then 5 seconds, with a 5-minute hard cap.
408
- * Override via `opts.cadence` if you need different numbers
409
- * (e.g. tests that don't want to wait the full 30 seconds before
410
- * the slow window kicks in). [[KycStatusTimeoutError]] is thrown
411
- * if the timeout elapses without reaching a terminal state.
412
- *
413
- * `opts.signal` cancels the loop synchronously — the helper
414
- * stops sleeping and rejects with the abort reason; an
415
- * already-in-flight `kycStatus()` request is allowed to settle
416
- * but its result is discarded.
417
- *
418
- * `opts.onUpdate` fires once per snapshot, including
419
- * intermediate `pending` ones. Errors thrown from the callback
420
- * are swallowed so a misbehaving UI handler can't strand the
421
- * poll loop.
422
- *
423
- * @throws [[KycStatusTimeoutError]] when the cadence's
424
- * `timeoutMs` elapses without a terminal status.
425
- * @throws the abort reason when `opts.signal` is aborted.
426
- * @throws the underlying RPC error when the contract or
427
- * transport raises (e.g. session expiry mid-poll).
428
- */
429
- kycStatusPoll(opts?: KycPollOptions): Promise<KycStatus>;
430
- /**
431
- * The server-minted session ID once handshake has completed, or
432
- * `null` beforehand (pentest M-1 / MAT-983).
433
- */
434
- getSessionId(): SessionId | null;
435
- getStatus(): SessionStatus;
436
- getDid(): Did | null;
437
- getLastSetCookie(): string | null;
438
- getLastResponseHeaders(): Record<string, string>;
439
- isAuthenticated(): boolean;
440
- /**
441
- * Run a WASM state machine flow to completion.
442
- *
443
- * Clears both `wasmState[method]` and `finalizedPayload[method]`
444
- * at entry so a flow that previously threw partway (e.g. an RPC
445
- * error) starts from a clean slate on retry. Without the reset,
446
- * stale state from the failed attempt leaks into the new flow
447
- * and `tryFinalize` may either spuriously succeed or run `next()`
448
- * against a state that no longer matches the action we're sending.
449
- *
450
- * The `tryFinalize`-then-`next` order is load-bearing: the loop's
451
- * exit condition fires *after* the previous iteration's
452
- * `handleWasmRequest` has flushed the outbound peer reply, so
453
- * every state-machine emission reaches the wire before we extract
454
- * the final payload.
455
- */
456
- private runFlow;
457
- /**
458
- * Try to finalize the current flow. Returns the finish() payload
459
- * (a serialized Session for handshake, a serialized DID for auth)
460
- * or `null` if the state machine has not reached its terminal phase
461
- * yet.
462
- *
463
- * The "not yet finalized" case is the loop's signal to keep
464
- * iterating, not a real error. Any *other* failure must propagate
465
- * so callers see real WASM errors instead of silent retries that
466
- * spin forever.
467
- *
468
- * The terminal payload is stored in `finalizedPayload[method]`
469
- * (a separate field from `wasmState[method]`) so the in-flight
470
- * state-machine bytes and the finalized session/DID bytes never
471
- * occupy the same slot. `getSessionState()` reads from
472
- * `finalizedPayload.handshake`.
473
- */
474
- private tryFinalize;
475
- /**
476
- * Handle a WASM request based on its type
477
- */
478
- private handleWasmRequest;
479
- /**
480
- * Handle a send-remote request by calling the RPC endpoint
481
- */
482
- private handleSendRemote;
483
- private captureHandshakeResult;
484
- /**
485
- * Handle a guest-to-host request using configured handlers
486
- */
487
- private handleGuestToHost;
488
- /**
489
- * Send an RPC request with automatic encryption/decryption
490
- */
491
- private sendRpcRequest;
492
- /**
493
- * Send a session-authenticated JSON-RPC request with plaintext
494
- * params/result (no SessionEncryption layer). Used for tenant-facing
495
- * read endpoints (`token.get-balance`, `token.get-usage`) where the
496
- * server-side handler reads the JSON envelope directly.
497
- *
498
- * Auto-attaches the `Session-Id` header; surfaces JSON-RPC errors
499
- * the same way as {@link sendRpcRequest} (typed
500
- * `InsufficientCreditError` when applicable, `RpcError` otherwise).
501
- * Returns the parsed `result` field — typically a JSON object the
502
- * caller will narrow to the endpoint's response type.
503
- */
504
- private sendUnencryptedSessionRpc;
505
- /**
506
- * Inspect a JSON-RPC response for an `error` field and throw the
507
- * appropriate typed exception. No-op when `response.error` is
508
- * absent. Shared by every RPC path so wire-shape changes in the
509
- * server's error envelope (typed-error subclasses, request-id
510
- * placement, detail formatting) only need to land in one place.
511
- *
512
- * JSON-RPC `error.message` is the generic category string
513
- * ("Invalid params", "Internal error", …). The node attaches the
514
- * actionable text and per-request correlation id in `error.data`
515
- * — see `node/api/src/responses/rpc.rs::from_service_error`.
516
- * Extract both so callers (and toasts that only render `.message`)
517
- * get the real reason plus an id an operator can grep in
518
- * `api::error` logs.
519
- *
520
- * T3-TS-030 chargepoint denials surface as a typed
521
- * `InsufficientCreditError` so frontends can branch on
522
- * `instanceof` to render an "out of credit" UI without
523
- * prefix-matching the message themselves. Wire format is pinned
524
- * by `api/src/error.rs::service_insufficient_credit_wire_format_is_stable`.
525
- */
526
- private throwIfRpcError;
527
- private sendMultipartRpcRequest;
528
- /**
529
- * Capture the server-minted `Session-Id` from the last handshake
530
- * response headers (pentest M-1 / MAT-983). Validates shape so a
531
- * broken or MITM'd response fails loudly instead of leaving a
532
- * garbage value in the client. Idempotent: only the first valid
533
- * mint per session is honoured — subsequent handshake RPC legs
534
- * (none exist today, but the state-machine loop can iterate) do
535
- * not overwrite an already-set value.
536
- */
537
- private captureMintedSessionId;
538
- /**
539
- * Get the finalized session blob (for `session.encrypt` calls).
540
- * Populated by `tryFinalize` once the handshake state machine
541
- * reaches its terminal phase.
542
- */
543
- private getSessionState;
544
- }
@@ -1,131 +0,0 @@
1
- /**
2
- * Transport layer for T3n SDK
3
- *
4
- * Abstracts the communication with T3n nodes, allowing for different
5
- * implementations (HTTP, WebSocket, Mock) and easy testing.
6
- */
7
- /**
8
- * JSON-RPC request structure
9
- */
10
- export interface JsonRpcRequest {
11
- jsonrpc: "2.0";
12
- method: string;
13
- params: unknown;
14
- id: string | number;
15
- }
16
- /**
17
- * JSON-RPC response structure
18
- */
19
- export interface JsonRpcResponse {
20
- jsonrpc: "2.0";
21
- result?: unknown;
22
- error?: {
23
- code: number;
24
- message: string;
25
- data?: unknown;
26
- };
27
- id: string | number;
28
- }
29
- /**
30
- * Transport interface for sending requests to T3n nodes
31
- */
32
- export interface Transport {
33
- /**
34
- * Send a JSON-RPC request to the T3n node
35
- * @param request - The JSON-RPC request to send
36
- * @param headers - Additional headers to include
37
- * @returns Promise that resolves to the JSON-RPC response
38
- */
39
- send(request: JsonRpcRequest, headers: Record<string, string>): Promise<JsonRpcResponse>;
40
- /**
41
- * Send a JSON-RPC request with an attached binary blob (multipart/form-data).
42
- * Part 1 (name=jsonrpc): JSON-RPC envelope.
43
- * Part 2 (name=blob): raw binary bytes (e.g. WASM bytecode).
44
- */
45
- sendMultipart?(request: JsonRpcRequest, headers: Record<string, string>, blob: Blob): Promise<JsonRpcResponse>;
46
- /**
47
- * Optional accessor for the latest Set-Cookie header value.
48
- * (Useful in Node.js demos/tests; browsers block HttpOnly cookies.)
49
- */
50
- getLastSetCookie?(): string | null;
51
- /**
52
- * Optional accessor for the last response headers (debugging).
53
- */
54
- getLastResponseHeaders?(): Record<string, string>;
55
- }
56
- /**
57
- * HTTP transport implementation using fetch
58
- */
59
- export declare class HttpTransport implements Transport {
60
- private baseUrl;
61
- private timeout;
62
- private lastSetCookie;
63
- private lastResponseHeaders;
64
- constructor(baseUrl: string, timeout?: number);
65
- getLastSetCookie(): string | null;
66
- getLastResponseHeaders(): Record<string, string>;
67
- send(request: JsonRpcRequest, headers: Record<string, string>): Promise<JsonRpcResponse>;
68
- sendMultipart(request: JsonRpcRequest, headers: Record<string, string>, blob: Blob): Promise<JsonRpcResponse>;
69
- }
70
- /**
71
- * Mock transport for testing
72
- *
73
- * @example
74
- * ```typescript
75
- * const mockTransport = new MockTransport();
76
- * mockTransport.mockResponse("auth.handshake", { result: "..." });
77
- *
78
- * const client = new T3nClient({
79
- * transport: mockTransport,
80
- * wasmComponent: mockWasm,
81
- * });
82
- * ```
83
- */
84
- export declare class MockTransport implements Transport {
85
- private responses;
86
- private responseHeaders;
87
- private lastResponseHeaders;
88
- private requests;
89
- private multipartRequests;
90
- /**
91
- * Mock a response for a specific method
92
- */
93
- mockResponse(method: string, response: Partial<JsonRpcResponse>): void;
94
- /**
95
- * Mock response headers for a specific method. Used by tests to
96
- * simulate the server-minted `Session-Id` header the SDK picks up
97
- * from the `auth.handshake` response (MAT-983). Unset methods
98
- * default to no headers.
99
- */
100
- mockResponseHeaders(method: string, headers: Record<string, string>): void;
101
- /**
102
- * Mock an error response for a specific method
103
- */
104
- mockError(method: string, code: number, message: string, data?: unknown): void;
105
- getLastResponseHeaders(): Record<string, string>;
106
- /**
107
- * Get all requests that were sent
108
- */
109
- getRequests(): Array<{
110
- request: JsonRpcRequest;
111
- headers: Record<string, string>;
112
- }>;
113
- /**
114
- * Get requests for a specific method
115
- */
116
- getRequestsForMethod(method: string): Array<{
117
- request: JsonRpcRequest;
118
- headers: Record<string, string>;
119
- }>;
120
- getMultipartRequests(): Array<{
121
- request: JsonRpcRequest;
122
- headers: Record<string, string>;
123
- blob: Blob;
124
- }>;
125
- /**
126
- * Clear all recorded requests
127
- */
128
- clearRequests(): void;
129
- sendMultipart(request: JsonRpcRequest, headers: Record<string, string>, blob: Blob): Promise<JsonRpcResponse>;
130
- send(request: JsonRpcRequest, headers: Record<string, string>): Promise<JsonRpcResponse>;
131
- }