@andco/sdk 0.0.3 → 0.0.4
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 +1 -1
- package/README.md +93 -49
- package/dist/auth.d.ts +5 -3
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +10 -17
- package/dist/browser/controller.d.ts +46 -0
- package/dist/browser/controller.d.ts.map +1 -1
- package/dist/browser/controller.js +102 -12
- package/dist/browser/frame.d.ts.map +1 -1
- package/dist/browser/frame.js +6 -15
- package/dist/browser/index.d.ts +8 -2
- package/dist/browser/index.d.ts.map +1 -1
- package/dist/browser/index.js +48 -33
- package/dist/browser/popup.d.ts +4 -0
- package/dist/browser/popup.d.ts.map +1 -1
- package/dist/browser/popup.js +23 -7
- package/dist/cli/index.d.ts +4 -6
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +7 -13
- package/dist/cli/server.d.ts +5 -0
- package/dist/cli/server.d.ts.map +1 -1
- package/dist/cli/server.js +5 -0
- package/dist/client.d.ts +66 -19
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +117 -78
- package/dist/config.d.ts +2 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -0
- package/dist/credentials.d.ts +58 -4
- package/dist/credentials.d.ts.map +1 -1
- package/dist/credentials.js +0 -0
- package/dist/errors.d.ts +23 -21
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +18 -20
- package/dist/globals.d.ts +25 -0
- package/dist/globals.d.ts.map +1 -0
- package/dist/globals.js +15 -0
- package/dist/grants-api.d.ts +34 -0
- package/dist/grants-api.d.ts.map +1 -0
- package/dist/grants-api.js +48 -0
- package/dist/grants.d.ts +16 -0
- package/dist/grants.d.ts.map +1 -0
- package/dist/grants.js +13 -0
- package/dist/index.d.ts +12 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -3
- package/dist/inflight.d.ts +31 -0
- package/dist/inflight.d.ts.map +1 -0
- package/dist/inflight.js +26 -0
- package/dist/intents.d.ts +346 -70
- package/dist/intents.d.ts.map +1 -1
- package/dist/intents.js +629 -112
- package/dist/oauth.d.ts +55 -6
- package/dist/oauth.d.ts.map +1 -1
- package/dist/oauth.js +86 -57
- package/dist/presenter.d.ts +25 -11
- package/dist/presenter.d.ts.map +1 -1
- package/dist/presenter.js +39 -6
- package/dist/resource.d.ts +109 -0
- package/dist/resource.d.ts.map +1 -0
- package/dist/resource.js +151 -0
- package/dist/rest.d.ts +23 -4
- package/dist/rest.d.ts.map +1 -1
- package/dist/rest.js +46 -5
- package/dist/server/index.d.ts +3 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -0
- package/dist/server-metadata.generated.d.ts.map +1 -1
- package/dist/server-metadata.generated.js +8 -4
- package/dist/service.d.ts +109 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +241 -0
- package/dist/session-store.d.ts +12 -4
- package/dist/session-store.d.ts.map +1 -1
- package/dist/session-store.js +64 -14
- package/dist/storage.d.ts +16 -23
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +27 -25
- package/dist/tokens.d.ts +46 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +148 -0
- package/package.json +13 -3
package/LICENSE
CHANGED
|
@@ -187,7 +187,7 @@
|
|
|
187
187
|
same "printed page" as the copyright notice for easier
|
|
188
188
|
identification within third-party archives.
|
|
189
189
|
|
|
190
|
-
Copyright 2026
|
|
190
|
+
Copyright 2026 Haulmer
|
|
191
191
|
|
|
192
192
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
193
|
you may not use this file except in compliance with the License.
|
package/README.md
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
# `@andco/sdk`
|
|
2
2
|
|
|
3
|
+
> [!WARNING]
|
|
4
|
+
> **Pre-1.0 software.** Until version 1.0.0 is released, any new version may include breaking
|
|
5
|
+
> changes, including minor and patch releases. Expect any API incompatibility and data resets
|
|
6
|
+
> in the Andco environments without prior notice. Pin an exact version and review the release
|
|
7
|
+
> notes before upgrading.
|
|
8
|
+
|
|
3
9
|
The core AndCo SDK: one client shape (`AndcoClient`) with a builder per runtime — browser,
|
|
4
|
-
server, and CLI. There is no separate `@andco/sdk-js` package; this package is the universal
|
|
10
|
+
server, and CLI. There is no separate `@andco/andco-sdk-js` package; this package is the universal
|
|
5
11
|
one, and its `./browser`, `./server`, and `./cli` subpaths are how it adapts to each runtime.
|
|
6
12
|
|
|
7
13
|
## Install
|
|
@@ -74,7 +80,7 @@ const { data, error } = await andco.rest.http.GET("/catalog/permissions");
|
|
|
74
80
|
if (error) throw error;
|
|
75
81
|
```
|
|
76
82
|
|
|
77
|
-
##
|
|
83
|
+
## Attaching credentials
|
|
78
84
|
|
|
79
85
|
Two ways to get an `AndcoClientAuthed`:
|
|
80
86
|
|
|
@@ -83,8 +89,8 @@ Two ways to get an `AndcoClientAuthed`:
|
|
|
83
89
|
for the common server case where a handler already pulled a token out of its own session:
|
|
84
90
|
|
|
85
91
|
```ts
|
|
86
|
-
const
|
|
87
|
-
const { data } = await
|
|
92
|
+
const client = andco.with(await accessTokenFromCookie(request.headers));
|
|
93
|
+
const { data } = await client.rest.http.GET("/userinfo");
|
|
88
94
|
```
|
|
89
95
|
|
|
90
96
|
- `andco.authorized()` returns the instance's own session, seen as authorized — a fresh reference
|
|
@@ -97,20 +103,23 @@ Two ways to get an `AndcoClientAuthed`:
|
|
|
97
103
|
|
|
98
104
|
`AndcoCredentials` is the entire contract the SDK needs for authorization:
|
|
99
105
|
|
|
100
|
-
```ts
|
|
106
|
+
```ts nocheck
|
|
101
107
|
export interface AndcoCredentials {
|
|
102
108
|
accessTokenFor(resource: string): Promise<string | null>;
|
|
109
|
+
readonly config?: AndcoConfig;
|
|
103
110
|
}
|
|
104
111
|
```
|
|
105
112
|
|
|
106
|
-
|
|
113
|
+
`config` is optional: `andco.with()`, `andco.authorized()` and `service.credentials()` carry it, so
|
|
114
|
+
`new Bank(credentials)` takes its client id and endpoint from the credentials instead of declaring
|
|
115
|
+
them again. Any object with `accessTokenFor` works — a Better Auth account, a Passport session, a row in your
|
|
107
116
|
own table, or a plain closure — without adopting the SDK's own persistence.
|
|
108
117
|
|
|
109
118
|
## Resource Server Definitions
|
|
110
119
|
|
|
111
120
|
A Resource Server Definition is any object that implements:
|
|
112
121
|
|
|
113
|
-
```ts
|
|
122
|
+
```ts nocheck
|
|
114
123
|
interface AndcoResourceServer<Client> {
|
|
115
124
|
readonly id: string;
|
|
116
125
|
readonly resource: string;
|
|
@@ -125,67 +134,102 @@ satisfy `AndcoCredentials` and can be passed straight to `use(...)`.
|
|
|
125
134
|
|
|
126
135
|
## Intents
|
|
127
136
|
|
|
128
|
-
`andco.intents` is domain-neutral
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
137
|
+
`andco.intents` is the domain-neutral lifecycle of an Intent: read, present, approve, reject,
|
|
138
|
+
cancel and follow it. Creating one belongs to the Resource Server Definition that owns the domain —
|
|
139
|
+
`bank.transfers.create`, `bank.deposits.create`, `bank.accounts.create` in
|
|
140
|
+
[`@andco/bank-sdk`](https://www.npmjs.com/package/@andco/bank-sdk) — composed over this same
|
|
141
|
+
surface (see [Resource Server Definitions](#resource-server-definitions) above). Every method takes
|
|
142
|
+
the Intent or its id.
|
|
133
143
|
|
|
134
144
|
```ts
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
},
|
|
145
|
+
import { Bank } from "@andco/bank-sdk";
|
|
146
|
+
|
|
147
|
+
const bank = new Bank(andco.authorized());
|
|
148
|
+
|
|
149
|
+
const { data: deposit, error } = await bank.deposits.create(
|
|
150
|
+
{ amount: { currency: "CLP", value: "25990" } },
|
|
151
|
+
{ idempotencyKey: "deposit:8472" },
|
|
141
152
|
);
|
|
142
153
|
if (error) throw error;
|
|
154
|
+
|
|
155
|
+
const presentation = andco.intents.present(deposit, { presentation: "popup" });
|
|
156
|
+
const { data } = await presentation.result;
|
|
157
|
+
if (data?.outcome === "complete") console.log("[deposit] %s", data.intent.status);
|
|
143
158
|
```
|
|
144
159
|
|
|
145
|
-
- `
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
- `
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
`
|
|
158
|
-
|
|
159
|
-
|
|
160
|
+
- `present(intent | id | factory, { presentation, returnTo, errorReturnTo, signal })` — returns a
|
|
161
|
+
controller `{ status, result, close() }` at once. `presentation` is `"popup"` (default),
|
|
162
|
+
`"newtab"` or `"redirect"`. The window opens inside the click that called it; `result` settles
|
|
163
|
+
with `{ outcome, intent }`, the Intent read again afterwards. With `"redirect"` the document is
|
|
164
|
+
replaced and `result` never settles. The SDK always generates and checks `state`.
|
|
165
|
+
- `fromCallback(url, { state? })` — on the return page of a `"redirect"` or `"newtab"`, reads the
|
|
166
|
+
outcome and the Intent. It refuses a `state` this browser did not start (`invalid_callback`) and
|
|
167
|
+
consumes each presentation once.
|
|
168
|
+
- `presentationURL(intent, { presentation })` — synchronous URL that presents the Intent, with its
|
|
169
|
+
callbacks bound; `presentationURLFor(intentId, options)` reads the Intent first. Never intercepted
|
|
170
|
+
by a native Host.
|
|
171
|
+
- `get(intent)` — reads the authoritative state of one Intent, as `Result<AndcoIntent>`.
|
|
172
|
+
- `approve(intent, { grant? })` — approves with this credential's Grant, which must hold `approve`
|
|
173
|
+
on the Intent's family, within its maximum. Fails with `amount_out_of_grant` above the maximum
|
|
174
|
+
(the Intent stays pending for another approver) and `intent_expired` past its deadline.
|
|
175
|
+
- `reject(intent, { reason })` — rejects as an approver; `cancel(intent, { reason })` withdraws it as
|
|
176
|
+
the creator. The reason is required, 1–280 characters.
|
|
177
|
+
- `subscribe(intent, [{ types, after, onCheckpoint, onError, pollIntervalMs }], handler)` — follows
|
|
178
|
+
the Intent's durable facts in order. Returns an unsubscribe function.
|
|
179
|
+
- `events(intent, after?, signal?)` — reads one page of durable Intent facts.
|
|
180
|
+
- `create(type, input, options?)` and `createSigned(type, instruction)` — the generic creation a
|
|
181
|
+
third-party Resource Server Definition composes. `type` is sent as the body's own `type` and
|
|
182
|
+
checked against the response.
|
|
183
|
+
|
|
184
|
+
A Service (`createAndcoService` from `@andco/sdk/server`) gets the same surface, bound to its
|
|
185
|
+
Client Credentials for one Organization, with `service.intents({ subject?, grant? })`:
|
|
160
186
|
|
|
161
187
|
```ts
|
|
162
|
-
|
|
188
|
+
await service.intents({ subject: { type: "organization", identifier: "123" } }).approve(transferId);
|
|
189
|
+
```
|
|
163
190
|
|
|
164
|
-
|
|
165
|
-
|
|
191
|
+
The browser must retrieve the authoritative Intent state after presentation; callback parameters
|
|
192
|
+
are correlation data, not financial state. In React, `useIntent(intentId | null)` from
|
|
193
|
+
`@andco/sdk-react` tracks an Intent until it reaches a terminal status.
|
|
166
194
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
195
|
+
## Tokens, Grants and Resource Servers
|
|
196
|
+
|
|
197
|
+
A Resource Server of your own verifies what reaches it with the same instance:
|
|
198
|
+
|
|
199
|
+
```ts
|
|
200
|
+
const { data: claims, error } = await andco.tokens.verify(bearer, {
|
|
201
|
+
audience: "https://api.casa-norte.example",
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
const { data: grant } = await andco.grants.get(bearer);
|
|
205
|
+
|
|
206
|
+
const orders = andco.resource({ resource: "https://api.casa-norte.example" });
|
|
207
|
+
export async function GET(request: Request) {
|
|
208
|
+
const outcome = await orders.protect({ scopes: ["orders:read"] })(request);
|
|
209
|
+
if (outcome instanceof Response) return outcome; // 401 or 403 with WWW-Authenticate
|
|
210
|
+
return Response.json({ subject: outcome.data?.subject });
|
|
211
|
+
}
|
|
174
212
|
```
|
|
175
213
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
214
|
+
- `tokens.verify(token, { audience?, clockToleranceSeconds? })` verifies signature (RS256/ES256),
|
|
215
|
+
`exp`, `nbf`, `iss` and `aud` offline against the Authorization Server's JWKS, cached by `kid`.
|
|
216
|
+
- `grants.get(token)` reads the live Grant behind a token (`GET /authorization`), in its Grant form.
|
|
217
|
+
- `resource({ resource }).protect({ scopes, authorizationDetails })` is a guard that answers
|
|
218
|
+
`401`/`403` with `WWW-Authenticate`; `.hono()` and `.express()` adapt it to a framework.
|
|
219
|
+
|
|
220
|
+
Concurrent token refreshes share one in-flight promise through `AndcoInflight` (`MemoryInflight` by
|
|
221
|
+
default; pass your own as `inflight` to share it across processes). `auth.refresh()` goes through
|
|
222
|
+
it too.
|
|
179
223
|
|
|
180
224
|
## Exports
|
|
181
225
|
|
|
182
226
|
- `@andco/sdk` — `AndcoClient`, `AndcoAuth`, `AndcoConfig`, credential types and helpers,
|
|
183
|
-
`AndcoIntents`, `AndcoOAuth`, `AndcoRest`, `AndcoSessionStore`, storage adapters, and error
|
|
227
|
+
`AndcoIntents`, `AndcoTokens`, `AndcoGrants`, `AndcoOAuth`, `AndcoRest`, `AndcoSessionStore`, storage adapters, and error
|
|
184
228
|
types. See `packages/sdk/src/index.ts` for the exhaustive list.
|
|
185
229
|
- `@andco/sdk/browser` — `createAndcoInstanceForBrowser`, `browserPresenter`, the popup/iframe
|
|
186
230
|
bridge, and the Andco Button controller.
|
|
187
231
|
- `@andco/sdk/server` — `createAndcoInstanceForServer`.
|
|
188
|
-
- `@andco/sdk/cli` — `createAndcoInstanceForCLI`, `
|
|
232
|
+
- `@andco/sdk/cli` — `createAndcoInstanceForCLI`, `AndcoPresenterCli`.
|
|
189
233
|
|
|
190
234
|
There is intentionally no wildcard subpath export. This prevents internal files and runtime-only
|
|
191
235
|
implementations from becoming accidental public API.
|
|
@@ -194,6 +238,6 @@ implementations from becoming accidental public API.
|
|
|
194
238
|
|
|
195
239
|
- Never place confidential OAuth client secrets in browser code.
|
|
196
240
|
- Use a distinct OAuth client for each environment.
|
|
197
|
-
- Request the smallest
|
|
241
|
+
- Request the smallest permissions needed by the current operation: OIDC scopes only for identity, and a Grant Request per resource.
|
|
198
242
|
- Preserve idempotency keys across retries of the same financial operation.
|
|
199
243
|
- Treat all returned identity, account, and profile fields as personal data.
|
package/dist/auth.d.ts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import { type AndCoRandomSource } from "@andco/protocol";
|
|
2
1
|
import type { AndcoConfig } from "./config.js";
|
|
3
2
|
import type { AndcoSession } from "./credentials.js";
|
|
4
3
|
import { Result } from "./errors.js";
|
|
4
|
+
import { type AndcoGlobals } from "./globals.js";
|
|
5
5
|
import type { AndcoAuthorizationOptions, AndcoAuthorizationRequest, AndcoOAuth } from "./oauth.js";
|
|
6
6
|
import type { AndcoPresentation, AndcoPresenter } from "./presenter.js";
|
|
7
7
|
import type { AndcoSessionStore } from "./session-store.js";
|
|
8
|
+
/** Authorization parameters plus the presentation used to start sign-in. */
|
|
8
9
|
export type AndcoSignInOptions = AndcoAuthorizationOptions & {
|
|
9
10
|
presentation?: AndcoPresentation;
|
|
10
11
|
};
|
|
12
|
+
/** Collaborators that prepare, present, exchange, and persist one authorization. */
|
|
11
13
|
export type AndcoAuthOptions = {
|
|
12
14
|
/** Read for the registered callback, which has to be known before the request is prepared. */
|
|
13
15
|
config: AndcoConfig;
|
|
@@ -16,8 +18,8 @@ export type AndcoAuthOptions = {
|
|
|
16
18
|
presenter: AndcoPresenter;
|
|
17
19
|
/** Generates the Presentation ID. Injected so a test can make one deterministic. */
|
|
18
20
|
randomId?: () => string;
|
|
19
|
-
/**
|
|
20
|
-
|
|
21
|
+
/** Platform capabilities; `crypto` sources the Presentation ID. Defaults to the runtime's own. */
|
|
22
|
+
globals?: AndcoGlobals;
|
|
21
23
|
/**
|
|
22
24
|
* Persists a transaction before it is presented.
|
|
23
25
|
*
|
package/dist/auth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAqB,MAAM,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,KAAK,YAAY,EAAkB,MAAM,cAAc,CAAC;AACjE,OAAO,KAAK,EAAE,yBAAyB,EAAE,yBAAyB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACnG,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACxE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE5D,4EAA4E;AAC5E,MAAM,MAAM,kBAAkB,GAAG,yBAAyB,GAAG;IAC3D,YAAY,CAAC,EAAE,iBAAiB,CAAC;CAClC,CAAC;AAEF,oFAAoF;AACpF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,8FAA8F;IAC9F,MAAM,EAAE,WAAW,CAAC;IACpB,KAAK,EAAE,UAAU,CAAC;IAClB,KAAK,EAAE,iBAAiB,CAAC;IACzB,SAAS,EAAE,cAAc,CAAC;IAC1B,oFAAoF;IACpF,QAAQ,CAAC,EAAE,MAAM,MAAM,CAAC;IACxB,kGAAkG;IAClG,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,CAAC,OAAO,EAAE,yBAAyB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnF,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,SAAS;;gBAID,OAAO,EAAE,gBAAgB;IAK5C;;;;;OAKG;IACU,MAAM,CAAC,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,MAAM,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IA4C3F,iGAAiG;IACpF,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAI7C;;;;;;;;OAQG;IACI,QAAQ,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,KAAK,IAAI,GAAG,MAAM,IAAI;IAK7E,gFAAgF;IACnE,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IAO/D,uFAAuF;IAC1E,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;CAGtD"}
|
package/dist/auth.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { RANDOM_UUID, SafeURL } from "@andco/protocol";
|
|
2
2
|
import { ANDCO_ERROR_CODES, Result } from "./errors.js";
|
|
3
|
+
import { resolveGlobals } from "./globals.js";
|
|
3
4
|
/**
|
|
4
5
|
* Sign-in for a runtime that owns exactly one session.
|
|
5
6
|
*
|
|
@@ -19,8 +20,10 @@ import { ANDCO_ERROR_CODES, Result } from "./errors.js";
|
|
|
19
20
|
*/
|
|
20
21
|
export class AndcoAuth {
|
|
21
22
|
#options;
|
|
23
|
+
#globals;
|
|
22
24
|
constructor(options) {
|
|
23
25
|
this.#options = options;
|
|
26
|
+
this.#globals = options.globals ?? resolveGlobals();
|
|
24
27
|
}
|
|
25
28
|
/**
|
|
26
29
|
* Runs one authorization end to end.
|
|
@@ -30,13 +33,13 @@ export class AndcoAuth {
|
|
|
30
33
|
*/
|
|
31
34
|
async signIn(options = {}) {
|
|
32
35
|
const { config, oauth, presenter, store } = this.#options;
|
|
33
|
-
const presentationId = this.#options.randomId ? this.#options.randomId() : RANDOM_UUID(this.#
|
|
36
|
+
const presentationId = this.#options.randomId ? this.#options.randomId() : RANDOM_UUID(this.#globals.crypto);
|
|
34
37
|
// The callback has to be known before the request is prepared, because the presenter needs it
|
|
35
38
|
// to open its window. It is registered configuration, so it is knowable without the round trip.
|
|
36
39
|
const configured = options.redirectTo ?? config.redirectTo;
|
|
37
40
|
if (!configured) {
|
|
38
41
|
return Result.fail(ANDCO_ERROR_CODES.INVALID_CONFIGURATION, {
|
|
39
|
-
message: "redirectTo is required, on the instance or on the request",
|
|
42
|
+
message: "`redirectTo` is required, on the instance or on the request",
|
|
40
43
|
});
|
|
41
44
|
}
|
|
42
45
|
// Deferred: preparing the request may push it (PAR), and a presenter has to open its window
|
|
@@ -46,11 +49,12 @@ export class AndcoAuth {
|
|
|
46
49
|
kind: "oauth",
|
|
47
50
|
url: async () => {
|
|
48
51
|
const request = await oauth.createAuthorizationRequest(options);
|
|
49
|
-
if (request.error)
|
|
50
|
-
|
|
52
|
+
if (request.error) {
|
|
53
|
+
throw request.error;
|
|
54
|
+
}
|
|
51
55
|
await this.#options.persistTransaction?.(request.data);
|
|
52
56
|
prepared = request.data;
|
|
53
|
-
return
|
|
57
|
+
return request.data.authorizationUrl;
|
|
54
58
|
},
|
|
55
59
|
presentation: options.presentation ?? "popup",
|
|
56
60
|
returnTo: new SafeURL(configured),
|
|
@@ -98,17 +102,6 @@ export class AndcoAuth {
|
|
|
98
102
|
}
|
|
99
103
|
/** Refreshes now rather than waiting for a token request to find the session stale. */
|
|
100
104
|
async refresh() {
|
|
101
|
-
|
|
102
|
-
await store.ready;
|
|
103
|
-
const current = store.getSnapshot();
|
|
104
|
-
if (!current?.refreshToken)
|
|
105
|
-
return Result.fail(ANDCO_ERROR_CODES.SESSION_MISSING);
|
|
106
|
-
const refreshed = await this.#options.oauth.refresh(current.refreshToken);
|
|
107
|
-
if (refreshed.error)
|
|
108
|
-
return Result.fail(refreshed.error);
|
|
109
|
-
const written = await store.set(refreshed.data);
|
|
110
|
-
if (written.error)
|
|
111
|
-
return Result.fail(written.error);
|
|
112
|
-
return Result.ok(refreshed.data);
|
|
105
|
+
return this.#options.store.refresh();
|
|
113
106
|
}
|
|
114
107
|
}
|
|
@@ -8,15 +8,18 @@ import type { AndcoPresentation } from "../presenter.js";
|
|
|
8
8
|
export type AndcoButtonFlow = "authorization" | "intent";
|
|
9
9
|
/** Observable lifecycle of one hosted Andco surface. */
|
|
10
10
|
export type AndcoButtonStatus = "idle" | "connecting" | "ready" | "authorizing" | "complete" | "dismissed" | "error";
|
|
11
|
+
/** Hosted surface URL, lifecycle status, and the most recent failure. */
|
|
11
12
|
export type AndcoButtonSnapshot = {
|
|
12
13
|
status: AndcoButtonStatus;
|
|
13
14
|
iframeURL: string;
|
|
14
15
|
ready: boolean;
|
|
15
16
|
error: AndcoError | null;
|
|
16
17
|
};
|
|
18
|
+
/** Hosted control configuration and callbacks for authorization or Intent presentation. */
|
|
17
19
|
export type AndcoButtonControllerOptions = {
|
|
18
20
|
/** The instance whose configuration, protocol, and session this surface acts on. */
|
|
19
21
|
client: AndcoClient;
|
|
22
|
+
/** Authorization by default; `intent` requires an `intentId`. */
|
|
20
23
|
flow?: AndcoButtonFlow;
|
|
21
24
|
/**
|
|
22
25
|
* Required when `flow` is `"intent"`. Resolved after the user activates, never before: the iframe
|
|
@@ -35,14 +38,20 @@ export type AndcoButtonControllerOptions = {
|
|
|
35
38
|
presentation?: AndcoPresentation;
|
|
36
39
|
/** Request-local authorization options, applied when the user activates the surface. */
|
|
37
40
|
authorization?: AndcoAuthorizationOptions;
|
|
41
|
+
/** Runs after the hosted surface completes its handshake. */
|
|
38
42
|
onReady?: (event: AndCoReadyEvent) => void;
|
|
43
|
+
/** Runs when the user activates the hosted control, before authorization starts. */
|
|
39
44
|
onActivate?: (event: AndCoClickEvent) => void;
|
|
40
45
|
/** Carries the Presentation Result on an Intent flow, and nothing on an authorization. */
|
|
41
46
|
onComplete?: (outcome?: AndcoPresentationResult) => void;
|
|
47
|
+
/** Runs when the user closes or declines the presentation. */
|
|
42
48
|
onDismiss?: () => void;
|
|
49
|
+
/** Receives lifecycle failures; use the error code for diagnostics. */
|
|
43
50
|
onError?: (error: AndcoError, event?: AndCoErrorEvent) => void;
|
|
44
51
|
};
|
|
45
52
|
type Listener = (snapshot: AndcoButtonSnapshot) => void;
|
|
53
|
+
/** Renders a style record — property names only, no camelCase — as inline CSS text. */
|
|
54
|
+
declare function frameStyleText(style: Readonly<Record<string, string>>): string;
|
|
46
55
|
/**
|
|
47
56
|
* Coordinates one hosted Andco iframe without owning how a framework renders it.
|
|
48
57
|
*
|
|
@@ -53,6 +62,15 @@ type Listener = (snapshot: AndcoButtonSnapshot) => void;
|
|
|
53
62
|
* Render `snapshot.iframeURL` in your platform's own iframe element, then hand that element to
|
|
54
63
|
* `connect()` once it mounts.
|
|
55
64
|
*
|
|
65
|
+
* The frame's fixed attributes and default sizing are published as static data —
|
|
66
|
+
* {@link AndcoButtonController.IFRAME_SANDBOX}, {@link AndcoButtonController.IFRAME_REFERRER_POLICY},
|
|
67
|
+
* {@link AndcoButtonController.IFRAME_DEFAULT_STYLE}, and {@link AndcoButtonController.frameProps} for
|
|
68
|
+
* the snapshot-derived `src`/`hidden` pair — so every binding applies the same contract to its own
|
|
69
|
+
* element instead of redeclaring it. `sdk-script`'s custom element is the one exception on sizing: it
|
|
70
|
+
* sizes itself through a stylesheet layer (using {@link AndcoButtonController.DEFAULT_SIZE}, so the
|
|
71
|
+
* visible default stays the same) and stretches its internal iframe to fill it, so that an author's
|
|
72
|
+
* own CSS on `andco-button` wins without touching the iframe's inline style.
|
|
73
|
+
*
|
|
56
74
|
* @example
|
|
57
75
|
* ```ts
|
|
58
76
|
* const controller = new AndcoButtonController({ client: andco, onComplete: reload });
|
|
@@ -62,12 +80,39 @@ type Listener = (snapshot: AndcoButtonSnapshot) => void;
|
|
|
62
80
|
*/
|
|
63
81
|
export declare class AndcoButtonController {
|
|
64
82
|
#private;
|
|
83
|
+
/** @see {@link IFRAME_SANDBOX} */
|
|
84
|
+
static readonly IFRAME_SANDBOX = "allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox";
|
|
85
|
+
/** @see {@link IFRAME_REFERRER_POLICY} */
|
|
86
|
+
static readonly IFRAME_REFERRER_POLICY = "no-referrer";
|
|
87
|
+
/** @see {@link IFRAME_BASE_STYLE} */
|
|
88
|
+
static readonly IFRAME_BASE_STYLE: Readonly<Record<string, string>>;
|
|
89
|
+
/** @see {@link DEFAULT_SIZE} */
|
|
90
|
+
static readonly DEFAULT_SIZE: Readonly<Record<string, string>>;
|
|
91
|
+
/** @see {@link IFRAME_DEFAULT_STYLE} */
|
|
92
|
+
static readonly IFRAME_DEFAULT_STYLE: Readonly<Record<string, string>>;
|
|
93
|
+
/**
|
|
94
|
+
* Renders a style record as inline CSS text, for a binding that needs a string rather than an
|
|
95
|
+
* object — Svelte's `style` attribute and `sdk-script`'s `HTMLElement.style.cssText`.
|
|
96
|
+
*/
|
|
97
|
+
static readonly frameStyleText: typeof frameStyleText;
|
|
98
|
+
/**
|
|
99
|
+
* The two iframe attributes a snapshot drives, mapped from protocol terms to DOM ones.
|
|
100
|
+
*
|
|
101
|
+
* Every binding applies this on top of {@link AndcoButtonController.IFRAME_SANDBOX} and
|
|
102
|
+
* {@link AndcoButtonController.IFRAME_REFERRER_POLICY}: those two are fixed, this pair changes
|
|
103
|
+
* with the connection.
|
|
104
|
+
*/
|
|
105
|
+
static frameProps(snapshot: AndcoButtonSnapshot): {
|
|
106
|
+
src: string;
|
|
107
|
+
hidden: boolean;
|
|
108
|
+
};
|
|
65
109
|
constructor(options: AndcoButtonControllerOptions);
|
|
66
110
|
get snapshot(): AndcoButtonSnapshot;
|
|
67
111
|
/** Observes lifecycle changes. The return value is the unsubscribe. */
|
|
68
112
|
subscribe(listener: Listener): () => void;
|
|
69
113
|
/** Attaches a mounted iframe. The return value disconnects it. */
|
|
70
114
|
connect(iframe: HTMLIFrameElement): () => void;
|
|
115
|
+
/** Detaches the iframe bridge while retaining listeners and the last iframe for reuse. */
|
|
71
116
|
disconnect(): void;
|
|
72
117
|
/**
|
|
73
118
|
* Returns the surface to its ready state so it can be activated again.
|
|
@@ -78,6 +123,7 @@ export declare class AndcoButtonController {
|
|
|
78
123
|
* recover, so this reconnects against the last-connected iframe instead.
|
|
79
124
|
*/
|
|
80
125
|
restart(): void;
|
|
126
|
+
/** Releases the bridge, iframe reference, and all snapshot listeners. */
|
|
81
127
|
destroy(): void;
|
|
82
128
|
}
|
|
83
129
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../src/browser/controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EAIf,eAAe,EACf,cAAc,EACf,MAAM,iBAAiB,CAAC;AAEzB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAqB,UAAU,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,KAAK,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAC5E,OAAO,KAAK,EAAE,yBAAyB,EAA6B,MAAM,aAAa,CAAC;AACxF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAGzD,8CAA8C;AAC9C,MAAM,MAAM,eAAe,GAAG,eAAe,GAAG,QAAQ,CAAC;AAEzD,wDAAwD;AACxD,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,YAAY,GAAG,OAAO,GAAG,aAAa,GAAG,UAAU,GAAG,WAAW,GAAG,OAAO,CAAC;AAErH,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,EAAE,iBAAiB,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC,oFAAoF;IACpF,MAAM,EAAE,WAAW,CAAC;IACpB,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,iFAAiF;IACjF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yEAAyE;IACzE,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,oGAAoG;IACpG,YAAY,CAAC,EAAE,iBAAiB,CAAC;IACjC,wFAAwF;IACxF,aAAa,CAAC,EAAE,yBAAyB,CAAC;IAC1C,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;IAC3C,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;IAC9C,0FAA0F;IAC1F,UAAU,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,uBAAuB,KAAK,IAAI,CAAC;IACzD,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,CAAC,EAAE,eAAe,KAAK,IAAI,CAAC;CAChE,CAAC;AAEF,KAAK,QAAQ,GAAG,CAAC,QAAQ,EAAE,mBAAmB,KAAK,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../src/browser/controller.ts"],"names":[],"mappings":"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"}
|