@ambasada/sdk 0.8.20 → 0.8.22
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 +8 -8
- package/dist/apps.d.ts +1 -1
- package/dist/auth.d.ts +10 -10
- package/dist/client.d.ts +3 -3
- package/dist/define.d.ts +5 -5
- package/dist/errors.d.ts +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/platform.js +9 -9
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @ambasada/sdk
|
|
2
2
|
|
|
3
|
-
The
|
|
3
|
+
The ambasada platform client: Sign in with ambasada, mini-app notifications, the project store, files. Node ≥ 22,
|
|
4
4
|
Bun, Cloudflare Workers, Next.js route handlers. No runtime dependencies.
|
|
5
5
|
|
|
6
6
|
```ts
|
|
@@ -9,8 +9,8 @@ import { ambasada } from "@ambasada/sdk"; // AMBASADA_API_KEY, AMBASADA_API_URL
|
|
|
9
9
|
|
|
10
10
|
| | |
|
|
11
11
|
|---|---|
|
|
12
|
-
| `ambasada.auth.login(req)` / `.callback(req)` / `.user(req)` / `.logout(req)` / `.shell(req)` | Sign in with
|
|
13
|
-
| `ambasada.apps.verifyWebhook(req)` / `.notify({ … })` | the
|
|
12
|
+
| `ambasada.auth.login(req)` / `.callback(req)` / `.user(req)` / `.logout(req)` / `.shell(req)` | Sign in with ambasada (below) |
|
|
13
|
+
| `ambasada.apps.verifyWebhook(req)` / `.notify({ … })` | the ambasada app's events and push notifications (docs/MINI-APPS.md §7) |
|
|
14
14
|
| `ambasada.store.get(key)` / `.put(key, value)` / `.delete(key)` / `.list(prefix)` | JSON key-value, 1 MB per app |
|
|
15
15
|
| `ambasada.files.put(data)` | a file at a public URL |
|
|
16
16
|
| `ambasada.project()`, `ambasada.keys()` | the app's id; the API's signing keys |
|
|
@@ -25,16 +25,16 @@ The Hack A Ton economy (TON, checkout, offerings, the agent) is the add-on
|
|
|
25
25
|
`VillageError`, `offering`, `defineCompany`, …) are still exported here, `@deprecated`, until 2.0. Note that
|
|
26
26
|
`createAmbasada` returns the platform client from 0.8; selling code uses `createVillage`.
|
|
27
27
|
|
|
28
|
-
## Sign in with
|
|
28
|
+
## Sign in with ambasada
|
|
29
29
|
|
|
30
|
-
People sign in to your product with their
|
|
30
|
+
People sign in to your product with their ambasada ID (OAuth 2.0 code flow + PKCE, consent screen). Zero
|
|
31
31
|
setup on `https://<slug>.ambasada.dev`: `ambasada deploy` injects `AMBASADA_AUTH_CLIENT_ID`,
|
|
32
32
|
`AMBASADA_AUTH_CLIENT_SECRET` and `AMBASADA_AUTH_ISSUER`; `ambasada dev` injects fake ones and its mock signs you in
|
|
33
33
|
as "Fake Visitor". Fetch-style handlers for Workers and Next.js route handlers, WebCrypto only.
|
|
34
34
|
|
|
35
35
|
```ts
|
|
36
36
|
const ambasada = createAmbasada({ env });
|
|
37
|
-
// GET /api/auth/login?returnTo=/path → redirect to the
|
|
37
|
+
// GET /api/auth/login?returnTo=/path → redirect to the ambasada ID consent screen
|
|
38
38
|
return ambasada.auth.login(req, { returnTo: "/", email: true }); // email: also ask for their email
|
|
39
39
|
// GET /api/auth/callback → checks state, exchanges the code, reads the profile, sets the cookie
|
|
40
40
|
return ambasada.auth.callback(req);
|
|
@@ -45,7 +45,7 @@ return ambasada.auth.logout(req, { returnTo: "/" });
|
|
|
45
45
|
```
|
|
46
46
|
|
|
47
47
|
`AmbasadaUser` is `{ id, name, avatar_url, builder, builder_since, member_since, pass, email? }`: `id` is the
|
|
48
|
-
stable
|
|
48
|
+
stable ambasada ID (`user_…`), `pass` is `"builder" | "visitor" | "staff" | null` for the current event, `email`
|
|
49
49
|
only if you asked and they agreed. The session is an HttpOnly `SameSite=Lax` cookie (`__Host-ambasada_session`
|
|
50
50
|
on https) holding the signed profile (HMAC-SHA256, key derived from the client secret), valid 7 days; no token is
|
|
51
51
|
kept. `returnTo` must be a path on your site. `user()` also takes `{ headers }` (Next server components:
|
|
@@ -55,4 +55,4 @@ origin of the callback URL when the Worker sees another host. More: docs/AUTH.md
|
|
|
55
55
|
|
|
56
56
|
`pnpm build` writes `dist/` (one ESM file + `.d.ts`); `publishConfig` points the published package at it.
|
|
57
57
|
|
|
58
|
-
Built by [CENTYSO](https://centyso.com) for [
|
|
58
|
+
Built by [CENTYSO](https://centyso.com) for [ambasada](https://ambasada.id). MIT.
|
package/dist/apps.d.ts
CHANGED
|
@@ -56,7 +56,7 @@ export interface AppNotifyInput {
|
|
|
56
56
|
url: string;
|
|
57
57
|
/** Notification tokens from `notifications_enabled`, ≤ 100 per call. */
|
|
58
58
|
tokens?: string[];
|
|
59
|
-
/**
|
|
59
|
+
/** ambasada user ids, for Verified apps with the identity scope. */
|
|
60
60
|
users?: string[];
|
|
61
61
|
/** Default "transactional", the only one Listed apps may send. */
|
|
62
62
|
category?: NotificationCategory;
|
package/dist/auth.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* "Sign in with
|
|
2
|
+
* "Sign in with ambasada" (docs/AUTH.md): OAuth 2.0 authorization code + PKCE against ambasada ID,
|
|
3
3
|
* for products hosted at https://<slug>.ambasada.dev. The deploy injects AMBASADA_AUTH_CLIENT_ID,
|
|
4
4
|
* AMBASADA_AUTH_CLIENT_SECRET and AMBASADA_AUTH_ISSUER; `ambasada dev` injects fake ones that point at
|
|
5
5
|
* the mock. Four fetch handlers, WebCrypto only, no dependencies:
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* derived from the client secret), host-only (`__Host-` on https), so no other *.ambasada.dev product
|
|
14
14
|
* can read, set or forge it. No access token is kept.
|
|
15
15
|
*
|
|
16
|
-
* Silent sign-in inside the
|
|
16
|
+
* Silent sign-in inside the ambasada app (docs/MINI-APPS.md §5): the page asks the shell for a short-lived
|
|
17
17
|
* token (`auth.getToken()` from @ambasada/miniapp) and POSTs it here; the same session cookie comes back:
|
|
18
18
|
*
|
|
19
19
|
* POST /api/auth/shell { token } → return ambasada.auth.shell(req) // { user } + cookie
|
|
@@ -21,11 +21,11 @@
|
|
|
21
21
|
*/
|
|
22
22
|
/** The person signed in, as `GET /v1/auth/profile` returns them. */
|
|
23
23
|
export interface AmbasadaUser {
|
|
24
|
-
/**
|
|
24
|
+
/** ambasada ID user id (`user_…`): stable, use it as your user key. */
|
|
25
25
|
id: string;
|
|
26
26
|
name: string | null;
|
|
27
27
|
avatar_url: string | null;
|
|
28
|
-
/** Has the builder flag on their
|
|
28
|
+
/** Has the builder flag on their ambasada ID. */
|
|
29
29
|
builder: boolean;
|
|
30
30
|
builder_since: string | null;
|
|
31
31
|
member_since: string | null;
|
|
@@ -42,7 +42,7 @@ export interface AuthConfig {
|
|
|
42
42
|
audiences?: string[];
|
|
43
43
|
/** The hub (default https://hub.ambasada.id): signs shell tokens, and has the full profile behind one (`/api/apps/profile`). */
|
|
44
44
|
hubUrl?: string;
|
|
45
|
-
/** Where `/v1/auth/profile` lives (the
|
|
45
|
+
/** Where `/v1/auth/profile` lives (the ambasada API). */
|
|
46
46
|
apiUrl: string;
|
|
47
47
|
/** Your public origin if the Worker sees another host (behind a proxy). Default: the request's origin. */
|
|
48
48
|
publicUrl?: string;
|
|
@@ -83,7 +83,7 @@ export declare function readCookie(req: WithHeaders, name: string): string | nul
|
|
|
83
83
|
* any control character (browsers drop tabs and newlines, so "/\t/evil" would become "//evil").
|
|
84
84
|
*/
|
|
85
85
|
export declare function safeReturnTo(value: string | null | undefined): string;
|
|
86
|
-
/** Who a verified shell token is for. Name and avatar are what
|
|
86
|
+
/** Who a verified shell token is for. Name and avatar are what ambasada ID had when the shell minted it. */
|
|
87
87
|
export interface ShellUser {
|
|
88
88
|
user_id: string;
|
|
89
89
|
name: string | null;
|
|
@@ -99,11 +99,11 @@ export declare class AmbasadaAuth {
|
|
|
99
99
|
/** True when the product has credentials (hosted on ambasada.dev, or `ambasada dev`). */
|
|
100
100
|
get configured(): boolean;
|
|
101
101
|
private settings;
|
|
102
|
-
/** Redirects to
|
|
102
|
+
/** Redirects to ambasada ID (consent screen) with PKCE; a short-lived signed cookie holds the state. */
|
|
103
103
|
login(req: Request, options?: LoginOptions): Promise<Response>;
|
|
104
104
|
/**
|
|
105
|
-
*
|
|
106
|
-
* verifier and the client secret), reads their profile from the
|
|
105
|
+
* ambasada ID sends the person back here: checks the state, exchanges the code (with the PKCE
|
|
106
|
+
* verifier and the client secret), reads their profile from the ambasada API, sets the session cookie
|
|
107
107
|
* and redirects to `returnTo`. A refused consent goes back to `returnTo` signed out.
|
|
108
108
|
*/
|
|
109
109
|
callback(req: Request): Promise<Response>;
|
|
@@ -111,7 +111,7 @@ export declare class AmbasadaAuth {
|
|
|
111
111
|
user(req: WithHeaders): Promise<AmbasadaUser | null>;
|
|
112
112
|
/**
|
|
113
113
|
* Verifies a shell token (RS256, 60 s) from the hub (`<AMBASADA_HUB_URL>/.well-known/jwks.json`, default
|
|
114
|
-
* https://hub.ambasada.id) or
|
|
114
|
+
* https://hub.ambasada.id) or ambasada ID (`<AMBASADA_AUTH_ISSUER>/.well-known/jwks.json`, the Clerk JWT template
|
|
115
115
|
* `ambasada-miniapp`), keys cached 1 h: signature, expiry, issuer, and that its `aud` is this app (its origin or
|
|
116
116
|
* id). Returns who it is, or throws `AmbasadaError("UNAUTHORIZED")`; `NOT_CONFIGURED` (503) while the issuer
|
|
117
117
|
* publishes no keys.
|
package/dist/client.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ export interface AmbasadaOptions {
|
|
|
20
20
|
* an `AMBASADA_API` service binding there: the client calls the API through it (no zone in between, so no 522).
|
|
21
21
|
*/
|
|
22
22
|
env?: Record<string, unknown>;
|
|
23
|
-
/** Sign in with
|
|
23
|
+
/** Sign in with ambasada credentials. Default: `AMBASADA_AUTH_CLIENT_ID`, `_CLIENT_SECRET`, `_ISSUER`, and `PUBLIC_URL`. */
|
|
24
24
|
auth?: {
|
|
25
25
|
clientId?: string;
|
|
26
26
|
clientSecret?: string;
|
|
@@ -54,7 +54,7 @@ export interface RawResponse {
|
|
|
54
54
|
json: unknown;
|
|
55
55
|
}
|
|
56
56
|
/**
|
|
57
|
-
* The
|
|
57
|
+
* The ambasada client. `import { ambasada } from "@ambasada/sdk"` gives one configured from the
|
|
58
58
|
* environment; `createAmbasada({ env })` makes another (e.g. from a Worker's `env`).
|
|
59
59
|
*/
|
|
60
60
|
export declare class Ambasada {
|
|
@@ -71,7 +71,7 @@ export declare class Ambasada {
|
|
|
71
71
|
get apiUrl(): string;
|
|
72
72
|
protected get apiKey(): string;
|
|
73
73
|
/**
|
|
74
|
-
* Sign in with
|
|
74
|
+
* Sign in with ambasada (docs/AUTH.md): `login(req)`, `callback(req)`, `user(req)`, `logout(req)`.
|
|
75
75
|
* Credentials come with the deploy to ambasada.dev, and from `ambasada dev` locally.
|
|
76
76
|
*/
|
|
77
77
|
readonly auth: AmbasadaAuth;
|
package/dist/define.d.ts
CHANGED
|
@@ -3,18 +3,18 @@
|
|
|
3
3
|
* (`ambasada dev`, `deploy`, `link`, `check`). Add-ons add their own keys (the village: `idea`, `offerings`, `agent`).
|
|
4
4
|
*/
|
|
5
5
|
/**
|
|
6
|
-
* The app's
|
|
6
|
+
* The app's ambasada mini app (docs/MINI-APPS.md §15, decision 15.6): optional. With it, `ambasada dev` and `deploy`
|
|
7
7
|
* serve /.well-known/ambasada.json (from this and `id`, `name`, `description`), `ambasada check` runs the mini-app
|
|
8
|
-
* rules on the entry, and the deploy registers the app with the
|
|
8
|
+
* rules on the entry, and the deploy registers the app with the ambasada app. `ambasada miniapp enable` adds it.
|
|
9
9
|
*/
|
|
10
10
|
export interface MiniAppConfig {
|
|
11
|
-
/** The path the
|
|
11
|
+
/** The path the ambasada app opens: "/app" (the in-app layout under it), or "/" for the whole site. */
|
|
12
12
|
entry: string;
|
|
13
13
|
/** A 1024×1024 opaque PNG, a path on the app (default "/icon.png"). */
|
|
14
14
|
icon?: string;
|
|
15
15
|
/** Who it is for (default "4+"). The hub refuses to register a mini app without one; above 12+ it opens only for people old enough. */
|
|
16
16
|
age_rating?: "4+" | "9+" | "12+" | "17+";
|
|
17
|
-
/** How the
|
|
17
|
+
/** How the ambasada app shows it (default "tool"). */
|
|
18
18
|
kind?: "product" | "event" | "tool" | "assistant";
|
|
19
19
|
/** Where people get help: "mailto:you@team.ro" or an https URL (default: the app's `support`). Needed to be Listed. */
|
|
20
20
|
support?: string;
|
|
@@ -57,7 +57,7 @@ export interface AppConfig {
|
|
|
57
57
|
product?: {
|
|
58
58
|
url?: string;
|
|
59
59
|
};
|
|
60
|
-
/** The mini app inside the
|
|
60
|
+
/** The mini app inside the ambasada app, at a path of this one. Off without it. */
|
|
61
61
|
miniapp?: MiniAppConfig;
|
|
62
62
|
/** age_rating, kind and tags, for an app with or without a mini app (the mini app's own win). */
|
|
63
63
|
listing?: ListingConfig;
|
package/dist/errors.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Typed errors. Every
|
|
2
|
+
* Typed errors. Every ambasada API failure is `{ "error": { "code", "message", ... } }` with a code
|
|
3
3
|
* from `@ambasada/protocol` (packages/protocol/src/errors.ts); the SDK turns it into an `AmbasadaError`
|
|
4
4
|
* with the same `code`, so you branch on `error.code` instead of parsing messages:
|
|
5
5
|
*
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @ambasada/sdk: the
|
|
2
|
+
* @ambasada/sdk: the ambasada platform client. Sign-in, mini-app notifications, the project store, files.
|
|
3
3
|
*
|
|
4
4
|
* import { ambasada } from "@ambasada/sdk"; // AMBASADA_API_KEY, AMBASADA_API_URL from the env
|
|
5
|
-
* const user = await ambasada.auth.user(req); // Sign in with
|
|
5
|
+
* const user = await ambasada.auth.user(req); // Sign in with ambasada (docs/AUTH.md)
|
|
6
6
|
* await ambasada.apps.notify({ notification_id, title, body, url, users }); // mini-app pushes (docs/MINI-APPS.md §7)
|
|
7
7
|
* await ambasada.store.put("settings", { theme: "dark" });
|
|
8
8
|
*
|
package/dist/platform.js
CHANGED
|
@@ -435,7 +435,7 @@ var AmbasadaAuth = class {
|
|
|
435
435
|
settings(req) {
|
|
436
436
|
const c = this.config();
|
|
437
437
|
if (!c.clientId || !c.clientSecret || !c.issuer) {
|
|
438
|
-
throw new AmbasadaError("CONFIG", "Sign in with
|
|
438
|
+
throw new AmbasadaError("CONFIG", "Sign in with ambasada is not set up: it works on https://<slug>.ambasada.dev after `ambasada deploy`, and locally under `ambasada dev` (AMBASADA_AUTH_CLIENT_ID / _SECRET / _ISSUER)");
|
|
439
439
|
}
|
|
440
440
|
const origin = (c.publicUrl ? new URL(c.publicUrl) : new URL(req.url)).origin;
|
|
441
441
|
return {
|
|
@@ -450,7 +450,7 @@ var AmbasadaAuth = class {
|
|
|
450
450
|
fetch: c.fetch ?? ((input, init) => fetch(input, init))
|
|
451
451
|
};
|
|
452
452
|
}
|
|
453
|
-
/** Redirects to
|
|
453
|
+
/** Redirects to ambasada ID (consent screen) with PKCE; a short-lived signed cookie holds the state. */
|
|
454
454
|
async login(req, options = {}) {
|
|
455
455
|
const s = this.settings(req);
|
|
456
456
|
const { verifier, challenge } = await pkce();
|
|
@@ -470,8 +470,8 @@ var AmbasadaAuth = class {
|
|
|
470
470
|
return redirect(url.toString(), [setCookie(STATE_COOKIE, value, s.secure, STATE_TTL_S)]);
|
|
471
471
|
}
|
|
472
472
|
/**
|
|
473
|
-
*
|
|
474
|
-
* verifier and the client secret), reads their profile from the
|
|
473
|
+
* ambasada ID sends the person back here: checks the state, exchanges the code (with the PKCE
|
|
474
|
+
* verifier and the client secret), reads their profile from the ambasada API, sets the session cookie
|
|
475
475
|
* and redirects to `returnTo`. A refused consent goes back to `returnTo` signed out.
|
|
476
476
|
*/
|
|
477
477
|
async callback(req) {
|
|
@@ -496,9 +496,9 @@ var AmbasadaAuth = class {
|
|
|
496
496
|
body: new URLSearchParams({ grant_type: "authorization_code", code, redirect_uri: s.redirectUri, code_verifier: saved.verifier })
|
|
497
497
|
});
|
|
498
498
|
const tokens = await token.json().catch(() => ({}));
|
|
499
|
-
if (!token.ok || !tokens.access_token) throw new AmbasadaError("UNAUTHORIZED", `
|
|
499
|
+
if (!token.ok || !tokens.access_token) throw new AmbasadaError("UNAUTHORIZED", `ambasada ID refused the sign-in (${tokens.error ?? token.status})`, 401);
|
|
500
500
|
const res = await s.fetch(`${s.apiUrl}/v1/auth/profile`, { headers: { authorization: `Bearer ${tokens.access_token}`, accept: "application/json" } });
|
|
501
|
-
if (!res.ok) throw new AmbasadaError("UPSTREAM_ERROR", `could not read the
|
|
501
|
+
if (!res.ok) throw new AmbasadaError("UPSTREAM_ERROR", `could not read the ambasada profile (${res.status})`, 502);
|
|
502
502
|
const user = await res.json();
|
|
503
503
|
const exp = Math.floor(Date.now() / 1e3) + SESSION_TTL_S;
|
|
504
504
|
const session = await signCookie(s.clientSecret, "session", { user, exp });
|
|
@@ -512,7 +512,7 @@ var AmbasadaAuth = class {
|
|
|
512
512
|
}
|
|
513
513
|
/**
|
|
514
514
|
* Verifies a shell token (RS256, 60 s) from the hub (`<AMBASADA_HUB_URL>/.well-known/jwks.json`, default
|
|
515
|
-
* https://hub.ambasada.id) or
|
|
515
|
+
* https://hub.ambasada.id) or ambasada ID (`<AMBASADA_AUTH_ISSUER>/.well-known/jwks.json`, the Clerk JWT template
|
|
516
516
|
* `ambasada-miniapp`), keys cached 1 h: signature, expiry, issuer, and that its `aud` is this app (its origin or
|
|
517
517
|
* id). Returns who it is, or throws `AmbasadaError("UNAUTHORIZED")`; `NOT_CONFIGURED` (503) while the issuer
|
|
518
518
|
* publishes no keys.
|
|
@@ -564,7 +564,7 @@ var AmbasadaAuth = class {
|
|
|
564
564
|
* Answers `{ user }`.
|
|
565
565
|
*/
|
|
566
566
|
async shell(req) {
|
|
567
|
-
if (!this.configured) throw notConfigured("this app has no Sign in with
|
|
567
|
+
if (!this.configured) throw notConfigured("this app has no Sign in with ambasada credentials (AMBASADA_AUTH_*: on ambasada.dev after `ambasada deploy`, and under `ambasada dev`)");
|
|
568
568
|
const s = this.settings(req);
|
|
569
569
|
if (req.method !== "POST") throw new AmbasadaError("INVALID_REQUEST", "POST the shell token as JSON { token }", 405);
|
|
570
570
|
let token = /^Bearer\s+(.+)$/i.exec(req.headers.get("authorization") ?? "")?.[1]?.trim();
|
|
@@ -645,7 +645,7 @@ var Ambasada = class {
|
|
|
645
645
|
return key;
|
|
646
646
|
}
|
|
647
647
|
/**
|
|
648
|
-
* Sign in with
|
|
648
|
+
* Sign in with ambasada (docs/AUTH.md): `login(req)`, `callback(req)`, `user(req)`, `logout(req)`.
|
|
649
649
|
* Credentials come with the deploy to ambasada.dev, and from `ambasada dev` locally.
|
|
650
650
|
*/
|
|
651
651
|
auth = new AmbasadaAuth(() => ({
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ambasada/sdk",
|
|
3
|
-
"version": "0.8.
|
|
4
|
-
"description": "The
|
|
3
|
+
"version": "0.8.22",
|
|
4
|
+
"description": "The ambasada platform SDK: Sign in with ambasada, mini-app notifications, the project store, files.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"sideEffects": false,
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"node": ">=22"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@ambasada/village": "^0.8.
|
|
30
|
+
"@ambasada/village": "^0.8.22"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/node": "^22.20.5",
|