@sovereignfs/sdk 1.25.0 → 2.0.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.
Files changed (55) hide show
  1. package/LICENSE +373 -661
  2. package/README.md +97 -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/README.md ADDED
@@ -0,0 +1,97 @@
1
+ # @sovereignfs/sdk
2
+
3
+ How a JavaScript app's **server** talks to the Sovereign server it signs people in with. It wraps the generated
4
+ [`@sovereignfs/api-client`](../api-client) with what is tedious to do by hand: tokens, failure, and version support. What it
5
+ promises is in [`docs/specs/sdk.md`](../../docs/specs/sdk.md); the contract it never goes beyond is
6
+ [`spec/core-api/openapi.yaml`](../../spec/core-api/openapi.yaml).
7
+
8
+ > Not published yet.
9
+
10
+ ```ts
11
+ import { createSovereign } from '@sovereignfs/sdk';
12
+
13
+ // Reads NOTES_URL, NOTES_CORE_URL, NOTES_CORE_CLIENT_ID and NOTES_CORE_CLIENT_SECRET (or its _FILE form).
14
+ // Makes no network call, so the app starts while core is down.
15
+ export const sv = createSovereign({ prefix: 'NOTES', tokenStore });
16
+
17
+ const { users } = await sv.as(person).directory.searchUsers({ q: 'ada' }); // as a person
18
+ const branding = await sv.instance.getBranding(); // public, so no actor
19
+ ```
20
+
21
+ - **Actors.** Every call names who it is made as: `sv.as(person)`, `sv.app`, or `sv.instance` for public calls. A method exists
22
+ only on an actor that can use it, and the SDK never falls back from one to another.
23
+ - **Tokens.** An app never handles an access token. Access tokens are cached in memory until 30 seconds before they expire. A
24
+ person's refresh token is kept in a **token store** the app gives (`read`, and a conditional `write`); core rotates it on every
25
+ use and takes one used twice for theft, so refreshes run one at a time for each person, and an app that runs more than one
26
+ process gives its store a `lock`.
27
+ - **Failure.** Every call can fail, as one of `CoreApiError`, `CoreUnavailableError`, `SessionEndedError`, `NotSupportedError` or
28
+ `ConfigurationError`. Requests time out (5 seconds), safe ones are retried, and nothing logs a token or a person's details.
29
+ - **Version support.** The SDK reads the instance document and raises `NotSupportedError` for a capability this core lacks,
30
+ without sending the call.
31
+ - **Signing in.** `sv.auth` signs people in through core with [`openid-client`](https://github.com/panva/openid-client) (PKCE `S256`,
32
+ `state`, `nonce`), keeps the sessions in a **session store** the app gives, and ends them on sign-out and on core's back-channel
33
+ logout notice, which it verifies (signature, issuer, audience, age, event, `jti`, and no `nonce`). The session cookie is host-only
34
+ (`__Host-`, `Secure`, `HttpOnly`) over https. `getSession` makes no call to core.
35
+ - **Server only.** Importing it in a browser throws.
36
+
37
+ ## In Next.js
38
+
39
+ ```ts
40
+ // app/auth/[action]/route.ts: /auth/login, /auth/callback, /auth/logout (POST) and /auth/backchannel-logout (POST)
41
+ import { nextAuth } from '@sovereignfs/sdk/next';
42
+ export const { GET, POST } = nextAuth(sv).handlers;
43
+
44
+ // A Server Component:
45
+ const session = await nextAuth(sv).requireSession('/notes'); // sends a person to sign in, and back
46
+ ```
47
+
48
+ Register `https://notes.example.test/auth/callback` as the app's sign-in address, `https://notes.example.test/` as its address after
49
+ sign-out, and `https://notes.example.test/auth/backchannel-logout` as its back-channel logout address. `next` is an optional peer
50
+ dependency, and nothing outside `/next` needs it.
51
+
52
+ ## Testing an app
53
+
54
+ `@sovereignfs/sdk/testing` is a core that needs no server:
55
+
56
+ ```ts
57
+ import { createFakeCore } from '@sovereignfs/sdk/testing';
58
+
59
+ const core = createFakeCore();
60
+ core.addPerson({ sub: 'u1', name: 'Ada Lovelace' });
61
+ const sv = createSovereign({
62
+ appUrl: 'https://notes.example.test',
63
+ coreUrl: core.url,
64
+ ...core.credentials,
65
+ tokenStore,
66
+ fetch: core.fetch,
67
+ });
68
+ await tokenStore.write('u1', core.signIn('u1'), undefined);
69
+
70
+ core.fail({ kind: 'down' }); // or 'timeout', 'lost-answer', or { kind: 'status', status: 503 }
71
+
72
+ // Signing in: the person agrees at core, and core can send a logout notice, wrong in any way you want to test.
73
+ const back = core.authorize((await sv.auth.beginSignIn()).url, 'u1');
74
+ const notice = await core.logoutToken({ sub: 'u1' }, { wrong: 'expired' });
75
+ ```
76
+
77
+ `memorySessionStore()` is a session store for tests.
78
+
79
+ ## Core versions
80
+
81
+ This release supports core `>=0.46.0` (`sovereign.core` in `package.json`). A core too old to say what it serves is treated as
82
+ serving the Directory and the instance document, and nothing else. The guide for an app's developer is
83
+ [Using the SDK](../../docs/using-the-sdk.md).
84
+
85
+ ## Which exports are stable
86
+
87
+ `createSovereign` and the error types are **stable** and change only in a major version; everything else is **experimental** and may
88
+ change in a minor version ([`src/stability.ts`](src/stability.ts) lists both).
89
+
90
+ ## Versions
91
+
92
+ This SDK starts at `2.0.0`. Versions up to `1.25.0` of `@sovereignfs/sdk` are the legacy SDK, which ran inside the old platform and is
93
+ not an earlier release of this one.
94
+
95
+ ## Licence
96
+
97
+ MPL-2.0, so an app under any licence can use it.
@@ -0,0 +1,2 @@
1
+
2
+ export { };
@@ -0,0 +1,4 @@
1
+ // src/browser.ts
2
+ throw new Error(
3
+ "@sovereignfs/sdk runs on a server and must not be imported in a browser: ask your own server for what the page needs. The instance document at /.well-known/sovereign-instance is public and a page may fetch it directly."
4
+ );
@@ -0,0 +1,65 @@
1
+ // src/errors.ts
2
+ var SovereignError = class extends Error {
3
+ name = "SovereignError";
4
+ };
5
+ var ConfigurationError = class extends SovereignError {
6
+ name = "ConfigurationError";
7
+ };
8
+ var CoreApiError = class extends SovereignError {
9
+ constructor(status, code, message, retryAfter) {
10
+ super(message);
11
+ this.status = status;
12
+ this.code = code;
13
+ this.retryAfter = retryAfter;
14
+ }
15
+ status;
16
+ code;
17
+ retryAfter;
18
+ name = "CoreApiError";
19
+ };
20
+ var CoreUnavailableError = class extends SovereignError {
21
+ name = "CoreUnavailableError";
22
+ /**
23
+ * Whether the request may have reached core. `false` only when it certainly did not (the connection was refused, the
24
+ * name did not resolve, or the call had no time left), so that nothing can have happened to it.
25
+ */
26
+ requestSent;
27
+ /** For a 5xx, its status: core answered, with a failure. */
28
+ status;
29
+ constructor(message, details = {}) {
30
+ super(message, details.cause === void 0 ? void 0 : { cause: details.cause });
31
+ this.requestSent = details.requestSent ?? true;
32
+ this.status = details.status;
33
+ }
34
+ };
35
+ var SessionEndedError = class extends SovereignError {
36
+ name = "SessionEndedError";
37
+ };
38
+ var NotSupportedError = class extends SovereignError {
39
+ constructor(capability) {
40
+ super(`This instance's core does not offer ${capability}.`);
41
+ this.capability = capability;
42
+ }
43
+ capability;
44
+ name = "NotSupportedError";
45
+ };
46
+ var NotAuthenticatedError = class extends SovereignError {
47
+ name = "NotAuthenticatedError";
48
+ constructor() {
49
+ super("There is no signed-in person.");
50
+ }
51
+ };
52
+ var SignInError = class extends SovereignError {
53
+ name = "SignInError";
54
+ };
55
+
56
+ export {
57
+ SovereignError,
58
+ ConfigurationError,
59
+ CoreApiError,
60
+ CoreUnavailableError,
61
+ SessionEndedError,
62
+ NotSupportedError,
63
+ NotAuthenticatedError,
64
+ SignInError
65
+ };