@sovereignfs/sdk 1.25.0 → 2.0.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.
Files changed (55) hide show
  1. package/LICENSE +373 -661
  2. package/README.md +103 -0
  3. package/dist/browser.d.ts +2 -0
  4. package/dist/browser.js +4 -0
  5. package/dist/chunk-NYL4LLXF.js +65 -0
  6. package/dist/index.d.ts +57 -529
  7. package/dist/index.js +1283 -572
  8. package/dist/next.d.ts +37 -0
  9. package/dist/next.js +68 -0
  10. package/dist/session-DJl2XYMc.d.ts +44 -0
  11. package/dist/sovereign-CazGKEVc.d.ts +517 -0
  12. package/dist/testing.d.ts +181 -0
  13. package/dist/testing.js +560 -0
  14. package/package.json +53 -27
  15. package/dist/chunk-MLKGABMK.js +0 -9
  16. package/dist/chunk-NQLGPAL6.js +0 -62
  17. package/dist/e2ee-crypto.d.ts +0 -84
  18. package/dist/e2ee-crypto.js +0 -162
  19. package/dist/e2ee-device.d.ts +0 -21
  20. package/dist/e2ee-device.js +0 -13
  21. package/dist/e2ee-object.d.ts +0 -50
  22. package/dist/e2ee-object.js +0 -75
  23. package/dist/e2ee-state-U-GEJ7oL.d.ts +0 -411
  24. package/dist/e2ee-state.d.ts +0 -1
  25. package/dist/e2ee-state.js +0 -36
  26. package/src/__tests__/e2ee-crypto.test.ts +0 -156
  27. package/src/__tests__/e2ee-object.test.ts +0 -90
  28. package/src/__tests__/e2ee-state.test.ts +0 -83
  29. package/src/__tests__/sdk.test.ts +0 -427
  30. package/src/__tests__/sessions.test.ts +0 -63
  31. package/src/activity.ts +0 -19
  32. package/src/auth.ts +0 -134
  33. package/src/connections.ts +0 -106
  34. package/src/data.ts +0 -88
  35. package/src/db.ts +0 -34
  36. package/src/directory.ts +0 -31
  37. package/src/e2ee-crypto.ts +0 -260
  38. package/src/e2ee-device.ts +0 -75
  39. package/src/e2ee-object.ts +0 -118
  40. package/src/e2ee-state.ts +0 -72
  41. package/src/e2ee.ts +0 -65
  42. package/src/env.ts +0 -37
  43. package/src/errors.ts +0 -46
  44. package/src/host.ts +0 -251
  45. package/src/index.ts +0 -160
  46. package/src/mailer.ts +0 -7
  47. package/src/notifications.ts +0 -36
  48. package/src/platform.ts +0 -10
  49. package/src/plugins.ts +0 -146
  50. package/src/portability.ts +0 -176
  51. package/src/secrets.ts +0 -68
  52. package/src/sessions.ts +0 -36
  53. package/src/storage.ts +0 -49
  54. package/src/types.ts +0 -418
  55. package/src/unimplemented.ts +0 -48
package/dist/next.d.ts ADDED
@@ -0,0 +1,37 @@
1
+ import { Session } from './index.js';
2
+ import { Sovereign } from './index.js';
3
+
4
+ type NextAuthOptions = {
5
+ /** Where the route is mounted. It must match the SDK's `redirectPath` (`/auth/callback`) and `postLogoutPath`. `/auth` unless changed. */
6
+ basePath?: string;
7
+ /** Where a person goes when a sign-in fails. `/` with `?error=signin` unless changed. */
8
+ errorPath?: string;
9
+ /**
10
+ * Deletes what the app holds of a person who deleted their account, and mounts `/auth/account-deleted` for core to tell it so (sdk §9.1). Without
11
+ * it that address is not there. It must be safe to run twice, and it must not resolve until the data is gone.
12
+ */
13
+ onAccountDeleted?: (person: {
14
+ readonly sub: string;
15
+ }) => Promise<void> | void;
16
+ };
17
+ type RouteHandler = (request: Request) => Promise<Response>;
18
+ /**
19
+ * Binds the SDK's sign-in to Next.js (sdk §9): the route handlers for `/auth/login`, `/auth/callback`, `/auth/logout` and
20
+ * `/auth/backchannel-logout` (and `/auth/account-deleted` when `onAccountDeleted` is given), and the session as a Server Component, route or action reads it. Mount the handlers once:
21
+ *
22
+ * // app/auth/[action]/route.ts
23
+ * export const { GET, POST } = nextAuth(sv).handlers;
24
+ *
25
+ * `next` is an optional peer dependency of the SDK, and this is the only entry that uses it.
26
+ */
27
+ declare function nextAuth(sv: Sovereign, options?: NextAuthOptions): {
28
+ handlers: {
29
+ GET: RouteHandler;
30
+ POST: RouteHandler;
31
+ };
32
+ getSession: () => Promise<Session | null>;
33
+ requireSession: (returnTo?: string) => Promise<Session>;
34
+ };
35
+
36
+ export { nextAuth };
37
+ export type { NextAuthOptions };
package/dist/next.js ADDED
@@ -0,0 +1,68 @@
1
+ import {
2
+ SignInError
3
+ } from "./chunk-NYL4LLXF.js";
4
+
5
+ // src/next/index.ts
6
+ import { cookies } from "next/headers";
7
+ import { redirect } from "next/navigation";
8
+ function nextAuth(sv, options = {}) {
9
+ const base = (options.basePath ?? "/auth").replace(/\/+$/, "");
10
+ const errorPath = options.errorPath ?? "/?error=signin";
11
+ const go = (url, setCookies, status = 303) => {
12
+ const headers = new Headers({ location: url, "cache-control": "no-store" });
13
+ for (const cookie of setCookies) headers.append("set-cookie", cookie);
14
+ return new Response(null, { status, headers });
15
+ };
16
+ const actionOf = (request) => {
17
+ const path = new URL(request.url).pathname;
18
+ return path.startsWith(`${base}/`) ? path.slice(base.length + 1).replace(/\/+$/, "") : "";
19
+ };
20
+ const GET = async (request) => {
21
+ switch (actionOf(request)) {
22
+ case "login": {
23
+ const returnTo = new URL(request.url).searchParams.get("returnTo") ?? void 0;
24
+ const step = await sv.auth.beginSignIn(returnTo ? { returnTo } : {});
25
+ return go(step.url, step.setCookies, 302);
26
+ }
27
+ case "callback": {
28
+ try {
29
+ const done = await sv.auth.completeSignIn(request);
30
+ return go(done.returnTo, done.setCookies);
31
+ } catch (error) {
32
+ if (!(error instanceof SignInError)) throw error;
33
+ return go(errorPath, []);
34
+ }
35
+ }
36
+ default:
37
+ return new Response("Not found", { status: 404 });
38
+ }
39
+ };
40
+ const POST = async (request) => {
41
+ switch (actionOf(request)) {
42
+ case "logout": {
43
+ const step = await sv.auth.signOut(request);
44
+ return go(step.url, step.setCookies);
45
+ }
46
+ case "backchannel-logout":
47
+ return sv.auth.handleBackChannelLogout(request);
48
+ case "account-deleted":
49
+ return options.onAccountDeleted ? sv.auth.handleAccountDeleted(request, options.onAccountDeleted) : new Response("Not found", { status: 404 });
50
+ default:
51
+ return new Response("Not found", { status: 404 });
52
+ }
53
+ };
54
+ const getSession = async () => {
55
+ const store = await cookies();
56
+ const request = new Request(sv.links.profile, { headers: { cookie: store.toString() } });
57
+ return sv.auth.getSession(request);
58
+ };
59
+ const requireSession = async (returnTo = "/") => {
60
+ const session = await getSession();
61
+ if (session) return session;
62
+ return redirect(`${base}/login?returnTo=${encodeURIComponent(returnTo)}`);
63
+ };
64
+ return { handlers: { GET, POST }, getSession, requireSession };
65
+ }
66
+ export {
67
+ nextAuth
68
+ };
@@ -0,0 +1,44 @@
1
+ /**
2
+ * What the app keeps for the SDK's sign-in (sdk §9.1). The app owns its data, so the SDK ships no store outside `/testing`; a
3
+ * database row or a cache entry with an expiry is enough.
4
+ */
5
+ type StoredRecord = {
6
+ kind: 'session' | 'pending' | 'seen';
7
+ /** Whose session it is, so a logout notice naming only the person can end every one. */
8
+ sub?: string;
9
+ /** The core session it belongs to, so a logout notice can end just it. */
10
+ sid?: string;
11
+ /** What the SDK keeps; the store keeps it as it is. */
12
+ data: Record<string, unknown>;
13
+ };
14
+ interface SessionStore {
15
+ /** The record, or nothing when there is none or it has expired. A store MUST NOT return a record after its `expiresAt`. */
16
+ get(id: string): Promise<StoredRecord | undefined> | StoredRecord | undefined;
17
+ set(id: string, record: StoredRecord, expiresAt: number): Promise<void> | void;
18
+ delete(id: string): Promise<void> | void;
19
+ /** Removes every session of a person (`sub`), or the ones of one core session (`sid`). */
20
+ deleteMatching(match: {
21
+ sub?: string;
22
+ sid?: string;
23
+ }): Promise<void> | void;
24
+ }
25
+ /** A signed-in person, as the app sees them (sdk §9). The claims are those of the scopes granted. */
26
+ type Session = {
27
+ /** Who the person is. It is stable: identify a person by it and by nothing else. */
28
+ readonly sub: string;
29
+ /** The core session this one belongs to, when core gave one. */
30
+ readonly sid: string | undefined;
31
+ /** What the ID token said: `name`, `email`, `picture`, and so on, for the scopes granted. */
32
+ readonly claims: Readonly<Record<string, unknown>>;
33
+ /** The scopes granted. */
34
+ readonly scopes: readonly string[];
35
+ /** The roles core gave a trusted app, or none. */
36
+ readonly roles: readonly string[];
37
+ /** When this session ends, in milliseconds. */
38
+ readonly expiresAt: number;
39
+ };
40
+ /** True when core said the person has this role. An app that was not given `roles` has none (sdk §9). */
41
+ declare function hasRole(session: Pick<Session, 'roles'>, role: string): boolean;
42
+
43
+ export { hasRole as h };
44
+ export type { Session as S, SessionStore as a, StoredRecord as b };
@@ -0,0 +1,517 @@
1
+ import { a as SessionStore, S as Session } from './session-DJl2XYMc.js';
2
+
3
+ /**
4
+ * How it looks. It changes nothing else.
5
+ */
6
+ type NotificationCategory = 'info' | 'success' | 'warning' | 'error';
7
+ type Notification = {
8
+ id: string;
9
+ title: string;
10
+ body: string | null;
11
+ url: string | null;
12
+ category: NotificationCategory;
13
+ createdAt: string;
14
+ readAt: string | null;
15
+ expiresAt: string;
16
+ };
17
+ type NotificationCount = {
18
+ count: number;
19
+ };
20
+ type DirectoryUser = {
21
+ /**
22
+ * The person's stable identifier, the same `sub` an ID token carries. Never reused.
23
+ */
24
+ sub: string;
25
+ name: string;
26
+ /**
27
+ * The address of their picture, or null when they have none.
28
+ */
29
+ picture: string | null;
30
+ };
31
+ type DirectoryUserStatus = {
32
+ sub: string;
33
+ name: string;
34
+ picture: string | null;
35
+ /**
36
+ * False for a deactivated account, which cannot sign in.
37
+ */
38
+ active: boolean;
39
+ };
40
+ type InstanceBranding = {
41
+ /**
42
+ * The instance's name.
43
+ */
44
+ name: string;
45
+ /**
46
+ * The instance's own address, which is also its issuer.
47
+ */
48
+ url: string;
49
+ logo: {
50
+ /**
51
+ * A logo for a light page. When only one logo is set it is given for both.
52
+ */
53
+ light: string | null;
54
+ /**
55
+ * A logo for a dark page.
56
+ */
57
+ dark: string | null;
58
+ };
59
+ favicon: string | null;
60
+ /**
61
+ * What this core serves, so an app can tell a capability it lacks from a failure. A core that does not
62
+ * publish this field serves the Directory and the instance document, and nothing else.
63
+ */
64
+ api: {
65
+ /**
66
+ * The API versions served, such as `v1`.
67
+ */
68
+ versions: string[];
69
+ /**
70
+ * The groups of calls served, such as `directory`. A capability is listed when every call of it is
71
+ * served.
72
+ */
73
+ capabilities: ('directory' | 'notifications' | 'email' | 'activity')[];
74
+ };
75
+ };
76
+
77
+ /** What each operation takes and answers with. */
78
+ interface Operations {
79
+ /**
80
+ * Dismiss all of this app's notifications
81
+ */
82
+ dismissAllNotifications: {
83
+ request: undefined;
84
+ response: NotificationCount;
85
+ };
86
+ /**
87
+ * Dismiss one notification
88
+ */
89
+ dismissNotification: {
90
+ request: {
91
+ params: {
92
+ id: string;
93
+ };
94
+ };
95
+ response: Notification;
96
+ };
97
+ /**
98
+ * Find one person by their whole email address
99
+ */
100
+ findDirectoryUserByEmail: {
101
+ request: {
102
+ query: {
103
+ email: string;
104
+ };
105
+ };
106
+ response: DirectoryUser;
107
+ };
108
+ /**
109
+ * The instance's name, logos and favicon
110
+ */
111
+ getInstanceBranding: {
112
+ request: undefined;
113
+ response: InstanceBranding;
114
+ };
115
+ /**
116
+ * The notifications this app sent a person
117
+ */
118
+ listNotifications: {
119
+ request: {
120
+ query?: {
121
+ unread?: boolean;
122
+ limit?: number;
123
+ };
124
+ };
125
+ response: {
126
+ notifications: Notification[];
127
+ };
128
+ };
129
+ /**
130
+ * Record something about a person
131
+ */
132
+ logActivity: {
133
+ request: {
134
+ body: {
135
+ /**
136
+ * The person's `sub`.
137
+ */
138
+ to: string;
139
+ summary: string;
140
+ link?: string;
141
+ /**
142
+ * When it happened. Now when not given.
143
+ */
144
+ at?: string;
145
+ key?: string;
146
+ };
147
+ };
148
+ response: {
149
+ id: string;
150
+ };
151
+ };
152
+ /**
153
+ * Mark all of this app's notifications read
154
+ */
155
+ markAllNotificationsRead: {
156
+ request: undefined;
157
+ response: NotificationCount;
158
+ };
159
+ /**
160
+ * Mark one notification read
161
+ */
162
+ markNotificationRead: {
163
+ request: {
164
+ params: {
165
+ id: string;
166
+ };
167
+ };
168
+ response: Notification;
169
+ };
170
+ /**
171
+ * Look people up by their `sub`
172
+ */
173
+ resolveDirectoryUsers: {
174
+ request: {
175
+ body: {
176
+ /**
177
+ * The `sub` of each person. 1 to 100, each at most 255 characters.
178
+ */
179
+ subs: string[];
180
+ };
181
+ };
182
+ response: {
183
+ users: DirectoryUserStatus[];
184
+ };
185
+ };
186
+ /**
187
+ * Search for people by name
188
+ */
189
+ searchDirectoryUsers: {
190
+ request: {
191
+ query: {
192
+ q: string;
193
+ limit?: number;
194
+ };
195
+ };
196
+ response: {
197
+ users: DirectoryUser[];
198
+ };
199
+ };
200
+ /**
201
+ * Email a person
202
+ */
203
+ sendEmail: {
204
+ request: {
205
+ body: {
206
+ /**
207
+ * The person's `sub`.
208
+ */
209
+ to: string;
210
+ subject: string;
211
+ /**
212
+ * Plain text. Line breaks are kept; nothing is read as markup.
213
+ */
214
+ body: string;
215
+ /**
216
+ * One link, `https`, on the origin of one of the app's registered addresses.
217
+ */
218
+ link?: string;
219
+ /**
220
+ * A send with the same key for the same person within a day is the same send.
221
+ */
222
+ key?: string;
223
+ };
224
+ };
225
+ response: {
226
+ id: string;
227
+ };
228
+ };
229
+ /**
230
+ * Tell a person something
231
+ */
232
+ sendNotification: {
233
+ request: {
234
+ body: {
235
+ /**
236
+ * The person's `sub`.
237
+ */
238
+ to: string;
239
+ title: string;
240
+ body?: string;
241
+ /**
242
+ * Where choosing it goes. `https`, on the origin of one of the app's registered addresses.
243
+ */
244
+ url?: string;
245
+ category?: NotificationCategory;
246
+ /**
247
+ * At most 90 days on. 30 days on when not given.
248
+ */
249
+ expiresAt?: string;
250
+ key?: string;
251
+ };
252
+ };
253
+ response: {
254
+ id: string;
255
+ };
256
+ };
257
+ }
258
+
259
+ /** What the SDK tells an app's logger about a request: the operation, the status and how long it took. Never a token or a person. */
260
+ type LogEvent = {
261
+ operation: string;
262
+ /** The HTTP status, or `error` when there was none. */
263
+ status: number | 'error';
264
+ durationMs: number;
265
+ };
266
+ type Logger = (event: LogEvent) => void;
267
+ /** The clock, sleep and chance the transport uses, so a test controls all three. */
268
+ type Environment = {
269
+ now: () => number;
270
+ sleep: (ms: number) => Promise<void>;
271
+ random: () => number;
272
+ };
273
+ type TransportOptions = {
274
+ fetch?: typeof fetch;
275
+ timeoutMs?: number;
276
+ logger?: Logger;
277
+ environment?: Environment;
278
+ };
279
+ /** What a request is, to the transport: its name for the logger, whether it may be sent again, and when its call must be over. */
280
+ type Meta = {
281
+ operation: string;
282
+ safeToRepeat: boolean;
283
+ deadline: number;
284
+ };
285
+ /** The deadline of one call: everything it does, a refresh and a version check included, must fit in the overall limit. */
286
+ type Deadline = number;
287
+ /**
288
+ * Sends requests to core (sdk §6): every request has a timeout; a failure to get a usable answer (a network failure, a
289
+ * timeout or a 5xx) is a `CoreUnavailableError`, after at most two more tries when the request is safe to repeat, all
290
+ * inside the call's deadline; a 429 is tried once more after `Retry-After` only if that fits. Nothing else is repeated.
291
+ */
292
+ declare class Transport {
293
+ #private;
294
+ constructor(options?: TransportOptions);
295
+ get environment(): Environment;
296
+ get timeoutMs(): number;
297
+ /** A deadline for a call that starts now. */
298
+ deadline(): Deadline;
299
+ /** `fetch` for one request of an operation, retrying as the specification says. */
300
+ send(input: RequestInfo | URL, init: RequestInit, meta: Meta): Promise<Response>;
301
+ }
302
+
303
+ /**
304
+ * What an app gives the SDK to keep a person's refresh token (sdk §5.1). The SDK ships none, since the app owns its data.
305
+ *
306
+ * Core rotates a refresh token on every use and treats one used twice as stolen, so the store must not lose a rotation.
307
+ */
308
+ interface TokenStore {
309
+ /** The person's refresh token, or nothing when they have none. */
310
+ read(sub: string): Promise<string | undefined> | string | undefined;
311
+ /**
312
+ * Stores `next` in place of `previous`, atomically: it MUST NOT store when the stored token is no longer `previous`, as
313
+ * with a database's conditional update. Resolves `true` when it stored `next`, and `false` (or rejects) when it did not.
314
+ * `previous` is `undefined` for a person with no token yet.
315
+ */
316
+ write(sub: string, next: string, previous: string | undefined): Promise<boolean> | boolean;
317
+ /**
318
+ * Runs `work` while no other process holds the lock for this person. An app that runs more than one process MUST give
319
+ * one: two processes sending the same refresh token are indistinguishable from theft (sdk §5.1).
320
+ */
321
+ lock?<T>(sub: string, work: () => Promise<T>): Promise<T>;
322
+ /**
323
+ * Forgets the person's refresh token. Optional: the SDK calls it when core says the person deleted their account (sdk §9.1), and
324
+ * an app whose store has no such call deletes the token in its own handler.
325
+ */
326
+ delete?(sub: string): Promise<void> | void;
327
+ }
328
+
329
+ type AuthOptions = {
330
+ appUrl: string;
331
+ coreUrl: string;
332
+ clientId: string;
333
+ clientSecret: string;
334
+ transport: Transport;
335
+ sessionStore: SessionStore;
336
+ /** Where a refresh token is kept, so the app can call as the person it signed in (sdk §5.1). */
337
+ tokenStore: TokenStore | undefined;
338
+ /** What the app asks to see. `offline_access` is what lets it call core as the person later. */
339
+ scopes: readonly string[];
340
+ /** The app's callback path. */
341
+ redirectPath: string;
342
+ /** Where core sends the person after signing out; registered with core. */
343
+ postLogoutPath: string;
344
+ /** How long a session lasts at most, in seconds. */
345
+ sessionSeconds: number | undefined;
346
+ };
347
+ /** What to do next after a step of signing in or out: where to send the person, and the cookies to set. */
348
+ type Step = {
349
+ url: string;
350
+ setCookies: string[];
351
+ };
352
+ /**
353
+ * Signing people in through core with `openid-client`, and ending sessions (sdk §9). Framework-neutral: it takes a web
354
+ * `Request` and returns data and `Set-Cookie` values. It keeps nothing itself: the state of a sign-in and the sessions are
355
+ * records in the app's store.
356
+ */
357
+ declare class Auth {
358
+ #private;
359
+ constructor(options: AuthOptions);
360
+ /** The app's own callback address, which must be registered with core. */
361
+ get redirectUri(): string;
362
+ /** The address core sends the person to after signing out, which must be registered with core. */
363
+ get postLogoutRedirectUri(): string;
364
+ /** Starts a sign-in: the address to send the person to, and the cookie that names what to remember. */
365
+ beginSignIn(options?: {
366
+ returnTo?: string;
367
+ }): Promise<Step>;
368
+ /** Finishes a sign-in at the callback: checks what came back, and makes the session. */
369
+ completeSignIn(request: Request): Promise<{
370
+ session: Session;
371
+ returnTo: string;
372
+ setCookies: string[];
373
+ }>;
374
+ /** The current session, or nothing. It makes no call to core: the session is the app's own. */
375
+ getSession(request: Request): Promise<Session | null>;
376
+ /** The current session, or `NotAuthenticatedError`. */
377
+ requireSession(request: Request): Promise<Session>;
378
+ /**
379
+ * Ends the app's session and clears its cookie, and gives the address of core's sign-out, which ends the person's core
380
+ * session too. It ends the app's session even if the person never returns from core.
381
+ */
382
+ signOut(request: Request): Promise<Step>;
383
+ /** Verifies a logout token from core and ends the sessions it names (sdk §9.1). */
384
+ handleBackChannelLogout(request: Request): Promise<Response>;
385
+ /**
386
+ * Handles core's account-deleted notice (sdk §9.1, core-app §4.5): the person deleted their account, and the app must delete what it
387
+ * holds of them. The token is refused, `400` and `invalid_request`, by the rules of a logout token for its own type and event. For a
388
+ * good one the SDK ends the person's sessions and forgets their stored refresh token (when the token store can), runs `handler`, and
389
+ * answers `200` only after it has resolved. When the handler throws it answers `500` and says nothing of why, and core sends the
390
+ * notice again, so the handler must be safe to run twice.
391
+ */
392
+ handleAccountDeleted(request: Request, handler: (person: {
393
+ readonly sub: string;
394
+ }) => Promise<void> | void): Promise<Response>;
395
+ }
396
+
397
+ /** How an app names its settings: by the prefix `spec/env.md` defines, or one by one (an external app). */
398
+ type SettingsSource = {
399
+ /** The app's prefix: `NOTES` reads `NOTES_URL`, `NOTES_CORE_URL`, `NOTES_CORE_CLIENT_ID` and `NOTES_CORE_CLIENT_SECRET`. */
400
+ prefix: string;
401
+ /** The environment to read. Defaults to `process.env`. */
402
+ env?: Record<string, string | undefined>;
403
+ /** Reads a `*_FILE` file. Defaults to the file system; a test passes its own. */
404
+ readFile?: (path: string) => string;
405
+ } | {
406
+ appUrl: string;
407
+ coreUrl: string;
408
+ clientId: string;
409
+ clientSecret: string;
410
+ };
411
+
412
+ /** A person an app calls core as: what `getSession` returns has a `sub`, and so does `{ sub }`. */
413
+ type Person = {
414
+ readonly sub: string;
415
+ };
416
+ type Name = keyof Operations;
417
+ type Answer<K extends Name> = Operations[K]['response'];
418
+ type Request$1<K extends Name> = Operations[K]['request'];
419
+ /** The Directory (sdk §8): finding people on the instance. Needs the `directory` scope. */
420
+ interface DirectoryApi {
421
+ searchUsers(request: NonNullable<Request$1<'searchDirectoryUsers'>>['query']): Promise<Answer<'searchDirectoryUsers'>>;
422
+ resolveUsers(request: NonNullable<Request$1<'resolveDirectoryUsers'>>['body']): Promise<Answer<'resolveDirectoryUsers'>>;
423
+ findUserByEmail(request: NonNullable<Request$1<'findDirectoryUserByEmail'>>['query']): Promise<Answer<'findDirectoryUserByEmail'>>;
424
+ }
425
+ /** A person's notifications from this app (sdk §8, core-app §4.1): the ones the app sent them, never another app's. */
426
+ interface NotificationsApi {
427
+ list(request?: NonNullable<Request$1<'listNotifications'>>['query']): Promise<Answer<'listNotifications'>>;
428
+ markRead(id: string): Promise<Answer<'markNotificationRead'>>;
429
+ markAllRead(): Promise<Answer<'markAllNotificationsRead'>>;
430
+ dismiss(id: string): Promise<Answer<'dismissNotification'>>;
431
+ dismissAll(): Promise<Answer<'dismissAllNotifications'>>;
432
+ }
433
+ /** What an app can do as a person. */
434
+ interface PersonClient {
435
+ readonly directory: DirectoryApi;
436
+ readonly notifications: NotificationsApi;
437
+ }
438
+ /** What an app can do as itself, with no person (sdk §4): tell a person something. */
439
+ interface AppClient {
440
+ readonly notifications: {
441
+ /** Sends a notification. Accepted whether or not the person receives it: core says nothing of them. */
442
+ send(message: NonNullable<Request$1<'sendNotification'>>['body']): Promise<Answer<'sendNotification'>>;
443
+ };
444
+ readonly email: EmailApi;
445
+ readonly activity: ActivityApi;
446
+ }
447
+ /** What an app can say to a person by email, as itself (sdk §8, core-app §4.2). */
448
+ interface EmailApi {
449
+ /**
450
+ * Emails a person named by `sub`, in the instance's own template, without being given their address. Accepted whether or not
451
+ * the person receives it: core says nothing of them or their address, and never reports delivery. An instance without mail does
452
+ * not list `email`, and this raises `NotSupportedError` without sending.
453
+ */
454
+ sendToUser(message: NonNullable<Request$1<'sendEmail'>>['body']): Promise<Answer<'sendEmail'>>;
455
+ }
456
+ /** What an app can note about a person, as itself (sdk §8, core-app §4.3). */
457
+ interface ActivityApi {
458
+ /**
459
+ * Records one line about something the app did for a person named by `sub`, in the person's own activity page in core. Accepted
460
+ * whether or not it is kept: core says nothing of the person. There is no way to read it back, as the app or as the person.
461
+ */
462
+ log(entry: NonNullable<Request$1<'logActivity'>>['body']): Promise<Answer<'logActivity'>>;
463
+ }
464
+ /** What anyone can ask, with no token. */
465
+ interface InstanceApi {
466
+ getBranding(): Promise<InstanceBranding>;
467
+ }
468
+ /** Addresses of core's own pages for a person, which the SDK offers and has no call for (sdk §9). */
469
+ interface Links {
470
+ readonly profile: string;
471
+ readonly security: string;
472
+ readonly preferences: string;
473
+ readonly connectedApps: string;
474
+ readonly deleteAccount: string;
475
+ }
476
+ interface Sovereign {
477
+ /** Calls made as this person, with their token. Needs a token store. */
478
+ as(person: Person): PersonClient;
479
+ /** Calls made as the app itself. It never holds a person's token. */
480
+ readonly app: AppClient;
481
+ /** Public calls. */
482
+ readonly instance: InstanceApi;
483
+ /** Signing people in, their sessions, and signing out (sdk §9). Needs a session store. */
484
+ readonly auth: Auth;
485
+ /** Core's own pages for a person. */
486
+ readonly links: Links;
487
+ }
488
+ type SovereignOptions = SettingsSource & {
489
+ /** Where a person's refresh token is kept (sdk §5.1). Needed to call as a person. */
490
+ tokenStore?: TokenStore;
491
+ /** Where the app's sessions are kept (sdk §9.1). Needed to sign people in. */
492
+ sessionStore?: SessionStore;
493
+ /** What the app asks to see when it signs a person in. `openid profile email offline_access` unless changed. */
494
+ scopes?: string[];
495
+ /** The app's callback path, which must be registered with core. `/auth/callback` unless changed. */
496
+ redirectPath?: string;
497
+ /** Where core returns a person after they sign out, which must be registered with core. `/` unless changed. */
498
+ postLogoutPath?: string;
499
+ /** How long a session lasts at most, in seconds. Thirty days unless changed. */
500
+ sessionSeconds?: number;
501
+ /** Each request's timeout. 5 seconds unless changed. */
502
+ timeoutMs?: number;
503
+ /** Told the operation, status and duration of each request, and nothing else. */
504
+ logger?: Logger;
505
+ /** `fetch`, when the runtime has none or a test supplies its own. */
506
+ fetch?: typeof fetch;
507
+ /** The clock, sleep and chance, for a test. */
508
+ environment?: Environment;
509
+ };
510
+ /**
511
+ * Makes the SDK's client (sdk §2). It reads its settings and makes no network call, so an app starts while core is down.
512
+ * Make one per process.
513
+ */
514
+ declare function createSovereign(options: SovereignOptions): Sovereign;
515
+
516
+ export { Auth as b, createSovereign as i };
517
+ export type { ActivityApi as A, DirectoryApi as D, EmailApi as E, InstanceApi as I, Links as L, NotificationsApi as N, Person as P, SettingsSource as S, TokenStore as T, AppClient as a, LogEvent as c, Logger as d, PersonClient as e, Sovereign as f, SovereignOptions as g, Step as h };