@alter-ai/connect 0.11.0 → 0.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alter AI, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Alter Connect SDK
2
2
 
3
- A lightweight JavaScript SDK for embedding OAuth integrations into an application. The SDK opens Alter Connect in a popup and reports the completed grants through typed callbacks.
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
 
@@ -12,7 +12,8 @@ A lightweight JavaScript SDK for embedding OAuth integrations into an applicatio
12
12
  npm install @alter-ai/connect
13
13
  ```
14
14
 
15
- Or use via CDN:
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>
@@ -33,8 +34,8 @@ const alterApp = new App({
33
34
  });
34
35
 
35
36
  const session = await alterApp.createConnectSession({
36
- allowedProviders: ["google", "slack", "github"],
37
- returnUrl: "https://yourapp.com/callback",
37
+ allowedProviders: ["provider-id"],
38
+ allowedOrigin: "https://app.example.com",
38
39
  // Optional. When the user approves access, the Connect UI also lets them set
39
40
  // their own usage limits on the connection being created — deny rules,
40
41
  // human approval, time windows, request quotas, and operation/parameter
@@ -45,7 +46,7 @@ const session = await alterApp.createConnectSession({
45
46
  // allowUserPolicyRules: false,
46
47
  });
47
48
 
48
- const session_token = session.sessionToken;
49
+ const sessionToken = session.sessionToken;
49
50
  ```
50
51
 
51
52
  ### 3. Open the Connect UI
@@ -79,9 +80,9 @@ connectButton.addEventListener('click', () => {
79
80
  if (!sessionToken) return;
80
81
  void alterConnect.open({
81
82
  token: sessionToken,
82
- onSuccess: (connections, completion) => {
83
- console.log('Connected!', connections);
84
- connections.forEach(conn => console.log(conn.provider, conn.grant_id));
83
+ onSuccess: (grants, completion) => {
84
+ console.log('Connected!', grants);
85
+ grants.forEach(grant => console.log(grant.provider, grant.grant_id));
85
86
  completion.failedGrants.forEach(failure => {
86
87
  console.warn(failure.providerId, failure.reason, failure.message);
87
88
  });
@@ -96,84 +97,20 @@ connectButton.addEventListener('click', () => {
96
97
  });
97
98
  ```
98
99
 
99
- The SDK handles the OAuth flow, popup windows, mobile redirects, and security checks.
100
+ The package launches the hosted OAuth Connect UI and validates desktop popup results before callbacks receive them.
100
101
 
101
102
  ## Framework Examples
102
103
 
103
- ### React
104
-
105
- ```jsx
106
- import { useState } from 'react';
107
- import AlterConnect from '@alter-ai/connect';
108
-
109
- function ConnectButton() {
110
- const [alterConnect] = useState(() => AlterConnect.create());
111
-
112
- const handleConnect = async () => {
113
- const { session_token } = await fetch('/api/alter/session')
114
- .then(r => r.json());
115
-
116
- await alterConnect.open({
117
- token: session_token,
118
- onSuccess: (connections) => {
119
- console.log('Connected!', connections);
120
- }
121
- });
122
- };
123
-
124
- return <button onClick={handleConnect}>Connect Account</button>;
125
- }
126
- ```
127
-
128
- ### Vue
129
-
130
- ```vue
131
- <template>
132
- <button @click="handleConnect">Connect Account</button>
133
- </template>
134
-
135
- <script setup>
136
- import { ref, onMounted } from 'vue';
137
- import AlterConnect from '@alter-ai/connect';
138
-
139
- const alterConnect = ref(null);
140
-
141
- onMounted(() => {
142
- alterConnect.value = AlterConnect.create();
143
- });
144
-
145
- async function handleConnect() {
146
- const { session_token } = await fetch('/api/alter/session')
147
- .then(r => r.json());
104
+ React, Vue, Angular, Svelte, and vanilla applications use the same pattern:
148
105
 
149
- await alterConnect.value.open({
150
- token: session_token,
151
- onSuccess: (connections) => console.log('Connected!', connections)
152
- });
153
- }
154
- </script>
155
- ```
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.
156
110
 
157
- ### Vanilla JavaScript (CDN)
158
-
159
- ```html
160
- <button id="connect-btn">Connect Account</button>
161
-
162
- <script src="https://cdn.jsdelivr.net/npm/@alter-ai/connect@latest/dist/alter-connect.umd.js"></script>
163
- <script>
164
- const alterConnect = AlterConnect.create();
165
-
166
- document.getElementById('connect-btn').addEventListener('click', async () => {
167
- const { session_token } = await fetch('/api/alter/session')
168
- .then(r => r.json());
169
-
170
- await alterConnect.open({
171
- token: session_token,
172
- onSuccess: (connections) => console.log('Connected!', connections)
173
- });
174
- });
175
- </script>
176
- ```
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`.
177
114
 
178
115
  ## API Reference
179
116
 
@@ -190,6 +127,7 @@ const alterConnect = AlterConnect.create({
190
127
  | Option | Type | Description | Default |
191
128
  |--------|------|-------------|---------|
192
129
  | `debug` | `boolean` | Enable debug logging | `false` |
130
+ | `baseURL` | `string` | Reserved and unsupported. Passing any value throws. | — |
193
131
 
194
132
  **Note:** Visual customization (colors, fonts, logo) is configured via the Developer Portal branding settings. Alter Connect applies the configured branding automatically.
195
133
 
@@ -197,12 +135,12 @@ const alterConnect = AlterConnect.create({
197
135
 
198
136
  ### `alterConnect.open(options)`
199
137
 
200
- Opens the Connect UI. On desktop, opens a centered popup window (500x700px). On mobile, uses a full-page redirect flow.
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).
201
139
 
202
140
  ```javascript
203
141
  await alterConnect.open({
204
142
  token: 'sess_abc123...',
205
- onSuccess: (connections, completion) => { /* ... */ },
143
+ onSuccess: (grants, completion) => { /* ... */ },
206
144
  onError: (error) => { /* ... */ },
207
145
  onExit: () => { /* ... */ },
208
146
  onEvent: (eventName, metadata) => { /* ... */ }
@@ -213,11 +151,17 @@ await alterConnect.open({
213
151
  |-----------|------|----------|-------------|
214
152
  | `token` | `string` | Yes | Short-lived session token created by the application backend with an Alter SDK |
215
153
  | `onSuccess` | `(grants, completion) => void` | Yes | Called with the legacy grants array and a typed completion object. `completion.failedGrants` identifies partial failures. |
216
- | `onError` | `(error) => void` | No | Called when connection fails. A total usage-limit application failure has code `grant_policy_application_failed` and typed `failedGrants`. |
217
- | `onExit` | `function` | No | Called when user closes popup |
218
- | `onEvent` | `function` | No | Called for analytics events |
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 |
219
157
 
220
- **Upgrading from 0.8.x:** `open()` no longer accepts a `baseURL` option the widget always opens against the production Alter host. A `baseURL` override is reserved for a future release but not currently supported; contact support if you need to target a non-production deployment.
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.
159
+
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.
221
165
 
222
166
  **Completion data (`onSuccess`):**
223
167
 
@@ -262,7 +206,8 @@ bare success.
262
206
 
263
207
  ### `alterConnect.close()`
264
208
 
265
- Manually closes the Connect UI.
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`.
266
211
 
267
212
  ```javascript
268
213
  alterConnect.close();
@@ -293,17 +238,33 @@ const unsubscribe = alterConnect.on('success', (grants, completion) => {
293
238
  // Later: unsubscribe();
294
239
  ```
295
240
 
296
- **Events:** `success`, `error`, `exit`, `close`, `event`
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
+ ```
297
255
 
298
256
  ---
299
257
 
300
258
  ### `alterConnect.isOpen()`
301
259
 
302
- Checks if the Connect UI is currently open.
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.
303
264
 
304
265
  ```javascript
305
266
  if (alterConnect.isOpen()) {
306
- console.log('Modal is open');
267
+ console.log('Connect is open');
307
268
  }
308
269
  ```
309
270
 
@@ -314,40 +275,54 @@ if (alterConnect.isOpen()) {
314
275
  Gets the SDK version.
315
276
 
316
277
  ```javascript
317
- console.log(alterConnect.getVersion()); // e.g., "0.3.0"
278
+ console.log(alterConnect.getVersion()); // "0.2.0" in package 0.11.0
318
279
  ```
319
280
 
320
- ## Mobile Support
281
+ `getVersion()` reads a runtime constant embedded in the bundle. It does not currently match the package metadata version.
321
282
 
322
- The SDK automatically detects mobile devices and switches to an optimized flow:
283
+ ## Mobile behavior
284
+
285
+ The SDK chooses the flow using user-agent, touch, viewport, and orientation checks:
323
286
 
324
287
  | Device | Flow | How It Works |
325
288
  |--------|------|-------------|
326
289
  | Desktop | Popup | Opens a centered popup (500x700px) and reports completion through callbacks |
327
- | Phone (<=480px) | Redirect | Full-page redirect, returns via URL params |
328
- | Tablet (portrait) | Redirect | Full-page redirect for better UX |
329
- | Tablet (landscape) | Popup | Uses popup flow like desktop |
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 |
330
293
 
331
- No code changes needed the SDK handles device detection automatically.
294
+ **Redirect completion:** full-page navigation destroys the callbacks and listeners registered by the original page. Set `returnUrl` on the session and Connect navigates back to it with the outcome, which is delivered to the callbacks registered on the instance constructed by the returning page. Callback parameters that do not carry the per-flow value the SDK minted are ignored. Sessions created without a `returnUrl`, and returns arriving more than five minutes after the flow started, complete through `createConnectSession()` plus `pollConnectSession()` on the application backend.
332
295
 
333
- For mobile redirect flow, include a return URL when creating the session:
334
-
335
- ```typescript
336
- const session = await alterApp.createConnectSession({
337
- allowedProviders: ["google", "slack"],
338
- allowedOrigin: "https://app.example.com",
339
- returnUrl: "https://app.example.com/",
340
- });
341
- ```
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.
342
299
 
343
300
  ## Security
344
301
 
345
- - **API keys stay on the backend.** The frontend only ever holds a short-lived, single-use, scope-restricted session token minted by the application backend.
346
- - **No secrets in the browser.** The SDK is a popup launcher + callback listener; it does not handle credentials directly.
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.
347
304
 
348
305
  ## Provider selection
349
306
 
350
- When `allowedProviders` lists a single provider, the Connect UI skips the selection screen and opens that provider's OAuth flow directly. With two or more, the UI shows a provider picker.
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`.
351
326
 
352
327
  ## TypeScript Support
353
328
 
@@ -355,10 +330,13 @@ Full TypeScript definitions included:
355
330
 
356
331
  ```typescript
357
332
  import AlterConnect, {
358
- AlterConnectConfig,
359
- ConnectCompletion,
360
- Grant,
361
- AlterError
333
+ type AlterConnectConfig,
334
+ type OpenOptions,
335
+ type ConnectCompletion,
336
+ type ConnectFailedGrant,
337
+ type Provider,
338
+ type Grant,
339
+ type AlterError
362
340
  } from '@alter-ai/connect';
363
341
 
364
342
  const alterConnect = AlterConnect.create({ debug: true });
@@ -381,23 +359,11 @@ await alterConnect.open({
381
359
  });
382
360
  ```
383
361
 
384
- ## Bundle Size
385
-
386
- | Format | Minified size |
387
- |--------|---------------|
388
- | **CJS** | ~74KB |
389
- | **ESM** | ~74KB |
390
- | **UMD** | ~74KB |
391
-
392
- The package has one runtime dependency: Zod 4 validates complete OAuth
393
- `postMessage` payloads before callback data is consumed.
362
+ The package has one runtime dependency: Zod 4 validates complete cross-window result payloads before callback data is consumed.
394
363
 
395
364
  ## Browser Support
396
365
 
397
- - Chrome/Edge 90+
398
- - Firefox 88+
399
- - Safari 14+
400
- - 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.
401
367
 
402
368
  ## Troubleshooting
403
369
 
@@ -408,31 +374,32 @@ The package has one runtime dependency: Zod 4 validates complete OAuth
408
374
  **Solution:** Ensure `alterConnect.open()` is called directly from a user interaction (click event):
409
375
 
410
376
  ```javascript
411
- // Bad - may be blocked
377
+ // May be blocked: the network request runs after the click.
412
378
  button.addEventListener('click', async () => {
413
- const token = await fetchToken(); // Async delay
414
- alterConnect.open({ token }); // May be blocked
379
+ const token = await fetchToken();
380
+ void alterConnect.open({ token, onSuccess, onError });
415
381
  });
416
382
 
417
- // Good - no async delay before open()
383
+ // Reliable: fetch before enabling the button.
384
+ const token = await fetchToken();
385
+ button.disabled = false;
386
+
418
387
  button.addEventListener('click', () => {
419
- fetchToken().then(token => {
420
- alterConnect.open({ token }); // Called synchronously
421
- });
388
+ void alterConnect.open({ token, onSuccess, onError });
422
389
  });
423
390
  ```
424
391
 
425
392
  ### Session Token Expired
426
393
 
427
- **Problem:** `session_expired` error
394
+ **Problem:** The hosted Connect page reports that the session expired or is invalid
428
395
 
429
- **Solution:** Session tokens expire after 10 minutes. Create a new session token.
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.
430
397
 
431
398
  ### CORS Errors
432
399
 
433
- **Problem:** CORS error when calling Alter API
400
+ **Problem:** Session creation was attempted from browser code
434
401
 
435
- **Solution:** Session tokens must be created by the application backend, not the frontend. The SDK handles the frontend flow.
402
+ **Solution:** Create sessions on the application backend with an Alter server SDK, then return only the short-lived session token to the browser.
436
403
 
437
404
  ## Support
438
405
 
@@ -442,4 +409,4 @@ button.addEventListener('click', () => {
442
409
 
443
410
  ## License
444
411
 
445
- MIT License - See [LICENSE](LICENSE) file for details
412
+ MIT
@@ -0,0 +1,9 @@
1
+ Third-Party Notices
2
+
3
+ This distribution may include:
4
+
5
+ - Zod, Copyright (c) 2020 Colin McDonnell, MIT License.
6
+ - tslib, Copyright (c) Microsoft Corporation, 0BSD License.
7
+
8
+ The complete corresponding license texts are available in the installed package metadata and
9
+ the upstream projects. These notices do not modify Alter Connect's MIT License.