@terminal3/t3n-sdk 1.3.1 → 2.1.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.
- package/README.md +204 -0
- package/dist/index.d.ts +421 -82
- package/dist/index.esm.js +1 -1
- package/dist/index.js +1 -1
- package/dist/src/client/t3n-client.d.ts +145 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/types/index.d.ts +1 -0
- package/dist/src/types/user.d.ts +194 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -86,6 +86,88 @@ const oidcCredentials = {
|
|
|
86
86
|
const did = await client.authenticate(createOidcAuthInput(oidcCredentials));
|
|
87
87
|
```
|
|
88
88
|
|
|
89
|
+
## Migrating from 1.x
|
|
90
|
+
|
|
91
|
+
`@terminal3/t3n-sdk@2.0.0` cuts over to `tee:user/contracts@2.0.0`
|
|
92
|
+
(MAT-1374). The implicit-dispatch monolith `user-upsert` on the user
|
|
93
|
+
contract was split into three explicit functions on the same
|
|
94
|
+
contract:
|
|
95
|
+
|
|
96
|
+
- `otp-request` — request + dispatch an OTP code.
|
|
97
|
+
- `otp-verify` — redeem an OTP and bind the contact.
|
|
98
|
+
- `user-upsert` (slim) — Level 1 user-input ingest only. Rejects
|
|
99
|
+
callers without a verified email with the typed
|
|
100
|
+
`UserUpsertError { kind: "EmailNotVerified" }` (wire form
|
|
101
|
+
`email_not_verified:<detail>`).
|
|
102
|
+
|
|
103
|
+
If you used the typed `T3nClient` methods to wrap `executeAction`,
|
|
104
|
+
the SDK now ships `client.otpRequest` / `client.otpVerify` /
|
|
105
|
+
`client.submitUserInput` (plus a convenience
|
|
106
|
+
`client.runOtpThenUserInput`) so you can migrate one call site at a
|
|
107
|
+
time:
|
|
108
|
+
|
|
109
|
+
| Pre-2.0.0 (`tee:user@1.5.0`) | 2.x (`tee:user@2.0.0`, contract ≥ 2.1.0 for discriminated OTP) |
|
|
110
|
+
| ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
|
|
111
|
+
| `executeAction({ function_name: "user-upsert", input: { profile: { email_address } } })` | `client.otpRequest({ emailChannel: { emailAddress } })` |
|
|
112
|
+
| `executeAction({ function_name: "user-upsert", input: { profile, otp_code } })` | `client.otpVerify({ otpCode, request: { emailChannel: { emailAddress } } })` |
|
|
113
|
+
| `executeAction({ function_name: "user-upsert", input: { profile, keys: { generic_api: { otp_channel: "sms" } } } })` | `client.otpRequest({ smsChannel: { phoneNumber } })` |
|
|
114
|
+
| `executeAction({ function_name: "user-upsert", input: { profile } })` (post-OTP) | `client.submitUserInput({ profile })` |
|
|
115
|
+
|
|
116
|
+
### Worked example: full email + L1 ingest
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
import { T3nClient, UserUpsertError } from "@terminal3/t3n-sdk";
|
|
120
|
+
|
|
121
|
+
// 1) Bind the user's email via OTP.
|
|
122
|
+
const requested = await client.otpRequest({
|
|
123
|
+
emailChannel: { emailAddress: "alice@example.com" },
|
|
124
|
+
});
|
|
125
|
+
const code = await prompt(`Code sent to ${requested.contact}: `);
|
|
126
|
+
await client.otpVerify({
|
|
127
|
+
otpCode: code,
|
|
128
|
+
request: { emailChannel: { emailAddress: "alice@example.com" } },
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// 2) Slim user-upsert: Level 1 user-input ingest. Rejects with
|
|
132
|
+
// UserUpsertError(kind: "EmailNotVerified") if step 1 was skipped.
|
|
133
|
+
try {
|
|
134
|
+
const result = await client.submitUserInput({
|
|
135
|
+
profile: {
|
|
136
|
+
first_name: "Alice",
|
|
137
|
+
last_name: "Smith",
|
|
138
|
+
country_of_residence: "US",
|
|
139
|
+
// ...other Level-1 fields
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
console.log("tx:", result.txHash);
|
|
143
|
+
} catch (err) {
|
|
144
|
+
if (err instanceof UserUpsertError && err.kind === "EmailNotVerified") {
|
|
145
|
+
// run otp-request + otp-verify, then retry.
|
|
146
|
+
}
|
|
147
|
+
throw err;
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
For tests that "just want it to work", `runOtpThenUserInput` chains
|
|
152
|
+
the three calls behind a single `getOtpCode` callback.
|
|
153
|
+
|
|
154
|
+
### Hard-error fields
|
|
155
|
+
|
|
156
|
+
Passing any of these to the wrong function returns the typed
|
|
157
|
+
`UserUpsertError(kind: "LegacyField")` (wire form
|
|
158
|
+
`legacy_field:<detail>`):
|
|
159
|
+
|
|
160
|
+
- `otp_code` to anything other than `otp-verify`.
|
|
161
|
+
- `keys.generic_api.otp_channel` to anything (channel is now a
|
|
162
|
+
top-level field).
|
|
163
|
+
|
|
164
|
+
### Raw `executeAction` callers
|
|
165
|
+
|
|
166
|
+
If you bypass the typed wrappers, see the migration table above:
|
|
167
|
+
the function names (`otp-request` / `otp-verify` / `user-upsert`)
|
|
168
|
+
and JSON input shapes line up 1:1 with the wrappers' camelCase
|
|
169
|
+
fields converted to `snake_case`.
|
|
170
|
+
|
|
89
171
|
## Architecture
|
|
90
172
|
|
|
91
173
|
The T3n SDK follows the same architectural principles as the server's `rpc.rs`:
|
|
@@ -560,6 +642,128 @@ When upserting a profile with an email address (either for a new profile or when
|
|
|
560
642
|
3. Guide you through the Google OAuth flow
|
|
561
643
|
4. Allow you to paste the ID token back into the terminal
|
|
562
644
|
|
|
645
|
+
#### KYC Walkthrough (MAT-1371)
|
|
646
|
+
|
|
647
|
+
The `--kyc-walkthrough` mode drives the full MetaMask KYC L1 + L2 flow against a running Trinity node, end-to-end, without needing the actual MetaMask front-end. It is useful for:
|
|
648
|
+
|
|
649
|
+
- Smoke-testing a freshly-built node (`node/bin/start-local`) before pointing the real FE at it.
|
|
650
|
+
- Reproducing edge cases (rejected KYC, OTP expiry, merge suggestions) deterministically.
|
|
651
|
+
- Showing internal stakeholders what the L1 + L2 flow looks like without spinning up MetaMask Snap + Veriff sandbox.
|
|
652
|
+
|
|
653
|
+
**Quick start (interactive):**
|
|
654
|
+
|
|
655
|
+
```bash
|
|
656
|
+
pnpm demo:real-wasm --kyc-walkthrough
|
|
657
|
+
```
|
|
658
|
+
|
|
659
|
+
The walkthrough then prompts at each step in the order Trinity expects:
|
|
660
|
+
|
|
661
|
+
1. **Handshake** + **authenticate** (existing prompts — wallet / OIDC).
|
|
662
|
+
2. **Email OTP** — request + verify on `tee:user/contracts::user-upsert` (email channel).
|
|
663
|
+
3. **Phone OTP** — request + verify on the same function (SMS channel via `keys.generic_api.otp_channel: "sms"` shadow). Gated by the contract on a verified email.
|
|
664
|
+
4. **Level 1 user-input** — single `user-upsert` call carrying `first_name`, `last_name`, `country_of_residence`, `document_issuance_country`, `ssn`, `address`.
|
|
665
|
+
5. **`create-kyc-provider-session`** ([MAT-1284](https://linear.app/terminal3/issue/MAT-1284)) — prints the Veriff `session_url`. The CLI does **not** auto-open the URL.
|
|
666
|
+
6. **`kyc-status` poll** — until terminal status (`verified`, `rejected`, or `orphan`) or the timeout fires. Each snapshot is logged.
|
|
667
|
+
|
|
668
|
+
**Veriff is driven manually.** The CLI prints the `session_url` and waits — open it in your browser, drive the Veriff flow to the desired terminal state (approve / reject / abandon), then come back to the terminal. The poll loop will see the contract update and surface the terminal status.
|
|
669
|
+
|
|
670
|
+
##### KYC walkthrough flags
|
|
671
|
+
|
|
672
|
+
| Flag | Default | Meaning |
|
|
673
|
+
|---|---|---|
|
|
674
|
+
| `--kyc-walkthrough` | off | Enable the walkthrough mode. Implicitly enabled by `--scenario`. |
|
|
675
|
+
| `--scenario <name>` | none | One of `happy-path-l1`, `happy-path-l2`, `rejected`. See *Scenarios* below. |
|
|
676
|
+
| `--auth-method <eth\|metamask\|oidc>` | interactive prompt | Skip the auth-method prompt. |
|
|
677
|
+
| `--email <addr>` | interactive prompt (or `demo+kyc@example.com` in scenario mode) | Email to verify in the L1 OTP step. |
|
|
678
|
+
| `--phone <e164>` | interactive prompt (or `+14155552671` in scenario mode) | Phone in E.164 format for the SMS OTP step. |
|
|
679
|
+
| `--first-name <name>` | interactive prompt (or `Ada` in scenario mode) | L1 first name. |
|
|
680
|
+
| `--last-name <name>` | interactive prompt (or `Lovelace` in scenario mode) | L1 last name. |
|
|
681
|
+
| `--country <ISO-2>` | `US` | Convenience: applies to both `country_of_residence` and `document_issuance_country` unless overridden individually. |
|
|
682
|
+
| `--country-of-residence <ISO-2>` | `--country` value | L1 residence country. |
|
|
683
|
+
| `--document-issuance-country <ISO-2>` | `--country` value | L1 document issuance country. |
|
|
684
|
+
| `--ssn <value>` | interactive prompt (or `123-45-6789` in scenario mode) | L1 SSN (9 digits or `XXX-XX-XXXX`). |
|
|
685
|
+
| `--address <text>` | interactive prompt (or default address in scenario mode) | L1 residential address. |
|
|
686
|
+
| `--kyc-provider-id <id>` | `veriff` | Provider id passed to `create-kyc-provider-session` and `kyc-status`. |
|
|
687
|
+
| `--kyc-poll-fast-ms <ms>` | `2000` | Fast-cadence poll interval (T3-TS-026 §8.4). |
|
|
688
|
+
| `--kyc-poll-slow-ms <ms>` | `5000` | Slow-cadence poll interval. |
|
|
689
|
+
| `--kyc-poll-switch-at-ms <ms>` | `30000` | Elapsed threshold to switch from fast to slow. |
|
|
690
|
+
| `--kyc-poll-timeout-ms <ms>` | `300000` | Total cap before `KycStatusTimeoutError` is raised. |
|
|
691
|
+
| `--skip-create-kyc-session` | off | Stop after L1 — useful when no Veriff sandbox is configured. |
|
|
692
|
+
|
|
693
|
+
##### Scenarios
|
|
694
|
+
|
|
695
|
+
Pre-baked shortcuts that auto-feed answers (mock-mode OTP codes, default L1 fields) so the walkthrough runs without operator input. All scenarios still require a running Trinity node configured with the **mock OTP provider** (otherwise the deterministic OTP code algorithm doesn't match what the node actually generated).
|
|
696
|
+
|
|
697
|
+
| Scenario | What it does | Veriff |
|
|
698
|
+
|---|---|---|
|
|
699
|
+
| `happy-path-l1` | Email OTP → phone OTP → L1 upsert. Stops before `create-kyc-provider-session`. | Not exercised. |
|
|
700
|
+
| `happy-path-l2` | Full L1 + L2 flow. Asserts `kyc-status` terminates with `verified`. | **Manual**: open the printed `session_url` and drive Veriff to approval. |
|
|
701
|
+
| `rejected` | Full L1 + L2 flow. Asserts `kyc-status` terminates with `rejected`. | **Manual**: open the printed `session_url` and drive Veriff to a rejection. |
|
|
702
|
+
|
|
703
|
+
Examples:
|
|
704
|
+
|
|
705
|
+
```bash
|
|
706
|
+
# L1-only happy path — no Veriff sandbox needed
|
|
707
|
+
pnpm demo:real-wasm --scenario happy-path-l1
|
|
708
|
+
|
|
709
|
+
# Full L1 + L2 happy path — drive Veriff to approved manually
|
|
710
|
+
pnpm demo:real-wasm --scenario happy-path-l2
|
|
711
|
+
|
|
712
|
+
# Rejection scenario — drive Veriff to a rejection manually
|
|
713
|
+
pnpm demo:real-wasm --scenario rejected
|
|
714
|
+
|
|
715
|
+
# Custom inputs without a scenario
|
|
716
|
+
pnpm demo:real-wasm --kyc-walkthrough \
|
|
717
|
+
--auth-method eth \
|
|
718
|
+
--email me@example.com \
|
|
719
|
+
--phone +14155551234 \
|
|
720
|
+
--country US \
|
|
721
|
+
--first-name Ada --last-name Lovelace
|
|
722
|
+
```
|
|
723
|
+
|
|
724
|
+
##### Mock-mode OTP code
|
|
725
|
+
|
|
726
|
+
When the Trinity node runs against the mock OTP provider (the default for `node/bin/start-local`), the OTP code is a **deterministic** 6-digit hash of the contact value. The walkthrough always prints the calculated mock code in the OTP-pending log line, so even without a scenario you can simply enter that code at the prompt to advance.
|
|
727
|
+
|
|
728
|
+
The algorithm matches `node/wasm/src/otp.rs` byte-for-byte: `String((hash * 31 + byte_i) % 1_000_000).padStart(6, "0")` over the UTF-8 bytes of the contact (email address or E.164 phone).
|
|
729
|
+
|
|
730
|
+
##### Sample output (excerpt)
|
|
731
|
+
|
|
732
|
+
```text
|
|
733
|
+
🚦 KYC walkthrough mode enabled (MAT-1371)
|
|
734
|
+
Scenario: happy-path-l1
|
|
735
|
+
|
|
736
|
+
📧 Step 5/8 — Email OTP: demo+kyc@example.com
|
|
737
|
+
5️⃣ Executing action: user-upsert...
|
|
738
|
+
✅ Action executed successfully
|
|
739
|
+
📨 Email verification required
|
|
740
|
+
📬 OTP code sent to: demo+kyc@example.com
|
|
741
|
+
📡 Channel: email
|
|
742
|
+
💡 Mock mode OTP code (deterministic): 482913
|
|
743
|
+
🤖 Scenario auto-feeding OTP code: 482913
|
|
744
|
+
🔄 Verifying Email OTP code...
|
|
745
|
+
5️⃣ Executing action: user-upsert...
|
|
746
|
+
✅ OTP verified successfully
|
|
747
|
+
📝 Transaction hash: tx:1:42
|
|
748
|
+
|
|
749
|
+
📱 Step 6/8 — Phone OTP: +14155552671
|
|
750
|
+
6️⃣ Executing action: user-upsert...
|
|
751
|
+
...
|
|
752
|
+
|
|
753
|
+
📋 KYC walkthrough summary
|
|
754
|
+
✅ email-otp demo+kyc@example.com
|
|
755
|
+
✅ phone-otp +14155552671
|
|
756
|
+
✅ l1-upsert tx:1:44
|
|
757
|
+
⏭️ create-kyc-session (scenario)
|
|
758
|
+
⏭️ kyc-status-poll (scenario)
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
##### Caveats
|
|
762
|
+
|
|
763
|
+
- **No version bump.** The walkthrough is dev tooling — it does not introduce new `@terminal3/t3n-sdk` public-API surface, so `package.json` is not bumped.
|
|
764
|
+
- **Spec alignment.** The walkthrough follows the as-built code, which differs from earlier T3-TS-026 versions on two points: `user-upsert` has no `op:` discriminator (dispatch is implicit on input shape), and the function for L2 session creation is `tee:user/contracts::create-kyc-provider-session` rather than `tee:kyc/contracts::create-session`. See [T3-TS-026 v0.6.0 changelog](../../docs/specs/T3-TS-026-kyc-frontend-integration.md) and [MAT-1374](https://linear.app/terminal3/issue/MAT-1374) for the deferred discriminator-vs-split decision.
|
|
765
|
+
- **`STRICT_SCENARIO=true`** in the environment turns scenario assertion failures into a non-zero exit code (otherwise they're logged but the demo exits zero). `--scenario` always exits non-zero on a terminal-status mismatch.
|
|
766
|
+
|
|
563
767
|
## WASM Integration
|
|
564
768
|
|
|
565
769
|
The SDK can work with both real T3n WASM components and mock components for testing.
|