@terminal3/t3n-sdk 2.10.1 → 2.12.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.
@@ -9,6 +9,7 @@ import { type ContractResponseSchema } from "./contract-response";
9
9
  import { SessionId, Did, SessionStatus, AuthInput, HandshakeResult } from "../types";
10
10
  import { KycPollOptions, KycStatus } from "../types/kyc";
11
11
  import { OtpChannel, OtpRequestInput, OtpRequestResult, OtpVerifyInput, OtpVerifyResult, SubmitUserInputArgs, SubmitUserInputResult } from "../types/user";
12
+ import { GetUsageOptions, UsagePage } from "../types/token";
12
13
  /**
13
14
  * Main T3n SDK Client
14
15
  */
@@ -101,6 +102,22 @@ export declare class T3nClient {
101
102
  * optionally validates it with a schema.
102
103
  */
103
104
  execute(payload: unknown): Promise<string>;
105
+ /**
106
+ * Fetch the caller's usage feed — balance plus a bounded slice of
107
+ * recent token-ledger entries (charges, mints, transfers), newest
108
+ * first. T3-TS-030 Phase 1D step A (`token.get-usage`).
109
+ *
110
+ * `limit` defaults to 50 server-side and clamps silently to
111
+ * `1..=200`. `afterSeq` is the inclusive upper-bound `seq_no`
112
+ * passed back from a previous page's `next_cursor` to walk older
113
+ * entries. `kinds` filters by `TokenTxKind`; an empty array is
114
+ * treated as "no filter".
115
+ *
116
+ * Unlike {@link execute}, the body of this RPC is plaintext —
117
+ * `token.get-usage` is a session-authed read endpoint and the
118
+ * server-side handler does not run the encryption layer.
119
+ */
120
+ getUsage(opts?: GetUsageOptions): Promise<UsagePage>;
104
121
  /**
105
122
  * Execute an action with an attached binary blob using multipart RPC.
106
123
  */
@@ -353,6 +370,12 @@ export declare class T3nClient {
353
370
  organisationDid?: string;
354
371
  attestations?: unknown;
355
372
  keys?: Record<string, unknown>;
373
+ /**
374
+ * MAT-1618 testnet self-admit, propagated to the inner
375
+ * {@link submitUserInput} call. Inspect `result.tenantAdmit` for
376
+ * the outcome.
377
+ */
378
+ becomeDevTenant?: boolean;
356
379
  getOtpCode: (contact: string, channel: OtpChannel) => Promise<string> | string;
357
380
  }): Promise<SubmitUserInputResult>;
358
381
  /**
@@ -445,6 +468,41 @@ export declare class T3nClient {
445
468
  * Send an RPC request with automatic encryption/decryption
446
469
  */
447
470
  private sendRpcRequest;
471
+ /**
472
+ * Send a session-authenticated JSON-RPC request with plaintext
473
+ * params/result (no SessionEncryption layer). Used for tenant-facing
474
+ * read endpoints (`token.get-balance`, `token.get-usage`) where the
475
+ * server-side handler reads the JSON envelope directly.
476
+ *
477
+ * Auto-attaches the `Session-Id` header; surfaces JSON-RPC errors
478
+ * the same way as {@link sendRpcRequest} (typed
479
+ * `InsufficientCreditError` when applicable, `RpcError` otherwise).
480
+ * Returns the parsed `result` field — typically a JSON object the
481
+ * caller will narrow to the endpoint's response type.
482
+ */
483
+ private sendUnencryptedSessionRpc;
484
+ /**
485
+ * Inspect a JSON-RPC response for an `error` field and throw the
486
+ * appropriate typed exception. No-op when `response.error` is
487
+ * absent. Shared by every RPC path so wire-shape changes in the
488
+ * server's error envelope (typed-error subclasses, request-id
489
+ * placement, detail formatting) only need to land in one place.
490
+ *
491
+ * JSON-RPC `error.message` is the generic category string
492
+ * ("Invalid params", "Internal error", …). The node attaches the
493
+ * actionable text and per-request correlation id in `error.data`
494
+ * — see `node/api/src/responses/rpc.rs::from_service_error`.
495
+ * Extract both so callers (and toasts that only render `.message`)
496
+ * get the real reason plus an id an operator can grep in
497
+ * `api::error` logs.
498
+ *
499
+ * T3-TS-030 chargepoint denials surface as a typed
500
+ * `InsufficientCreditError` so frontends can branch on
501
+ * `instanceof` to render an "out of credit" UI without
502
+ * prefix-matching the message themselves. Wire format is pinned
503
+ * by `api/src/error.rs::service_insufficient_credit_wire_format_is_stable`.
504
+ */
505
+ private throwIfRpcError;
448
506
  private sendMultipartRpcRequest;
449
507
  /**
450
508
  * Capture the server-minted `Session-Id` from the last handshake
@@ -38,7 +38,9 @@ export interface Transport {
38
38
  */
39
39
  send(request: JsonRpcRequest, headers: Record<string, string>): Promise<JsonRpcResponse>;
40
40
  /**
41
- * Optionally send a JSON-RPC request with an attached binary blob.
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).
42
44
  */
43
45
  sendMultipart?(request: JsonRpcRequest, headers: Record<string, string>, blob: Blob): Promise<JsonRpcResponse>;
44
46
  /**
@@ -18,7 +18,7 @@ export type { SessionId, Did, OidcCredentials, AuthInput, EthAuthInput, OidcAuth
18
18
  export { SessionStatus, AuthMethod, createEthAuthInput, createOidcAuthInput, } from "./types";
19
19
  export type { KycStatus, KycStatusKind, KycPollOptions, KycPollCadence, } from "./types/kyc";
20
20
  export { DEFAULT_KYC_POLL_CADENCE, TERMINAL_KYC_STATUSES, KycStatusTimeoutError, } from "./types/kyc";
21
- export type { OtpChannel, OtpRequestInput, OtpRequestResult, OtpVerifyInput, OtpVerifyResult, OtpMergeSuggestion, UserInputProfile, SubmitUserInputArgs, SubmitUserInputResult, UserUpsertErrorKind, } from "./types/user";
21
+ export type { OtpChannel, OtpRequestInput, OtpRequestResult, OtpVerifyInput, OtpVerifyResult, OtpMergeSuggestion, UserInputProfile, SubmitUserInputArgs, SubmitUserInputResult, TenantAdmitProjection, TenantAdmitStatus, UserUpsertErrorKind, } from "./types/user";
22
22
  export { UserUpsertError } from "./types/user";
23
23
  export { OrgDataClient, SessionOrgDataClient, createOrgDataClientFromSession, } from "./client/org-data";
24
24
  export type { OrgDataClientOptions, CreatePolicyInput, UpdateMetaInput, SetWritersInput, SetGrantsInput, DeleteGrantsInput, WriteDataInput, DeleteDataInput, DeleteScopeInput, PolicyGetInput, WritersGetInput, GrantsGetInput, DataListInput, DataGetInput, ExecuteOrgDataActionOptions, } from "./client/org-data";
@@ -42,3 +42,4 @@ export * from "./session";
42
42
  export * from "./auth";
43
43
  export * from "./user";
44
44
  export * from "./org-data";
45
+ export * from "./token";
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Token-metering types — T3-TS-030 wire shapes.
3
+ *
4
+ * Mirrors `node/primitives/src/token.rs`. `u128` fields land on the
5
+ * wire as JSON numbers (same convention as the existing
6
+ * `token.get-balance` response) — JS clients with histories that
7
+ * exceed 2⁵³ tokens should switch to a streaming JSON parser; the
8
+ * common case fits in `number` losslessly.
9
+ */
10
+ /**
11
+ * `primitives::token::BalanceRow` — caller's credit row.
12
+ *
13
+ * `available` and `reserved` are `u128` on the server; the wire
14
+ * format is a JSON number. Callers should not assume bigint until
15
+ * the SDK migrates to a streaming parser (tracked separately).
16
+ */
17
+ export interface BalanceRow {
18
+ available: number;
19
+ reserved: number;
20
+ last_settled_seq_no: number;
21
+ version: number;
22
+ credit_exhausted: boolean;
23
+ }
24
+ /**
25
+ * `primitives::token::TokenTxKind` snake_case wire alphabet. Every
26
+ * value listed here is accepted by the server-side
27
+ * `token.get-usage` `kinds` filter.
28
+ */
29
+ export type TokenTxKind = "mint" | "burn" | "charge" | "transfer" | "bridge_mint_attest" | "bridge_burn_attest";
30
+ /**
31
+ * Per-caller view direction. `"in"` = caller's balance went up;
32
+ * `"out"` = caller's balance went down. Derived host-side from
33
+ * `(caller_did, tx.src, tx.dst)`; the same `seq_no` appears in both
34
+ * parties' usage feeds with opposite directions on a transfer.
35
+ */
36
+ export type Direction = "in" | "out";
37
+ /**
38
+ * Discriminated union mirroring `primitives::token::ChargeReason`.
39
+ * Present only when the row's `kind === "charge"`.
40
+ */
41
+ export type ChargeReason = {
42
+ kind: "contract_register";
43
+ script_name: string;
44
+ version: string;
45
+ } | {
46
+ kind: "invocation";
47
+ script_name: string;
48
+ function: string;
49
+ fuel_consumed: number;
50
+ fuel_tokens: number;
51
+ host_call_count: number;
52
+ host_call_tokens: number;
53
+ } | {
54
+ kind: "kv_bytes";
55
+ map: string;
56
+ } | {
57
+ kind: "cas_bytes";
58
+ backend: string;
59
+ } | {
60
+ kind: "outbox_egress";
61
+ upstream: string;
62
+ };
63
+ /**
64
+ * One row in the caller's usage feed — a per-caller projection of
65
+ * the underlying `TokenTx`. `counterparty` is the other party from
66
+ * the caller's perspective: `tx.dst` on outbound entries, `tx.src`
67
+ * on inbound. `null` when the underlying tx has no counterparty
68
+ * (mints have no `src`; burns and charges have no `dst`).
69
+ */
70
+ export interface UsageEntry {
71
+ seq_no: number;
72
+ kind: TokenTxKind;
73
+ amount: number;
74
+ timestamp_ms: number;
75
+ direction: Direction;
76
+ counterparty?: string | null;
77
+ reason?: ChargeReason | null;
78
+ note?: string | null;
79
+ }
80
+ /**
81
+ * Response shape of `token.get-usage` — caller's balance plus a
82
+ * paginated slice of their most-recent `token:tx_log` entries.
83
+ *
84
+ * `next_cursor` is the inclusive upper-bound `seq_no` to pass back
85
+ * as `after_seq` to fetch the next page. `null` (or absent) means
86
+ * the caller's history is fully drained.
87
+ */
88
+ export interface UsagePage {
89
+ balance: BalanceRow;
90
+ entries: UsageEntry[];
91
+ next_cursor?: number | null;
92
+ }
93
+ /**
94
+ * Request shape — all fields optional. `limit` clamps to
95
+ * `1..=200` server-side; out-of-range values snap silently to the
96
+ * bounds. `kinds: []` is treated as "no filter".
97
+ */
98
+ export interface GetUsageOptions {
99
+ limit?: number;
100
+ after_seq?: number;
101
+ kinds?: TokenTxKind[];
102
+ }
@@ -138,6 +138,41 @@ export interface SubmitUserInputArgs {
138
138
  * Most callers should leave this `undefined`.
139
139
  */
140
140
  requireExistingUser?: boolean;
141
+ /**
142
+ * MAT-1618 testnet self-admit. When `true`, the contract additionally
143
+ * runs the self-admit side-effect after the profile commit: writes
144
+ * the caller's DID into `idx:_tenants` + `idx:_tenant_quotas` and
145
+ * mints any operator-configured welcome credits. Inspect the
146
+ * `tenantAdmit` field on the response for the outcome.
147
+ *
148
+ * Testnet-only. Production clusters return
149
+ * `tenantAdmit.status = "refused"` with
150
+ * `reason = "not_testnet" | "self_admit_disabled" | "not_eth_derived"`.
151
+ * The profile commit always succeeds regardless of the inner outcome.
152
+ *
153
+ * Most callers should leave this `undefined`.
154
+ */
155
+ becomeDevTenant?: boolean;
156
+ }
157
+ /**
158
+ * MAT-1618 self-admit projection from `user-upsert`. Present only
159
+ * when the caller set `becomeDevTenant: true` on the request.
160
+ *
161
+ * - `status: "admitted"` — first call for this DID; tenant record
162
+ * committed and (if configured) welcome credits minted into
163
+ * `grantedCredits`.
164
+ * - `status: "already-admitted"` — replay; idempotent no-op.
165
+ * - `status: "refused"` — the cluster declined the self-admit;
166
+ * `reason` is the machine-readable cause (`not_testnet`,
167
+ * `self_admit_disabled`, `not_eth_derived`), `detail` is a
168
+ * human-readable string.
169
+ */
170
+ export type TenantAdmitStatus = "admitted" | "already-admitted" | "refused";
171
+ export interface TenantAdmitProjection {
172
+ status: TenantAdmitStatus;
173
+ grantedCredits?: string;
174
+ reason?: string;
175
+ detail?: string;
141
176
  }
142
177
  /**
143
178
  * Response shape returned by the slim `user-upsert`. Carries the
@@ -150,6 +185,11 @@ export interface SubmitUserInputResult {
150
185
  refusedFields?: string[];
151
186
  mergeSuggestion?: OtpMergeSuggestion;
152
187
  userFound?: boolean;
188
+ /**
189
+ * MAT-1618 self-admit projection. Present only when the caller
190
+ * set `becomeDevTenant: true` on the request.
191
+ */
192
+ tenantAdmit?: TenantAdmitProjection;
153
193
  }
154
194
  /**
155
195
  * Discriminator for {@link UserUpsertError}. Mirrors the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@terminal3/t3n-sdk",
3
- "version": "2.10.1",
3
+ "version": "2.12.0",
4
4
  "type": "module",
5
5
  "description": "T3n TypeScript SDK - A minimal SDK that mirrors the server's RPC handler approach",
6
6
  "main": "dist/index.js",
@@ -41,6 +41,11 @@
41
41
  "demo": "pnpm build && tsx demo.ts",
42
42
  "demo:dev": "tsx demo.ts",
43
43
  "demo:real-wasm": "tsx demo.ts",
44
+ "demo:tenant:admit": "tsx --tsconfig tsconfig.demo.json tenant-demo.ts --cmd admit",
45
+ "demo:tenant:setup": "tsx --tsconfig tsconfig.demo.json tenant-demo.ts --cmd setup",
46
+ "demo:tenant:search": "tsx --tsconfig tsconfig.demo.json tenant-demo.ts --cmd search",
47
+ "demo:tenant:book": "tsx --tsconfig tsconfig.demo.json tenant-demo.ts --cmd book",
48
+ "demo:tenant:self-admit": "tsx --tsconfig tsconfig.demo.json demo-tenant-self-admit.ts",
44
49
  "verify:pack": "node scripts/verify-pack.js",
45
50
  "prepublishOnly": "pnpm run build:public && pnpm run verify:pack",
46
51
  "release": "node scripts/release.js release",