@ranwhenparked/trustap-sdk 0.1.0 → 0.3.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 +139 -175
- package/dist/client.d.ts +24 -0
- package/dist/client.js +66 -0
- package/{src/schema.d.ts → dist/generated/types.d.ts} +2929 -6162
- package/dist/generated/types.js +5 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/webhooks/index.d.ts +2 -0
- package/dist/webhooks/index.js +2 -0
- package/dist/webhooks/schemas.d.ts +1067 -0
- package/dist/webhooks/schemas.js +243 -0
- package/dist/webhooks/state.d.ts +31 -0
- package/dist/webhooks/state.js +150 -0
- package/mod.ts +1 -0
- package/package.json +31 -32
- package/deno.json +0 -9
- package/eslint.config.js +0 -21
- package/scripts/build-node.mjs +0 -75
- package/scripts/generate-operations-map.mjs +0 -57
- package/scripts/generate-security-map.mjs +0 -92
- package/src/__tests__/auth-middleware.test.ts +0 -171
- package/src/__tests__/client-factory.test.ts +0 -105
- package/src/__tests__/error-handling.test.ts +0 -302
- package/src/__tests__/helpers/mock-http-client.ts +0 -193
- package/src/__tests__/helpers/run-guard.ts +0 -24
- package/src/__tests__/helpers/test-fixtures.ts +0 -82
- package/src/__tests__/node-client.test.ts +0 -244
- package/src/__tests__/operation-proxy.test.ts +0 -268
- package/src/__tests__/query-params.test.ts +0 -232
- package/src/__tests__/setup.ts +0 -44
- package/src/__tests__/types.test.ts +0 -169
- package/src/client-deno.ts +0 -219
- package/src/client-factory.ts +0 -45
- package/src/core.ts +0 -619
- package/src/index.deno.ts +0 -28
- package/src/index.ts +0 -36
- package/src/operations-map.ts +0 -667
- package/src/security-map.ts +0 -770
- package/src/state-machine.ts +0 -79
- package/src/webhook-schemas.ts +0 -400
- package/tsconfig.build.json +0 -27
- package/tsconfig.json +0 -22
- package/vitest.config.ts +0 -31
package/README.md
CHANGED
|
@@ -1,251 +1,215 @@
|
|
|
1
|
-
#
|
|
1
|
+
# trustap-sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@ranwhenparked/trustap-sdk)
|
|
4
|
+
[](https://opensource.org/licenses/ISC)
|
|
5
|
+
[](https://www.typescriptlang.org/)
|
|
4
6
|
|
|
5
|
-
|
|
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
|
-
##
|
|
9
|
+
## Overview
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
Trustap provides escrow and payment protection for peer-to-peer and marketplace transactions. This SDK offers:
|
|
14
12
|
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
20
|
+
```bash
|
|
21
|
+
npm install trustap-sdk
|
|
22
|
+
```
|
|
29
23
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
headers: { Authorization: `Bearer ${accessToken}` },
|
|
33
|
-
});
|
|
24
|
+
```bash
|
|
25
|
+
yarn add trustap-sdk
|
|
34
26
|
```
|
|
35
27
|
|
|
36
|
-
|
|
28
|
+
```bash
|
|
29
|
+
pnpm add trustap-sdk
|
|
30
|
+
```
|
|
37
31
|
|
|
38
|
-
|
|
32
|
+
## Quick Start
|
|
39
33
|
|
|
40
|
-
```
|
|
41
|
-
|
|
34
|
+
```typescript
|
|
35
|
+
import { createTrustapClient, TRUSTAP_BASE_URLS } from "trustap-sdk";
|
|
42
36
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
//
|
|
51
|
-
const { data
|
|
42
|
+
// Calculate transaction fees
|
|
43
|
+
const { data, error } = await client.GET("/charge", {
|
|
52
44
|
params: {
|
|
53
|
-
|
|
54
|
-
query: { status: "paid" },
|
|
45
|
+
query: { price: 10000, currency: "USD" },
|
|
55
46
|
},
|
|
56
47
|
});
|
|
57
48
|
```
|
|
58
49
|
|
|
59
50
|
## Authentication
|
|
60
51
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
### Basic Auth (Server-to-server)
|
|
52
|
+
### API Key (Server-side)
|
|
64
53
|
|
|
65
|
-
|
|
54
|
+
Use API key authentication for server-to-server requests:
|
|
66
55
|
|
|
67
|
-
```
|
|
68
|
-
const
|
|
69
|
-
|
|
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
|
-
###
|
|
62
|
+
### OAuth (User-authenticated)
|
|
78
63
|
|
|
79
|
-
|
|
64
|
+
Use OAuth for requests on behalf of authenticated users:
|
|
80
65
|
|
|
81
|
-
```
|
|
82
|
-
const
|
|
83
|
-
|
|
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
|
-
|
|
72
|
+
## Client Usage
|
|
96
73
|
|
|
97
|
-
###
|
|
74
|
+
### Standard Client
|
|
98
75
|
|
|
99
|
-
|
|
76
|
+
```typescript
|
|
77
|
+
import { createTrustapClient } from "trustap-sdk";
|
|
100
78
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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
|
-
|
|
88
|
+
### Path-based Client
|
|
114
89
|
|
|
115
|
-
|
|
90
|
+
```typescript
|
|
91
|
+
import { createTrustapPathClient } from "trustap-sdk";
|
|
116
92
|
|
|
117
|
-
|
|
118
|
-
import {
|
|
119
|
-
trustapWebhookEventSchema,
|
|
120
|
-
type TrustapWebhookEvent,
|
|
121
|
-
} from "@ranwhenparked/trustap-sdk";
|
|
93
|
+
const client = createTrustapPathClient({ /* options */ });
|
|
122
94
|
|
|
123
|
-
//
|
|
124
|
-
const
|
|
125
|
-
|
|
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
|
-
|
|
131
|
-
|
|
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
|
-
###
|
|
130
|
+
### Type-safe Handlers
|
|
135
131
|
|
|
136
|
-
|
|
132
|
+
Create handlers with compile-time exhaustiveness checking:
|
|
137
133
|
|
|
138
|
-
```
|
|
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 =
|
|
137
|
+
const handlers = createOnlineWebhookHandlers({
|
|
145
138
|
"basic_tx.joined": (event) => {
|
|
146
|
-
console.log("
|
|
139
|
+
console.log("Seller joined:", event.target_preview.joined);
|
|
147
140
|
},
|
|
148
141
|
"basic_tx.paid": (event) => {
|
|
149
|
-
console.log("
|
|
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
|
-
|
|
152
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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
|
-
|
|
174
|
+
### State Machine
|
|
189
175
|
|
|
190
176
|
Map webhook events to transaction states:
|
|
191
177
|
|
|
192
|
-
```
|
|
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 =
|
|
199
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
187
|
+
Import only what you need:
|
|
224
188
|
|
|
225
|
-
|
|
189
|
+
```typescript
|
|
190
|
+
// Full SDK
|
|
191
|
+
import { createTrustapClient, trustapWebhookEventSchema } from "trustap-sdk";
|
|
226
192
|
|
|
227
|
-
|
|
228
|
-
import {
|
|
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
|
-
##
|
|
200
|
+
## Deno
|
|
232
201
|
|
|
233
|
-
|
|
202
|
+
```typescript
|
|
203
|
+
import { createTrustapClient } from "./mod.ts";
|
|
234
204
|
|
|
235
|
-
|
|
236
|
-
|
|
205
|
+
// Or from npm
|
|
206
|
+
import { createTrustapClient } from "npm:trustap-sdk";
|
|
237
207
|
```
|
|
238
208
|
|
|
239
|
-
|
|
209
|
+
## API Reference
|
|
240
210
|
|
|
241
|
-
-
|
|
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
|
-
|
|
213
|
+
## License
|
|
246
214
|
|
|
247
|
-
|
|
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
|
package/dist/client.d.ts
ADDED
|
@@ -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
|
+
}
|