@alter-ai/connect 0.10.0 → 0.11.1
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 +190 -183
- package/dist/alter-connect.cjs.js +1 -1
- package/dist/alter-connect.cjs.js.map +1 -1
- package/dist/alter-connect.esm.js +1 -1
- package/dist/alter-connect.esm.js.map +1 -1
- package/dist/alter-connect.umd.js +1 -1
- package/dist/alter-connect.umd.js.map +1 -1
- package/dist/types/core/alter-connect.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/oauth/handler.d.ts +3 -3
- package/dist/types/oauth/handler.d.ts.map +1 -1
- package/dist/types/types/index.d.ts +11 -1
- package/dist/types/types/index.d.ts.map +1 -1
- package/package.json +15 -4
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Alter Connect SDK
|
|
2
2
|
|
|
3
|
-
A lightweight JavaScript SDK for embedding OAuth integrations into
|
|
3
|
+
A lightweight JavaScript SDK for embedding OAuth integrations into an application. The SDK opens Alter Connect in a desktop popup, uses full-page navigation on mobile-classified devices, and validates completion data before invoking typed callbacks.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
**Typed OAuth callbacks | Zod-validated payloads | TypeScript included**
|
|
6
6
|
|
|
7
7
|
## Quick Start
|
|
8
8
|
|
|
@@ -12,18 +12,19 @@ A lightweight JavaScript SDK for embedding OAuth integrations into your applicat
|
|
|
12
12
|
npm install @alter-ai/connect
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
The package metadata declares Node.js 20.19 or later for npm tooling. Browser
|
|
16
|
+
execution does not require Node.js. Or use the UMD bundle via CDN:
|
|
16
17
|
|
|
17
18
|
```html
|
|
18
19
|
<script src="https://cdn.jsdelivr.net/npm/@alter-ai/connect@latest/dist/alter-connect.umd.js"></script>
|
|
19
20
|
```
|
|
20
21
|
|
|
21
|
-
### 2. Get a Session Token from
|
|
22
|
+
### 2. Get a Session Token from the Application Backend
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
The application backend creates a short-lived session token using the [Alter SDK](https://www.npmjs.com/package/@alter-ai/alter-sdk):
|
|
24
25
|
|
|
25
26
|
```typescript
|
|
26
|
-
//
|
|
27
|
+
// Application backend (Node.js example using @alter-ai/alter-sdk)
|
|
27
28
|
import { App, CallerType } from "@alter-ai/alter-sdk";
|
|
28
29
|
|
|
29
30
|
const alterApp = new App({
|
|
@@ -33,11 +34,19 @@ const alterApp = new App({
|
|
|
33
34
|
});
|
|
34
35
|
|
|
35
36
|
const session = await alterApp.createConnectSession({
|
|
36
|
-
allowedProviders: ["
|
|
37
|
-
|
|
37
|
+
allowedProviders: ["provider-id"],
|
|
38
|
+
allowedOrigin: "https://app.example.com",
|
|
39
|
+
// Optional. When the user approves access, the Connect UI also lets them set
|
|
40
|
+
// their own usage limits on the connection being created — deny rules,
|
|
41
|
+
// human approval, time windows, request quotas, and operation/parameter
|
|
42
|
+
// rules. They can only narrow what they are granting, never widen it, and
|
|
43
|
+
// they can change or remove the limits later in their wallet.
|
|
44
|
+
//
|
|
45
|
+
// This step is enabled by default. To hide it, pass:
|
|
46
|
+
// allowUserPolicyRules: false,
|
|
38
47
|
});
|
|
39
48
|
|
|
40
|
-
const
|
|
49
|
+
const sessionToken = session.sessionToken;
|
|
41
50
|
```
|
|
42
51
|
|
|
43
52
|
### 3. Open the Connect UI
|
|
@@ -48,104 +57,60 @@ import AlterConnect from '@alter-ai/connect';
|
|
|
48
57
|
// Initialize SDK (no API key needed!)
|
|
49
58
|
const alterConnect = AlterConnect.create();
|
|
50
59
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
// Open Connect UI
|
|
55
|
-
await alterConnect.open({
|
|
56
|
-
token: session_token,
|
|
57
|
-
onSuccess: (connections) => {
|
|
58
|
-
console.log('Connected!', connections);
|
|
59
|
-
// Save each connection.grant_id to your database
|
|
60
|
-
connections.forEach(conn => console.log(conn.provider, conn.grant_id));
|
|
61
|
-
},
|
|
62
|
-
onError: (error) => {
|
|
63
|
-
console.error('Failed:', error);
|
|
64
|
-
},
|
|
65
|
-
onExit: () => {
|
|
66
|
-
console.log('User closed the window');
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
That's it! The SDK handles the OAuth flow, popup windows, mobile redirects, and all security.
|
|
72
|
-
|
|
73
|
-
## Framework Examples
|
|
74
|
-
|
|
75
|
-
### React
|
|
76
|
-
|
|
77
|
-
```jsx
|
|
78
|
-
import { useState } from 'react';
|
|
79
|
-
import AlterConnect from '@alter-ai/connect';
|
|
80
|
-
|
|
81
|
-
function ConnectButton() {
|
|
82
|
-
const [alterConnect] = useState(() => AlterConnect.create());
|
|
83
|
-
|
|
84
|
-
const handleConnect = async () => {
|
|
85
|
-
const { session_token } = await fetch('/api/alter/session')
|
|
86
|
-
.then(r => r.json());
|
|
87
|
-
|
|
88
|
-
await alterConnect.open({
|
|
89
|
-
token: session_token,
|
|
90
|
-
onSuccess: (connections) => {
|
|
91
|
-
console.log('Connected!', connections);
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
return <button onClick={handleConnect}>Connect Account</button>;
|
|
60
|
+
const connectButton = document.querySelector('#connect-button');
|
|
61
|
+
if (!(connectButton instanceof HTMLButtonElement)) {
|
|
62
|
+
throw new Error('Connect button is missing');
|
|
97
63
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
onMounted(() => {
|
|
114
|
-
alterConnect.value = AlterConnect.create();
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
async function handleConnect() {
|
|
118
|
-
const { session_token } = await fetch('/api/alter/session')
|
|
119
|
-
.then(r => r.json());
|
|
64
|
+
connectButton.disabled = true;
|
|
65
|
+
|
|
66
|
+
// Prefetch before the user interacts. Browsers may block a popup opened only
|
|
67
|
+
// after an awaited network request has detached it from the click gesture.
|
|
68
|
+
let sessionToken = null;
|
|
69
|
+
fetch('/api/alter/session')
|
|
70
|
+
.then(response => response.json())
|
|
71
|
+
.then(({ session_token }) => {
|
|
72
|
+
sessionToken = session_token;
|
|
73
|
+
connectButton.disabled = false;
|
|
74
|
+
})
|
|
75
|
+
.catch(() => {
|
|
76
|
+
console.error('Unable to prepare the Connect session');
|
|
77
|
+
});
|
|
120
78
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
79
|
+
connectButton.addEventListener('click', () => {
|
|
80
|
+
if (!sessionToken) return;
|
|
81
|
+
void alterConnect.open({
|
|
82
|
+
token: sessionToken,
|
|
83
|
+
onSuccess: (grants, completion) => {
|
|
84
|
+
console.log('Connected!', grants);
|
|
85
|
+
grants.forEach(grant => console.log(grant.provider, grant.grant_id));
|
|
86
|
+
completion.failedGrants.forEach(failure => {
|
|
87
|
+
console.warn(failure.providerId, failure.reason, failure.message);
|
|
88
|
+
});
|
|
89
|
+
},
|
|
90
|
+
onError: (error) => {
|
|
91
|
+
console.error('Failed:', error);
|
|
92
|
+
},
|
|
93
|
+
onExit: () => {
|
|
94
|
+
console.log('User closed the window');
|
|
95
|
+
}
|
|
124
96
|
});
|
|
125
|
-
}
|
|
126
|
-
</script>
|
|
97
|
+
});
|
|
127
98
|
```
|
|
128
99
|
|
|
129
|
-
|
|
100
|
+
The package launches the hosted OAuth Connect UI and validates desktop popup results before callbacks receive them.
|
|
130
101
|
|
|
131
|
-
|
|
132
|
-
<button id="connect-btn">Connect Account</button>
|
|
102
|
+
## Framework Examples
|
|
133
103
|
|
|
134
|
-
|
|
135
|
-
<script>
|
|
136
|
-
const alterConnect = AlterConnect.create();
|
|
104
|
+
React, Vue, Angular, Svelte, and vanilla applications use the same pattern:
|
|
137
105
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
106
|
+
1. Create one reusable `AlterConnect` instance.
|
|
107
|
+
2. Fetch a session token before enabling the Connect button.
|
|
108
|
+
3. Call `open()` directly in the click handler so popup activation remains attached to the user gesture.
|
|
109
|
+
4. Destroy the instance when the owning component unmounts.
|
|
141
110
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
});
|
|
146
|
-
});
|
|
147
|
-
</script>
|
|
148
|
-
```
|
|
111
|
+
The CDN bundle exposes the same class as `window.AlterConnect`. CommonJS
|
|
112
|
+
consumers read the default export with
|
|
113
|
+
`require('@alter-ai/connect').default`.
|
|
149
114
|
|
|
150
115
|
## API Reference
|
|
151
116
|
|
|
@@ -162,19 +127,20 @@ const alterConnect = AlterConnect.create({
|
|
|
162
127
|
| Option | Type | Description | Default |
|
|
163
128
|
|--------|------|-------------|---------|
|
|
164
129
|
| `debug` | `boolean` | Enable debug logging | `false` |
|
|
130
|
+
| `baseURL` | `string` | Reserved and unsupported. Passing any value throws. | — |
|
|
165
131
|
|
|
166
|
-
**Note:** Visual customization (colors, fonts, logo) is configured via the Developer Portal branding settings.
|
|
132
|
+
**Note:** Visual customization (colors, fonts, logo) is configured via the Developer Portal branding settings. Alter Connect applies the configured branding automatically.
|
|
167
133
|
|
|
168
134
|
---
|
|
169
135
|
|
|
170
136
|
### `alterConnect.open(options)`
|
|
171
137
|
|
|
172
|
-
|
|
138
|
+
Attempts to open Connect. Desktop uses a centered popup window (500×700 px). Mobile-classified devices use full-page navigation, with the limitation documented under [Mobile behavior](#mobile-behavior).
|
|
173
139
|
|
|
174
140
|
```javascript
|
|
175
141
|
await alterConnect.open({
|
|
176
142
|
token: 'sess_abc123...',
|
|
177
|
-
onSuccess: (
|
|
143
|
+
onSuccess: (grants, completion) => { /* ... */ },
|
|
178
144
|
onError: (error) => { /* ... */ },
|
|
179
145
|
onExit: () => { /* ... */ },
|
|
180
146
|
onEvent: (eventName, metadata) => { /* ... */ }
|
|
@@ -183,51 +149,65 @@ await alterConnect.open({
|
|
|
183
149
|
|
|
184
150
|
| Parameter | Type | Required | Description |
|
|
185
151
|
|-----------|------|----------|-------------|
|
|
186
|
-
| `token` | `string` | Yes |
|
|
187
|
-
| `onSuccess` | `
|
|
188
|
-
| `onError` | `
|
|
189
|
-
| `onExit` | `function` | No | Called when user closes popup |
|
|
190
|
-
| `onEvent` | `
|
|
152
|
+
| `token` | `string` | Yes | Short-lived session token created by the application backend with an Alter SDK |
|
|
153
|
+
| `onSuccess` | `(grants, completion) => void` | Yes | Called with the legacy grants array and a typed completion object. `completion.failedGrants` identifies partial failures. |
|
|
154
|
+
| `onError` | `(error) => void` | No | Called for failures delivered to the browser SDK, such as a blocked popup, a malformed result, or a total usage-limit application failure. Hosted-page-only failures require backend polling. |
|
|
155
|
+
| `onExit` | `function` | No | Called when the user closes the desktop popup; this does not mark the backend session denied |
|
|
156
|
+
| `onEvent` | `(eventName, metadata) => void` | No | Called once with `connect_opened` after each launch attempt, including a blocked popup |
|
|
157
|
+
|
|
158
|
+
The promise resolves after the launch attempt, not after user authorization. Missing or invalid `token` or `onSuccess` values reject the returned promise with `code: "invalid_options"`. `baseURL` is not an `OpenOptions` field: TypeScript rejects it, while plain JavaScript ignores it.
|
|
191
159
|
|
|
192
|
-
|
|
160
|
+
`onSuccess`, `onError`, and `onExit` run through the event emitter's exception
|
|
161
|
+
guard. If one throws, the SDK logs the exception and does not perform that
|
|
162
|
+
callback's final state/listener cleanup; callback code should not throw.
|
|
163
|
+
`onEvent` is invoked directly, so an exception from it rejects `open()` after
|
|
164
|
+
the launch attempt.
|
|
193
165
|
|
|
194
|
-
**
|
|
166
|
+
**Completion data (`onSuccess`):**
|
|
195
167
|
|
|
196
|
-
|
|
168
|
+
For compatibility, the first argument remains an array of `Grant` objects. The
|
|
169
|
+
second argument makes partial completion explicit:
|
|
197
170
|
|
|
198
171
|
```typescript
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
operation: 'creation' | 'reauth';
|
|
207
|
-
scopes: string[]; // Granted OAuth scopes
|
|
208
|
-
status: 'active' | 'pending' | 'error';
|
|
209
|
-
metadata?: {
|
|
210
|
-
account_display_name?: string;
|
|
211
|
-
account_email?: string;
|
|
212
|
-
};
|
|
172
|
+
interface ConnectCompletion {
|
|
173
|
+
grants: Grant[];
|
|
174
|
+
failedGrants: Array<{
|
|
175
|
+
providerId: string;
|
|
176
|
+
reason: string;
|
|
177
|
+
message: string;
|
|
178
|
+
}>;
|
|
213
179
|
}
|
|
214
180
|
```
|
|
215
181
|
|
|
182
|
+
`failedGrants` is empty for full success. When at least one provider succeeds,
|
|
183
|
+
it identifies any providers whose grants were revoked because the selected
|
|
184
|
+
usage limits could not be applied. Unknown `reason` strings are preserved.
|
|
185
|
+
|
|
216
186
|
**Error Object (onError):**
|
|
217
187
|
|
|
218
188
|
```typescript
|
|
219
189
|
{
|
|
220
|
-
code: string;
|
|
221
|
-
message: string;
|
|
222
|
-
details?:
|
|
190
|
+
code: string;
|
|
191
|
+
message: string;
|
|
192
|
+
details?: Record<string, unknown>;
|
|
193
|
+
failedGrants?: Array<{
|
|
194
|
+
providerId: string;
|
|
195
|
+
reason: string;
|
|
196
|
+
message: string;
|
|
197
|
+
}>;
|
|
223
198
|
}
|
|
224
199
|
```
|
|
225
200
|
|
|
201
|
+
A completion with no successful grants and one or more failed grants invokes
|
|
202
|
+
`onError` with `code: "grant_policy_application_failed"` instead of reporting
|
|
203
|
+
bare success.
|
|
204
|
+
|
|
226
205
|
---
|
|
227
206
|
|
|
228
207
|
### `alterConnect.close()`
|
|
229
208
|
|
|
230
|
-
|
|
209
|
+
Closes an active popup if one exists, stops the current flow handler, removes
|
|
210
|
+
the current per-open callbacks, resets `isOpen()`, and emits `close`.
|
|
231
211
|
|
|
232
212
|
```javascript
|
|
233
213
|
alterConnect.close();
|
|
@@ -250,24 +230,41 @@ alterConnect.destroy();
|
|
|
250
230
|
Register an event listener. Returns an unsubscribe function.
|
|
251
231
|
|
|
252
232
|
```javascript
|
|
253
|
-
const unsubscribe = alterConnect.on('success', (
|
|
254
|
-
console.log('Connected:',
|
|
233
|
+
const unsubscribe = alterConnect.on('success', (grants, completion) => {
|
|
234
|
+
console.log('Connected:', grants);
|
|
235
|
+
console.log('Providers not kept:', completion.failedGrants);
|
|
255
236
|
});
|
|
256
237
|
|
|
257
238
|
// Later: unsubscribe();
|
|
258
239
|
```
|
|
259
240
|
|
|
260
|
-
**
|
|
241
|
+
**Emitted bus events:** `success`, `error`, `exit`, `close`. The `success` handler receives `(grants, completion)`. Analytics does not emit an `event` bus event; use the per-open `onEvent` callback.
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
### `alterConnect.off(event, handler)`
|
|
246
|
+
|
|
247
|
+
Removes a previously registered event handler. Pass the same handler reference
|
|
248
|
+
that was supplied to `on()`.
|
|
249
|
+
|
|
250
|
+
```javascript
|
|
251
|
+
const handleError = error => console.error(error);
|
|
252
|
+
alterConnect.on('error', handleError);
|
|
253
|
+
alterConnect.off('error', handleError);
|
|
254
|
+
```
|
|
261
255
|
|
|
262
256
|
---
|
|
263
257
|
|
|
264
258
|
### `alterConnect.isOpen()`
|
|
265
259
|
|
|
266
|
-
|
|
260
|
+
Returns the SDK's open-state flag. It becomes `true` when a launch starts and
|
|
261
|
+
returns to `false` when `close()` runs. A manually closed popup resets it
|
|
262
|
+
automatically only when the supplied `onExit` callback returns normally; if
|
|
263
|
+
`onExit` is omitted or throws, call `close()` explicitly before opening again.
|
|
267
264
|
|
|
268
265
|
```javascript
|
|
269
266
|
if (alterConnect.isOpen()) {
|
|
270
|
-
console.log('
|
|
267
|
+
console.log('Connect is open');
|
|
271
268
|
}
|
|
272
269
|
```
|
|
273
270
|
|
|
@@ -278,41 +275,54 @@ if (alterConnect.isOpen()) {
|
|
|
278
275
|
Gets the SDK version.
|
|
279
276
|
|
|
280
277
|
```javascript
|
|
281
|
-
console.log(alterConnect.getVersion()); //
|
|
278
|
+
console.log(alterConnect.getVersion()); // "0.2.0" in package 0.11.0
|
|
282
279
|
```
|
|
283
280
|
|
|
284
|
-
|
|
281
|
+
`getVersion()` reads a runtime constant embedded in the bundle. It does not currently match the package metadata version.
|
|
282
|
+
|
|
283
|
+
## Mobile behavior
|
|
285
284
|
|
|
286
|
-
The SDK
|
|
285
|
+
The SDK chooses the flow using user-agent, touch, viewport, and orientation checks:
|
|
287
286
|
|
|
288
287
|
| Device | Flow | How It Works |
|
|
289
288
|
|--------|------|-------------|
|
|
290
|
-
| Desktop | Popup | Opens centered popup (500x700px)
|
|
291
|
-
|
|
|
292
|
-
|
|
|
293
|
-
|
|
|
289
|
+
| Desktop | Popup | Opens a centered popup (500x700px) and reports completion through callbacks |
|
|
290
|
+
| Mobile-classified viewport ≤480 px | Redirect | Navigates the full page to Connect |
|
|
291
|
+
| Mobile-classified viewport 481–1024 px in portrait | Redirect | Navigates the full page to Connect |
|
|
292
|
+
| Other devices | Popup | Uses the desktop callback flow |
|
|
294
293
|
|
|
295
|
-
|
|
294
|
+
**Current limitation:** OAuth Connect does not navigate back to the session's `returnUrl`. Full-page navigation also destroys the callbacks and listeners registered by the original page. Do not rely on browser callbacks for mobile completion; use `createConnectSession()` plus `pollConnectSession()` on the application backend.
|
|
296
295
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
// Backend session creation with mobile support
|
|
301
|
-
body: JSON.stringify({
|
|
302
|
-
allowed_providers: ['google', 'slack'],
|
|
303
|
-
allowed_origin: 'https://yourapp.com', // For desktop popup (postMessage)
|
|
304
|
-
return_url: 'https://yourapp.com/' // For mobile redirect (return destination)
|
|
305
|
-
})
|
|
306
|
-
```
|
|
296
|
+
Closing the desktop popup invokes `onExit` locally but sends no denial
|
|
297
|
+
transition to the backend. A server-side poll remains pending until its timeout
|
|
298
|
+
or the session expires.
|
|
307
299
|
|
|
308
300
|
## Security
|
|
309
301
|
|
|
310
|
-
- **API keys stay on the backend.** The frontend only
|
|
311
|
-
- **No
|
|
302
|
+
- **API keys stay on the backend.** The frontend only receives a short-lived session token minted by the application backend.
|
|
303
|
+
- **No API keys or provider credentials in the browser.** The frontend receives only the short-lived Connect session token.
|
|
312
304
|
|
|
313
305
|
## Provider selection
|
|
314
306
|
|
|
315
|
-
When
|
|
307
|
+
The hosted UI renders the providers permitted by the session. When that provider is not already connected, a one-provider allowlist still renders one available provider. A verified user with an existing healthy connection can use the consent fast path; `requestedGrant`, when present, controls the sibling grant requested on that connection. Set `switchAccount: true` while creating the session to force full provider authorization.
|
|
308
|
+
|
|
309
|
+
## Session-controlled behavior
|
|
310
|
+
|
|
311
|
+
Provider allowlists, per-provider requested scopes, origin binding, user identity, requested sibling grants, delegated agents, onward-delegation permission, delegation scope constraints, account switching, grant-expiry bounds, and policy authoring are session-creation concerns. They are not browser `OpenOptions`. Grant-expiry bounds add a duration picker to the hosted confirmation screen.
|
|
312
|
+
|
|
313
|
+
A recovery session from the server SDKs' `createConnectSessionForError()` is
|
|
314
|
+
opened like any other session by passing its `sessionToken` to `open()`. This
|
|
315
|
+
browser package exports none of the server-side exception classes, including
|
|
316
|
+
`ReAuthRequiredError`, `NoDelegatedGrantError`, `GrantNotFoundError`,
|
|
317
|
+
`CredentialRevokedError`, or the headless-flow `ConnectFlowError` family
|
|
318
|
+
(`ConnectDeniedError`, `ConnectConfigError`, `ConnectTimeoutError`).
|
|
319
|
+
|
|
320
|
+
With `allowUserPolicyRules` enabled, the hosted UI can author narrowing deny,
|
|
321
|
+
human-approval, time-window, quota, and operation/parameter content rules.
|
|
322
|
+
Human approval occurs on later API calls; Connect only authors the rule. If
|
|
323
|
+
selected rules fail to apply for some providers,
|
|
324
|
+
`completion.failedGrants` reports the partial failure. If no grant remains,
|
|
325
|
+
`onError` receives `grant_policy_application_failed`.
|
|
316
326
|
|
|
317
327
|
## TypeScript Support
|
|
318
328
|
|
|
@@ -320,21 +330,28 @@ Full TypeScript definitions included:
|
|
|
320
330
|
|
|
321
331
|
```typescript
|
|
322
332
|
import AlterConnect, {
|
|
323
|
-
AlterConnectConfig,
|
|
324
|
-
|
|
325
|
-
|
|
333
|
+
type AlterConnectConfig,
|
|
334
|
+
type OpenOptions,
|
|
335
|
+
type ConnectCompletion,
|
|
336
|
+
type ConnectFailedGrant,
|
|
337
|
+
type Provider,
|
|
338
|
+
type Grant,
|
|
339
|
+
type AlterError
|
|
326
340
|
} from '@alter-ai/connect';
|
|
327
341
|
|
|
328
342
|
const alterConnect = AlterConnect.create({ debug: true });
|
|
329
343
|
|
|
330
344
|
await alterConnect.open({
|
|
331
345
|
token: sessionToken,
|
|
332
|
-
onSuccess: (grants: Grant[]) => {
|
|
346
|
+
onSuccess: (grants: Grant[], completion: ConnectCompletion) => {
|
|
333
347
|
for (const grant of grants) {
|
|
334
|
-
console.log(grant.grant_id); // Store this in
|
|
348
|
+
console.log(grant.grant_id); // Store this in the application database
|
|
335
349
|
console.log(grant.provider);
|
|
336
350
|
console.log(grant.scopes);
|
|
337
351
|
}
|
|
352
|
+
for (const failure of completion.failedGrants) {
|
|
353
|
+
console.warn(failure.providerId, failure.message);
|
|
354
|
+
}
|
|
338
355
|
},
|
|
339
356
|
onError: (error: AlterError) => {
|
|
340
357
|
console.error(error.code, error.message);
|
|
@@ -342,22 +359,11 @@ await alterConnect.open({
|
|
|
342
359
|
});
|
|
343
360
|
```
|
|
344
361
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
| Format | Size | Gzipped |
|
|
348
|
-
|--------|------|---------|
|
|
349
|
-
| **CJS** | ~10KB | ~3.5KB |
|
|
350
|
-
| **ESM** | ~10KB | ~3.4KB |
|
|
351
|
-
| **UMD** | ~11KB | ~3.5KB |
|
|
352
|
-
|
|
353
|
-
Zero runtime dependencies.
|
|
362
|
+
The package has one runtime dependency: Zod 4 validates complete cross-window result payloads before callback data is consumed.
|
|
354
363
|
|
|
355
364
|
## Browser Support
|
|
356
365
|
|
|
357
|
-
-
|
|
358
|
-
- Firefox 88+
|
|
359
|
-
- Safari 14+
|
|
360
|
-
- Mobile browsers (iOS Safari 14+, Chrome Mobile)
|
|
366
|
+
The CJS, ESM, and UMD bundles target ES2020 and use modern browser APIs including popup or full-page navigation, `postMessage`, URL parsing, and `sessionStorage`. Importing the package is side-effect free, but `AlterConnect.create()` must run in a browser because it checks redirect state during construction. The repository's product E2E suite exercises Chromium; it does not establish a cross-browser version matrix. The package metadata declares Node.js 20.19 or later for npm tooling; CDN browser use does not require Node.js.
|
|
361
367
|
|
|
362
368
|
## Troubleshooting
|
|
363
369
|
|
|
@@ -368,31 +374,32 @@ Zero runtime dependencies.
|
|
|
368
374
|
**Solution:** Ensure `alterConnect.open()` is called directly from a user interaction (click event):
|
|
369
375
|
|
|
370
376
|
```javascript
|
|
371
|
-
//
|
|
377
|
+
// May be blocked: the network request runs after the click.
|
|
372
378
|
button.addEventListener('click', async () => {
|
|
373
|
-
const token = await fetchToken();
|
|
374
|
-
alterConnect.open({ token });
|
|
379
|
+
const token = await fetchToken();
|
|
380
|
+
void alterConnect.open({ token, onSuccess, onError });
|
|
375
381
|
});
|
|
376
382
|
|
|
377
|
-
//
|
|
383
|
+
// Reliable: fetch before enabling the button.
|
|
384
|
+
const token = await fetchToken();
|
|
385
|
+
button.disabled = false;
|
|
386
|
+
|
|
378
387
|
button.addEventListener('click', () => {
|
|
379
|
-
|
|
380
|
-
alterConnect.open({ token }); // Called synchronously
|
|
381
|
-
});
|
|
388
|
+
void alterConnect.open({ token, onSuccess, onError });
|
|
382
389
|
});
|
|
383
390
|
```
|
|
384
391
|
|
|
385
392
|
### Session Token Expired
|
|
386
393
|
|
|
387
|
-
**Problem:**
|
|
394
|
+
**Problem:** The hosted Connect page reports that the session expired or is invalid
|
|
388
395
|
|
|
389
|
-
**Solution:** Session tokens
|
|
396
|
+
**Solution:** Session tokens have a 10-minute default lifetime. Create a new session and open its token. An expiry detected while binding or loading the hosted page is rendered there; it is not a reliable browser `onError` callback.
|
|
390
397
|
|
|
391
398
|
### CORS Errors
|
|
392
399
|
|
|
393
|
-
**Problem:**
|
|
400
|
+
**Problem:** Session creation was attempted from browser code
|
|
394
401
|
|
|
395
|
-
**Solution:**
|
|
402
|
+
**Solution:** Create sessions on the application backend with an Alter server SDK, then return only the short-lived session token to the browser.
|
|
396
403
|
|
|
397
404
|
## Support
|
|
398
405
|
|
|
@@ -402,4 +409,4 @@ button.addEventListener('click', () => {
|
|
|
402
409
|
|
|
403
410
|
## License
|
|
404
411
|
|
|
405
|
-
MIT
|
|
412
|
+
MIT
|