@ranwhenparked/trustap-sdk 0.1.0 → 0.2.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 (43) hide show
  1. package/README.md +139 -175
  2. package/dist/client.d.ts +24 -0
  3. package/dist/client.js +66 -0
  4. package/{src/schema.d.ts → dist/generated/types.d.ts} +971 -4157
  5. package/dist/generated/types.js +5 -0
  6. package/dist/index.d.ts +3 -0
  7. package/dist/index.js +3 -0
  8. package/dist/webhooks/index.d.ts +2 -0
  9. package/dist/webhooks/index.js +2 -0
  10. package/dist/webhooks/schemas.d.ts +1067 -0
  11. package/dist/webhooks/schemas.js +243 -0
  12. package/dist/webhooks/state.d.ts +31 -0
  13. package/dist/webhooks/state.js +150 -0
  14. package/mod.ts +1 -0
  15. package/package.json +31 -32
  16. package/deno.json +0 -9
  17. package/eslint.config.js +0 -21
  18. package/scripts/build-node.mjs +0 -75
  19. package/scripts/generate-operations-map.mjs +0 -57
  20. package/scripts/generate-security-map.mjs +0 -92
  21. package/src/__tests__/auth-middleware.test.ts +0 -171
  22. package/src/__tests__/client-factory.test.ts +0 -105
  23. package/src/__tests__/error-handling.test.ts +0 -302
  24. package/src/__tests__/helpers/mock-http-client.ts +0 -193
  25. package/src/__tests__/helpers/run-guard.ts +0 -24
  26. package/src/__tests__/helpers/test-fixtures.ts +0 -82
  27. package/src/__tests__/node-client.test.ts +0 -244
  28. package/src/__tests__/operation-proxy.test.ts +0 -268
  29. package/src/__tests__/query-params.test.ts +0 -232
  30. package/src/__tests__/setup.ts +0 -44
  31. package/src/__tests__/types.test.ts +0 -169
  32. package/src/client-deno.ts +0 -219
  33. package/src/client-factory.ts +0 -45
  34. package/src/core.ts +0 -619
  35. package/src/index.deno.ts +0 -28
  36. package/src/index.ts +0 -36
  37. package/src/operations-map.ts +0 -667
  38. package/src/security-map.ts +0 -770
  39. package/src/state-machine.ts +0 -79
  40. package/src/webhook-schemas.ts +0 -400
  41. package/tsconfig.build.json +0 -27
  42. package/tsconfig.json +0 -22
  43. package/vitest.config.ts +0 -31
package/README.md CHANGED
@@ -1,251 +1,215 @@
1
- # @ranwhenparked/trustap-sdk
1
+ # trustap-sdk
2
2
 
3
- A lightweight typed [Trustap API](https://docs.trustap.com/apis/openapi) wrapper built with openapi-typescript + openapi-fetch.
3
+ [![npm version](https://img.shields.io/npm/v/@ranwhenparked/trustap-sdk.svg)](https://www.npmjs.com/package/@ranwhenparked/trustap-sdk)
4
+ [![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://opensource.org/licenses/ISC)
5
+ [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)
4
6
 
5
- ## Installation
6
-
7
- ```bash
8
- npm install @ranwhenparked/trustap-sdk
9
- ```
7
+ Type-safe TypeScript SDK for the [Trustap API](https://trustap.com) with webhook validation.
10
8
 
11
- ## Usage
9
+ ## Overview
12
10
 
13
- ### Path-based client
11
+ Trustap provides escrow and payment protection for peer-to-peer and marketplace transactions. This SDK offers:
14
12
 
15
- ```ts
16
- import { createTrustapClient } from "@ranwhenparked/trustap-sdk";
13
+ - **Type-safe API client** built on [openapi-fetch](https://openapi-ts.dev/openapi-fetch/) with auto-generated types
14
+ - **Webhook validation** using Zod schemas with TypeScript exhaustiveness checking
15
+ - **Transaction state machine** for tracking transaction lifecycle
16
+ - **Dual runtime support** for Node.js and Deno
17
17
 
18
- const trustap = createTrustapClient({
19
- apiUrl: process.env.TRUSTAP_API_URL!,
20
- basicAuth: {
21
- username: process.env.TRUSTAP_API_KEY!,
22
- password: "", // Trustap basic auth uses API key + empty password
23
- },
24
- });
18
+ ## Installation
25
19
 
26
- const { data, error } = await trustap["/users/me/balances"].GET({
27
- headers: { Authorization: `Bearer ${accessToken}` },
28
- });
20
+ ```bash
21
+ npm install trustap-sdk
22
+ ```
29
23
 
30
- // Or verb-based via raw
31
- const { data: d2 } = await trustap.raw.GET("/users/me/balances", {
32
- headers: { Authorization: `Bearer ${accessToken}` },
33
- });
24
+ ```bash
25
+ yarn add trustap-sdk
34
26
  ```
35
27
 
36
- ### Operation ID-based client
28
+ ```bash
29
+ pnpm add trustap-sdk
30
+ ```
37
31
 
38
- Call API methods directly by their operation ID with full type safety:
32
+ ## Quick Start
39
33
 
40
- ```ts
41
- const { data, error } = await trustap["users.getBalances"]();
34
+ ```typescript
35
+ import { createTrustapClient, TRUSTAP_BASE_URLS } from "trustap-sdk";
42
36
 
43
- // With path parameters
44
- const { data: tx } = await trustap["basic_client.getTransaction"]({
45
- params: {
46
- path: { client_id: "my-client", transaction_id: "123" },
47
- },
37
+ const client = createTrustapClient({
38
+ baseUrl: TRUSTAP_BASE_URLS.production,
39
+ auth: { type: "apiKey", apiKey: "your-api-key" },
48
40
  });
49
41
 
50
- // With query parameters
51
- const { data: transactions } = await trustap["basic_client.getTransactions"]({
42
+ // Calculate transaction fees
43
+ const { data, error } = await client.GET("/charge", {
52
44
  params: {
53
- path: { client_id: "my-client" },
54
- query: { status: "paid" },
45
+ query: { price: 10000, currency: "USD" },
55
46
  },
56
47
  });
57
48
  ```
58
49
 
59
50
  ## Authentication
60
51
 
61
- The SDK supports multiple authentication strategies:
62
-
63
- ### Basic Auth (Server-to-server)
52
+ ### API Key (Server-side)
64
53
 
65
- Configure HTTP Basic auth for server-to-server endpoints:
54
+ Use API key authentication for server-to-server requests:
66
55
 
67
- ```ts
68
- const trustap = createTrustapClient({
69
- apiUrl: "https://api.trustap.com",
70
- basicAuth: {
71
- username: process.env.TRUSTAP_API_KEY!,
72
- password: "", // Trustap uses API key + empty password
73
- },
56
+ ```typescript
57
+ const client = createTrustapClient({
58
+ auth: { type: "apiKey", apiKey: process.env.TRUSTAP_API_KEY },
74
59
  });
75
60
  ```
76
61
 
77
- ### OAuth2 Access Token
62
+ ### OAuth (User-authenticated)
78
63
 
79
- For user-context endpoints, provide a `getAccessToken` callback:
64
+ Use OAuth for requests on behalf of authenticated users:
80
65
 
81
- ```ts
82
- const trustap = createTrustapClient({
83
- apiUrl: "https://api.trustap.com",
84
- basicAuth: {
85
- username: process.env.TRUSTAP_API_KEY!,
86
- password: "",
87
- },
88
- getAccessToken: async () => {
89
- // Return the current user's OAuth2 access token
90
- return session.accessToken;
91
- },
66
+ ```typescript
67
+ const client = createTrustapClient({
68
+ auth: { type: "oauth", accessToken: userAccessToken },
92
69
  });
93
70
  ```
94
71
 
95
- The SDK automatically selects the correct auth strategy per endpoint based on the OpenAPI spec. For endpoints that support both, it prefers Basic auth for server-to-server calls.
72
+ ## Client Usage
96
73
 
97
- ### Auth Overrides
74
+ ### Standard Client
98
75
 
99
- Override the automatic auth selection for specific endpoints:
76
+ ```typescript
77
+ import { createTrustapClient } from "trustap-sdk";
100
78
 
101
- ```ts
102
- const trustap = createTrustapClient({
103
- apiUrl: "https://api.trustap.com",
104
- basicAuth: { username: apiKey, password: "" },
105
- getAccessToken: async () => userAccessToken,
106
- authOverrides: {
107
- "/charge": "basic", // Always use Basic auth
108
- "/users/me/balances": "oauth2", // Always use OAuth2
109
- },
79
+ const client = createTrustapClient({
80
+ baseUrl: TRUSTAP_BASE_URLS.staging, // or .production
81
+ auth: { type: "apiKey", apiKey: "..." },
110
82
  });
83
+
84
+ // Fully typed request/response
85
+ const { data, error } = await client.GET("/me/transactions");
111
86
  ```
112
87
 
113
- ## Webhook Schemas
88
+ ### Path-based Client
114
89
 
115
- Validate incoming Trustap webhooks with Zod schemas:
90
+ ```typescript
91
+ import { createTrustapPathClient } from "trustap-sdk";
116
92
 
117
- ```ts
118
- import {
119
- trustapWebhookEventSchema,
120
- type TrustapWebhookEvent,
121
- } from "@ranwhenparked/trustap-sdk";
93
+ const client = createTrustapPathClient({ /* options */ });
122
94
 
123
- // Parse and validate webhook payload
124
- const result = trustapWebhookEventSchema.safeParse(req.body);
125
- if (!result.success) {
126
- console.error("Invalid webhook:", result.error);
127
- return;
128
- }
95
+ // Alternative syntax
96
+ const { data } = await client["/me/transactions"].GET();
97
+ ```
129
98
 
130
- const event: TrustapWebhookEvent = result.data;
131
- console.log(event.code); // e.g., "basic_tx.paid"
99
+ ### Environments
100
+
101
+ ```typescript
102
+ import { TRUSTAP_BASE_URLS } from "trustap-sdk";
103
+
104
+ TRUSTAP_BASE_URLS.staging // https://dev.stage.trustap.com/api/v1
105
+ TRUSTAP_BASE_URLS.production // https://dev.trustap.com/api/v1
106
+ ```
107
+
108
+ ## Webhook Handling
109
+
110
+ ### Parsing Events
111
+
112
+ ```typescript
113
+ import { trustapWebhookEventSchema } from "trustap-sdk";
114
+
115
+ async function handleWebhook(req: Request) {
116
+ const body = await req.json();
117
+ const result = trustapWebhookEventSchema.safeParse(body);
118
+
119
+ if (!result.success) {
120
+ // Unknown or malformed event - fails loudly, no silent fallbacks
121
+ console.error("Invalid webhook:", result.error);
122
+ return;
123
+ }
124
+
125
+ const event = result.data;
126
+ // event.code is narrowed to specific event types
127
+ }
132
128
  ```
133
129
 
134
- ### Exhaustive Handler Pattern
130
+ ### Type-safe Handlers
135
131
 
136
- Use `createWebhookHandlers` for compile-time exhaustiveness checking:
132
+ Create handlers with compile-time exhaustiveness checking:
137
133
 
138
- ```ts
139
- import {
140
- createWebhookHandlers,
141
- type TrustapWebhookEvent,
142
- } from "@ranwhenparked/trustap-sdk";
134
+ ```typescript
135
+ import { createOnlineWebhookHandlers, type TrustapWebhookEvent } from "trustap-sdk";
143
136
 
144
- const handlers = createWebhookHandlers({
137
+ const handlers = createOnlineWebhookHandlers({
145
138
  "basic_tx.joined": (event) => {
146
- console.log("Transaction joined at:", event.target_preview.joined);
139
+ console.log("Seller joined:", event.target_preview.joined);
147
140
  },
148
141
  "basic_tx.paid": (event) => {
149
- console.log("Transaction paid at:", event.target_preview.paid);
142
+ console.log("Payment received:", event.target_preview.paid);
143
+ },
144
+ "basic_tx.tracked": (event) => {
145
+ console.log("Tracking:", event.target_preview.tracking);
150
146
  },
151
- "basic_tx.rejected": (event) => { /* ... */ },
152
- "basic_tx.cancelled": (event) => { /* ... */ },
153
- "basic_tx.claimed": (event) => { /* ... */ },
154
- "basic_tx.listing_transaction_accepted": (event) => { /* ... */ },
155
- "basic_tx.listing_transaction_rejected": (event) => { /* ... */ },
156
- "basic_tx.payment_failed": (event) => { /* ... */ },
157
- "basic_tx.payment_refunded": (event) => { /* ... */ },
158
- "basic_tx.payment_review_flagged": (event) => { /* ... */ },
159
- "basic_tx.payment_review_finished": (event) => { /* ... */ },
160
- "basic_tx.tracking_details_submission_deadline_extended": (event) => { /* ... */ },
161
- "basic_tx.tracked": (event) => { /* ... */ },
162
- "basic_tx.delivered": (event) => { /* ... */ },
163
- "basic_tx.complained": (event) => { /* ... */ },
164
- "basic_tx.complaint_period_ended": (event) => { /* ... */ },
165
- "basic_tx.funds_released": (event) => { /* ... */ },
166
- "basic_tx.funds_refunded": (event) => { /* ... */ },
147
+ // TypeScript errors if any event type is missing
148
+ // ... all 18 event types must be handled
167
149
  });
168
150
 
169
- // TypeScript will error if any event code is missing from handlers
170
-
171
- function handleWebhook(event: TrustapWebhookEvent) {
172
- const handler = handlers[event.code];
173
- handler(event as never);
151
+ function processEvent(event: TrustapWebhookEvent) {
152
+ handlers[event.code](event as any);
174
153
  }
175
154
  ```
176
155
 
177
- ### Individual Event Types
178
-
179
- Import specific event types for targeted handling:
180
-
181
- ```ts
182
- import type {
183
- BasicTxPaidEvent,
184
- BasicTxFundsReleasedEvent,
185
- } from "@ranwhenparked/trustap-sdk";
156
+ ### Switch with Exhaustiveness
157
+
158
+ ```typescript
159
+ import { assertNever, type TrustapWebhookEvent } from "trustap-sdk";
160
+
161
+ function handleEvent(event: TrustapWebhookEvent) {
162
+ switch (event.code) {
163
+ case "basic_tx.joined":
164
+ return handleJoined(event);
165
+ case "basic_tx.paid":
166
+ return handlePaid(event);
167
+ // ... handle all cases
168
+ default:
169
+ assertNever(event); // Compile error if any case is missing
170
+ }
171
+ }
186
172
  ```
187
173
 
188
- ## State Machine
174
+ ### State Machine
189
175
 
190
176
  Map webhook events to transaction states:
191
177
 
192
- ```ts
193
- import {
194
- mapWebhookToTrustapState,
195
- type TrustapTransactionState,
196
- } from "@ranwhenparked/trustap-sdk";
178
+ ```typescript
179
+ import { mapWebhookToOnlineState } from "trustap-sdk";
197
180
 
198
- const state = mapWebhookToTrustapState("basic_tx.paid");
199
- // state: "paid"
200
-
201
- const unknown = mapWebhookToTrustapState("unknown.event");
202
- // unknown: null
181
+ const state = mapWebhookToOnlineState("basic_tx.paid");
182
+ // Returns: "paid"
203
183
  ```
204
184
 
205
- ### Available States
206
-
207
- ```ts
208
- type TrustapTransactionState =
209
- | "created"
210
- | "joined"
211
- | "rejected"
212
- | "paid"
213
- | "cancelled"
214
- | "cancelled_with_payment"
215
- | "payment_refunded"
216
- | "tracked"
217
- | "delivered"
218
- | "complained"
219
- | "complaint_period_ended"
220
- | "funds_released";
221
- ```
185
+ ## Subpath Imports
222
186
 
223
- ## Deno
187
+ Import only what you need:
224
188
 
225
- For Deno projects, import from the Deno-specific entry point:
189
+ ```typescript
190
+ // Full SDK
191
+ import { createTrustapClient, trustapWebhookEventSchema } from "trustap-sdk";
226
192
 
227
- ```ts
228
- import { createTrustapClient } from "@ranwhenparked/trustap-sdk/deno";
193
+ // Webhooks only (smaller bundle)
194
+ import { trustapWebhookEventSchema, createOnlineWebhookHandlers } from "trustap-sdk/webhooks";
195
+
196
+ // Types only (no runtime code)
197
+ import type { paths, components } from "trustap-sdk/types";
229
198
  ```
230
199
 
231
- ## Development
200
+ ## Deno
232
201
 
233
- ### Generate types from OpenAPI spec
202
+ ```typescript
203
+ import { createTrustapClient } from "./mod.ts";
234
204
 
235
- ```bash
236
- npm run generate
205
+ // Or from npm
206
+ import { createTrustapClient } from "npm:trustap-sdk";
237
207
  ```
238
208
 
239
- This pulls the latest OpenAPI schema from `https://docs.trustap.com/apis/trustap-openapi.yaml` and generates:
209
+ ## API Reference
240
210
 
241
- - `src/schema.d.ts` - TypeScript types for all API paths and operations
242
- - `src/operations-map.ts` - Mapping of operation IDs to paths/methods
243
- - `src/security-map.ts` - Security requirements per endpoint
211
+ This SDK's types are auto-generated from the [Trustap OpenAPI specification](https://docs.trustap.com). For endpoint documentation, see the [Trustap API docs](https://docs.trustap.com).
244
212
 
245
- Individual generation scripts:
213
+ ## License
246
214
 
247
- ```bash
248
- npm run generate:types # Generate schema.d.ts
249
- npm run generate:ops # Generate operations-map.ts
250
- npm run generate:security # Generate security-map.ts
251
- ```
215
+ ISC
@@ -0,0 +1,24 @@
1
+ import { type Client, type ClientOptions, type PathBasedClient } from "openapi-fetch";
2
+ import type { paths } from "./generated/types.ts";
3
+ export declare const TRUSTAP_BASE_URLS: {
4
+ readonly staging: "https://dev.stage.trustap.com/api/v1";
5
+ readonly production: "https://dev.trustap.com/api/v1";
6
+ };
7
+ export type TrustapClient = Client<paths>;
8
+ export type TrustapPathClient = PathBasedClient<paths>;
9
+ export type TrustapAuth = {
10
+ type: "apiKey";
11
+ apiKey: string;
12
+ } | {
13
+ type: "oauth";
14
+ accessToken: string;
15
+ };
16
+ export type TrustapClientOptions = Omit<ClientOptions, "baseUrl" | "headers"> & {
17
+ baseUrl?: string;
18
+ headers?: ClientOptions["headers"];
19
+ auth?: TrustapAuth;
20
+ };
21
+ export declare function createApiKeyAuthHeader(apiKey: string): string;
22
+ export declare function createBearerAuthHeader(accessToken: string): string;
23
+ export declare function createTrustapClient(options?: TrustapClientOptions): TrustapClient;
24
+ export declare function createTrustapPathClient(options?: TrustapClientOptions): TrustapPathClient;
package/dist/client.js ADDED
@@ -0,0 +1,66 @@
1
+ import createClient, { createPathBasedClient, mergeHeaders, } from "openapi-fetch";
2
+ export const TRUSTAP_BASE_URLS = {
3
+ staging: "https://dev.stage.trustap.com/api/v1",
4
+ production: "https://dev.trustap.com/api/v1",
5
+ };
6
+ export function createApiKeyAuthHeader(apiKey) {
7
+ return `Basic ${encodeBase64(`${apiKey}:`)}`;
8
+ }
9
+ export function createBearerAuthHeader(accessToken) {
10
+ return `Bearer ${accessToken}`;
11
+ }
12
+ export function createTrustapClient(options = {}) {
13
+ const { baseUrl = TRUSTAP_BASE_URLS.staging, headers, auth, ...rest } = options;
14
+ const authHeader = resolveAuthHeader(auth);
15
+ const clientOptions = { baseUrl, ...rest };
16
+ if (authHeader) {
17
+ clientOptions.headers = withAuthHeader(headers, authHeader);
18
+ }
19
+ else if (headers) {
20
+ clientOptions.headers = headers;
21
+ }
22
+ return createClient(clientOptions);
23
+ }
24
+ export function createTrustapPathClient(options = {}) {
25
+ const { baseUrl = TRUSTAP_BASE_URLS.staging, headers, auth, ...rest } = options;
26
+ const authHeader = resolveAuthHeader(auth);
27
+ const clientOptions = { baseUrl, ...rest };
28
+ if (authHeader) {
29
+ clientOptions.headers = withAuthHeader(headers, authHeader);
30
+ }
31
+ else if (headers) {
32
+ clientOptions.headers = headers;
33
+ }
34
+ return createPathBasedClient(clientOptions);
35
+ }
36
+ function resolveAuthHeader(auth) {
37
+ if (!auth)
38
+ return undefined;
39
+ return auth.type === "apiKey"
40
+ ? createApiKeyAuthHeader(auth.apiKey)
41
+ : createBearerAuthHeader(auth.accessToken);
42
+ }
43
+ function withAuthHeader(headers, value) {
44
+ return mergeHeaders(headers, { Authorization: value });
45
+ }
46
+ const BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
47
+ function encodeBase64(input) {
48
+ if (typeof globalThis.btoa === "function") {
49
+ return globalThis.btoa(input);
50
+ }
51
+ const bytes = new TextEncoder().encode(input);
52
+ let output = "";
53
+ for (let i = 0; i < bytes.length; i += 3) {
54
+ const b1 = bytes[i] ?? 0;
55
+ const b2 = bytes[i + 1] ?? 0;
56
+ const b3 = bytes[i + 2] ?? 0;
57
+ const hasB2 = i + 1 < bytes.length;
58
+ const hasB3 = i + 2 < bytes.length;
59
+ const triplet = (b1 << 16) | (b2 << 8) | b3;
60
+ output += BASE64_ALPHABET[(triplet >> 18) & 0x3f];
61
+ output += BASE64_ALPHABET[(triplet >> 12) & 0x3f];
62
+ output += hasB2 ? BASE64_ALPHABET[(triplet >> 6) & 0x3f] : "=";
63
+ output += hasB3 ? BASE64_ALPHABET[triplet & 0x3f] : "=";
64
+ }
65
+ return output;
66
+ }